summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml94
-rw-r--r--.gitignore4
-rw-r--r--Documentation/Makefile4
-rw-r--r--Documentation/MyFirstContribution.txt4
-rw-r--r--Documentation/RelNotes/2.29.0.txt442
-rw-r--r--Documentation/blame-options.txt6
-rwxr-xr-xDocumentation/cmd-list.perl27
-rw-r--r--Documentation/config.txt6
-rw-r--r--Documentation/config/commitgraph.txt8
-rw-r--r--Documentation/config/core.txt2
-rw-r--r--Documentation/config/extensions.txt8
-rw-r--r--Documentation/config/feature.txt4
-rw-r--r--Documentation/config/fetch.txt5
-rw-r--r--Documentation/config/fmt-merge-msg.txt12
-rw-r--r--Documentation/config/maintenance.txt16
-rw-r--r--Documentation/config/mergetool.txt10
-rw-r--r--Documentation/config/protocol.txt3
-rw-r--r--Documentation/config/receive.txt22
-rw-r--r--Documentation/config/sendemail.txt5
-rw-r--r--Documentation/config/uploadpack.txt18
-rw-r--r--Documentation/diff-generate-patch.txt3
-rw-r--r--Documentation/diff-options.txt14
-rw-r--r--Documentation/fetch-options.txt17
-rw-r--r--Documentation/git-apply.txt20
-rw-r--r--Documentation/git-bisect.txt15
-rw-r--r--Documentation/git-branch.txt10
-rw-r--r--Documentation/git-bundle.txt9
-rw-r--r--Documentation/git-checkout.txt1
-rw-r--r--Documentation/git-clone.txt6
-rw-r--r--Documentation/git-commit-graph.txt12
-rw-r--r--Documentation/git-fetch.txt4
-rw-r--r--Documentation/git-for-each-ref.txt52
-rw-r--r--Documentation/git-help.txt8
-rw-r--r--Documentation/git-imap-send.txt34
-rw-r--r--Documentation/git-index-pack.txt6
-rw-r--r--Documentation/git-init.txt2
-rw-r--r--Documentation/git-log.txt54
-rw-r--r--Documentation/git-maintenance.txt79
-rw-r--r--Documentation/git-multi-pack-index.txt11
-rw-r--r--Documentation/git-notes.txt2
-rw-r--r--Documentation/git-pack-objects.txt11
-rw-r--r--Documentation/git-rebase.txt34
-rw-r--r--Documentation/git-rev-list.txt40
-rw-r--r--Documentation/git-show-index.txt2
-rw-r--r--Documentation/git-tag.txt11
-rw-r--r--Documentation/git-update-ref.txt13
-rw-r--r--Documentation/git-worktree.txt162
-rw-r--r--Documentation/git.txt25
-rw-r--r--Documentation/gitcredentials.txt2
-rw-r--r--Documentation/gitfaq.txt86
-rw-r--r--Documentation/githooks.txt66
-rw-r--r--Documentation/object-format-disclaimer.txt6
-rw-r--r--Documentation/pretty-options.txt4
-rw-r--r--Documentation/ref-reachability-filters.txt7
-rw-r--r--Documentation/rev-list-description.txt61
-rw-r--r--Documentation/rev-list-options.txt52
-rw-r--r--Documentation/revisions.txt3
-rw-r--r--Documentation/technical/api-parse-options.txt4
-rw-r--r--Documentation/technical/bundle-format.txt30
-rw-r--r--Documentation/technical/commit-graph-format.txt15
-rw-r--r--Documentation/technical/commit-graph.txt6
-rw-r--r--Documentation/technical/hash-function-transition.txt2
-rw-r--r--Documentation/technical/http-protocol.txt5
-rw-r--r--Documentation/technical/index-format.txt34
-rw-r--r--Documentation/technical/pack-format.txt43
-rw-r--r--Documentation/technical/pack-protocol.txt41
-rw-r--r--Documentation/technical/partial-clone.txt13
-rw-r--r--Documentation/technical/protocol-capabilities.txt29
-rw-r--r--Documentation/technical/shallow.txt2
-rw-r--r--Makefile188
l---------RelNotes2
-rw-r--r--add-interactive.c28
-rw-r--r--add-patch.c245
-rw-r--r--apply.c31
-rw-r--r--archive.c4
-rw-r--r--argv-array.c109
-rw-r--r--bisect.c114
-rw-r--r--bisect.h9
-rw-r--r--blame.c13
-rw-r--r--bloom.c69
-rw-r--r--bloom.h29
-rw-r--r--branch.c2
-rw-r--r--builtin.h6
-rw-r--r--builtin/add.c34
-rw-r--r--builtin/am.c112
-rw-r--r--builtin/annotate.c10
-rw-r--r--builtin/bisect--helper.c330
-rw-r--r--builtin/blame.c29
-rw-r--r--builtin/branch.c6
-rw-r--r--builtin/bugreport.c (renamed from bugreport.c)14
-rw-r--r--builtin/bundle.c19
-rw-r--r--builtin/check-ignore.c4
-rw-r--r--builtin/checkout.c28
-rw-r--r--builtin/clean.c34
-rw-r--r--builtin/clone.c59
-rw-r--r--builtin/commit-graph.c68
-rw-r--r--builtin/commit.c40
-rw-r--r--builtin/config.c29
-rw-r--r--builtin/credential-cache--daemon.c (renamed from credential-cache--daemon.c)29
-rw-r--r--builtin/credential-cache.c (renamed from credential-cache.c)37
-rw-r--r--builtin/credential-store.c (renamed from credential-store.c)6
-rw-r--r--builtin/describe.c44
-rw-r--r--builtin/difftool.c30
-rw-r--r--builtin/fast-export.c10
-rw-r--r--builtin/fast-import.c (renamed from fast-import.c)27
-rw-r--r--builtin/fetch-pack.c4
-rw-r--r--builtin/fetch.c153
-rw-r--r--builtin/for-each-ref.c2
-rw-r--r--builtin/fsck.c2
-rw-r--r--builtin/gc.c415
-rw-r--r--builtin/grep.c9
-rw-r--r--builtin/help.c2
-rw-r--r--builtin/index-pack.c475
-rw-r--r--builtin/init-db.c42
-rw-r--r--builtin/log.c43
-rw-r--r--builtin/ls-files.c4
-rw-r--r--builtin/ls-remote.c14
-rw-r--r--builtin/merge.c10
-rw-r--r--builtin/mv.c7
-rw-r--r--builtin/name-rev.c2
-rw-r--r--builtin/pack-objects.c72
-rw-r--r--builtin/pull.c163
-rw-r--r--builtin/push.c54
-rw-r--r--builtin/range-diff.c4
-rw-r--r--builtin/rebase.c141
-rw-r--r--builtin/receive-pack.c598
-rw-r--r--builtin/remote-ext.c4
-rw-r--r--builtin/remote.c29
-rw-r--r--builtin/repack.c84
-rw-r--r--builtin/replace.c18
-rw-r--r--builtin/reset.c2
-rw-r--r--builtin/rev-list.c9
-rw-r--r--builtin/rev-parse.c2
-rw-r--r--builtin/send-pack.c19
-rw-r--r--builtin/show-branch.c18
-rw-r--r--builtin/stash.c171
-rw-r--r--builtin/submodule--helper.c591
-rw-r--r--builtin/tag.c8
-rw-r--r--builtin/update-ref.c2
-rw-r--r--builtin/upload-archive.c12
-rw-r--r--builtin/verify-pack.c23
-rw-r--r--builtin/worktree.c100
-rw-r--r--bundle.c107
-rw-r--r--bundle.h6
-rw-r--r--cache.h35
-rw-r--r--checkout.c3
-rwxr-xr-xci/lib.sh5
-rwxr-xr-xci/run-build-and-tests.sh6
-rw-r--r--column.c14
-rw-r--r--command-list.txt3
-rw-r--r--commit-graph.c400
-rw-r--r--commit-graph.h27
-rw-r--r--commit-slab-decl.h1
-rw-r--r--commit-slab-impl.h13
-rw-r--r--commit-slab.h10
-rw-r--r--commit.c23
-rw-r--r--commit.h8
-rw-r--r--compat/bswap.h24
-rw-r--r--compat/mingw.c7
-rw-r--r--compat/terminal.c18
-rw-r--r--compat/vcbuild/README4
-rwxr-xr-xcompat/vcbuild/scripts/clink.pl2
-rw-r--r--config.c49
-rw-r--r--config.h15
-rw-r--r--config.mak.dev2
-rw-r--r--connect.c73
-rw-r--r--connected.c45
-rw-r--r--contrib/buildsystems/CMakeLists.txt960
-rw-r--r--contrib/buildsystems/Generators/Vcxproj.pm1
-rwxr-xr-xcontrib/buildsystems/engine.pl2
-rw-r--r--contrib/completion/git-completion.bash30
-rw-r--r--contrib/diff-highlight/DiffHighlight.pm2
-rwxr-xr-xcontrib/mw-to-git/git-mw.perl2
-rwxr-xr-xcontrib/mw-to-git/git-remote-mediawiki.perl80
-rw-r--r--contrib/mw-to-git/git-remote-mediawiki.txt2
-rw-r--r--contrib/mw-to-git/t/.gitignore2
-rw-r--r--contrib/mw-to-git/t/README10
-rw-r--r--contrib/mw-to-git/t/install-wiki/.gitignore1
-rw-r--r--contrib/mw-to-git/t/install-wiki/LocalSettings.php129
-rw-r--r--contrib/mw-to-git/t/install-wiki/db_install.php120
-rwxr-xr-xcontrib/mw-to-git/t/t9360-mw-to-git-clone.sh8
-rwxr-xr-xcontrib/mw-to-git/t/t9363-mw-to-git-export-import.sh9
-rwxr-xr-xcontrib/mw-to-git/t/test-gitmw-lib.sh162
-rwxr-xr-xcontrib/mw-to-git/t/test-gitmw.pl22
-rw-r--r--contrib/mw-to-git/t/test.config23
-rw-r--r--contrib/subtree/git-subtree.txt6
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh6
-rw-r--r--contrib/svn-fe/.gitignore4
-rw-r--r--contrib/svn-fe/Makefile105
-rw-r--r--contrib/svn-fe/svn-fe.c18
-rw-r--r--contrib/svn-fe/svn-fe.txt71
-rwxr-xr-xcontrib/svn-fe/svnrdump_sim.py68
-rw-r--r--convert.c5
-rw-r--r--credential.c4
-rw-r--r--daemon.c60
-rw-r--r--diff-lib.c38
-rw-r--r--diff.c97
-rw-r--r--diff.h19
-rw-r--r--dir.c35
-rw-r--r--dir.h21
-rw-r--r--editor.c2
-rw-r--r--entry.c2
-rw-r--r--environment.c16
-rw-r--r--exec-cmd.c18
-rw-r--r--exec-cmd.h4
-rw-r--r--fetch-negotiator.c5
-rw-r--r--fetch-pack.c258
-rw-r--r--fetch-pack.h18
-rw-r--r--fmt-merge-msg.c35
-rw-r--r--fsmonitor.c8
-rw-r--r--fuzz-commit-graph.c5
-rwxr-xr-xgenerate-cmdlist.sh2
-rwxr-xr-xgit-add--interactive.perl91
-rwxr-xr-xgit-bisect.sh76
-rw-r--r--git-compat-util.h20
-rwxr-xr-xgit-cvsexportcommit.perl18
-rw-r--r--git-mergetool--lib.sh35
-rwxr-xr-xgit-p4.py2
-rw-r--r--git-rebase--preserve-merges.sh10
-rwxr-xr-xgit-send-email.perl8
-rwxr-xr-xgit-submodule.sh186
-rw-r--r--git.c38
-rw-r--r--gpg-interface.c22
-rw-r--r--graph.c16
-rw-r--r--grep.c2
-rw-r--r--hashmap.h2
-rw-r--r--help.c6
-rw-r--r--help.h2
-rw-r--r--http-backend.c8
-rw-r--r--http-fetch.c6
-rw-r--r--http-push.c18
-rw-r--r--http.c8
-rw-r--r--ident.c72
-rw-r--r--imap-send.c2
-rw-r--r--interdiff.c28
-rw-r--r--interdiff.h8
-rw-r--r--line-log.c10
-rw-r--r--list-objects-filter-options.c25
-rw-r--r--list-objects-filter-options.h6
-rw-r--r--log-tree.c25
-rw-r--r--ls-refs.c18
-rw-r--r--ls-refs.h4
-rw-r--r--mem-pool.c69
-rw-r--r--mem-pool.h14
-rw-r--r--merge-recursive.c8
-rw-r--r--merge-recursive.h13
-rw-r--r--merge.c21
-rw-r--r--mergetools/bc5
-rw-r--r--mergetools/bc31
-rw-r--r--mergetools/gvimdiff31
-rw-r--r--mergetools/meld85
-rw-r--r--mergetools/nvimdiff (renamed from mergetools/gvimdiff2)0
-rw-r--r--mergetools/vimdiff21
-rw-r--r--mergetools/vimdiff21
-rw-r--r--mergetools/vimdiff31
-rw-r--r--midx.c82
-rw-r--r--negotiator/noop.c44
-rw-r--r--negotiator/noop.h8
-rw-r--r--object-store.h25
-rw-r--r--object.h1
-rw-r--r--oidset.c9
-rw-r--r--oidset.h9
-rw-r--r--pack-bitmap-write.c3
-rw-r--r--pack-write.c17
-rw-r--r--packfile.c12
-rw-r--r--packfile.h1
-rw-r--r--pager.c8
-rw-r--r--parse-options-cb.c8
-rw-r--r--parse-options.c26
-rw-r--r--parse-options.h2
-rw-r--r--path.c2
-rw-r--r--path.h9
-rw-r--r--pathspec.c10
-rw-r--r--perl/Git.pm26
-rw-r--r--po/fr.po2
-rw-r--r--pretty.c20
-rw-r--r--pretty.h3
-rw-r--r--progress.c12
-rw-r--r--promisor-remote.c46
-rw-r--r--protocol.c8
-rw-r--r--quote.c55
-rw-r--r--quote.h19
-rw-r--r--range-diff.c40
-rw-r--r--range-diff.h4
-rw-r--r--read-cache.c35
-rw-r--r--ref-filter.c248
-rw-r--r--ref-filter.h9
-rw-r--r--refs.c248
-rw-r--r--refs.h18
-rw-r--r--refs/debug.c398
-rw-r--r--refs/files-backend.c62
-rw-r--r--refs/packed-backend.c1
-rw-r--r--refs/refs-internal.h29
-rw-r--r--refspec.c28
-rw-r--r--refspec.h33
-rw-r--r--remote-curl.c115
-rw-r--r--remote-testsvn.c341
-rw-r--r--remote.c32
-rw-r--r--remote.h13
-rw-r--r--repo-settings.c5
-rw-r--r--repository.c4
-rw-r--r--repository.h2
-rw-r--r--revision.c166
-rw-r--r--revision.h9
-rw-r--r--run-command.c78
-rw-r--r--run-command.h14
-rw-r--r--send-pack.c177
-rw-r--r--sequencer.c319
-rw-r--r--sequencer.h4
-rw-r--r--serve.c20
-rw-r--r--serve.h4
-rw-r--r--setup.c105
-rw-r--r--sha1-file.c29
-rw-r--r--sha1-name.c45
-rw-r--r--shallow.c14
-rw-r--r--sideband.c2
-rw-r--r--split-index.c6
-rw-r--r--strvec.c109
-rw-r--r--strvec.h (renamed from argv-array.h)58
-rw-r--r--sub-process.c2
-rw-r--r--submodule-config.c8
-rw-r--r--submodule.c259
-rw-r--r--submodule.h8
-rw-r--r--t/README4
-rw-r--r--t/chainlint.sed66
-rw-r--r--t/helper/.gitignore2
-rw-r--r--t/helper/test-bloom.c6
-rw-r--r--t/helper/test-config.c2
-rw-r--r--t/helper/test-line-buffer.c81
-rw-r--r--t/helper/test-proc-receive.c176
-rw-r--r--t/helper/test-read-graph.c3
-rw-r--r--t/helper/test-read-midx.c8
-rw-r--r--t/helper/test-run-command.c52
-rw-r--r--t/helper/test-svn-fe.c52
-rw-r--r--t/helper/test-tool.c1
-rw-r--r--t/helper/test-tool.h1
-rw-r--r--t/helper/test-trace2.c2
-rw-r--r--t/lib-pack.sh11
-rw-r--r--t/lib-submodule-update.sh1
-rw-r--r--t/lib-t6000.sh5
-rw-r--r--t/perf/README9
-rwxr-xr-xt/perf/p1400-update-ref.sh13
-rwxr-xr-xt/perf/p5302-pack-index.sh47
-rwxr-xr-xt/perf/p5303-many-packs.sh4
-rw-r--r--t/perf/perf-lib.sh2
-rwxr-xr-xt/t0000-basic.sh33
-rwxr-xr-xt/t0001-init.sh74
-rwxr-xr-xt/t0040-parse-options.sh2
-rwxr-xr-xt/t0081-line-buffer.sh90
-rwxr-xr-xt/t0095-bloom.sh8
-rwxr-xr-xt/t0410-partial-clone.sh13
-rwxr-xr-xt/t1006-cat-file.sh2
-rwxr-xr-xt/t1050-large.sh1
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh4
-rwxr-xr-xt/t1300-config.sh13
-rwxr-xr-xt/t1302-repo-version.sh3
-rwxr-xr-xt/t1400-update-ref.sh32
-rwxr-xr-xt/t1405-main-ref-store.sh5
-rwxr-xr-xt/t1410-reflog.sh1
-rwxr-xr-xt/t1416-ref-transaction-hooks.sh27
-rwxr-xr-xt/t1450-fsck.sh3
-rwxr-xr-xt/t1500-rev-parse.sh1
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh18
-rwxr-xr-xt/t1507-rev-parse-upstream.sh2
-rwxr-xr-xt/t2025-checkout-no-overlay.sh12
-rwxr-xr-xt/t2072-restore-pathspec-file.sh19
-rwxr-xr-xt/t2406-worktree-repair.sh179
-rwxr-xr-xt/t3000-ls-files-others.sh24
-rwxr-xr-xt/t3200-branch.sh5
-rwxr-xr-xt/t3201-branch-contains.sh74
-rwxr-xr-xt/t3206-range-diff.sh12
-rwxr-xr-xt/t3305-notes-fanout.sh2
-rwxr-xr-xt/t3308-notes-merge.sh1
-rwxr-xr-xt/t3404-rebase-interactive.sh57
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh2
-rwxr-xr-xt/t3432-rebase-fast-forward.sh7
-rwxr-xr-xt/t3436-rebase-more-options.sh180
-rwxr-xr-xt/t3500-cherry.sh23
-rwxr-xr-xt/t3501-revert-cherry-pick.sh4
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh24
-rwxr-xr-xt/t3600-rm.sh1
-rwxr-xr-xt/t3701-add-interactive.sh59
-rwxr-xr-xt/t3800-mktag.sh1
-rwxr-xr-xt/t4002-diff-basic.sh2
-rwxr-xr-xt/t4005-diff-rename-2.sh4
-rwxr-xr-xt/t4010-diff-pathspec.sh4
-rwxr-xr-xt/t4013-diff-various.sh70
-rw-r--r--t/t4013/diff.diff-tree_--root_-p_--abbrev=10_initial29
-rw-r--r--t/t4013/diff.diff-tree_--root_-p_--full-index_--abbrev=10_initial29
-rw-r--r--t/t4013/diff.diff-tree_--root_-p_--full-index_initial29
-rw-r--r--t/t4013/diff.log_--decorate=full_--all2
-rw-r--r--t/t4013/diff.log_--decorate_--all2
-rw-r--r--t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master78
-rw-r--r--t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master78
-rw-r--r--t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master78
-rw-r--r--t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_2
-rw-r--r--t/t4013/diff.log_--patch-with-stat_master2
-rw-r--r--t/t4013/diff.log_--patch-with-stat_master_--_dir_2
-rw-r--r--t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master2
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_--summary_master2
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_master2
-rw-r--r--t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master2
-rw-r--r--t/t4013/diff.log_--root_-p_master2
-rw-r--r--t/t4013/diff.log_--root_master2
-rw-r--r--t/t4013/diff.log_-m_-p_--first-parent_master2
-rw-r--r--t/t4013/diff.log_-m_-p_master4
-rw-r--r--t/t4013/diff.log_-p_--first-parent_master24
-rw-r--r--t/t4013/diff.log_-p_master2
-rw-r--r--t/t4013/diff.log_master2
-rw-r--r--t/t4013/diff.show_--first-parent_master2
-rw-r--r--t/t4013/diff.show_-c_master2
-rw-r--r--t/t4013/diff.show_-m_master4
-rw-r--r--t/t4013/diff.show_master2
-rw-r--r--t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master2
-rw-r--r--t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master2
-rwxr-xr-xt/t4015-diff-whitespace.sh38
-rw-r--r--t/t4018/fortran-block-data5
-rw-r--r--t/t4018/fortran-comment13
-rw-r--r--t/t4018/fortran-comment-keyword14
-rw-r--r--t/t4018/fortran-comment-legacy13
-rw-r--r--t/t4018/fortran-comment-legacy-star13
-rw-r--r--t/t4018/fortran-external-function9
-rw-r--r--t/t4018/fortran-external-subroutine5
-rw-r--r--t/t4018/fortran-module5
-rw-r--r--t/t4018/fortran-module-procedure13
-rw-r--r--t/t4018/fortran-program5
-rwxr-xr-xt/t4027-diff-submodule.sh1
-rwxr-xr-xt/t4034-diff-words.sh2
-rwxr-xr-xt/t4067-diff-partial-clone.sh8
-rwxr-xr-xt/t4104-apply-boundary.sh57
-rwxr-xr-xt/t4134-apply-submodule.sh1
-rwxr-xr-xt/t4140-apply-ita.sh56
-rwxr-xr-xt/t4150-am.sh24
-rwxr-xr-xt/t4200-rerere.sh3
-rwxr-xr-xt/t4202-log.sh82
-rwxr-xr-xt/t4211-line-log.sh1
-rwxr-xr-xt/t4216-log-bloom.sh270
-rwxr-xr-xt/t5300-pack-object.sh39
-rwxr-xr-xt/t5302-pack-index.sh3
-rwxr-xr-xt/t5308-pack-detect-duplicates.sh20
-rwxr-xr-xt/t5313-pack-bounds-checks.sh1
-rwxr-xr-xt/t5318-commit-graph.sh44
-rwxr-xr-xt/t5319-multi-pack-index.sh106
-rwxr-xr-xt/t5324-split-commit-graph.sh21
-rwxr-xr-xt/t5411-proc-receive-hook.sh117
-rw-r--r--t/t5411/common-functions.sh56
-rw-r--r--t/t5411/once-0010-report-status-v1.sh94
-rw-r--r--t/t5411/test-0000-standard-git-push.sh143
-rw-r--r--t/t5411/test-0001-standard-git-push--porcelain.sh147
-rw-r--r--t/t5411/test-0002-pre-receive-declined.sh33
-rw-r--r--t/t5411/test-0003-pre-receive-declined--porcelain.sh34
-rw-r--r--t/t5411/test-0010-proc-receive-settings.sh7
-rw-r--r--t/t5411/test-0011-no-hook-error.sh64
-rw-r--r--t/t5411/test-0012-no-hook-error--porcelain.sh66
-rw-r--r--t/t5411/test-0013-bad-protocol.sh217
-rw-r--r--t/t5411/test-0014-bad-protocol--porcelain.sh160
-rw-r--r--t/t5411/test-0020-report-ng.sh67
-rw-r--r--t/t5411/test-0021-report-ng--porcelain.sh69
-rw-r--r--t/t5411/test-0022-report-unexpect-ref.sh45
-rw-r--r--t/t5411/test-0023-report-unexpect-ref--porcelain.sh46
-rw-r--r--t/t5411/test-0024-report-unknown-ref.sh34
-rw-r--r--t/t5411/test-0025-report-unknown-ref--porcelain.sh35
-rw-r--r--t/t5411/test-0026-push-options.sh79
-rw-r--r--t/t5411/test-0027-push-options--porcelain.sh82
-rw-r--r--t/t5411/test-0030-report-ok.sh35
-rw-r--r--t/t5411/test-0031-report-ok--porcelain.sh36
-rw-r--r--t/t5411/test-0032-report-with-options.sh256
-rw-r--r--t/t5411/test-0033-report-with-options--porcelain.sh265
-rw-r--r--t/t5411/test-0034-report-ft.sh44
-rw-r--r--t/t5411/test-0035-report-ft--porcelain.sh45
-rw-r--r--t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh227
-rw-r--r--t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh172
-rw-r--r--t/t5411/test-0038-report-mixed-refs.sh89
-rw-r--r--t/t5411/test-0039-report-mixed-refs--porcelain.sh91
-rw-r--r--t/t5411/test-0040-process-all-refs.sh113
-rw-r--r--t/t5411/test-0041-process-all-refs--porcelain.sh114
-rw-r--r--t/t5411/test-0050-proc-receive-refs-with-modifiers.sh135
-rwxr-xr-xt/t5504-fetch-receive-strict.sh1
-rwxr-xr-xt/t5510-fetch.sh41
-rwxr-xr-xt/t5514-fetch-multiple.sh2
-rwxr-xr-xt/t5516-fetch-push.sh2
-rwxr-xr-xt/t5521-pull-options.sh7
-rwxr-xr-xt/t5530-upload-pack-error.sh1
-rwxr-xr-xt/t5534-push-signed.sh23
-rwxr-xr-xt/t5539-fetch-http-shallow.sh4
-rwxr-xr-xt/t5541-http-push-smart.sh15
-rwxr-xr-xt/t5553-set-upstream.sh6
-rwxr-xr-xt/t5554-noop-fetch-negotiator.sh22
-rwxr-xr-xt/t5562-http-backend-content-length.sh1
-rwxr-xr-xt/t5601-clone.sh20
-rwxr-xr-xt/t5607-clone-bundle.sh31
-rwxr-xr-xt/t5616-partial-clone.sh107
-rwxr-xr-xt/t5702-protocol-v2.sh56
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh1
-rwxr-xr-xt/t6000-rev-list-misc.sh11
-rwxr-xr-xt/t6002-rev-list-bisect.sh45
-rwxr-xr-xt/t6006-rev-list-format.sh1
-rwxr-xr-xt/t6018-rev-list-glob.sh5
-rwxr-xr-xt/t6030-bisect-porcelain.sh111
-rwxr-xr-xt/t6100-rev-list-in-order.sh4
-rwxr-xr-xt/t6101-rev-parse-parents.sh2
-rwxr-xr-xt/t6200-fmt-merge-msg.sh56
-rwxr-xr-xt/t6300-for-each-ref.sh129
-rwxr-xr-xt/t6301-for-each-ref-errors.sh2
-rwxr-xr-xt/t6302-for-each-ref-filter.sh4
-rwxr-xr-xt/t6400-merge-df.sh (renamed from t/t6020-merge-df.sh)0
-rwxr-xr-xt/t6401-merge-criss-cross.sh (renamed from t/t6021-merge-criss-cross.sh)0
-rwxr-xr-xt/t6402-merge-rename.sh (renamed from t/t6022-merge-rename.sh)0
-rwxr-xr-xt/t6403-merge-file.sh (renamed from t/t6023-merge-file.sh)0
-rwxr-xr-xt/t6404-recursive-merge.sh (renamed from t/t6024-recursive-merge.sh)0
-rwxr-xr-xt/t6405-merge-symlinks.sh (renamed from t/t6025-merge-symlinks.sh)0
-rwxr-xr-xt/t6406-merge-attr.sh (renamed from t/t6026-merge-attr.sh)4
-rwxr-xr-xt/t6407-merge-binary.sh (renamed from t/t6027-merge-binary.sh)0
-rwxr-xr-xt/t6408-merge-up-to-date.sh (renamed from t/t6028-merge-up-to-date.sh)0
-rwxr-xr-xt/t6409-merge-subtree.sh (renamed from t/t6029-merge-subtree.sh)0
-rwxr-xr-xt/t6411-merge-filemode.sh (renamed from t/t6031-merge-filemode.sh)0
-rwxr-xr-xt/t6412-merge-large-rename.sh (renamed from t/t6032-merge-large-rename.sh)0
-rwxr-xr-xt/t6413-merge-crlf.sh (renamed from t/t6033-merge-crlf.sh)0
-rwxr-xr-xt/t6414-merge-rename-nocruft.sh (renamed from t/t6034-merge-rename-nocruft.sh)0
-rwxr-xr-xt/t6415-merge-dir-to-symlink.sh (renamed from t/t6035-merge-dir-to-symlink.sh)0
-rwxr-xr-xt/t6416-recursive-corner-cases.sh (renamed from t/t6036-recursive-corner-cases.sh)4
-rwxr-xr-xt/t6417-merge-ours-theirs.sh (renamed from t/t6037-merge-ours-theirs.sh)0
-rwxr-xr-xt/t6418-merge-text-auto.sh (renamed from t/t6038-merge-text-auto.sh)29
-rwxr-xr-xt/t6419-merge-ignorecase.sh (renamed from t/t6039-merge-ignorecase.sh)0
-rwxr-xr-xt/t6422-merge-rename-corner-cases.sh (renamed from t/t6042-merge-rename-corner-cases.sh)68
-rwxr-xr-xt/t6423-merge-rename-directories.sh (renamed from t/t6043-merge-rename-directories.sh)39
-rwxr-xr-xt/t6424-merge-unrelated-index-changes.sh (renamed from t/t6044-merge-unrelated-index-changes.sh)0
-rwxr-xr-xt/t6425-merge-rename-delete.sh (renamed from t/t6045-merge-rename-delete.sh)3
-rwxr-xr-xt/t6426-merge-skip-unneeded-updates.sh (renamed from t/t6046-merge-skip-unneeded-updates.sh)4
-rwxr-xr-xt/t6427-diff3-conflict-markers.sh (renamed from t/t6047-diff3-conflict-markers.sh)0
-rwxr-xr-xt/t6430-merge-recursive.sh (renamed from t/t3030-merge-recursive.sh)0
-rwxr-xr-xt/t6431-merge-criscross.sh (renamed from t/t3031-merge-criscross.sh)0
-rwxr-xr-xt/t6432-merge-recursive-space-options.sh (renamed from t/t3032-merge-recursive-space-options.sh)0
-rwxr-xr-xt/t6433-merge-toplevel.sh (renamed from t/t3033-merge-toplevel.sh)0
-rwxr-xr-xt/t6434-merge-recursive-rename-options.sh (renamed from t/t3034-merge-recursive-rename-options.sh)0
-rwxr-xr-xt/t6435-merge-sparse.sh (renamed from t/t3035-merge-sparse.sh)0
-rwxr-xr-xt/t6436-merge-overwrite.sh (renamed from t/t7607-merge-overwrite.sh)0
-rwxr-xr-xt/t6437-submodule-merge.sh (renamed from t/t7405-submodule-merge.sh)4
-rwxr-xr-xt/t6438-submodule-directory-file-conflicts.sh (renamed from t/t7613-merge-submodule.sh)0
-rwxr-xr-xt/t6439-merge-co-error-msgs.sh (renamed from t/t7609-merge-co-error-msgs.sh)0
-rwxr-xr-xt/t6500-gc.sh27
-rwxr-xr-xt/t6501-freshen-objects.sh14
-rwxr-xr-xt/t7001-mv.sh19
-rwxr-xr-xt/t7003-filter-branch.sh3
-rwxr-xr-xt/t7004-tag.sh4
-rwxr-xr-xt/t7061-wtstatus-ignore.sh25
-rwxr-xr-xt/t7063-status-untracked-cache.sh150
-rwxr-xr-xt/t7102-reset.sh93
-rwxr-xr-xt/t7107-reset-pathspec-file.sh9
-rwxr-xr-xt/t7201-co.sh6
-rwxr-xr-xt/t7400-submodule-basic.sh26
-rwxr-xr-xt/t7401-submodule-summary.sh155
-rwxr-xr-xt/t7421-submodule-summary-add.sh69
-rwxr-xr-xt/t7506-status-submodule.sh12
-rwxr-xr-xt/t7508-status.sh41
-rwxr-xr-xt/t7518-ident-corner-cases.sh13
-rwxr-xr-xt/t7600-merge.sh20
-rwxr-xr-xt/t7601-merge-pull-config.sh21
-rwxr-xr-xt/t7608-merge-messages.sh10
-rwxr-xr-xt/t7900-maintenance.sh65
-rwxr-xr-xt/t8002-blame.sh29
-rwxr-xr-xt/t8003-blame-corner-cases.sh29
-rwxr-xr-xt/t8011-blame-split-file.sh2
-rwxr-xr-xt/t8013-blame-ignore-revs.sh61
-rwxr-xr-xt/t8014-blame-ignore-fuzzy.sh2
-rwxr-xr-xt/t9001-send-email.sh39
-rwxr-xr-xt/t9010-svn-fe.sh1105
-rwxr-xr-xt/t9011-svn-da.sh248
-rwxr-xr-xt/t9020-remote-svn.sh95
-rwxr-xr-xt/t9100-git-svn-basic.sh11
-rwxr-xr-xt/t9300-fast-import.sh118
-rwxr-xr-xt/t9301-fast-import-notes.sh12
-rwxr-xr-xt/t9350-fast-export.sh14
-rwxr-xr-xt/t9400-git-cvsserver-server.sh2
-rwxr-xr-xt/t9401-git-cvsserver-crlf.sh8
-rwxr-xr-xt/t9402-git-cvsserver-refs.sh2
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh22
-rwxr-xr-xt/t9700/test.pl12
-rwxr-xr-xt/t9832-unshelve.sh5
-rwxr-xr-xt/t9834-git-p4-file-dir-bug.sh2
-rw-r--r--t/test-lib-functions.sh112
-rw-r--r--t/test-lib.sh12
-rwxr-xr-xtemplates/hooks--pre-push.sample18
-rwxr-xr-xtemplates/hooks--update.sample2
-rw-r--r--tmp-objdir.c20
-rw-r--r--trailer.c4
-rw-r--r--transport-helper.c166
-rw-r--r--transport-internal.h4
-rw-r--r--transport.c173
-rw-r--r--transport.h11
-rw-r--r--tree-diff.c35
-rw-r--r--tree-walk.c9
-rw-r--r--unpack-trees.c12
-rw-r--r--unpack-trees.h4
-rw-r--r--upload-pack.c186
-rw-r--r--upload-pack.h4
-rw-r--r--userdiff.c6
-rw-r--r--vcs-svn/LICENSE32
-rw-r--r--vcs-svn/fast_export.c365
-rw-r--r--vcs-svn/line_buffer.c126
-rw-r--r--vcs-svn/line_buffer.txt77
-rw-r--r--vcs-svn/sliding_window.c79
-rw-r--r--vcs-svn/svndiff.c309
-rw-r--r--vcs-svn/svndump.c540
-rw-r--r--worktree.c153
-rw-r--r--worktree.h23
-rw-r--r--wrapper.c7
-rw-r--r--wt-status.c74
608 files changed, 17392 insertions, 9761 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 84a5dcff7a..fcfd138ff1 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -7,34 +7,33 @@ env:
jobs:
ci-config:
- runs-on: ubuntu-latest
- outputs:
- enabled: ${{ steps.check-ref.outputs.enabled }}
- steps:
- - name: try to clone ci-config branch
- continue-on-error: true
- run: |
- git -c protocol.version=2 clone \
- --no-tags \
- --single-branch \
- -b ci-config \
- --depth 1 \
- --no-checkout \
- --filter=blob:none \
- https://github.com/${{ github.repository }} \
- config-repo &&
- cd config-repo &&
- git checkout HEAD -- ci/config
- - id: check-ref
- name: check whether CI is enabled for ref
- run: |
- enabled=yes
- if test -x config-repo/ci/config/allow-ref &&
- ! config-repo/ci/config/allow-ref '${{ github.ref }}'
- then
- enabled=no
- fi
- echo "::set-output name=enabled::$enabled"
+ runs-on: ubuntu-latest
+ outputs:
+ enabled: ${{ steps.check-ref.outputs.enabled }}
+ steps:
+ - name: try to clone ci-config branch
+ run: |
+ git -c protocol.version=2 clone \
+ --no-tags \
+ --single-branch \
+ -b ci-config \
+ --depth 1 \
+ --no-checkout \
+ --filter=blob:none \
+ https://github.com/${{ github.repository }} \
+ config-repo &&
+ cd config-repo &&
+ git checkout HEAD -- ci/config || : ignore
+ - id: check-ref
+ name: check whether CI is enabled for ref
+ run: |
+ enabled=yes
+ if test -x config-repo/ci/config/allow-ref &&
+ ! config-repo/ci/config/allow-ref '${{ github.ref }}'
+ then
+ enabled=no
+ fi
+ echo "::set-output name=enabled::$enabled"
windows-build:
needs: ci-config
@@ -145,13 +144,6 @@ jobs:
## Unzip and remove the artifact
unzip artifacts.zip
rm artifacts.zip
- - name: generate Visual Studio solution
- shell: powershell
- run: |
- & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
- make NDEBUG=1 DEVELOPER=1 vcxproj
- "@
- if (!$?) { exit(1) }
- name: download vcpkg artifacts
shell: powershell
run: |
@@ -163,6 +155,17 @@ jobs:
Remove-Item compat.zip
- name: add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.0
+ - name: copy dlls to root
+ shell: powershell
+ run: |
+ & compat\vcbuild\vcpkg_copy_dlls.bat release
+ if (!$?) { exit(1) }
+ - name: generate Visual Studio solution
+ shell: bash
+ run: |
+ cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows \
+ -DIconv_LIBRARY=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows/lib/libiconv.lib -DIconv_INCLUDE_DIR=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows/include \
+ -DMSGFMT_EXE=`pwd`/git-sdk-64-minimal/mingw64/bin/msgfmt.exe -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON
- name: MSBuild
run: msbuild git.sln -property:Configuration=Release -property:Platform=x64 -maxCpuCount:4 -property:PlatformToolset=v142
- name: bundle artifact tar
@@ -171,8 +174,6 @@ jobs:
MSVC: 1
VCPKG_ROOT: ${{github.workspace}}\compat\vcbuild\vcpkg
run: |
- & 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 2>&1 | grep ^tar)\"
@@ -203,7 +204,7 @@ jobs:
- name: extract build artifacts
shell: bash
run: tar xf artifacts.tar.gz
- - name: test (parallel)
+ - name: test
shell: powershell
env:
MSYSTEM: MINGW64
@@ -214,12 +215,19 @@ jobs:
# Let Git ignore the SDK and the test-cache
printf '%s\n' /git-sdk-64-minimal/ /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 \
- ${{matrix.nr}} 10 t[0-9]*.sh)
+ ci/run-test-slice.sh ${{matrix.nr}} 10
"@
+ - name: ci/print-test-failures.sh
+ if: failure()
+ shell: powershell
+ run: |
+ & .\git-sdk-64-minimal\usr\bin\bash.exe -lc ci/print-test-failures.sh
+ - name: Upload failed tests' directories
+ if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+ uses: actions/upload-artifact@v1
+ with:
+ name: failed-tests-windows
+ path: ${{env.FAILED_TEST_ARTIFACTS}}
regular:
needs: ci-config
if: needs.ci-config.outputs.enabled == 'yes'
diff --git a/.gitignore b/.gitignore
index ee509a2ad2..800b812c6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,6 +90,7 @@
/git-ls-tree
/git-mailinfo
/git-mailsplit
+/git-maintenance
/git-merge
/git-merge-base
/git-merge-index
@@ -134,7 +135,6 @@
/git-remote-fd
/git-remote-ext
/git-remote-testpy
-/git-remote-testsvn
/git-repack
/git-replace
/git-request-pull
@@ -197,6 +197,7 @@
/git.spec
*.exe
*.[aos]
+*.o.json
*.py[co]
.depend/
*.gcda
@@ -218,6 +219,7 @@
/tags
/TAGS
/cscope*
+/compile_commands.json
*.hcc
*.obj
*.lib
diff --git a/Documentation/Makefile b/Documentation/Makefile
index ecd0b340b1..80d1908a44 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -17,6 +17,7 @@ MAN1_TXT += git.txt
MAN1_TXT += gitk.txt
MAN1_TXT += gitweb.txt
+# man5 / man7 guides (note: new guides should also be added to command-list.txt)
MAN5_TXT += gitattributes.txt
MAN5_TXT += githooks.txt
MAN5_TXT += gitignore.txt
@@ -294,6 +295,7 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
cmds-plumbingmanipulators.txt \
cmds-synchingrepositories.txt \
cmds-synchelpers.txt \
+ cmds-guide.txt \
cmds-purehelpers.txt \
cmds-foreignscminterface.txt
@@ -301,7 +303,7 @@ $(cmds_txt): cmd-list.made
cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
$(QUIET_GEN)$(RM) $@ && \
- $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \
+ $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(cmds_txt) $(QUIET_STDERR) && \
date >$@
mergetools_txt = mergetools-diff.txt mergetools-merge.txt
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index d85c9b5143..4f85a089ef 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -319,14 +319,14 @@ function body:
...
git_config(git_default_config, NULL);
- if (git_config_get_string_const("user.name", &cfg_name) > 0)
+ if (git_config_get_string_tmp("user.name", &cfg_name) > 0)
printf(_("No name is found in config\n"));
else
printf(_("Your name: %s\n"), cfg_name);
----
`git_config()` will grab the configuration from config files known to Git and
-apply standard precedence rules. `git_config_get_string_const()` will look up
+apply standard precedence rules. `git_config_get_string_tmp()` will look up
a specific key ("user.name") and give you the value. There are a number of
single-key lookup functions like this one; you can see them all (and more info
about how to use `git_config()`) in `Documentation/technical/api-config.txt`.
diff --git a/Documentation/RelNotes/2.29.0.txt b/Documentation/RelNotes/2.29.0.txt
new file mode 100644
index 0000000000..e430392340
--- /dev/null
+++ b/Documentation/RelNotes/2.29.0.txt
@@ -0,0 +1,442 @@
+Git 2.29 Release Notes
+======================
+
+Updates since v2.28
+-------------------
+
+UI, Workflows & Features
+
+ * "git help log" has been enhanced by sharing more material from the
+ documentation for the underlying "git rev-list" command.
+
+ * "git for-each-ref --format=<>" learned %(contents:size).
+
+ * "git merge" learned to selectively omit " into <branch>" at the end
+ of the title of default merge message with merge.suppressDest
+ configuration.
+
+ * The component to respond to "git fetch" request is made more
+ configurable to selectively allow or reject object filtering
+ specification used for partial cloning.
+
+ * Stop when "sendmail.*" configuration variables are defined, which
+ could be a mistaken attempt to define "sendemail.*" variables.
+
+ * The existing backends for "git mergetool" based on variants of vim
+ have been refactored and then support for "nvim" has been added.
+
+ * "git bisect" learns the "--first-parent" option to find the first
+ breakage along the first-parent chain.
+
+ * "git log --first-parent -p" showed patches only for single-parent
+ commits on the first-parent chain; the "--first-parent" option has
+ been made to imply "-m". Use "--no-diff-merges" to restore the
+ previous behaviour to omit patches for merge commits.
+
+ * The commit labels used to explain each side of conflicted hunks
+ placed by the sequencer machinery have been made more readable by
+ humans.
+
+ * The "--batch-size" option of "git multi-pack-index repack" command
+ is now used to specify that very small packfiles are collected into
+ one until the total size roughly exceeds it.
+
+ * The recent addition of SHA-256 support is marked as experimental in
+ the documentation.
+
+ * "git fetch" learned --no-write-fetch-head option to avoid writing
+ the FETCH_HEAD file.
+
+ * Command line completion (in contrib/) usually omits redundant,
+ deprecated and/or dangerous options from its output; it learned to
+ optionally include all of them.
+
+ * The output from the "diff" family of the commands had abbreviated
+ object names of blobs involved in the patch, but its length was not
+ affected by the --abbrev option. Now it is.
+
+ * "git worktree" gained a "repair" subcommand to help users recover
+ after moving the worktrees or repository manually without telling
+ Git. Also, "git init --separate-git-dir" no longer corrupts
+ administrative data related to linked worktrees.
+
+ * The "--format=" option to the "for-each-ref" command and friends
+ learned a few more tricks, e.g. the ":short" suffix that applies to
+ "objectname" now also can be used for "parent", "tree", etc.
+
+ * "git worktree add" learns that the "-d" is a synonym to "--detach"
+ option to create a new worktree without being on a branch.
+
+ * "format-patch --range-diff=<prev> <origin>..HEAD" has been taught
+ not to ignore <origin> when <prev> is a single version.
+
+ * "add -p" now allows editing paths that were only added in intent.
+
+ * The 'meld' backend of the "git mergetool" learned to give the
+ underlying 'meld' the '--auto-merge' option, which would help
+ reduce the amount of text that requires manual merging.
+
+ * "git for-each-ref" and friends that list refs used to allow only
+ one --merged or --no-merged to filter them; they learned to take
+ combination of both kind of filtering.
+
+ * "git maintenance", a "git gc"'s big brother, has been introduced to
+ take care of more repository maintenance tasks, not limited to the
+ object database cleaning.
+
+ * "git receive-pack" that accepts requests by "git push" learned to
+ outsource most of the ref updates to the new "proc-receive" hook.
+
+ * "git push" that wants to be atomic and wants to send push
+ certificate learned not to prepare and sign the push certificate
+ when it fails the local check (hence due to atomicity it is known
+ that no certificate is needed).
+
+ * "git commit-graph write" learned to limit the number of bloom
+ filters that are computed from scratch with the --max-new-filters
+ option.
+
+ * The transport protocol v2 has become the default again.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The changed-path Bloom filter is improved using ideas from an
+ independent implementation.
+
+ * Updates to the changed-paths bloom filter.
+
+ * The test framework has been updated so that most tests will run
+ with predictable (artificial) timestamps.
+
+ * Preliminary clean-up of the refs API in preparation for adding a
+ new refs backend "reftable".
+
+ * Dev support to limit the use of test_must_fail to only git commands.
+
+ * While packing many objects in a repository with a promissor remote,
+ lazily fetching missing objects from the promissor remote one by
+ one may be inefficient---the code now attempts to fetch all the
+ missing objects in batch (obviously this won't work for a lazy
+ clone that lazily fetches tree objects as you cannot even enumerate
+ what blobs are missing until you learn which trees are missing).
+
+ * The pretend-object mechanism checks if the given object already
+ exists in the object store before deciding to keep the data
+ in-core, but the check would have triggered lazy fetching of such
+ an object from a promissor remote.
+
+ * The argv_array API is useful for not just managing argv but any
+ "vector" (NULL-terminated array) of strings, and has seen adoption
+ to a certain degree. It has been renamed to "strvec" to reduce the
+ barrier to adoption.
+
+ * The final leg of SHA-256 transition plus doc updates. Note that
+ there is no inter-operability between SHA-1 and SHA-256
+ repositories yet.
+
+ * CMake support to build with MSVC for Windows bypassing the Makefile.
+
+ * A new helper function has_object() has been introduced to make it
+ easier to mark object existence checks that do and don't want to
+ trigger lazy fetches, and a few such checks are converted using it.
+
+ * A no-op replacement function implemented as a C preprocessor macro
+ does not perform as good a job as one implemented as a "static
+ inline" function in catching errors in parameters; replace the
+ former with the latter in <git-compat-util.h> header.
+
+ * Test framework update.
+ (merge d572f52a64 es/test-cmp-typocatcher later to maint).
+
+ * Updates to "git merge" tests, in preparation for a new merge
+ strategy backend.
+
+ * midx and commit-graph files now use the byte defined in their file
+ format specification for identifying the hash function used for
+ object names.
+
+ * The FETCH_HEAD is now always read from the filesystem regardless of
+ the ref backend in use, as its format is much richer than the
+ normal refs, and written directly by "git fetch" as a plain file..
+
+ * A handful of places in in-tree code still relied on being able to
+ execute the git subcommands, especially built-ins, in "git-foo"
+ form, which have been corrected.
+
+ * An unused binary has been discarded, and and a bunch of commands
+ have been turned into into built-in.
+
+ * A handful of places in in-tree code still relied on being able to
+ execute the git subcommands, especially built-ins, in "git-foo"
+ form, which have been corrected.
+
+ * When a packfile is removed by "git repack", multi-pack-index gets
+ cleared; the code was taught to do so less aggressively by first
+ checking if the midx actually refers to a pack that no longer
+ exists.
+
+ * Internal API clean-up to handle two options "diff-index" and "log"
+ have, which happen to share the same short form, more sensibly.
+
+ * The "add -i/-p" machinery has been written in C but it is not used
+ by default yet. It is made default to those who are participating
+ in feature.experimental experiment.
+
+ * Allow maintainers to tweak $(TAR) invocations done while making
+ distribution tarballs.
+
+ * "git index-pack" learned to resolve deltified objects with greater
+ parallelism.
+
+ * "diff-highlight" (in contrib/) had a logic to flush its output upon
+ seeing a blank line but the way it detected a blank line was broken.
+
+
+Fixes since v2.28
+-----------------
+
+ * "git clone --separate-git-dir=$elsewhere" used to stomp on the
+ contents of the existing directory $elsewhere, which has been
+ taught to fail when $elsewhere is not an empty directory.
+ (merge dfaa209a79 bw/fail-cloning-into-non-empty later to maint).
+
+ * With the base fix to 2.27 regresion, any new extensions in a v0
+ repository would still be silently honored, which is not quite
+ right. Instead, complain and die loudly.
+ (merge ec91ffca04 jk/reject-newer-extensions-in-v0 later to maint).
+
+ * Fetching from a lazily cloned repository resulted at the server
+ side in attempts to lazy fetch objects that the client side has,
+ many of which will not be available from the third-party anyway.
+ (merge 77aa0941ce jt/avoid-lazy-fetching-upon-have-check later to maint).
+
+ * Fix to an ancient bug caused by an over-eager attempt for
+ optimization.
+ (merge a98f7fb366 rs/add-index-entry-optim-fix later to maint).
+
+ * Pushing a ref whose name contains non-ASCII character with the
+ "--force-with-lease" option did not work over smart HTTP protocol,
+ which has been corrected.
+ (merge cd85b447bf bc/push-cas-cquoted-refname later to maint).
+
+ * "git mv src dst", when src is an unmerged path, errored out
+ correctly but with an incorrect error message to claim that src is
+ not tracked, which has been clarified.
+ (merge 9b906af657 ct/mv-unmerged-path-error later to maint).
+
+ * Fix to a regression introduced during 2.27 cycle.
+ (merge cada7308ad en/fill-directory-exponential later to maint).
+
+ * Command line completion (in contrib/) update.
+ (merge 688b87c81b mp/complete-show-color-moved later to maint).
+
+ * All "mergy" operations that internally use the merge-recursive
+ machinery should honor the merge.renormalize configuration, but
+ many of them didn't.
+
+ * Doc cleanup around "worktree".
+ (merge dc9c144be5 es/worktree-doc-cleanups later to maint).
+
+ * The "git blame --first-parent" option was not documented, but now
+ it is.
+ (merge 11bc12ae1e rp/blame-first-parent-doc later to maint).
+
+ * The logic to find the ref transaction hook script attempted to
+ cache the path to the found hook without realizing that it needed
+ to keep a copied value, as the API it used returned a transitory
+ buffer space. This has been corrected.
+ (merge 09b2aa30c9 ps/ref-transaction-hook later to maint).
+
+ * Recent versions of "git diff-files" shows a diff between the index
+ and the working tree for "intent-to-add" paths as a "new file"
+ patch; "git apply --cached" should be able to take "git diff-files"
+ and should act as an equivalent to "git add" for the path, but the
+ command failed to do so for such a path.
+ (merge 4c025c667e rp/apply-cached-with-i-t-a later to maint).
+
+ * "git diff [<tree-ish>] $path" for a $path that is marked with i-t-a
+ bit was not showing the mode bits from the working tree.
+ (merge cb0dd22b82 rp/ita-diff-modefix later to maint).
+
+ * Ring buffer with size 4 used for bin-hex translation resulted in a
+ wrong object name in the sequencer's todo output, which has been
+ corrected.
+ (merge 5da69c0dac ak/sequencer-fix-find-uniq-abbrev later to maint).
+
+ * When given more than one target line ranges, "git blame -La,b
+ -Lc,d" was over-eager to coalesce groups of original lines and
+ showed incorrect results, which has been corrected.
+ (merge c2ebaa27d6 jk/blame-coalesce-fix later to maint).
+
+ * The regexp to identify the function boundary for FORTRAN programs
+ has been updated.
+ (merge 75c3b6b2e8 pb/userdiff-fortran-update later to maint).
+
+ * A few end-user facing messages have been updated to be
+ hash-algorithm agnostic.
+ (merge 4279000d3e jc/object-names-are-not-sha-1 later to maint).
+
+ * "unlink" emulation on MinGW has been optimized.
+ (merge 680e0b4524 jh/mingw-unlink later to maint).
+
+ * The purpose of "git init --separate-git-dir" is to initialize a
+ new project with the repository separate from the working tree,
+ or, in the case of an existing project, to move the repository
+ (the .git/ directory) out of the working tree. It does not make
+ sense to use --separate-git-dir with a bare repository for which
+ there is no working tree, so disallow its use with bare
+ repositories.
+ (merge ccf236a23a es/init-no-separate-git-dir-in-bare later to maint).
+
+ * "ls-files -o" mishandled the top-level directory of another git
+ working tree that hangs in the current git working tree.
+ (merge ab282aa548 en/dir-nonbare-embedded later to maint).
+
+ * Fix some incorrect UNLEAK() annotations.
+ (merge 3e19816dc0 jk/unleak-fixes later to maint).
+
+ * Use more buffered I/O where we used to call many small write(2)s.
+ (merge a698d67b08 rs/more-buffered-io later to maint).
+
+ * The patch-id computation did not ignore the "incomplete last line"
+ marker like whitespaces.
+ (merge 82a62015a7 rs/patch-id-with-incomplete-line later to maint).
+
+ * Updates into a lazy/partial clone with a submodule did not work
+ well with transfer.fsckobjects set.
+
+ * The parser for "git for-each-ref --format=..." was too loose when
+ parsing the "%(trailers...)" atom, and forgot that "trailers" and
+ "trailers:<modifiers>" are the only two allowed forms, which has
+ been corrected.
+ (merge 2c22e102f8 hv/ref-filter-trailers-atom-parsing-fix later to maint).
+
+ * Long ago, we decided to use 3 threads by default when running the
+ index-pack task in parallel, which has been adjusted a bit upwards.
+ (merge fbff95b67f jk/index-pack-w-more-threads later to maint).
+
+ * "git restore/checkout --no-overlay" with wildcarded pathspec
+ mistakenly removed matching paths in subdirectories, which has been
+ corrected.
+ (merge bfda204ade rs/checkout-no-overlay-pathspec-fix later to maint).
+
+ * The description of --cached/--index options in "git apply --help"
+ has been updated.
+ (merge d064702be3 rp/apply-cached-doc later to maint).
+
+ * Feeding "$ZERO_OID" to "git log --ignore-missing --stdin", and
+ running "git log --ignore-missing $ZERO_OID" fell back to start
+ digging from HEAD; it has been corrected to become a no-op, like
+ "git log --tags=no-tag-matches-this-pattern" does.
+ (merge 04a0e98515 jk/rev-input-given-fix later to maint).
+
+ * Various callers of run_command API has been modernized.
+ (merge afbdba391e jc/run-command-use-embedded-args later to maint).
+
+ * List of options offered and accepted by "git add -i/-p" were
+ inconsistent, which have been corrected.
+ (merge ce910287e7 pw/add-p-allowed-options-fix later to maint).
+
+ * Various callers of run_command API has been modernized.
+ (merge afbdba391e jc/run-command-use-embedded-args later to maint).
+
+ * "git diff --stat -w" showed 0-line changes for paths whose changes
+ were only whitespaces, which was not intuitive. We now omit such
+ paths from the stat output.
+ (merge 1cf3d5db9b mr/diff-hide-stat-wo-textual-change later to maint).
+
+ * It was possible for xrealloc() to send a non-NULL pointer that has
+ been freed, which has been fixed.
+ (merge 6479ea4a8a jk/xrealloc-avoid-use-after-free later to maint).
+
+ * "git status" has trouble showing where it came from by interpreting
+ reflog entries that record certain events, e.g. "checkout @{u}", and
+ gives a hard/fatal error. Even though it inherently is impossible
+ to give a correct answer because the reflog entries lose some
+ information (e.g. "@{u}" does not record what branch the user was
+ on hence which branch 'the upstream' needs to be computed, and even
+ if the record were available, the relationship between branches may
+ have changed), at least hide the error to allow "status" show its
+ output.
+
+ * "git status --short" quoted a path with SP in it when tracked, but
+ not those that are untracked, ignored or unmerged. They are all
+ shown quoted consistently.
+
+ * "git diff/show" on a change that involves a submodule used to read
+ the information on commits in the submodule from a wrong repository
+ and gave a wrong information when the commit-graph is involved.
+ (merge 85a1ec2c32 mf/submodule-summary-with-correct-repository later to maint).
+
+ * Unlike "git config --local", "git config --worktree" did not fail
+ early and cleanly when started outside a git repository.
+ (merge 378fe5fc3d mt/config-fail-nongit-early later to maint).
+
+ * There is a logic to estimate how many objects are in the
+ repository, which is mean to run once per process invocation, but
+ it ran every time the estimated value was requested.
+ (merge 67bb65de5d jk/dont-count-existing-objects-twice later to maint).
+
+ * "git remote set-head" that failed still said something that hints
+ the operation went through, which was misleading.
+ (merge 5a07c6c3c2 cs/don-t-pretend-a-failed-remote-set-head-succeeded later to maint).
+
+ * "git fetch --all --ipv4/--ipv6" forgot to pass the protocol options
+ to instances of the "git fetch" that talk to individual remotes,
+ which has been corrected.
+ (merge 4e735c1326 ar/fetch-ipversion-in-all later to maint).
+
+ * The "unshelve" subcommand of "git p4" used incorrectly used
+ commit^N where it meant to say commit~N to name the Nth generation
+ ancestor, which has been corrected.
+ (merge 0acbf5997f ld/p4-unshelve-fix later to maint).
+
+ * "git clone" that clones from SHA-1 repository, while
+ GIT_DEFAULT_HASH set to use SHA-256 already, resulted in an
+ unusable repository that half-claims to be SHA-256 repository
+ with SHA-1 objects and refs. This has been corrected.
+
+ * Adjust sample hooks for hash algorithm other than SHA-1.
+ (merge d8d3d632f4 dl/zero-oid-in-hooks later to maint).
+
+ * "git range-diff" showed incorrect diffstat, which has been
+ corrected.
+
+ * Earlier we taught "git pull" to warn when the user does not say the
+ histories need to be merged, rebased or accepts only fast-
+ forwarding, but the warning triggered for those who have set the
+ pull.ff configuration variable.
+ (merge 54200cef86 ah/pull later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 84544f2ea3 sk/typofixes later to maint).
+ (merge b17f411ab5 ar/help-guides-doc later to maint).
+ (merge 98c6871fad rs/grep-simpler-parse-object-or-die-call later to maint).
+ (merge 861c4ce141 en/typofixes later to maint).
+ (merge 60e47f6773 sg/ci-git-path-fix-with-pyenv later to maint).
+ (merge e2bfa50ac3 jb/doc-packfile-name later to maint).
+ (merge 918d8ff780 es/worktree-cleanup later to maint).
+ (merge dc156bc31f ma/t1450-quotefix later to maint).
+ (merge 56e743426b en/merge-recursive-comment-fixes later to maint).
+ (merge 7d23ff818f rs/bisect-oid-to-hex-fix later to maint).
+ (merge de20baf2c9 ny/notes-doc-sample-update later to maint).
+ (merge f649aaaf82 so/rev-parser-errormessage-fix later to maint).
+ (merge 6103d58b7f bc/sha-256-cvs-svn-updates later to maint).
+ (merge ac900fddb7 ma/stop-progress-null-fix later to maint).
+ (merge e767963ab6 rs/upload-pack-sigchain-fix later to maint).
+ (merge a831908599 rs/preserve-merges-unused-code-removal later to maint).
+ (merge 6dfefe70a9 jb/commit-graph-doc-fix later to maint).
+ (merge 847b37271e pb/set-url-docfix later to maint).
+ (merge 748f733d54 mt/checkout-entry-dead-code-removal later to maint).
+ (merge ce820cbd58 dl/subtree-docs later to maint).
+ (merge 55fe225dde jk/leakfix later to maint).
+ (merge ee22a29215 so/pretty-abbrev-doc later to maint).
+ (merge 3100fd5588 jc/post-checkout-doc later to maint).
+ (merge 17bae89476 pb/doc-external-diff-env later to maint).
+ (merge 27ed6ccc12 jk/worktree-check-clean-leakfix later to maint).
+ (merge 1302badd16 ea/blame-use-oideq later to maint).
+ (merge e6d5a11fed al/t3200-back-on-a-branch later to maint).
+ (merge 324efcf6b6 pw/add-p-leakfix later to maint).
+ (merge 1c6ffb546b jk/add-i-fixes later to maint).
+ (merge e40e936551 cd/commit-graph-doc later to maint).
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 5d122db6e9..88750af7ae 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -36,6 +36,12 @@ include::line-range-format.txt[]
START. `git blame --reverse START` is taken as `git blame
--reverse START..HEAD` for convenience.
+--first-parent::
+ Follow only the first parent commit upon seeing a merge
+ commit. This option can be used to determine when a line
+ was introduced to a particular integration branch, rather
+ than when it was introduced to the history overall.
+
-p::
--porcelain::
Show in a format designed for machine consumption.
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 5aa73cfe45..af5da45d28 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -6,9 +6,14 @@ sub format_one {
my ($out, $nameattr) = @_;
my ($name, $attr) = @$nameattr;
my ($state, $description);
+ my $mansection;
$state = 0;
open I, '<', "$name.txt" or die "No such file $name.txt";
while (<I>) {
+ if (/^git[a-z0-9-]*\(([0-9])\)$/) {
+ $mansection = $1;
+ next;
+ }
if (/^NAME$/) {
$state = 1;
next;
@@ -27,7 +32,7 @@ sub format_one {
die "No description found in $name.txt";
}
if (my ($verify_name, $text) = ($description =~ /^($name) - (.*)/)) {
- print $out "linkgit:$name\[1\]::\n\t";
+ print $out "linkgit:$name\[$mansection\]::\n\t";
if ($attr =~ / deprecated /) {
print $out "(deprecated) ";
}
@@ -38,12 +43,15 @@ sub format_one {
}
}
-while (<>) {
+my ($input, @categories) = @ARGV;
+
+open IN, "<$input";
+while (<IN>) {
last if /^### command list/;
}
my %cmds = ();
-for (sort <>) {
+for (sort <IN>) {
next if /^#/;
chomp;
@@ -51,17 +59,10 @@ for (sort <>) {
$attr = '' unless defined $attr;
push @{$cmds{$cat}}, [$name, " $attr "];
}
+close IN;
-for my $cat (qw(ancillaryinterrogators
- ancillarymanipulators
- mainporcelain
- plumbinginterrogators
- plumbingmanipulators
- synchingrepositories
- foreignscminterface
- purehelpers
- synchelpers)) {
- my $out = "cmds-$cat.txt";
+for my $out (@categories) {
+ my ($cat) = $out =~ /^cmds-(.*)\.txt$/;
open O, '>', "$out+" or die "Cannot open output file $out+";
for (@{$cmds{$cat}}) {
format_one(\*O, $_);
diff --git a/Documentation/config.txt b/Documentation/config.txt
index ef0768b91a..bf706b950e 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -340,6 +340,8 @@ include::config/column.txt[]
include::config/commit.txt[]
+include::config/commitgraph.txt[]
+
include::config/credential.txt[]
include::config/completion.txt[]
@@ -348,6 +350,8 @@ include::config/diff.txt[]
include::config/difftool.txt[]
+include::config/extensions.txt[]
+
include::config/fastimport.txt[]
include::config/feature.txt[]
@@ -396,6 +400,8 @@ include::config/mailinfo.txt[]
include::config/mailmap.txt[]
+include::config/maintenance.txt[]
+
include::config/man.txt[]
include::config/merge.txt[]
diff --git a/Documentation/config/commitgraph.txt b/Documentation/config/commitgraph.txt
new file mode 100644
index 0000000000..4582c39fc4
--- /dev/null
+++ b/Documentation/config/commitgraph.txt
@@ -0,0 +1,8 @@
+commitGraph.maxNewFilters::
+ Specifies the default value for the `--max-new-filters` option of `git
+ commit-graph write` (c.f., linkgit:git-commit-graph[1]).
+
+commitGraph.readChangedPaths::
+ If true, then git will use the changed-path Bloom filters in the
+ commit-graph file (if it exists, and they are present). Defaults to
+ true. See linkgit:git-commit-graph[1] for more information.
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index 74619a9c03..02002cf109 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -399,7 +399,7 @@ the largest projects. You probably do not need to adjust this value.
Common unit suffixes of 'k', 'm', or 'g' are supported.
core.deltaBaseCacheLimit::
- Maximum number of bytes to reserve for caching base objects
+ Maximum number of bytes per thread to reserve for caching base objects
that may be referenced by multiple deltified objects. By storing the
entire decompressed base objects in a cache Git is able
to avoid unpacking and decompressing frequently used base
diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt
new file mode 100644
index 0000000000..4e23d73cdc
--- /dev/null
+++ b/Documentation/config/extensions.txt
@@ -0,0 +1,8 @@
+extensions.objectFormat::
+ Specify the hash algorithm to use. The acceptable values are `sha1` and
+ `sha256`. If not specified, `sha1` is assumed. It is an error to specify
+ this key unless `core.repositoryFormatVersion` is 1.
++
+Note that this setting should only be set by linkgit:git-init[1] or
+linkgit:git-clone[1]. Trying to change it after initialization will not
+work and will produce hard-to-diagnose issues.
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index c0cbf2bb1c..cdecd04e5b 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -14,10 +14,6 @@ feature.experimental::
+
* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
skipping more commits at a time, reducing the number of round trips.
-+
-* `protocol.version=2` speeds up fetches from repositories with many refs by
-allowing the client to specify which refs to list before the server lists
-them.
feature.manyFiles::
Enable config options that optimize for repos with many files in the
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
index b20394038d..6af6f5edb2 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -60,7 +60,10 @@ fetch.negotiationAlgorithm::
sent when negotiating the contents of the packfile to be sent by the
server. Set to "skipping" to use an algorithm that skips commits in an
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
+ packfile; or set to "noop" to not send any information at all, which
+ will almost certainly result in a larger-than-necessary packfile, but
+ will skip the negotiation step.
+ 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). If `feature.experimental` is enabled, then this
setting defaults to "skipping".
diff --git a/Documentation/config/fmt-merge-msg.txt b/Documentation/config/fmt-merge-msg.txt
index c73cfa90b7..a8e8f74d0a 100644
--- a/Documentation/config/fmt-merge-msg.txt
+++ b/Documentation/config/fmt-merge-msg.txt
@@ -8,3 +8,15 @@ merge.log::
most the specified number of one-line descriptions from the
actual commits that are being merged. Defaults to false, and
true is a synonym for 20.
+
+merge.suppressDest::
+ By adding a glob that matches the names of integration
+ branches to this multi-valued configuration variable, the
+ default merge message computed for merges into these
+ integration branches will omit " into <branch name>" from
+ its title.
++
+An element with an empty value can be used to clear the list
+of globs accumulated from previous configuration entries.
+When there is no `merge.suppressDest` variable defined, the
+default value of `master` is used for backward compatibility.
diff --git a/Documentation/config/maintenance.txt b/Documentation/config/maintenance.txt
new file mode 100644
index 0000000000..7cc6700d57
--- /dev/null
+++ b/Documentation/config/maintenance.txt
@@ -0,0 +1,16 @@
+maintenance.<task>.enabled::
+ This boolean config option controls whether the maintenance task
+ with name `<task>` is run when no `--task` option is specified to
+ `git maintenance run`. These config values are ignored if a
+ `--task` option exists. By default, only `maintenance.gc.enabled`
+ is true.
+
+maintenance.commit-graph.auto::
+ This integer config option controls how often the `commit-graph` task
+ should be run as part of `git maintenance run --auto`. If zero, then
+ the `commit-graph` task will not run with the `--auto` option. A
+ negative value will force the task to run every time. Otherwise, a
+ positive value implies the command should run when the number of
+ reachable commits that are not in the commit-graph file is at least
+ the value of `maintenance.commit-graph.auto`. The default value is
+ 100.
diff --git a/Documentation/config/mergetool.txt b/Documentation/config/mergetool.txt
index 09ed31dbfa..16a27443a3 100644
--- a/Documentation/config/mergetool.txt
+++ b/Documentation/config/mergetool.txt
@@ -30,6 +30,16 @@ mergetool.meld.hasOutput::
to `true` tells Git to unconditionally use the `--output` option,
and `false` avoids using `--output`.
+mergetool.meld.useAutoMerge::
+ When the `--auto-merge` is given, meld will merge all non-conflicting
+ parts automatically, highlight the conflicting parts and wait for
+ user decision. Setting `mergetool.meld.useAutoMerge` to `true` tells
+ Git to unconditionally use the `--auto-merge` option with `meld`.
+ Setting this value to `auto` makes git detect whether `--auto-merge`
+ is supported and will only use `--auto-merge` when available. A
+ value of `false` avoids using `--auto-merge` altogether, and is the
+ default value.
+
mergetool.keepBackup::
After performing a merge, the original file with conflict markers
can be saved as a file with a `.orig` extension. If this variable
diff --git a/Documentation/config/protocol.txt b/Documentation/config/protocol.txt
index c46e9b3d00..756591d77b 100644
--- a/Documentation/config/protocol.txt
+++ b/Documentation/config/protocol.txt
@@ -48,8 +48,7 @@ protocol.version::
If set, clients will attempt to communicate with a server
using the specified protocol version. If the server does
not support it, communication falls back to version 0.
- If unset, the default is `0`, unless `feature.experimental`
- is enabled, in which case the default is `2`.
+ If unset, the default is `2`.
Supported versions:
+
--
diff --git a/Documentation/config/receive.txt b/Documentation/config/receive.txt
index 65f78aac37..85d5b5a3d2 100644
--- a/Documentation/config/receive.txt
+++ b/Documentation/config/receive.txt
@@ -114,6 +114,28 @@ receive.hideRefs::
An attempt to update or delete a hidden ref by `git push` is
rejected.
+receive.procReceiveRefs::
+ This is a multi-valued variable that defines reference prefixes
+ to match the commands in `receive-pack`. Commands matching the
+ prefixes will be executed by an external hook "proc-receive",
+ instead of the internal `execute_commands` function. If this
+ variable is not defined, the "proc-receive" hook will never be
+ used, and all commands will be executed by the internal
+ `execute_commands` function.
++
+For example, if this variable is set to "refs/for", pushing to reference
+such as "refs/for/master" will not create or update a reference named
+"refs/for/master", but may create or update a pull request directly by
+running the hook "proc-receive".
++
+Optional modifiers can be provided in the beginning of the value to filter
+commands for specific actions: create (a), modify (m), delete (d).
+A `!` can be included in the modifiers to negate the reference prefix entry.
+E.g.:
++
+ git config --system --add receive.procReceiveRefs ad:refs/heads
+ git config --system --add receive.procReceiveRefs !:refs/heads
+
receive.updateServerInfo::
If set to true, git-receive-pack will run git-update-server-info
after receiving data from git-push and updating refs.
diff --git a/Documentation/config/sendemail.txt b/Documentation/config/sendemail.txt
index 0006faf800..cbc5af42fd 100644
--- a/Documentation/config/sendemail.txt
+++ b/Documentation/config/sendemail.txt
@@ -61,3 +61,8 @@ sendemail.smtpBatchSize::
sendemail.smtpReloginDelay::
Seconds wait before reconnecting to smtp server.
See also the `--relogin-delay` option of linkgit:git-send-email[1].
+
+sendemail.forbidSendmailVariables::
+ To avoid common misconfiguration mistakes, linkgit:git-send-email[1]
+ will abort with a warning if any configuration options for "sendmail"
+ exist. Set this variable to bypass the check.
diff --git a/Documentation/config/uploadpack.txt b/Documentation/config/uploadpack.txt
index ed1c835695..ee7b3ac94f 100644
--- a/Documentation/config/uploadpack.txt
+++ b/Documentation/config/uploadpack.txt
@@ -57,6 +57,24 @@ uploadpack.allowFilter::
If this option is set, `upload-pack` will support partial
clone and partial fetch object filtering.
+uploadpackfilter.allow::
+ Provides a default value for unspecified object filters (see: the
+ below configuration variable).
+ Defaults to `true`.
+
+uploadpackfilter.<filter>.allow::
+ Explicitly allow or ban the object filter corresponding to
+ `<filter>`, where `<filter>` may be one of: `blob:none`,
+ `blob:limit`, `tree`, `sparse:oid`, or `combine`. If using
+ combined filters, both `combine` and all of the nested filter
+ kinds must be allowed. Defaults to `uploadpackfilter.allow`.
+
+uploadpackfilter.tree.maxDepth::
+ Only allow `--filter=tree=<n>` when `n` is no more than the value of
+ `uploadpackfilter.tree.maxDepth`. If set, this also implies
+ `uploadpackfilter.tree.allow=true`, unless this configuration
+ variable had already been set. Has no effect if unset.
+
uploadpack.allowRefInWant::
If this option is set, `upload-pack` will support the `ref-in-want`
feature of the protocol version 2 `fetch` command. This feature
diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index e8ed6470fb..b10ff4caa6 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -10,7 +10,8 @@ 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.
+`GIT_EXTERNAL_DIFF` and the `GIT_DIFF_OPTS` environment variables
+(see linkgit:git[1]).
What the -p option produces is slightly different from the traditional
diff format:
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 7987d72b02..573fb9bb71 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -73,6 +73,11 @@ ifndef::git-format-patch[]
Synonym for `-p --raw`.
endif::git-format-patch[]
+ifdef::git-log[]
+-t::
+ Show the tree objects in the diff output.
+endif::git-log[]
+
--indent-heuristic::
Enable the heuristic that shifts diff hunk boundaries to make patches
easier to read. This is the default.
@@ -441,10 +446,11 @@ endif::git-format-patch[]
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
name in diff-raw format output and diff-tree header
- lines, show only a partial prefix. This is
- independent of the `--full-index` option above, which controls
- the diff-patch output format. Non default number of
- digits can be specified with `--abbrev=<n>`.
+ lines, show only a partial prefix.
+ In diff-patch output format, `--full-index` takes higher
+ precedence, i.e. if `--full-index` is specified, full blob
+ names will be shown regardless of `--abbrev`.
+ Non default number of digits can be specified with `--abbrev=<n>`.
-B[<n>][/<m>]::
--break-rewrites[=[<n>][/<m>]]::
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 6e2a160a47..2bf77b46fd 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -64,6 +64,15 @@ documented in linkgit:git-config[1].
--dry-run::
Show what would be done, without making any changes.
+ifndef::git-pull[]
+--[no-]write-fetch-head::
+ Write the list of remote refs fetched in the `FETCH_HEAD`
+ file directly under `$GIT_DIR`. This is the default.
+ Passing `--no-write-fetch-head` from the command line tells
+ Git not to write the file. Under `--dry-run` option, the
+ file is never written.
+endif::git-pull[]
+
-f::
--force::
When 'git fetch' is used with `<src>:<dst>` refspec it may
@@ -86,9 +95,11 @@ ifndef::git-pull[]
Allow several <repository> and <group> arguments to be
specified. No <refspec>s may be specified.
+--[no-]auto-maintenance::
--[no-]auto-gc::
- Run `git gc --auto` at the end to perform garbage collection
- if needed. This is enabled by default.
+ Run `git maintenance run --auto` at the end to perform automatic
+ repository maintenance if needed. (`--[no-]auto-gc` is a synonym.)
+ This is enabled by default.
--[no-]write-commit-graph::
Write a commit-graph after fetching. This overrides the config
@@ -186,7 +197,7 @@ ifndef::git-pull[]
endif::git-pull[]
--set-upstream::
- If the remote is fetched successfully, pull and add upstream
+ If the remote is fetched successfully, 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
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index b9aa39000f..91d9a8601c 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -61,18 +61,18 @@ OPTIONS
file and detects errors. Turns off "apply".
--index::
- When `--check` is in effect, or when applying the patch
- (which is the default when none of the options that
- disables it is in effect), make sure the patch is
- applicable to what the current index file records. If
- the file to be patched in the working tree is not
- up to date, it is flagged as an error. This flag also
- causes the index file to be updated.
+ Apply the patch to both the index and the working tree (or
+ merely check that it would apply cleanly to both if `--check` is
+ in effect). Note that `--index` expects index entries and
+ working tree copies for relevant paths to be identical (their
+ contents and metadata such as file mode must match), and will
+ raise an error if they are not, even if the patch would apply
+ cleanly to both the index and the working tree in isolation.
--cached::
- Apply a patch without touching the working tree. Instead take the
- cached data, apply the patch, and store the result in the index
- without using the working tree. This implies `--index`.
+ Apply the patch to just the index, without touching the working
+ tree. If `--check` is in effect, merely check that it would
+ apply cleanly to the index entry.
--intent-to-add::
When applying the patch only to the working tree, mark new
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 7586c5a843..fbb39fbdf5 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -16,8 +16,8 @@ DESCRIPTION
The command takes various subcommands, and different options depending
on the subcommand:
- git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
- [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
+ git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
+ [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>...]
git bisect terms [--term-good | --term-bad]
@@ -365,6 +365,17 @@ does not require a checked out tree.
+
If the repository is bare, `--no-checkout` is assumed.
+--first-parent::
++
+Follow only the first parent commit upon seeing a merge commit.
++
+In detecting regressions introduced through the merging of a branch, the merge
+commit will be identified as introduction of the bug and its ancestors will be
+ignored.
++
+This option is particularly useful in avoiding false positives when a merged
+branch contained broken or non-buildable commits, but the merge itself was OK.
+
EXAMPLES
--------
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 03c0824d52..ace4ad3da8 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -11,7 +11,7 @@ SYNOPSIS
'git branch' [--color[=<when>] | --no-color] [--show-current]
[-v [--abbrev=<length> | --no-abbrev]]
[--column[=<options>] | --no-column] [--sort=<key>]
- [(--merged | --no-merged) [<commit>]]
+ [--merged [<commit>]] [--no-merged [<commit>]]
[--contains [<commit>]] [--no-contains [<commit>]]
[--points-at <object>] [--format=<format>]
[(-r | --remotes) | (-a | --all)]
@@ -252,13 +252,11 @@ start-point is either a local or remote-tracking branch.
--merged [<commit>]::
Only list branches whose tips are reachable from the
- specified commit (HEAD if not specified). Implies `--list`,
- incompatible with `--no-merged`.
+ specified commit (HEAD if not specified). Implies `--list`.
--no-merged [<commit>]::
Only list branches whose tips are not reachable from the
- specified commit (HEAD if not specified). Implies `--list`,
- incompatible with `--merged`.
+ specified commit (HEAD if not specified). Implies `--list`.
<branchname>::
The name of the branch to create or delete.
@@ -370,6 +368,8 @@ serve four related but different purposes:
- `--no-merged` is used to find branches which are candidates for merging
into HEAD, since those branches are not fully contained by HEAD.
+include::ref-reachability-filters.txt[]
+
SEE ALSO
--------
linkgit:git-check-ref-format[1],
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index d34b0964be..53804cad4b 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -9,7 +9,8 @@ git-bundle - Move objects and refs by archive
SYNOPSIS
--------
[verse]
-'git bundle' create [-q | --quiet | --progress | --all-progress] [--all-progress-implied] <file> <git-rev-list-args>
+'git bundle' create [-q | --quiet | --progress | --all-progress] [--all-progress-implied]
+ [--version=<version>] <file> <git-rev-list-args>
'git bundle' verify [-q | --quiet] <file>
'git bundle' list-heads <file> [<refname>...]
'git bundle' unbundle <file> [<refname>...]
@@ -102,6 +103,12 @@ unbundle <file>::
is activated. Unlike --all-progress this flag doesn't actually
force any progress display by itself.
+--version=<version>::
+ Specify the bundle version. Version 2 is the older format and can only be
+ used with SHA-1 repositories; the newer version 3 contains capabilities that
+ permit extensions. The default is the oldest supported format, based on the
+ hash algorithm in use.
+
-q::
--quiet::
This flag makes the command not to report its progress
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 5b697eee1b..afa5c11fd3 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -198,6 +198,7 @@ Use `--no-guess` to disable this.
Create the new branch's reflog; see linkgit:git-branch[1] for
details.
+-d::
--detach::
Rather than checking out a branch to work on it, check out a
commit for inspection and discardable experiments.
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index c898310099..097e6a86c5 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -78,9 +78,9 @@ repository using this option and then delete branches (or use any
other Git command that makes any existing commit unreferenced) in the
source repository, some objects may become unreferenced (or dangling).
These objects may be removed by normal Git operations (such as `git commit`)
-which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
-If these objects are removed and were referenced by the cloned repository,
-then the cloned repository will become corrupt.
+which automatically call `git maintenance run --auto`. (See
+linkgit:git-maintenance[1].) If these objects are removed and were referenced
+by the cloned repository, then the cloned repository will become corrupt.
+
Note that running `git repack` without the `--local` option in a repository
cloned with `--shared` will copy objects from the source repository into a pack
diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt
index 8ca1764d3d..de6b6de230 100644
--- a/Documentation/git-commit-graph.txt
+++ b/Documentation/git-commit-graph.txt
@@ -62,7 +62,17 @@ existing commit-graph file.
With the `--changed-paths` option, compute and write information about the
paths changed between a commit and its first parent. This operation can
take a while on large repositories. It provides significant performance gains
-for getting history of a directory or a file with `git log -- <path>`.
+for getting history of a directory or a file with `git log -- <path>`. If
+this option is given, future commit-graph writes will automatically assume
+that this option was intended. Use `--no-changed-paths` to stop storing this
+data.
++
+With the `--max-new-filters=<n>` option, generate at most `n` new Bloom
+filters (if `--changed-paths` is specified). If `n` is `-1`, no limit is
+enforced. Only commits present in the new layer count against this
+limit. To retroactively compute Bloom filters over earlier layers, it is
+advised to use `--split=replace`. Overrides the `commitGraph.maxNewFilters`
+configuration.
+
With the `--split[=<strategy>]` option, write the commit-graph as a
chain of multiple commit-graph files stored in
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index 45b6d8e633..9067c2079e 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -48,6 +48,10 @@ include::fetch-options.txt[]
include::pull-fetch-param.txt[]
+--stdin::
+ Read refspecs, one per line, from stdin in addition to those provided
+ as arguments. The "tag <name>" format is not supported.
+
include::urls-remotes.txt[]
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 6dcd39f6f6..2962f85a50 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -11,7 +11,7 @@ SYNOPSIS
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
[--points-at=<object>]
- (--merged[=<object>] | --no-merged[=<object>])
+ [--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
DESCRIPTION
@@ -76,13 +76,11 @@ OPTIONS
--merged[=<object>]::
Only list refs whose tips are reachable from the
- specified commit (HEAD if not specified),
- incompatible with `--no-merged`.
+ specified commit (HEAD if not specified).
--no-merged[=<object>]::
Only list refs whose tips are not reachable from the
- specified commit (HEAD if not specified),
- incompatible with `--merged`.
+ specified commit (HEAD if not specified).
--contains[=<object>]::
Only list refs which contain the specified commit (HEAD if not
@@ -222,6 +220,8 @@ worktreepath::
In addition to the above, for commit and tag objects, the header
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
be used to specify the value in the header field.
+Fields `tree` and `parent` can also be used with modifier `:short` and
+`:short=<length>` just like `objectname`.
For commit and tag objects, the special `creatordate` and `creator`
fields will correspond to the appropriate date or name-email-date tuple
@@ -230,14 +230,35 @@ These are intended for working on a mix of annotated and lightweight tags.
Fields that have name-email-date tuple as its value (`author`,
`committer`, and `tagger`) can be suffixed with `name`, `email`,
-and `date` to extract the named component.
-
-The complete message in a commit and tag object is `contents`.
-Its first line is `contents:subject`, where subject is the concatenation
-of all lines of the commit message up to the first blank line. The next
-line is `contents:body`, where body is all of the lines after the first
-blank line. The optional GPG signature is `contents:signature`. The
-first `N` lines of the message is obtained using `contents:lines=N`.
+and `date` to extract the named component. For email fields (`authoremail`,
+`committeremail` and `taggeremail`), `:trim` can be appended to get the email
+without angle brackets, and `:localpart` to get the part before the `@` symbol
+out of the trimmed email.
+
+The message in a commit or a tag object is `contents`, from which
+`contents:<part>` can be used to extract various parts out of:
+
+contents:size::
+ The size in bytes of the commit or tag message.
+
+contents:subject::
+ The first paragraph of the message, which typically is a
+ single line, is taken as the "subject" of the commit or the
+ tag message.
+ Instead of `contents:subject`, field `subject` can also be used to
+ obtain same results. `:sanitize` can be appended to `subject` for
+ subject line suitable for filename.
+
+contents:body::
+ The remainder of the commit or the tag message that follows
+ the "subject".
+
+contents:signature::
+ The optional GPG signature of the tag.
+
+contents:lines=N::
+ The first `N` lines of the message.
+
Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1]
are obtained as `trailers` (or by using the historical alias
`contents:trailers`). Non-trailer lines from the trailer block can be omitted
@@ -385,6 +406,11 @@ Note also that multiple copies of an object may be present in the object
database; in this case, it is undefined which copy's size or delta base
will be reported.
+NOTES
+-----
+
+include::ref-reachability-filters.txt[]
+
SEE ALSO
--------
linkgit:git-show-ref[1]
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index f71db0daa2..44fe8860b3 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -8,7 +8,7 @@ git-help - Display help information about Git
SYNOPSIS
--------
[verse]
-'git help' [-a|--all [--[no-]verbose]] [-g|--guide]
+'git help' [-a|--all [--[no-]verbose]] [-g|--guides]
[-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
DESCRIPTION
@@ -21,8 +21,8 @@ on the standard output.
If the option `--all` or `-a` is given, all available commands are
printed on the standard output.
-If the option `--guide` or `-g` is given, a list of the useful
-Git guides is also printed on the standard output.
+If the option `--guides` or `-g` is given, a list of the
+Git concept guides is also printed on the standard output.
If a command, or a guide, is given, a manual page for that command or
guide is brought up. The 'man' program is used by default for this
@@ -58,7 +58,7 @@ OPTIONS
-g::
--guides::
- Prints a list of useful guides on the standard output. This
+ Prints a list of the Git concept guides on the standard output. This
option overrides any given command or guide name.
-i::
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 65b53fcc47..63cf498ce9 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -51,17 +51,13 @@ OPTIONS
CONFIGURATION
-------------
-To use the tool, imap.folder and either imap.tunnel or imap.host must be set
+To use the tool, `imap.folder` and either `imap.tunnel` or `imap.host` must be set
to appropriate values.
-Variables
-~~~~~~~~~
-
include::config/imap.txt[]
-Examples
-~~~~~~~~
-
+EXAMPLES
+--------
Using tunnel mode:
..........................
@@ -89,14 +85,18 @@ Using direct mode with SSL:
user = bob
pass = p4ssw0rd
port = 123
- sslverify = false
+ ; sslVerify = false
.........................
-EXAMPLES
---------
-To submit patches using GMail's IMAP interface, first, edit your ~/.gitconfig
-to specify your account settings:
+[NOTE]
+You may want to use `sslVerify=false`
+while troubleshooting, if you suspect that the reason you are
+having trouble connecting is because the certificate you use at
+the private server `example.com` you are trying to set up (or
+have set up) may not be verified correctly.
+
+Using Gmail's IMAP interface:
---------
[imap]
@@ -104,17 +104,21 @@ to specify your account settings:
host = imaps://imap.gmail.com
user = user@gmail.com
port = 993
- sslverify = false
---------
-You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error
+[NOTE]
+You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
+[NOTE]
+If your Gmail account is set to another language than English, the name of the "Drafts"
+folder will be localized.
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
-Just make sure to disable line wrapping in the email client (GMail's web
+Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 9316d9a80b..af0c26232c 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -100,12 +100,14 @@ OPTIONS
value is set or outside a repository.
+
This option cannot be used with --stdin.
++
+include::object-format-disclaimer.txt[]
NOTES
-----
-Once the index has been created, the list of object names is sorted
-and the SHA-1 hash of that list is printed to stdout. If --stdin was
+Once the index has been created, the hash that goes into the name of
+the pack/idx file is printed to stdout. If --stdin was
also used then this is prefixed by either "pack\t", or "keep\t" if a
new .keep file was successfully created. This is useful to remove a
.keep file used as a lock to prevent the race with 'git repack'
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index ddfe265da5..f35f70f13d 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -53,6 +53,8 @@ current working directory.
Specify the given object format (hash algorithm) for the repository. The valid
values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default.
++
+include::object-format-disclaimer.txt[]
--template=<template_directory>::
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 20e6d21a74..2b8ac5ff88 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -15,9 +15,12 @@ DESCRIPTION
-----------
Shows the commit logs.
-The command takes options applicable to the `git rev-list`
+:git-log: 1
+include::rev-list-description.txt[]
+
+The command takes options applicable to the linkgit:git-rev-list[1]
command to control what is shown and how, and options applicable to
-the `git diff-*` commands to control how the changes
+the linkgit:git-diff[1] command to control how the changes
each commit introduces are shown.
@@ -111,8 +114,51 @@ include::rev-list-options.txt[]
include::pretty-formats.txt[]
-COMMON DIFF OPTIONS
--------------------
+DIFF FORMATTING
+---------------
+
+By default, `git log` does not generate any diff output. The options
+below can be used to show the changes made by each commit.
+
+Note that unless one of `-c`, `--cc`, or `-m` is given, merge commits
+will never show a diff, even if a diff format like `--patch` is
+selected, nor will they match search options like `-S`. The exception is
+when `--first-parent` is in use, in which merges are treated like normal
+single-parent commits (this can be overridden by providing a
+combined-diff option or with `--no-diff-merges`).
+
+-c::
+ With this option, diff output for a merge commit
+ shows the differences from each of the parents to the merge result
+ simultaneously instead of showing pairwise diff between a parent
+ and the result one at a time. Furthermore, it lists only files
+ which were modified from all parents.
+
+--cc::
+ This flag implies the `-c` option and further compresses the
+ patch output by omitting uninteresting hunks whose contents in
+ the parents have only two variants and the merge result picks
+ one of them without modification.
+
+--combined-all-paths::
+ This flag causes combined diffs (used for merge commits) to
+ list the name of the file from all parents. It thus only has
+ effect when -c or --cc are specified, and is likely only
+ useful if filename changes are detected (i.e. when either
+ rename or copy detection have been requested).
+
+-m::
+ This flag makes the merge commits show the full diff like
+ regular commits; for each merge parent, a separate log entry
+ and diff is generated. An exception is that only diff against
+ the first parent is shown when `--first-parent` option is given;
+ in that case, the output represents the changes the merge
+ brought _into_ the then-current branch.
+
+--diff-merges=off::
+--no-diff-merges::
+ Disable output of diffs for merge commits (default). Useful to
+ override `-m`, `-c`, or `--cc`.
:git-log: 1
include::diff-options.txt[]
diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt
new file mode 100644
index 0000000000..6abcb8255a
--- /dev/null
+++ b/Documentation/git-maintenance.txt
@@ -0,0 +1,79 @@
+git-maintenance(1)
+==================
+
+NAME
+----
+git-maintenance - Run tasks to optimize Git repository data
+
+
+SYNOPSIS
+--------
+[verse]
+'git maintenance' run [<options>]
+
+
+DESCRIPTION
+-----------
+Run tasks to optimize Git repository data, speeding up other Git commands
+and reducing storage requirements for the repository.
+
+Git commands that add repository data, such as `git add` or `git fetch`,
+are optimized for a responsive user experience. These commands do not take
+time to optimize the Git data, since such optimizations scale with the full
+size of the repository while these user commands each perform a relatively
+small action.
+
+The `git maintenance` command provides flexibility for how to optimize the
+Git repository.
+
+SUBCOMMANDS
+-----------
+
+run::
+ Run one or more maintenance tasks. If one or more `--task` options
+ are specified, then those tasks are run in that order. Otherwise,
+ the tasks are determined by which `maintenance.<task>.enabled`
+ config options are true. By default, only `maintenance.gc.enabled`
+ is true.
+
+TASKS
+-----
+
+commit-graph::
+ The `commit-graph` job updates the `commit-graph` files incrementally,
+ then verifies that the written data is correct. The incremental
+ write is safe to run alongside concurrent Git processes since it
+ will not expire `.graph` files that were in the previous
+ `commit-graph-chain` file. They will be deleted by a later run based
+ on the expiration delay.
+
+gc::
+ Clean up unnecessary files and optimize the local repository. "GC"
+ stands for "garbage collection," but this task performs many
+ smaller tasks. This task can be expensive for large repositories,
+ as it repacks all Git objects into a single pack-file. It can also
+ be disruptive in some situations, as it deletes stale data. See
+ linkgit:git-gc[1] for more details on garbage collection in Git.
+
+OPTIONS
+-------
+--auto::
+ When combined with the `run` subcommand, run maintenance tasks
+ only if certain thresholds are met. For example, the `gc` task
+ runs when the number of loose objects exceeds the number stored
+ in the `gc.auto` config setting, or when the number of pack-files
+ exceeds the `gc.autoPackLimit` config setting.
+
+--quiet::
+ Do not report progress or other information over `stderr`.
+
+--task=<task>::
+ If this option is specified one or more times, then only run the
+ specified tasks in the specified order. If no `--task=<task>`
+ arguments are specified, then only the tasks with
+ `maintenance.<task>.enabled` configured as `true` are considered.
+ See the 'TASKS' section for the list of accepted `<task>` values.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt
index 0c6619493c..eb0caa0439 100644
--- a/Documentation/git-multi-pack-index.txt
+++ b/Documentation/git-multi-pack-index.txt
@@ -51,11 +51,12 @@ repack::
multi-pack-index, then divide by the total number of objects in
the pack and multiply by the pack size. We select packs with
expected size below the batch size until the set of packs have
- total expected size at least the batch size. If the total size
- does not reach the batch size, then do nothing. If a new pack-
- file is created, rewrite the multi-pack-index to reference the
- new pack-file. A later run of 'git multi-pack-index expire' will
- delete the pack-files that were part of this batch.
+ total expected size at least the batch size, or all pack-files
+ are considered. If only one pack-file is selected, then do
+ nothing. If a new pack-file is created, rewrite the
+ multi-pack-index to reference the new pack-file. A later run of
+ 'git multi-pack-index expire' will delete the pack-files that
+ were part of this batch.
+
If `repack.packKeptObjects` is `false`, then any pack-files with an
associated `.keep` file will not be selected for the batch to repack.
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index ced2e8280e..0a4200674c 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -223,7 +223,7 @@ are taken from notes refs. A notes ref is usually a branch which
contains "files" whose paths are the object names for the objects
they describe, with some directory separators included for performance
reasons footnote:[Permitted pathnames have the form
-'ab'`/`'cd'`/`'ef'`/`'...'`/`'abcdef...': a sequence of directory
+'bf'`/`'fe'`/`'30'`/`'...'`/`'680d5a...': a sequence of directory
names of two hexadecimal digits each followed by a filename with the
rest of the object ID.].
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index eaa2f2a404..54d715ead1 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -270,15 +270,18 @@ So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle.
This option specifies how missing objects are handled.
+
The form '--missing=error' requests that pack-objects stop with an error if
-a missing object is encountered. This is the default action.
+a missing object is encountered. If the repository is a partial clone, an
+attempt to fetch missing objects will be made before declaring them missing.
+This is the default action.
+
The form '--missing=allow-any' will allow object traversal to continue
-if a missing object is encountered. Missing objects will silently be
-omitted from the results.
+if a missing object is encountered. No fetch of a missing object will occur.
+Missing objects will silently be omitted from the results.
+
The form '--missing=allow-promisor' is like 'allow-any', but will only
allow object traversal to continue for EXPECTED promisor missing objects.
-Unexpected missing object will raise an error.
+No fetch of a missing object will occur. An unexpected missing object will
+raise an error.
--exclude-promisor-objects::
Omit objects that are known to be in the promisor remote. (This
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 374d2486f7..38e15488f6 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -204,6 +204,7 @@ CONFIGURATION
-------------
include::config/rebase.txt[]
+include::config/sequencer.txt[]
OPTIONS
-------
@@ -459,17 +460,38 @@ with `--keep-base` in order to drop those commits from your branch.
See also INCOMPATIBLE OPTIONS below.
--ignore-whitespace::
+ Ignore whitespace differences when trying to reconcile
+differences. Currently, each backend implements an approximation of
+this behavior:
++
+apply backend: When applying a patch, ignore changes in whitespace in
+context lines. Unfortunately, this means that if the "old" lines being
+replaced by the patch differ only in whitespace from the existing
+file, you will get a merge conflict instead of a successful patch
+application.
++
+merge backend: Treat lines with only whitespace changes as unchanged
+when merging. Unfortunately, this means that any patch hunks that were
+intended to modify whitespace and nothing else will be dropped, even
+if the other side had no changes that conflicted.
+
--whitespace=<option>::
- These flags are passed to the 'git apply' program
+ This flag is passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
Implies --apply.
+
See also INCOMPATIBLE OPTIONS below.
--committer-date-is-author-date::
+ Instead of using the current time as the committer date, use
+ the author date of the commit being rebased as the committer
+ date. This option implies `--force-rebase`.
+
--ignore-date::
- These flags are passed to 'git am' to easily change the dates
- of the rebased commits (see linkgit:git-am[1]).
+--reset-author-date::
+ Instead of using the author date of the original commit, use
+ the current time as the author date of the rebased commit. This
+ option implies `--force-rebase`.
+
See also INCOMPATIBLE OPTIONS below.
@@ -607,9 +629,6 @@ INCOMPATIBLE OPTIONS
The following options:
* --apply
- * --committer-date-is-author-date
- * --ignore-date
- * --ignore-whitespace
* --whitespace
* -C
@@ -636,6 +655,9 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --signoff
* --preserve-merges and --rebase-merges
* --preserve-merges and --empty=
+ * --preserve-merges and --ignore-whitespace
+ * --preserve-merges and --committer-date-is-author-date
+ * --preserve-merges and --ignore-date
* --keep-base and --onto
* --keep-base and --root
* --fork-point and --root
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 025c911436..5da66232dc 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -14,44 +14,8 @@ SYNOPSIS
DESCRIPTION
-----------
-List commits that are reachable by following the `parent` links from the
-given commit(s), but exclude commits that are reachable from the one(s)
-given with a '{caret}' in front of them. The output is given in reverse
-chronological order by default.
-
-You can think of this as a set operation. Commits given on the command
-line form a set of commits that are reachable from any of them, and then
-commits reachable from any of the ones given with '{caret}' in front are
-subtracted from that set. The remaining commits are what comes out in the
-command's output. Various other options and paths parameters can be used
-to further limit the result.
-
-Thus, the following command:
-
------------------------------------------------------------------------
- $ git rev-list foo bar ^baz
------------------------------------------------------------------------
-
-means "list all the commits which are reachable from 'foo' or 'bar', but
-not from 'baz'".
-
-A special notation "'<commit1>'..'<commit2>'" can be used as a
-short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of
-the following may be used interchangeably:
-
------------------------------------------------------------------------
- $ git rev-list origin..HEAD
- $ git rev-list HEAD ^origin
------------------------------------------------------------------------
-
-Another special notation is "'<commit1>'...'<commit2>'" which is useful
-for merges. The resulting set of commits is the symmetric difference
-between the two operands. The following two commands are equivalent:
-
------------------------------------------------------------------------
- $ git rev-list A B --not $(git merge-base --all A B)
- $ git rev-list A...B
------------------------------------------------------------------------
+:git-rev-list: 1
+include::rev-list-description.txt[]
'rev-list' is a very essential Git command, since it
provides the ability to build and traverse commit ancestry graphs. For
diff --git a/Documentation/git-show-index.txt b/Documentation/git-show-index.txt
index 39b1d8eaa1..e49318a5a0 100644
--- a/Documentation/git-show-index.txt
+++ b/Documentation/git-show-index.txt
@@ -44,6 +44,8 @@ OPTIONS
valid values are 'sha1' and (if enabled) 'sha256'. The default is the
algorithm for the current repository (set by `extensions.objectFormat`), or
'sha1' if no value is set or outside a repository..
++
+include::object-format-disclaimer.txt[]
GIT
---
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index f6d9791780..56656d1be6 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -15,7 +15,7 @@ SYNOPSIS
'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
[--points-at <object>] [--column[=<options>] | --no-column]
[--create-reflog] [--sort=<key>] [--format=<format>]
- [--[no-]merged [<commit>]] [<pattern>...]
+ [--merged <commit>] [--no-merged <commit>] [<pattern>...]
'git tag' -v [--format=<format>] <tagname>...
DESCRIPTION
@@ -149,11 +149,11 @@ This option is only applicable when listing tags without annotation lines.
--merged [<commit>]::
Only list tags whose commits are reachable from the specified
- commit (`HEAD` if not specified), incompatible with `--no-merged`.
+ commit (`HEAD` if not specified).
--no-merged [<commit>]::
Only list tags whose commits are not reachable from the specified
- commit (`HEAD` if not specified), incompatible with `--merged`.
+ commit (`HEAD` if not specified).
--points-at <object>::
Only list tags of the given object (HEAD if not
@@ -377,6 +377,11 @@ $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
include::date-formats.txt[]
+NOTES
+-----
+
+include::ref-reachability-filters.txt[]
+
SEE ALSO
--------
linkgit:git-check-ref-format[1].
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 3e737c2360..d401234b03 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -148,12 +148,13 @@ still see a subset of the modifications.
LOGGING UPDATES
---------------
-If config parameter "core.logAllRefUpdates" is true and the ref is one under
-"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or
-the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append
-a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
-symbolic refs before creating the log name) describing the change
-in ref value. Log lines are formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is one
+under "refs/heads/", "refs/remotes/", "refs/notes/", or a pseudoref
+like HEAD or ORIG_HEAD; or the file "$GIT_DIR/logs/<ref>" exists then
+`git update-ref` will append a line to the log file
+"$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating
+the log name) describing the change in ref value. Log lines are
+formatted as:
oldsha1 SP newsha1 SP committer LF
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 4796c3c05e..32e8440cde 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -15,6 +15,7 @@ SYNOPSIS
'git worktree move' <worktree> <new-path>
'git worktree prune' [-n] [-v] [--expire <expire>]
'git worktree remove' [-f] <worktree>
+'git worktree repair' [<path>...]
'git worktree unlock' <worktree>
DESCRIPTION
@@ -25,11 +26,24 @@ Manage multiple working trees attached to the same repository.
A git repository can support multiple working trees, allowing you to check
out more than one branch at a time. With `git worktree add` a new working
tree is associated with the repository. This new working tree is called a
-"linked working tree" as opposed to the "main working tree" prepared by "git
-init" or "git clone". A repository has one main working tree (if it's not a
+"linked working tree" as opposed to the "main working tree" prepared by
+linkgit:git-init[1] or linkgit:git-clone[1].
+A repository has one main working tree (if it's not a
bare repository) and zero or more linked working trees. When you are done
with a linked working tree, remove it with `git worktree remove`.
+In its simplest form, `git worktree add <path>` automatically creates a
+new branch whose name is the final component of `<path>`, which is
+convenient if you plan to work on a new topic. For instance, `git
+worktree add ../hotfix` creates new branch `hotfix` and checks it out at
+path `../hotfix`. To instead work on an existing branch in a new working
+tree, use `git worktree add <path> <branch>`. On the other hand, if you
+just plan to make some experimental changes or do testing without
+disturbing existing development, it is often convenient to create a
+'throwaway' working tree not associated with any branch. For instance,
+`git worktree add -d <path>` creates a new working tree with a detached
+`HEAD` at the same commit as the current branch.
+
If a working tree is deleted without using `git worktree remove`, then
its associated administrative files, which reside in the repository
(see "DETAILS" below), will eventually be removed automatically (see
@@ -48,10 +62,10 @@ add <path> [<commit-ish>]::
Create `<path>` and checkout `<commit-ish>` into it. The new working directory
is linked to the current repository, sharing everything except working
-directory specific files such as HEAD, index, etc. `-` may also be
-specified as `<commit-ish>`; it is synonymous with `@{-1}`.
+directory specific files such as `HEAD`, `index`, etc. As a convenience,
+`<commit-ish>` may be a bare "`-`", which is synonymous with `@{-1}`.
+
-If <commit-ish> is a branch name (call it `<branch>`) and is not found,
+If `<commit-ish>` is a branch name (call it `<branch>`) and is not found,
and neither `-b` nor `-B` nor `--detach` are used, but there does
exist a tracking branch in exactly one remote (call it `<remote>`)
with a matching name, treat as equivalent to:
@@ -66,24 +80,24 @@ one for the purposes of disambiguation, even if the `<branch>` isn't
unique across all remotes. Set it to
e.g. `checkout.defaultRemote=origin` to always checkout remote
branches from there if `<branch>` is ambiguous but exists on the
-'origin' remote. See also `checkout.defaultRemote` in
+`origin` remote. See also `checkout.defaultRemote` in
linkgit:git-config[1].
+
If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,
-then, as a convenience, the new worktree is associated with a branch
+then, as a convenience, the new working tree is associated with a branch
(call it `<branch>`) named after `$(basename <path>)`. If `<branch>`
-doesn't exist, a new branch based on HEAD is automatically created as
+doesn't exist, a new branch based on `HEAD` is automatically created as
if `-b <branch>` was given. If `<branch>` does exist, it will be
-checked out in the new worktree, if it's not checked out anywhere
-else, otherwise the command will refuse to create the worktree (unless
+checked out in the new working tree, if it's not checked out anywhere
+else, otherwise the command will refuse to create the working tree (unless
`--force` is used).
list::
-List details of each worktree. The main worktree is listed first, followed by
-each of the linked worktrees. The output details include if the worktree is
-bare, the revision currently checked out, and the branch currently checked out
-(or 'detached HEAD' if none).
+List details of each working tree. The main working tree is listed first,
+followed by each of the linked working trees. The output details include
+whether the working tree is bare, the revision currently checked out, and the
+branch currently checked out (or "detached HEAD" if none).
lock::
@@ -96,11 +110,14 @@ with `--reason`.
move::
Move a working tree to a new location. Note that the main working tree
-or linked working trees containing submodules cannot be moved.
+or linked working trees containing submodules cannot be moved with this
+command. (The `git worktree repair` command, however, can reestablish
+the connection with linked working trees if you move the main working
+tree manually.)
prune::
-Prune working tree information in $GIT_DIR/worktrees.
+Prune working tree information in `$GIT_DIR/worktrees`.
remove::
@@ -109,6 +126,23 @@ and no modification in tracked files) can be removed. Unclean working
trees or ones with submodules can be removed with `--force`. The main
working tree cannot be removed.
+repair [<path>...]::
+
+Repair working tree administrative files, if possible, if they have
+become corrupted or outdated due to external factors.
++
+For instance, if the main working tree (or bare repository) is moved,
+linked working trees will be unable to locate it. Running `repair` in
+the main working tree will reestablish the connection from linked
+working trees back to the main working tree.
++
+Similarly, if a linked working tree is moved without using `git worktree
+move`, the main working tree (or bare repository) will be unable to
+locate it. Running `repair` within the recently-moved working tree will
+reestablish the connection. If multiple linked working trees are moved,
+running `repair` from any working tree with each tree's new `<path>` as
+an argument, will reestablish the connection to all the specified paths.
+
unlock::
Unlock a working tree, allowing it to be pruned, moved or deleted.
@@ -128,7 +162,7 @@ OPTIONS
`move` refuses to move a locked working tree unless `--force` is specified
twice. If the destination is already assigned to some other working tree but is
missing (for instance, if `<new-path>` was deleted manually), then `--force`
-allows the move to proceed; use --force twice if the destination is locked.
+allows the move to proceed; use `--force` twice if the destination is locked.
+
`remove` refuses to remove an unclean working tree unless `--force` is used.
To remove a locked working tree, specify `--force` twice.
@@ -137,13 +171,14 @@ To remove a locked working tree, specify `--force` twice.
-B <new-branch>::
With `add`, create a new branch named `<new-branch>` starting at
`<commit-ish>`, and check out `<new-branch>` into the new working tree.
- If `<commit-ish>` is omitted, it defaults to HEAD.
+ If `<commit-ish>` is omitted, it defaults to `HEAD`.
By default, `-b` refuses to create a new branch if it already
exists. `-B` overrides this safeguard, resetting `<new-branch>` to
`<commit-ish>`.
+-d::
--detach::
- With `add`, detach HEAD in the new working tree. See "DETACHED HEAD"
+ With `add`, detach `HEAD` in the new working tree. See "DETACHED HEAD"
in linkgit:git-checkout[1].
--[no-]checkout::
@@ -154,7 +189,7 @@ To remove a locked working tree, specify `--force` twice.
--[no-]guess-remote::
With `worktree add <path>`, without `<commit-ish>`, instead
- of creating a new branch from HEAD, if there exists a tracking
+ of creating a new branch from `HEAD`, if there exists a tracking
branch in exactly one remote matching the basename of `<path>`,
base the new branch on the remote-tracking branch, and mark
the remote-tracking branch as "upstream" from the new branch.
@@ -166,12 +201,12 @@ This can also be set up as the default behaviour by using the
When creating a new branch, if `<commit-ish>` is a branch,
mark it as "upstream" from the new branch. This is the
default if `<commit-ish>` is a remote-tracking branch. See
- "--track" in linkgit:git-branch[1] for details.
+ `--track` in linkgit:git-branch[1] for details.
--lock::
Keep the working tree locked after creation. This is the
equivalent of `git worktree lock` after `git worktree add`,
- but without race condition.
+ but without a race condition.
-n::
--dry-run::
@@ -185,14 +220,14 @@ This can also be set up as the default behaviour by using the
-q::
--quiet::
- With 'add', suppress feedback messages.
+ With `add`, suppress feedback messages.
-v::
--verbose::
With `prune`, report all removals.
--expire <time>::
- With `prune`, only expire unused working trees older than <time>.
+ With `prune`, only expire unused working trees older than `<time>`.
--reason <string>::
With `lock`, an explanation why the working tree is locked.
@@ -202,48 +237,48 @@ This can also be set up as the default behaviour by using the
absolute.
+
If the last path components in the working tree's path is unique among
-working trees, it can be used to identify worktrees. For example if
-you only have two working trees, at "/abc/def/ghi" and "/abc/def/ggg",
-then "ghi" or "def/ghi" is enough to point to the former working tree.
+working trees, it can be used to identify a working tree. For example if
+you only have two working trees, at `/abc/def/ghi` and `/abc/def/ggg`,
+then `ghi` or `def/ghi` is enough to point to the former working tree.
REFS
----
In multiple working trees, some refs may be shared between all working
-trees, some refs are local. One example is HEAD is different for all
-working trees. This section is about the sharing rules and how to access
+trees and some refs are local. One example is `HEAD` which is different for each
+working tree. This section is about the sharing rules and how to access
refs of one working tree from another.
In general, all pseudo refs are per working tree and all refs starting
-with "refs/" are shared. Pseudo refs are ones like HEAD which are
-directly under GIT_DIR instead of inside GIT_DIR/refs. There is one
-exception to this: refs inside refs/bisect and refs/worktree is not
+with `refs/` are shared. Pseudo refs are ones like `HEAD` which are
+directly under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are
+exceptions, however: refs inside `refs/bisect` and `refs/worktree` are not
shared.
Refs that are per working tree can still be accessed from another
-working tree via two special paths, main-worktree and worktrees. The
-former gives access to per-worktree refs of the main working tree,
+working tree via two special paths, `main-worktree` and `worktrees`. The
+former gives access to per-working tree refs of the main working tree,
while the latter to all linked working trees.
-For example, main-worktree/HEAD or main-worktree/refs/bisect/good
-resolve to the same value as the main working tree's HEAD and
-refs/bisect/good respectively. Similarly, worktrees/foo/HEAD or
-worktrees/bar/refs/bisect/bad are the same as
-GIT_COMMON_DIR/worktrees/foo/HEAD and
-GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad.
+For example, `main-worktree/HEAD` or `main-worktree/refs/bisect/good`
+resolve to the same value as the main working tree's `HEAD` and
+`refs/bisect/good` respectively. Similarly, `worktrees/foo/HEAD` or
+`worktrees/bar/refs/bisect/bad` are the same as
+`$GIT_COMMON_DIR/worktrees/foo/HEAD` and
+`$GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad`.
-To access refs, it's best not to look inside GIT_DIR directly. Instead
+To access refs, it's best not to look inside `$GIT_DIR` directly. Instead
use commands such as linkgit:git-rev-parse[1] or linkgit:git-update-ref[1]
which will handle refs correctly.
CONFIGURATION FILE
------------------
-By default, the repository "config" file is shared across all working
+By default, the repository `config` file is shared across all working
trees. If the config variables `core.bare` or `core.worktree` are
already present in the config file, they will be applied to the main
working trees only.
In order to have configuration specific to working trees, you can turn
-on "worktreeConfig" extension, e.g.:
+on the `worktreeConfig` extension, e.g.:
------------
$ git config extensions.worktreeConfig true
@@ -255,7 +290,7 @@ configuration in this file with `git config --worktree`. Older Git
versions will refuse to access repositories with this extension.
Note that in this file, the exception for `core.bare` and `core.worktree`
-is gone. If you have them in $GIT_DIR/config before, you must move
+is gone. If they exist in `$GIT_DIR/config`, you must move
them to the `config.worktree` of the main working tree. You may also
take this opportunity to review and move other configuration that you
do not want to share to all working trees:
@@ -268,7 +303,7 @@ do not want to share to all working trees:
DETAILS
-------
Each linked working tree has a private sub-directory in the repository's
-$GIT_DIR/worktrees directory. The private sub-directory's name is usually
+`$GIT_DIR/worktrees` directory. The private sub-directory's name is usually
the base name of the linked working tree's path, possibly appended with a
number to make it unique. For example, when `$GIT_DIR=/path/main/.git` the
command `git worktree add /path/other/test-next next` creates the linked
@@ -276,51 +311,52 @@ working tree in `/path/other/test-next` and also creates a
`$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1`
if `test-next` is already taken).
-Within a linked working tree, $GIT_DIR is set to point to this private
+Within a linked working tree, `$GIT_DIR` is set to point to this private
directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and
-$GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR
+`$GIT_COMMON_DIR` is set to point back to the main working tree's `$GIT_DIR`
(e.g. `/path/main/.git`). These settings are made in a `.git` file located at
the top directory of the linked working tree.
Path resolution via `git rev-parse --git-path` uses either
-$GIT_DIR or $GIT_COMMON_DIR depending on the path. For example, in the
+`$GIT_DIR` or `$GIT_COMMON_DIR` depending on the path. For example, in the
linked working tree `git rev-parse --git-path HEAD` returns
`/path/main/.git/worktrees/test-next/HEAD` (not
`/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git
rev-parse --git-path refs/heads/master` uses
-$GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`,
-since refs are shared across all working trees, except refs/bisect and
-refs/worktree.
+`$GIT_COMMON_DIR` and returns `/path/main/.git/refs/heads/master`,
+since refs are shared across all working trees, except `refs/bisect` and
+`refs/worktree`.
See linkgit:gitrepository-layout[5] for more information. The rule of
thumb is do not make any assumption about whether a path belongs to
-$GIT_DIR or $GIT_COMMON_DIR when you need to directly access something
-inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path.
+`$GIT_DIR` or `$GIT_COMMON_DIR` when you need to directly access something
+inside `$GIT_DIR`. Use `git rev-parse --git-path` to get the final path.
-If you manually move a linked working tree, you need to update the 'gitdir' file
+If you manually move a linked working tree, you need to update the `gitdir` file
in the entry's directory. For example, if a linked working tree is moved
to `/newpath/test-next` and its `.git` file points to
`/path/main/.git/worktrees/test-next`, then update
`/path/main/.git/worktrees/test-next/gitdir` to reference `/newpath/test-next`
-instead.
+instead. Better yet, run `git worktree repair` to reestablish the connection
+automatically.
-To prevent a $GIT_DIR/worktrees entry from being pruned (which
+To prevent a `$GIT_DIR/worktrees` entry from being pruned (which
can be useful in some situations, such as when the
entry's working tree is stored on a portable device), use the
`git worktree lock` command, which adds a file named
-'locked' to the entry's directory. The file contains the reason in
+`locked` to the entry's directory. The file contains the reason in
plain text. For example, if a linked working tree's `.git` file points
to `/path/main/.git/worktrees/test-next` then a file named
`/path/main/.git/worktrees/test-next/locked` will prevent the
`test-next` entry from being pruned. See
linkgit:gitrepository-layout[5] for details.
-When extensions.worktreeConfig is enabled, the config file
+When `extensions.worktreeConfig` is enabled, the config file
`.git/worktrees/<id>/config.worktree` is read after `.git/config` is.
LIST OUTPUT FORMAT
------------------
-The worktree list command has two output formats. The default format shows the
+The `worktree list` command has two output formats. The default format shows the
details on a single line with columns. For example:
------------
@@ -333,10 +369,10 @@ $ git worktree list
Porcelain Format
~~~~~~~~~~~~~~~~
The porcelain format has a line per attribute. Attributes are listed with a
-label and value separated by a single space. Boolean attributes (like 'bare'
-and 'detached') are listed as a label only, and are only present if and only
-if the value is true. The first attribute of a worktree is always `worktree`,
-an empty line indicates the end of the record. For example:
+label and value separated by a single space. Boolean attributes (like `bare`
+and `detached`) are listed as a label only, and are present only
+if the value is true. The first attribute of a working tree is always
+`worktree`, an empty line indicates the end of the record. For example:
------------
$ git worktree list --porcelain
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 3e50065198..c463b937a8 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -304,6 +304,13 @@ users typically do not use them directly.
include::cmds-purehelpers.txt[]
+Guides
+------
+
+The following documentation pages are guides about Git concepts.
+
+include::cmds-guide.txt[]
+
Configuration Mechanism
-----------------------
@@ -497,7 +504,8 @@ double-quotes and respecting backslash escapes. E.g., the value
If this variable is set, the default hash algorithm for new
repositories will be set to this value. This value is currently
ignored when cloning; the setting of the remote repository
- is used instead. The default is "sha1".
+ is used instead. The default is "sha1". THIS VARIABLE IS
+ EXPERIMENTAL! See `--object-format` in linkgit:git-init[1].
Git Commits
~~~~~~~~~~~
@@ -543,8 +551,9 @@ Git Diffs
`GIT_EXTERNAL_DIFF`::
When the environment variable `GIT_EXTERNAL_DIFF` is set, the
- program named by it is called, instead of the diff invocation
- described above. For a path that is added, removed, or modified,
+ program named by it is called to generate diffs, and Git
+ does not use its builtin diff machinery.
+ For a path that is added, removed, or modified,
`GIT_EXTERNAL_DIFF` is called with 7 parameters:
path old-file old-hex old-mode new-file new-hex new-mode
@@ -597,6 +606,12 @@ other
an editor is to be launched. See also linkgit:git-var[1]
and the `core.editor` option in linkgit:git-config[1].
+`GIT_SEQUENCE_EDITOR`::
+ This environment variable overrides the configured Git editor
+ when editing the todo list of an interactive rebase. See also
+ linkit::git-rebase[1] and the `sequence.editor` option in
+ linkit::git-config[1].
+
`GIT_SSH`::
`GIT_SSH_COMMAND`::
If either of these environment variables is set then 'git fetch'
@@ -707,6 +722,10 @@ of clones and fetches.
time of each Git command.
See `GIT_TRACE` for available trace output options.
+`GIT_TRACE_REFS`::
+ Enables trace messages for operations on the ref database.
+ See `GIT_TRACE` for available trace output options.
+
`GIT_TRACE_SETUP`::
Enables trace messages printing the .git, working tree and current
working directory after Git has completed its setup phase.
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
index 9e481aec85..758bf39ba3 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -3,7 +3,7 @@ gitcredentials(7)
NAME
----
-gitcredentials - providing usernames and passwords to Git
+gitcredentials - Providing usernames and passwords to Git
SYNOPSIS
--------
diff --git a/Documentation/gitfaq.txt b/Documentation/gitfaq.txt
index 9cd7a592ac..afdaeab850 100644
--- a/Documentation/gitfaq.txt
+++ b/Documentation/gitfaq.txt
@@ -241,6 +241,59 @@ How do I know if I want to do a fetch or a pull?::
ignore the upstream changes. A pull consists of a fetch followed
immediately by either a merge or rebase. See linkgit:git-pull[1].
+Merging and Rebasing
+--------------------
+
+[[long-running-squash-merge]]
+What kinds of problems can occur when merging long-lived branches with squash merges?::
+ In general, there are a variety of problems that can occur when using squash
+ merges to merge two branches multiple times. These can include seeing extra
+ commits in `git log` output, with a GUI, or when using the `...` notation to
+ express a range, as well as the possibility of needing to re-resolve conflicts
+ again and again.
++
+When Git does a normal merge between two branches, it considers exactly three
+points: the two branches and a third commit, called the _merge base_, which is
+usually the common ancestor of the commits. The result of the merge is the sum
+of the changes between the merge base and each head. When you merge two
+branches with a regular merge commit, this results in a new commit which will
+end up as a merge base when they're merged again, because there is now a new
+common ancestor. Git doesn't have to consider changes that occurred before the
+merge base, so you don't have to re-resolve any conflicts you resolved before.
++
+When you perform a squash merge, a merge commit isn't created; instead, the
+changes from one side are applied as a regular commit to the other side. This
+means that the merge base for these branches won't have changed, and so when Git
+goes to perform its next merge, it considers all of the changes that it
+considered the last time plus the new changes. That means any conflicts may
+need to be re-resolved. Similarly, anything using the `...` notation in `git
+diff`, `git log`, or a GUI will result in showing all of the changes since the
+original merge base.
++
+As a consequence, if you want to merge two long-lived branches repeatedly, it's
+best to always use a regular merge commit.
+
+[[merge-two-revert-one]]
+If I make a change on two branches but revert it on one, why does the merge of those branches include the change?::
+ By default, when Git does a merge, it uses a strategy called the recursive
+ strategy, which does a fancy three-way merge. In such a case, when Git
+ performs the merge, it considers exactly three points: the two heads and a
+ third point, called the _merge base_, which is usually the common ancestor of
+ those commits. Git does not consider the history or the individual commits
+ that have happened on those branches at all.
++
+As a result, if both sides have a change and one side has reverted that change,
+the result is to include the change. This is because the code has changed on
+one side and there is no net change on the other, and in this scenario, Git
+adopts the change.
++
+If this is a problem for you, you can do a rebase instead, rebasing the branch
+with the revert onto the other branch. A rebase in this scenario will revert
+the change, because a rebase applies each individual commit, including the
+revert. Note that rebases rewrite history, so you should avoid rebasing
+published branches unless you're sure you're comfortable with that. See the
+NOTES section in linkgit:git-rebase[1] for more details.
+
Hooks
-----
@@ -310,6 +363,39 @@ information about how to configure files as text or binary.
You can also control this behavior with the `core.whitespace` setting if you
don't wish to remove the carriage returns from your line endings.
+[[always-modified-files-case]]
+Why do I have a file that's always modified?::
+ Internally, Git always stores file names as sequences of bytes and doesn't
+ perform any encoding or case folding. However, Windows and macOS by default
+ both perform case folding on file names. As a result, it's possible to end up
+ with multiple files or directories whose names differ only in case. Git can
+ handle this just fine, but the file system can store only one of these files,
+ so when Git reads the other file to see its contents, it looks modified.
++
+It's best to remove one of the files such that you only have one file. You can
+do this with commands like the following (assuming two files `AFile.txt` and
+`afile.txt`) on an otherwise clean working tree:
++
+----
+$ git rm --cached AFile.txt
+$ git commit -m 'Remove files conflicting in case'
+$ git checkout .
+----
++
+This avoids touching the disk, but removes the additional file. Your project
+may prefer to adopt a naming convention, such as all-lowercase names, to avoid
+this problem from occurring again; such a convention can be checked using a
+`pre-receive` hook or as part of a continuous integration (CI) system.
++
+It is also possible for perpetually modified files to occur on any platform if a
+smudge or clean filter is in use on your system but a file was previously
+committed without running the smudge or clean filter. To fix this, run the
+following on an otherwise clean working tree:
++
+----
+$ git add --renormalize .
+----
+
[[recommended-storage-settings]]
What's the recommended way to store files in Git?::
While Git can store and handle any file of any type, there are some
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 31b601e4bc..6e461ace6e 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -193,7 +193,9 @@ worktree. The hook is given three parameters: the ref of the previous HEAD,
the ref of the new HEAD (which may or may not have changed), and a flag
indicating whether the checkout was a branch checkout (changing branches,
flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git switch` or `git checkout`.
+This hook cannot affect the outcome of `git switch` or `git checkout`,
+other than that the hook's exit status becomes the exit status of
+these two commands.
It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is
used. The first parameter given to the hook is the null-ref, the second the
@@ -333,6 +335,68 @@ The default 'update' hook, when enabled--and with
`hooks.allowunannotated` config option unset or set to false--prevents
unannotated tags to be pushed.
+[[proc-receive]]
+proc-receive
+~~~~~~~~~~~~
+
+This hook is invoked by linkgit:git-receive-pack[1]. If the server has
+set the multi-valued config variable `receive.procReceiveRefs`, and the
+commands sent to 'receive-pack' have matching reference names, these
+commands will be executed by this hook, instead of by the internal
+`execute_commands()` function. This hook is responsible for updating
+the relevant references and reporting the results back to 'receive-pack'.
+
+This hook executes once for the receive operation. It takes no
+arguments, but uses a pkt-line format protocol to communicate with
+'receive-pack' to read commands, push-options and send results. In the
+following example for the protocol, the letter 'S' stands for
+'receive-pack' and the letter 'H' stands for this hook.
+
+ # Version and features negotiation.
+ S: PKT-LINE(version=1\0push-options atomic...)
+ S: flush-pkt
+ H: PKT-LINE(version=1\0push-options...)
+ H: flush-pkt
+
+ # Send commands from server to the hook.
+ S: PKT-LINE(<old-oid> <new-oid> <ref>)
+ S: ... ...
+ S: flush-pkt
+ # Send push-options only if the 'push-options' feature is enabled.
+ S: PKT-LINE(push-option)
+ S: ... ...
+ S: flush-pkt
+
+ # Receive result from the hook.
+ # OK, run this command successfully.
+ H: PKT-LINE(ok <ref>)
+ # NO, I reject it.
+ H: PKT-LINE(ng <ref> <reason>)
+ # Fall through, let 'receive-pack' to execute it.
+ H: PKT-LINE(ok <ref>)
+ H: PKT-LINE(option fall-through)
+ # OK, but has an alternate reference. The alternate reference name
+ # and other status can be given in option directives.
+ H: PKT-LINE(ok <ref>)
+ H: PKT-LINE(option refname <refname>)
+ H: PKT-LINE(option old-oid <old-oid>)
+ H: PKT-LINE(option new-oid <new-oid>)
+ H: PKT-LINE(option forced-update)
+ H: ... ...
+ H: flush-pkt
+
+Each command for the 'proc-receive' hook may point to a pseudo-reference
+and always has a zero-old as its old-oid, while the 'proc-receive' hook
+may update an alternate reference and the alternate reference may exist
+already with a non-zero old-oid. For this case, this hook will use
+"option" directives to report extended attributes for the reference given
+by the leading "ok" directive.
+
+The report of the commands of this hook should have the same order as
+the input. The exit status of the 'proc-receive' hook only determines
+the success or failure of the group of commands sent to it, unless
+atomic push is in use.
+
[[post-receive]]
post-receive
~~~~~~~~~~~~
diff --git a/Documentation/object-format-disclaimer.txt b/Documentation/object-format-disclaimer.txt
new file mode 100644
index 0000000000..4cb106f0d1
--- /dev/null
+++ b/Documentation/object-format-disclaimer.txt
@@ -0,0 +1,6 @@
+THIS OPTION IS EXPERIMENTAL! SHA-256 support is experimental and still
+in an early stage. A SHA-256 repository will in general not be able to
+share work with "regular" SHA-1 repositories. It should be assumed
+that, e.g., Git internal file formats in relation to SHA-256
+repositories may change in backwards-incompatible ways. Only use
+`--object-format=sha256` for testing purposes.
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 7a6da6db78..17c5aac4b7 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -25,8 +25,8 @@ people using 80-column terminals.
--no-abbrev-commit::
Show the full 40-byte hexadecimal commit object name. This negates
- `--abbrev-commit` and those options which imply it such as
- "--oneline". It also overrides the `log.abbrevCommit` variable.
+ `--abbrev-commit`, either explicit or implied by other options such
+ as "--oneline". It also overrides the `log.abbrevCommit` variable.
--oneline::
This is a shorthand for "--pretty=oneline --abbrev-commit"
diff --git a/Documentation/ref-reachability-filters.txt b/Documentation/ref-reachability-filters.txt
new file mode 100644
index 0000000000..9bae46d84c
--- /dev/null
+++ b/Documentation/ref-reachability-filters.txt
@@ -0,0 +1,7 @@
+When combining multiple `--contains` and `--no-contains` filters, only
+references that contain at least one of the `--contains` commits and
+contain none of the `--no-contains` commits are shown.
+
+When combining multiple `--merged` and `--no-merged` filters, only
+references that are reachable from at least one of the `--merged`
+commits and from none of the `--no-merged` commits are shown.
diff --git a/Documentation/rev-list-description.txt b/Documentation/rev-list-description.txt
new file mode 100644
index 0000000000..a9efa7fa27
--- /dev/null
+++ b/Documentation/rev-list-description.txt
@@ -0,0 +1,61 @@
+List commits that are reachable by following the `parent` links from the
+given commit(s), but exclude commits that are reachable from the one(s)
+given with a '{caret}' in front of them. The output is given in reverse
+chronological order by default.
+
+You can think of this as a set operation. Commits reachable from any of
+the commits given on the command line form a set, and then commits reachable
+from any of the ones given with '{caret}' in front are subtracted from that
+set. The remaining commits are what comes out in the command's output.
+Various other options and paths parameters can be used to further limit the
+result.
+
+Thus, the following command:
+
+ifdef::git-rev-list[]
+-----------------------------------------------------------------------
+$ git rev-list foo bar ^baz
+-----------------------------------------------------------------------
+endif::git-rev-list[]
+ifdef::git-log[]
+-----------------------------------------------------------------------
+$ git log foo bar ^baz
+-----------------------------------------------------------------------
+endif::git-log[]
+
+means "list all the commits which are reachable from 'foo' or 'bar', but
+not from 'baz'".
+
+A special notation "'<commit1>'..'<commit2>'" can be used as a
+short-hand for "^'<commit1>' '<commit2>'". For example, either of
+the following may be used interchangeably:
+
+ifdef::git-rev-list[]
+-----------------------------------------------------------------------
+$ git rev-list origin..HEAD
+$ git rev-list HEAD ^origin
+-----------------------------------------------------------------------
+endif::git-rev-list[]
+ifdef::git-log[]
+-----------------------------------------------------------------------
+$ git log origin..HEAD
+$ git log HEAD ^origin
+-----------------------------------------------------------------------
+endif::git-log[]
+
+Another special notation is "'<commit1>'...'<commit2>'" which is useful
+for merges. The resulting set of commits is the symmetric difference
+between the two operands. The following two commands are equivalent:
+
+ifdef::git-rev-list[]
+-----------------------------------------------------------------------
+$ git rev-list A B --not $(git merge-base --all A B)
+$ git rev-list A...B
+-----------------------------------------------------------------------
+endif::git-rev-list[]
+ifdef::git-log[]
+-----------------------------------------------------------------------
+$ git log A B --not $(git merge-base --all A B)
+$ git log A...B
+-----------------------------------------------------------------------
+endif::git-log[]
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index b01b2b6773..002379056a 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -128,8 +128,7 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
because merges into a topic branch tend to be only about
adjusting to updated upstream from time to time, and
this option allows you to ignore the individual commits
- brought in to your history by such a merge. Cannot be
- combined with --bisect.
+ brought in to your history by such a merge.
--not::
Reverses the meaning of the '{caret}' prefix (or lack thereof)
@@ -207,7 +206,7 @@ ifndef::git-rev-list[]
Pretend as if the bad bisection ref `refs/bisect/bad`
was listed and as if it was followed by `--not` and the good
bisection refs `refs/bisect/good-*` on the command
- line. Cannot be combined with --first-parent.
+ line.
endif::git-rev-list[]
--stdin::
@@ -743,7 +742,7 @@ outputs 'midpoint', the output of the two commands
would be of roughly the same length. Finding the change which
introduces a regression is thus reduced to a binary search: repeatedly
generate and test new 'midpoint's until the commit chain is of length
-one. Cannot be combined with --first-parent.
+one.
--bisect-vars::
This calculates the same as `--bisect`, except that refs in
@@ -1117,48 +1116,3 @@ ifdef::git-rev-list[]
by a tab.
endif::git-rev-list[]
endif::git-shortlog[]
-
-ifndef::git-shortlog[]
-ifndef::git-rev-list[]
-Diff Formatting
-~~~~~~~~~~~~~~~
-
-Listed below are options that control the formatting of diff output.
-Some of them are specific to linkgit:git-rev-list[1], however other diff
-options may be given. See linkgit:git-diff-files[1] for more options.
-
--c::
- With this option, diff output for a merge commit
- shows the differences from each of the parents to the merge result
- simultaneously instead of showing pairwise diff between a parent
- and the result one at a time. Furthermore, it lists only files
- which were modified from all parents.
-
---cc::
- This flag implies the `-c` option and further compresses the
- patch output by omitting uninteresting hunks whose contents in
- the parents have only two variants and the merge result picks
- one of them without modification.
-
---combined-all-paths::
- This flag causes combined diffs (used for merge commits) to
- list the name of the file from all parents. It thus only has
- effect when -c or --cc are specified, and is likely only
- useful if filename changes are detected (i.e. when either
- rename or copy detection have been requested).
-
--m::
- This flag makes the merge commits show the full diff like
- regular commits; for each merge parent, a separate log entry
- and diff is generated. An exception is that only diff against
- the first parent is shown when `--first-parent` option is given;
- in that case, the output represents the changes the merge
- brought _into_ the then-current branch.
-
--r::
- Show recursive diffs.
-
--t::
- Show the tree objects in the diff output. This implies `-r`.
-endif::git-rev-list[]
-endif::git-shortlog[]
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 1ad95065c1..d9169c062e 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -254,6 +254,9 @@ specifying a single revision, using the notation described in the
previous section, means the set of commits `reachable` from the given
commit.
+Specifying several revisions means the set of commits reachable from
+any of the given commits.
+
A commit's reachable set is the commit itself and the commits in
its ancestry chain.
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 2e2e7c10c6..5a60bbfa7f 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -232,9 +232,9 @@ There are some macros to easily define options:
will be overwritten, so this should only be used for options where
the last one specified on the command line wins.
-`OPT_PASSTHRU_ARGV(short, long, &argv_array_var, arg_str, description, flags)`::
+`OPT_PASSTHRU_ARGV(short, long, &strvec_var, arg_str, description, flags)`::
Introduce an option where all instances of it on the command-line will
- be reconstructed into an argv_array. This is useful when you need to
+ be reconstructed into a strvec. This is useful when you need to
pass the command-line option, which can be specified multiple times,
to another command.
diff --git a/Documentation/technical/bundle-format.txt b/Documentation/technical/bundle-format.txt
index 0e828151a5..bac558d049 100644
--- a/Documentation/technical/bundle-format.txt
+++ b/Documentation/technical/bundle-format.txt
@@ -7,6 +7,8 @@ The Git bundle format is a format that represents both refs and Git objects.
We will use ABNF notation to define the Git bundle format. See
protocol-common.txt for the details.
+A v2 bundle looks like this:
+
----
bundle = signature *prerequisite *reference LF pack
signature = "# v2 git bundle" LF
@@ -18,9 +20,28 @@ reference = obj-id SP refname LF
pack = ... ; packfile
----
+A v3 bundle looks like this:
+
+----
+bundle = signature *capability *prerequisite *reference LF pack
+signature = "# v3 git bundle" LF
+
+capability = "@" key ["=" value] LF
+prerequisite = "-" obj-id SP comment LF
+comment = *CHAR
+reference = obj-id SP refname LF
+key = 1*(ALPHA / DIGIT / "-")
+value = *(%01-09 / %0b-FF)
+
+pack = ... ; packfile
+----
+
== Semantics
-A Git bundle consists of three parts.
+A Git bundle consists of several parts.
+
+* "Capabilities", which are only in the v3 format, indicate functionality that
+ the bundle requires to be read properly.
* "Prerequisites" lists the objects that are NOT included in the bundle and the
reader of the bundle MUST already have, in order to use the data in the
@@ -46,3 +67,10 @@ put any string here. The reader of the bundle MUST ignore the comment.
Note that the prerequisites does not represent a shallow-clone boundary. The
semantics of the prerequisites and the shallow-clone boundaries are different,
and the Git bundle v2 format cannot represent a shallow clone repository.
+
+== Capabilities
+
+Because there is no opportunity for negotiation, unknown capabilities cause 'git
+bundle' to abort. The only known capability is `object-format`, which specifies
+the hash algorithm in use, and can take the same values as the
+`extensions.objectFormat` configuration value.
diff --git a/Documentation/technical/commit-graph-format.txt b/Documentation/technical/commit-graph-format.txt
index 1beef17182..b3b58880b9 100644
--- a/Documentation/technical/commit-graph-format.txt
+++ b/Documentation/technical/commit-graph-format.txt
@@ -32,7 +32,7 @@ the body into "chunks" and provide a binary lookup table at the beginning
of the body. The header includes certain values, such as number of chunks
and hash type.
-All 4-byte numbers are in network order.
+All multi-byte numbers are in network byte order.
HEADER:
@@ -42,8 +42,13 @@ HEADER:
1-byte version number:
Currently, the only valid version is 1.
- 1-byte Hash Version (1 = SHA-1)
- We infer the hash length (H) from this value.
+ 1-byte Hash Version
+ We infer the hash length (H) from this value:
+ 1 => SHA-1
+ 2 => SHA-256
+ If the hash type does not match the repository's hash algorithm, the
+ commit-graph file should be ignored with a warning presented to the
+ user.
1-byte number (C) of "chunks"
@@ -77,7 +82,7 @@ CHUNK DATA:
Commit Data (ID: {'C', 'D', 'A', 'T' }) (N * (H + 16) bytes)
* The first H bytes are for the OID of the root tree.
* The next 8 bytes are for the positions of the first two parents
- of the ith commit. Stores value 0x7000000 if no parent in that
+ of the ith commit. Stores value 0x70000000 if no parent in that
position. If there are more than two parents, the second value
has its most-significant bit on and the other bits store an array
position into the Extra Edge List chunk.
@@ -120,7 +125,7 @@ CHUNK DATA:
* The rest of the chunk is the concatenation of all the computed Bloom
filters for the commits in lexicographic order.
* Note: Commits with no changes or more than 512 changes have Bloom filters
- of length zero.
+ of length one, with either all bits set to zero or one respectively.
* The BDAT chunk is present if and only if BIDX is present.
Base Graphs List (ID: {'B', 'A', 'S', 'E'}) [Optional]
diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt
index 808fa30b99..f14a7659aa 100644
--- a/Documentation/technical/commit-graph.txt
+++ b/Documentation/technical/commit-graph.txt
@@ -210,12 +210,12 @@ file.
+---------------------+
| |
+-----------------------+ +---------------------+
- | graph-{hash2} |->| |
+ | graph-{hash2} |->| |
+-----------------------+ +---------------------+
| | |
+-----------------------+ +---------------------+
| | | |
- | graph-{hash1} |->| |
+ | graph-{hash1} |->| |
| | | |
+-----------------------+ +---------------------+
| tmp_graphXXX
@@ -223,7 +223,7 @@ file.
| |
| |
| |
- | graph-{hash0} |
+ | graph-{hash0} |
| |
| |
| |
diff --git a/Documentation/technical/hash-function-transition.txt b/Documentation/technical/hash-function-transition.txt
index 5b2db3be1e..6fd20ebbc2 100644
--- a/Documentation/technical/hash-function-transition.txt
+++ b/Documentation/technical/hash-function-transition.txt
@@ -650,7 +650,6 @@ Some initial steps can be implemented independently of one another:
The first user-visible change is the introduction of the objectFormat
extension (without compatObjectFormat). This requires:
-- implementing the loose-object-idx
- teaching fsck about this mode of operation
- using the hash function API (vtable) when computing object names
- signing objects and verifying signatures
@@ -658,6 +657,7 @@ extension (without compatObjectFormat). This requires:
repository
Next comes introduction of compatObjectFormat:
+- implementing the loose-object-idx
- translating object names between object formats
- translating object content between object formats
- generating and verifying signatures in the compat format
diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
index 51a79e63de..96d89ea9b2 100644
--- a/Documentation/technical/http-protocol.txt
+++ b/Documentation/technical/http-protocol.txt
@@ -401,8 +401,9 @@ at all in the request stream:
The stream is terminated by a pkt-line flush (`0000`).
A single "want" or "have" command MUST have one hex formatted
-SHA-1 as its value. Multiple SHA-1s MUST be sent by sending
-multiple commands.
+object name as its value. Multiple object names MUST be sent by sending
+multiple commands. Object names MUST be given using the object format
+negotiated through the `object-format` capability (default SHA-1).
The `have` list is created by popping the first 32 commits
from `c_pending`. Less can be supplied if `c_pending` empties.
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
index faa25c5c52..f9a3644711 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -3,8 +3,11 @@ Git index format
== The Git index file has the following format
- All binary numbers are in network byte order. Version 2 is described
- here unless stated otherwise.
+ All binary numbers are in network byte order.
+ In a repository using the traditional SHA-1, checksums and object IDs
+ (object names) mentioned below are all computed using SHA-1. Similarly,
+ in SHA-256 repositories, these values are computed using SHA-256.
+ Version 2 is described here unless stated otherwise.
- A 12-byte header consisting of
@@ -32,8 +35,7 @@ Git index format
Extension data
- - 160-bit SHA-1 over the content of the index file before this
- checksum.
+ - Hash checksum over the content of the index file before this checksum.
== Index entry
@@ -80,7 +82,7 @@ Git index format
32-bit file size
This is the on-disk size from stat(2), truncated to 32-bit.
- 160-bit SHA-1 for the represented object
+ Object name for the represented object
A 16-bit 'flags' field split into (high to low bits)
@@ -160,8 +162,8 @@ Git index format
- A newline (ASCII 10); and
- - 160-bit object name for the object that would result from writing
- this span of index as a tree.
+ - Object name for the object that would result from writing this span
+ of index as a tree.
An entry can be in an invalidated state and is represented by having
a negative number in the entry_count field. In this case, there is no
@@ -198,7 +200,7 @@ Git index format
stage 1 to 3 (a missing stage is represented by "0" in this field);
and
- - At most three 160-bit object names of the entry in stages from 1 to 3
+ - At most three object names of the entry in stages from 1 to 3
(nothing is written for a missing stage).
=== Split index
@@ -211,8 +213,8 @@ Git index format
The extension consists of:
- - 160-bit SHA-1 of the shared index file. The shared index file path
- is $GIT_DIR/sharedindex.<SHA-1>. If all 160 bits are zero, the
+ - Hash of the shared index file. The shared index file path
+ is $GIT_DIR/sharedindex.<hash>. If all bits are zero, the
index does not require a shared index file.
- An ewah-encoded delete bitmap, each bit represents an entry in the
@@ -253,10 +255,10 @@ Git index format
- 32-bit dir_flags (see struct dir_struct)
- - 160-bit SHA-1 of $GIT_DIR/info/exclude. Null SHA-1 means the file
+ - Hash of $GIT_DIR/info/exclude. A null hash means the file
does not exist.
- - 160-bit SHA-1 of core.excludesfile. Null SHA-1 means the file does
+ - Hash of core.excludesfile. A null hash means the file does
not exist.
- NUL-terminated string of per-dir exclude file name. This usually
@@ -285,13 +287,13 @@ The remaining data of each directory block is grouped by type:
- An ewah bitmap, the n-th bit records "check-only" bit of
read_directory_recursive() for the n-th directory.
- - An ewah bitmap, the n-th bit indicates whether SHA-1 and stat data
+ - An ewah bitmap, the n-th bit indicates whether hash and stat data
is valid for the n-th directory and exists in the next data.
- An array of stat data. The n-th data corresponds with the n-th
"one" bit in the previous ewah bitmap.
- - An array of SHA-1. The n-th SHA-1 corresponds with the n-th "one" bit
+ - An array of hashes. The n-th hash corresponds with the n-th "one" bit
in the previous ewah bitmap.
- One NUL.
@@ -330,12 +332,12 @@ The remaining data of each directory block is grouped by type:
- 32-bit offset to the end of the index entries
- - 160-bit SHA-1 over the extension types and their sizes (but not
+ - Hash over the extension types and their sizes (but not
their contents). E.g. if we have "TREE" extension that is N-bytes
long, "REUC" extension that is M-bytes long, followed by "EOIE",
then the hash would be:
- SHA-1("TREE" + <binary representation of N> +
+ Hash("TREE" + <binary representation of N> +
"REUC" + <binary representation of M>)
== Index Entry Offset Table
diff --git a/Documentation/technical/pack-format.txt b/Documentation/technical/pack-format.txt
index d3a142c652..f96b2e605f 100644
--- a/Documentation/technical/pack-format.txt
+++ b/Documentation/technical/pack-format.txt
@@ -1,6 +1,12 @@
Git pack format
===============
+== Checksums and object IDs
+
+In a repository using the traditional SHA-1, pack checksums, index checksums,
+and object IDs (object names) mentioned below are all computed using SHA-1.
+Similarly, in SHA-256 repositories, these values are computed using SHA-256.
+
== pack-*.pack files have the following format:
- A header appears at the beginning and consists of the following:
@@ -26,7 +32,7 @@ Git pack format
(deltified representation)
n-byte type and length (3-bit type, (n-1)*7+4-bit length)
- 20-byte base object name if OBJ_REF_DELTA or a negative relative
+ base object name if OBJ_REF_DELTA or a negative relative
offset from the delta object's position in the pack if this
is an OBJ_OFS_DELTA object
compressed delta data
@@ -34,7 +40,7 @@ Git pack format
Observation: length of each object is encoded in a variable
length format and is not constrained to 32-bit or anything.
- - The trailer records 20-byte SHA-1 checksum of all of the above.
+ - The trailer records a pack checksum of all of the above.
=== Object types
@@ -58,8 +64,8 @@ ofs-delta and ref-delta, which is only valid in a pack file.
Both ofs-delta and ref-delta store the "delta" to be applied to
another object (called 'base object') to reconstruct the object. The
-difference between them is, ref-delta directly encodes 20-byte base
-object name. If the base object is in the same pack, ofs-delta encodes
+difference between them is, ref-delta directly encodes base object
+name. If the base object is in the same pack, ofs-delta encodes
the offset of the base object in the pack instead.
The base object could also be deltified if it's in the same pack.
@@ -143,14 +149,14 @@ This is the instruction reserved for future expansion.
object is stored in the packfile as the offset from the
beginning.
- 20-byte object name.
+ one object name of the appropriate size.
- The file is concluded with a trailer:
- A copy of the 20-byte SHA-1 checksum at the end of
- corresponding packfile.
+ A copy of the pack checksum at the end of the corresponding
+ packfile.
- 20-byte SHA-1-checksum of all of the above.
+ Index checksum of all of the above.
Pack Idx file:
@@ -198,7 +204,7 @@ Pack file entry: <+
If it is not DELTA, then deflated bytes (the size above
is the size before compression).
If it is REF_DELTA, then
- 20-byte base object name SHA-1 (the size above is the
+ base object name (the size above is the
size of the delta data that follows).
delta data, deflated.
If it is OFS_DELTA, then
@@ -227,9 +233,9 @@ Pack file entry: <+
- A 256-entry fan-out table just like v1.
- - A table of sorted 20-byte SHA-1 object names. These are
- packed together without offset values to reduce the cache
- footprint of the binary search for a specific object name.
+ - A table of sorted object names. These are packed together
+ without offset values to reduce the cache footprint of the
+ binary search for a specific object name.
- A table of 4-byte CRC32 values of the packed object data.
This is new in v2 so compressed data can be copied directly
@@ -248,10 +254,10 @@ Pack file entry: <+
- The same trailer as a v1 pack file:
- A copy of the 20-byte SHA-1 checksum at the end of
+ A copy of the pack checksum at the end of
corresponding packfile.
- 20-byte SHA-1-checksum of all of the above.
+ Index checksum of all of the above.
== multi-pack-index (MIDX) files have the following format:
@@ -273,7 +279,12 @@ HEADER:
Git only writes or recognizes version 1.
1-byte Object Id Version
- Git only writes or recognizes version 1 (SHA1).
+ We infer the length of object IDs (OIDs) from this value:
+ 1 => SHA-1
+ 2 => SHA-256
+ If the hash type does not match the repository's hash algorithm,
+ the multi-pack-index file should be ignored with a warning
+ presented to the user.
1-byte number of "chunks"
@@ -329,4 +340,4 @@ CHUNK DATA:
TRAILER:
- 20-byte SHA1-checksum of the above contents.
+ Index checksum of the above contents.
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index a4573d12ce..e13a2c064d 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -503,8 +503,8 @@ The reference discovery phase is done nearly the same way as it is in the
fetching protocol. Each reference obj-id and name on the server is sent
in packet-line format to the client, followed by a flush-pkt. The only
real difference is that the capability listing is different - the only
-possible values are 'report-status', 'delete-refs', 'ofs-delta' and
-'push-options'.
+possible values are 'report-status', 'report-status-v2', 'delete-refs',
+'ofs-delta', 'atomic' and 'push-options'.
Reference Update Request and Packfile Transfer
----------------------------------------------
@@ -625,7 +625,7 @@ Report Status
-------------
After receiving the pack data from the sender, the receiver sends a
-report if 'report-status' capability is in effect.
+report if 'report-status' or 'report-status-v2' capability is in effect.
It is a short listing of what happened in that update. It will first
list the status of the packfile unpacking as either 'unpack ok' or
'unpack [error]'. Then it will list the status for each of the references
@@ -647,6 +647,41 @@ update was successful, or 'ng [refname] [error]' if the update was not.
error-msg = 1*(OCTET) ; where not "ok"
----
+The 'report-status-v2' capability extends the protocol by adding new option
+lines in order to support reporting of reference rewritten by the
+'proc-receive' hook. The 'proc-receive' hook may handle a command for a
+pseudo-reference which may create or update one or more references, and each
+reference may have different name, different new-oid, and different old-oid.
+
+----
+ report-status-v2 = unpack-status
+ 1*(command-status-v2)
+ flush-pkt
+
+ unpack-status = PKT-LINE("unpack" SP unpack-result)
+ unpack-result = "ok" / error-msg
+
+ command-status-v2 = command-ok-v2 / command-fail
+ command-ok-v2 = command-ok
+ *option-line
+
+ command-ok = PKT-LINE("ok" SP refname)
+ command-fail = PKT-LINE("ng" SP refname SP error-msg)
+
+ error-msg = 1*(OCTET) ; where not "ok"
+
+ option-line = *1(option-refname)
+ *1(option-old-oid)
+ *1(option-new-oid)
+ *1(option-forced-update)
+
+ option-refname = PKT-LINE("option" SP "refname" SP refname)
+ option-old-oid = PKT-LINE("option" SP "old-oid" SP obj-id)
+ option-new-oid = PKT-LINE("option" SP "new-oid" SP obj-id)
+ option-force = PKT-LINE("option" SP "forced-update")
+
+----
+
Updates can be unsuccessful for a number of reasons. The reference can have
changed since the reference discovery phase was originally sent, meaning
someone pushed in the meantime. The reference being pushed could be a
diff --git a/Documentation/technical/partial-clone.txt b/Documentation/technical/partial-clone.txt
index b9e17e7a28..0780d30cac 100644
--- a/Documentation/technical/partial-clone.txt
+++ b/Documentation/technical/partial-clone.txt
@@ -171,20 +171,13 @@ additional flag.
Fetching Missing Objects
------------------------
-- Fetching of objects is done using the existing transport mechanism using
- transport_fetch_refs(), setting a new transport option
- TRANS_OPT_NO_DEPENDENTS to indicate that only the objects themselves are
- desired, not any object that they refer to.
-+
-Because some transports invoke fetch_pack() in the same process, fetch_pack()
-has been updated to not use any object flags when the corresponding argument
-(no_dependents) is set.
+- Fetching of objects is done by invoking a "git fetch" subprocess.
- The local repository sends a request with the hashes of all requested
- objects as "want" lines, and does not perform any packfile negotiation.
+ objects, and does not perform any packfile negotiation.
It then receives a packfile.
-- Because we are reusing the existing fetch-pack mechanism, fetching
+- Because we are reusing the existing fetch mechanism, fetching
currently fetches all objects referred to by the requested objects, even
though they are not necessary.
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index 36ccd14f97..ba869a7d36 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -22,9 +22,9 @@ was sent. Server MUST NOT ignore capabilities that client requested
and server advertised. As a consequence of these rules, server MUST
NOT advertise capabilities it does not understand.
-The 'atomic', 'report-status', 'delete-refs', 'quiet', and 'push-cert'
-capabilities are sent and recognized by the receive-pack (push to server)
-process.
+The 'atomic', 'report-status', 'report-status-v2', 'delete-refs', 'quiet',
+and 'push-cert' capabilities are sent and recognized by the receive-pack
+(push to server) process.
The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
by both upload-pack and receive-pack protocols. The 'agent' capability
@@ -284,6 +284,17 @@ each reference was updated successfully. If any of those were not
successful, it will send back an error message. See pack-protocol.txt
for example messages.
+report-status-v2
+----------------
+
+Capability 'report-status-v2' extends capability 'report-status' by
+adding new "option" directives in order to support reference rewritten by
+the "proc-receive" hook. The "proc-receive" hook may handle a command
+for a pseudo-reference which may create or update a reference with
+different name, new-oid, and old-oid. While the capability
+'report-status' cannot report for such case. See pack-protocol.txt
+for details.
+
delete-refs
-----------
@@ -324,15 +335,19 @@ allow-tip-sha1-in-want
----------------------
If the upload-pack server advertises this capability, fetch-pack may
-send "want" lines with SHA-1s that exist at the server but are not
-advertised by upload-pack.
+send "want" lines with object names that exist at the server but are not
+advertised by upload-pack. For historical reasons, the name of this
+capability contains "sha1". Object names are always given using the
+object format negotiated through the 'object-format' capability.
allow-reachable-sha1-in-want
----------------------------
If the upload-pack server advertises this capability, fetch-pack may
-send "want" lines with SHA-1s that exist at the server but are not
-advertised by upload-pack.
+send "want" lines with object names that exist at the server but are not
+advertised by upload-pack. For historical reasons, the name of this
+capability contains "sha1". Object names are always given using the
+object format negotiated through the 'object-format' capability.
push-cert=<nonce>
-----------------
diff --git a/Documentation/technical/shallow.txt b/Documentation/technical/shallow.txt
index 01dedfe9ff..f3738baa0f 100644
--- a/Documentation/technical/shallow.txt
+++ b/Documentation/technical/shallow.txt
@@ -13,7 +13,7 @@ pretend as if they are root commits (e.g. "git log" traversal
stops after showing them; "git fsck" does not complain saying
the commits listed on their "parent" lines do not exist).
-Each line contains exactly one SHA-1. When read, a commit_graft
+Each line contains exactly one object name. When read, a commit_graft
will be constructed, which has nr_parent < 0 to make it easier
to discern from user provided grafts.
diff --git a/Makefile b/Makefile
index 372139f1f2..fb521dad71 100644
--- a/Makefile
+++ b/Makefile
@@ -348,6 +348,9 @@ all::
# Define NO_INSTALL_HARDLINKS if you prefer to use either symbolic links or
# copies to install built-in git commands e.g. git-cat-file.
#
+# Define SKIP_DASHED_BUILT_INS if you do not need the dashed versions of the
+# built-ins to be linked/copied at all.
+#
# Define USE_NED_ALLOCATOR if you want to replace the platforms default
# memory allocators with the nedmalloc allocator written by Niall Douglas.
#
@@ -462,6 +465,12 @@ all::
# the global variable _wpgmptr containing the absolute path of the current
# executable (this is the case on Windows).
#
+# Define GENERATE_COMPILATION_DATABASE to "yes" to generate JSON compilation
+# database entries during compilation if your compiler supports it, using the
+# `-MJ` flag. The JSON entries will be placed in the `compile_commands/`
+# directory, and the JSON compilation database 'compile_commands.json' will be
+# created at the root of the repository.
+#
# Define DEVELOPER to enable more compiler warnings. Compiler version
# and family are auto detected, but could be overridden by defining
# COMPILER_FEATURES (see config.mak.dev). You can still set
@@ -569,7 +578,6 @@ BUILT_INS =
COMPAT_CFLAGS =
COMPAT_OBJS =
XDIFF_OBJS =
-VCSSVN_OBJS =
GENERATED_H =
EXTRA_CPPFLAGS =
FUZZ_OBJS =
@@ -671,13 +679,9 @@ EXTRA_PROGRAMS =
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS += $(EXTRA_PROGRAMS)
-PROGRAM_OBJS += bugreport.o
-PROGRAM_OBJS += credential-store.o
PROGRAM_OBJS += daemon.o
-PROGRAM_OBJS += fast-import.o
PROGRAM_OBJS += http-backend.o
PROGRAM_OBJS += imap-send.o
-PROGRAM_OBJS += remote-testsvn.o
PROGRAM_OBJS += sh-i18n--envsubst.o
PROGRAM_OBJS += shell.o
@@ -719,6 +723,7 @@ TEST_BUILTINS_OBJS += test-parse-pathspec-file.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-proc-receive.o
TEST_BUILTINS_OBJS += test-progress.o
TEST_BUILTINS_OBJS += test-reach.o
TEST_BUILTINS_OBJS += test-read-cache.o
@@ -749,8 +754,6 @@ TEST_BUILTINS_OBJS += test-xml-encode.o
# Do not add more tests here unless they have extra dependencies. Add
# them in TEST_BUILTINS_OBJS above.
TEST_PROGRAMS_NEED_X += test-fake-ssh
-TEST_PROGRAMS_NEED_X += test-line-buffer
-TEST_PROGRAMS_NEED_X += test-svn-fe
TEST_PROGRAMS_NEED_X += test-tool
TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X))
@@ -775,6 +778,16 @@ BUILT_INS += git-whatchanged$X
# what 'all' will build and 'install' will install in gitexecdir,
# excluding programs for built-in commands
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
+ALL_COMMANDS_TO_INSTALL = $(ALL_PROGRAMS)
+ifeq (,$(SKIP_DASHED_BUILT_INS))
+ALL_COMMANDS_TO_INSTALL += $(BUILT_INS)
+else
+# git-upload-pack, git-receive-pack and git-upload-archive are special: they
+# are _expected_ to be present in the `bin/` directory in their dashed form.
+ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X)
+ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X)
+ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)
+endif
# what 'all' will build but not install in gitexecdir
OTHER_PROGRAMS = git$X
@@ -806,7 +819,6 @@ TEST_SHELL_PATH = $(SHELL_PATH)
LIB_FILE = libgit.a
XDIFF_LIB = xdiff/lib.a
-VCSSVN_LIB = vcs-svn/lib.a
GENERATED_H += config-list.h
GENERATED_H += command-list.h
@@ -828,7 +840,6 @@ LIB_OBJS += apply.o
LIB_OBJS += archive-tar.o
LIB_OBJS += archive-zip.o
LIB_OBJS += archive.o
-LIB_OBJS += argv-array.o
LIB_OBJS += attr.o
LIB_OBJS += base85.o
LIB_OBJS += bisect.o
@@ -892,7 +903,6 @@ LIB_OBJS += hashmap.o
LIB_OBJS += help.o
LIB_OBJS += hex.o
LIB_OBJS += ident.o
-LIB_OBJS += interdiff.o
LIB_OBJS += json-writer.o
LIB_OBJS += kwset.o
LIB_OBJS += levenshtein.o
@@ -917,6 +927,7 @@ LIB_OBJS += mergesort.o
LIB_OBJS += midx.o
LIB_OBJS += name-hash.o
LIB_OBJS += negotiator/default.o
+LIB_OBJS += negotiator/noop.o
LIB_OBJS += negotiator/skipping.o
LIB_OBJS += notes-cache.o
LIB_OBJS += notes-merge.o
@@ -958,6 +969,7 @@ LIB_OBJS += rebase.o
LIB_OBJS += ref-filter.o
LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
+LIB_OBJS += refs/debug.o
LIB_OBJS += refs/files-backend.o
LIB_OBJS += refs/iterator.o
LIB_OBJS += refs/packed-backend.o
@@ -986,6 +998,7 @@ LIB_OBJS += sigchain.o
LIB_OBJS += split-index.o
LIB_OBJS += stable-qsort.o
LIB_OBJS += strbuf.o
+LIB_OBJS += strvec.o
LIB_OBJS += streaming.o
LIB_OBJS += string-list.o
LIB_OBJS += sub-process.o
@@ -1042,6 +1055,7 @@ BUILTIN_OBJS += builtin/archive.o
BUILTIN_OBJS += builtin/bisect--helper.o
BUILTIN_OBJS += builtin/blame.o
BUILTIN_OBJS += builtin/branch.o
+BUILTIN_OBJS += builtin/bugreport.o
BUILTIN_OBJS += builtin/bundle.o
BUILTIN_OBJS += builtin/cat-file.o
BUILTIN_OBJS += builtin/check-attr.o
@@ -1052,6 +1066,9 @@ BUILTIN_OBJS += builtin/checkout-index.o
BUILTIN_OBJS += builtin/checkout.o
BUILTIN_OBJS += builtin/clean.o
BUILTIN_OBJS += builtin/clone.o
+BUILTIN_OBJS += builtin/credential-cache.o
+BUILTIN_OBJS += builtin/credential-cache--daemon.o
+BUILTIN_OBJS += builtin/credential-store.o
BUILTIN_OBJS += builtin/column.o
BUILTIN_OBJS += builtin/commit-graph.o
BUILTIN_OBJS += builtin/commit-tree.o
@@ -1067,6 +1084,7 @@ BUILTIN_OBJS += builtin/diff.o
BUILTIN_OBJS += builtin/difftool.o
BUILTIN_OBJS += builtin/env--helper.o
BUILTIN_OBJS += builtin/fast-export.o
+BUILTIN_OBJS += builtin/fast-import.o
BUILTIN_OBJS += builtin/fetch-pack.o
BUILTIN_OBJS += builtin/fetch.o
BUILTIN_OBJS += builtin/fmt-merge-msg.o
@@ -1214,7 +1232,6 @@ SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag))
BASIC_CFLAGS += -fsanitize=$(SANITIZE) -fno-sanitize-recover=$(SANITIZE)
BASIC_CFLAGS += -fno-omit-frame-pointer
ifneq ($(filter undefined,$(SANITIZERS)),)
-BASIC_CFLAGS += -DNO_UNALIGNED_LOADS
BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS
endif
ifneq ($(filter leak,$(SANITIZERS)),)
@@ -1258,6 +1275,27 @@ $(error please set COMPUTE_HEADER_DEPENDENCIES to yes, no, or auto \
endif
endif
+ifndef GENERATE_COMPILATION_DATABASE
+GENERATE_COMPILATION_DATABASE = no
+endif
+
+ifeq ($(GENERATE_COMPILATION_DATABASE),yes)
+compdb_check = $(shell $(CC) $(ALL_CFLAGS) \
+ -c -MJ /dev/null \
+ -x c /dev/null -o /dev/null 2>&1; \
+ echo $$?)
+ifneq ($(compdb_check),0)
+override GENERATE_COMPILATION_DATABASE = no
+$(warning GENERATE_COMPILATION_DATABASE is set to "yes", but your compiler does not \
+support generating compilation database entries)
+endif
+else
+ifneq ($(GENERATE_COMPILATION_DATABASE),no)
+$(error please set GENERATE_COMPILATION_DATABASE to "yes" or "no" \
+(not "$(GENERATE_COMPILATION_DATABASE)"))
+endif
+endif
+
ifdef SANE_TOOL_PATH
SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|'
@@ -1634,11 +1672,8 @@ ifdef NO_INET_PTON
endif
ifdef NO_UNIX_SOCKETS
BASIC_CFLAGS += -DNO_UNIX_SOCKETS
- EXCLUDED_PROGRAMS += git-credential-cache git-credential-cache--daemon
else
LIB_OBJS += unix-socket.o
- PROGRAM_OBJS += credential-cache.o
- PROGRAM_OBJS += credential-cache--daemon.o
endif
ifdef NO_ICONV
@@ -2066,9 +2101,9 @@ profile-fast: profile-clean
$(MAKE) PROFILE=USE all
-all:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
+all:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
ifneq (,$X)
- $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
+ $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) git$X)), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
endif
all::
@@ -2346,16 +2381,9 @@ XDIFF_OBJS += xdiff/xpatience.o
XDIFF_OBJS += xdiff/xprepare.o
XDIFF_OBJS += xdiff/xutils.o
-VCSSVN_OBJS += vcs-svn/fast_export.o
-VCSSVN_OBJS += vcs-svn/line_buffer.o
-VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/svndiff.o
-VCSSVN_OBJS += vcs-svn/svndump.o
-
TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
$(XDIFF_OBJS) \
- $(VCSSVN_OBJS) \
$(FUZZ_OBJS) \
common-main.o \
git.o
@@ -2381,16 +2409,30 @@ missing_dep_dirs =
dep_args =
endif
+compdb_dir = compile_commands
+
+ifeq ($(GENERATE_COMPILATION_DATABASE),yes)
+missing_compdb_dir = $(compdb_dir)
+$(missing_compdb_dir):
+ @mkdir -p $@
+
+compdb_file = $(compdb_dir)/$(subst /,-,$@.json)
+compdb_args = -MJ $(compdb_file)
+else
+missing_compdb_dir =
+compdb_args =
+endif
+
ASM_SRC := $(wildcard $(OBJECTS:o=S))
ASM_OBJ := $(ASM_SRC:S=o)
C_OBJ := $(filter-out $(ASM_OBJ),$(OBJECTS))
.SUFFIXES:
-$(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs)
- $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
-$(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs)
- $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+$(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
+ $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
+$(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
+ $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
%.s: %.c GIT-CFLAGS FORCE
$(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
@@ -2413,6 +2455,14 @@ else
$(OBJECTS): $(LIB_H) $(GENERATED_H)
endif
+ifeq ($(GENERATE_COMPILATION_DATABASE),yes)
+all:: compile_commands.json
+compile_commands.json:
+ @$(RM) $@
+ $(QUIET_GEN)sed -e '1s/^/[/' -e '$$s/,$$/]/' $(compdb_dir)/*.o.json > $@+
+ @if test -s $@+; then mv $@+ $@; else $(RM) $@+; fi
+endif
+
exec-cmd.sp exec-cmd.s exec-cmd.o: GIT-PREFIX
exec-cmd.sp exec-cmd.s exec-cmd.o: EXTRA_CPPFLAGS = \
'-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
@@ -2459,10 +2509,6 @@ endif
git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-git-bugreport$X: bugreport.o GIT-LDFLAGS $(GITLIBS)
- $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
- $(LIBS)
-
git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(IMAP_SEND_LDFLAGS) $(LIBS)
@@ -2474,10 +2520,6 @@ git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
-git-remote-testsvn$X: remote-testsvn.o GIT-LDFLAGS $(GITLIBS) $(VCSSVN_LIB)
- $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) \
- $(VCSSVN_LIB)
-
$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
$(QUIET_LNCP)$(RM) $@ && \
ln $< $@ 2>/dev/null || \
@@ -2494,9 +2536,6 @@ $(LIB_FILE): $(LIB_OBJS)
$(XDIFF_LIB): $(XDIFF_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
-$(VCSSVN_LIB): $(VCSSVN_OBJS)
- $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
-
export DEFAULT_EDITOR DEFAULT_PAGER
Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -2771,10 +2810,6 @@ perf: all
.PHONY: test perf
-t/helper/test-line-buffer$X: $(VCSSVN_LIB)
-
-t/helper/test-svn-fe$X: $(VCSSVN_LIB)
-
.PRECIOUS: $(TEST_OBJS)
t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
@@ -2899,20 +2934,8 @@ ifdef MSVC
# have already been rolled up into the exe's pdb file.
# We DO NOT have pdb files for the builtin commands (like git-status.exe)
# because it is just a copy/hardlink of git.exe, rather than a unique binary.
- $(INSTALL) git.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
- $(INSTALL) git-shell.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
- $(INSTALL) git-upload-pack.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
- $(INSTALL) git-credential-store.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-daemon.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-fast-import.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-http-backend.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-http-fetch.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-http-push.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-imap-send.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-remote-http.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-remote-testsvn.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-sh-i18n--envsubst.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
- $(INSTALL) git-show-index.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+ $(INSTALL) $(patsubst %.exe,%.pdb,$(filter-out $(BUILT_INS),$(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)))) '$(DESTDIR_SQ)$(bindir_SQ)'
+ $(INSTALL) $(patsubst %.exe,%.pdb,$(filter-out $(BUILT_INS) $(REMOTE_CURL_ALIASES),$(PROGRAMS))) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
ifndef DEBUG
$(INSTALL) $(vcpkg_rel_bin)/*.dll '$(DESTDIR_SQ)$(bindir_SQ)'
$(INSTALL) $(vcpkg_rel_bin)/*.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
@@ -2940,7 +2963,7 @@ ifndef NO_TCLTK
$(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install
endif
ifneq (,$X)
- $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
+ $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
endif
bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
@@ -2958,21 +2981,27 @@ endif
} && \
for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
$(RM) "$$bindir/$$p" && \
- test -n "$(INSTALL_SYMLINKS)" && \
- ln -s "git$X" "$$bindir/$$p" || \
- { test -z "$(NO_INSTALL_HARDLINKS)" && \
- ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
- ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
- cp "$$bindir/git$X" "$$bindir/$$p" || exit; } \
+ if test -z "$(SKIP_DASHED_BUILT_INS)"; \
+ then \
+ test -n "$(INSTALL_SYMLINKS)" && \
+ ln -s "git$X" "$$bindir/$$p" || \
+ { test -z "$(NO_INSTALL_HARDLINKS)" && \
+ ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
+ ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
+ cp "$$bindir/git$X" "$$bindir/$$p" || exit; }; \
+ fi \
done && \
for p in $(BUILT_INS); do \
$(RM) "$$execdir/$$p" && \
- test -n "$(INSTALL_SYMLINKS)" && \
- ln -s "$$destdir_from_execdir_SQ/$(bindir_relative_SQ)/git$X" "$$execdir/$$p" || \
- { test -z "$(NO_INSTALL_HARDLINKS)" && \
- ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
- ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
- cp "$$execdir/git$X" "$$execdir/$$p" || exit; } \
+ if test -z "$(SKIP_DASHED_BUILT_INS)"; \
+ then \
+ test -n "$(INSTALL_SYMLINKS)" && \
+ ln -s "$$destdir_from_execdir_SQ/$(bindir_relative_SQ)/git$X" "$$execdir/$$p" || \
+ { test -z "$(NO_INSTALL_HARDLINKS)" && \
+ ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
+ ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
+ cp "$$execdir/git$X" "$$execdir/$$p" || exit; }; \
+ fi \
done && \
remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
for p in $$remote_curl_aliases; do \
@@ -3024,6 +3053,9 @@ quick-install-html:
### Maintainer's dist rules
+# Allow tweaking to hide local environment effects, like perm bits.
+# With GNU tar, "--mode=u+rwX,og+rX,og-w" would be a good idea, for example.
+TAR_DIST_EXTRA_OPTS =
GIT_TARNAME = git-$(GIT_VERSION)
dist: git-archive$(X) configure
./git-archive --format=tar \
@@ -3032,7 +3064,7 @@ dist: git-archive$(X) configure
@cp configure $(GIT_TARNAME)
@echo $(GIT_VERSION) > $(GIT_TARNAME)/version
@$(MAKE) -C git-gui TARDIR=../$(GIT_TARNAME)/git-gui dist-version
- $(TAR) rf $(GIT_TARNAME).tar \
+ $(TAR) rf $(GIT_TARNAME).tar $(TAR_DIST_EXTRA_OPTS) \
$(GIT_TARNAME)/configure \
$(GIT_TARNAME)/version \
$(GIT_TARNAME)/git-gui/version
@@ -3046,7 +3078,7 @@ ifdef DC_SHA1_SUBMODULE
$(GIT_TARNAME)/sha1collisiondetection/lib/
@cp sha1collisiondetection/lib/ubc_check.[ch] \
$(GIT_TARNAME)/sha1collisiondetection/lib/
- $(TAR) rf $(GIT_TARNAME).tar \
+ $(TAR) rf $(GIT_TARNAME).tar $(TAR_DIST_EXTRA_OPTS) \
$(GIT_TARNAME)/sha1collisiondetection/LICENSE.txt \
$(GIT_TARNAME)/sha1collisiondetection/lib/sha1.[ch] \
$(GIT_TARNAME)/sha1collisiondetection/lib/ubc_check.[ch]
@@ -3063,7 +3095,7 @@ ifneq ($(INCLUDE_DLLS_IN_ARTIFACTS),)
OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll)
endif
-artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
+artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
$(MOFILES)
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
@@ -3080,7 +3112,7 @@ dist-doc:
$(RM) -r .doc-tmp-dir
mkdir .doc-tmp-dir
$(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc
- cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar .
+ cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar $(TAR_DIST_EXTRA_OPTS) .
gzip -n -9 -f $(htmldocs).tar
:
$(RM) -r .doc-tmp-dir
@@ -3090,7 +3122,7 @@ dist-doc:
man5dir=../.doc-tmp-dir/man5 \
man7dir=../.doc-tmp-dir/man7 \
install
- cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
+ cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar $(TAR_DIST_EXTRA_OPTS) .
gzip -n -9 -f $(manpages).tar
$(RM) -r .doc-tmp-dir
@@ -3112,12 +3144,12 @@ cocciclean:
clean: profile-clean coverage-clean cocciclean
$(RM) *.res
$(RM) $(OBJECTS)
- $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
+ $(RM) $(LIB_FILE) $(XDIFF_LIB)
$(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 bin-wrappers $(dep_dirs) $(compdb_dir) compile_commands.json
$(RM) -r po/build/
$(RM) *.pyc *.pyo */*.pyc */*.pyo $(GENERATED_H) $(ETAGS_TARGET) tags cscope*
$(RM) -r $(GIT_TARNAME) .doc-tmp-dir
@@ -3158,7 +3190,7 @@ endif
### Check documentation
#
-ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)
+ALL_COMMANDS = $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB)
ALL_COMMANDS += git
ALL_COMMANDS += git-citool
ALL_COMMANDS += git-gui
@@ -3198,7 +3230,7 @@ check-docs::
-e 's/\.txt//'; \
) | while read how cmd; \
do \
- case " $(patsubst %$X,%,$(ALL_COMMANDS) $(EXCLUDED_PROGRAMS)) " in \
+ case " $(patsubst %$X,%,$(ALL_COMMANDS) $(BUILT_INS) $(EXCLUDED_PROGRAMS)) " in \
*" $$cmd "*) ;; \
*) echo "removed but $$how: $$cmd" ;; \
esac; \
diff --git a/RelNotes b/RelNotes
index a7b4f6dc41..914365022e 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.28.0.txt \ No newline at end of file
+Documentation/RelNotes/2.29.0.txt \ No newline at end of file
diff --git a/add-interactive.c b/add-interactive.c
index 29cd2fe020..555c4abf32 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -935,18 +935,18 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
opts->prompt = N_("Patch update");
count = list_and_choose(s, files, opts);
if (count > 0) {
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
struct pathspec ps_selected = { 0 };
for (i = 0; i < files->items.nr; i++)
if (files->selected[i])
- argv_array_push(&args,
- files->items.items[i].string);
+ strvec_push(&args,
+ files->items.items[i].string);
parse_pathspec(&ps_selected,
PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
- PATHSPEC_LITERAL_PATH, "", args.argv);
+ PATHSPEC_LITERAL_PATH, "", args.v);
res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);
- argv_array_clear(&args);
+ strvec_clear(&args);
clear_pathspec(&ps_selected);
}
@@ -976,18 +976,18 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
count = list_and_choose(s, files, opts);
opts->flags = 0;
if (count > 0) {
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
- argv_array_pushl(&args, "git", "diff", "-p", "--cached",
- oid_to_hex(!is_initial ? &oid :
- s->r->hash_algo->empty_tree),
- "--", NULL);
+ strvec_pushl(&args, "git", "diff", "-p", "--cached",
+ oid_to_hex(!is_initial ? &oid :
+ s->r->hash_algo->empty_tree),
+ "--", NULL);
for (i = 0; i < files->items.nr; i++)
if (files->selected[i])
- argv_array_push(&args,
- files->items.items[i].string);
- res = run_command_v_opt(args.argv, 0);
- argv_array_clear(&args);
+ strvec_push(&args,
+ files->items.items[i].string);
+ res = run_command_v_opt(args.v, 0);
+ strvec_clear(&args);
}
putchar('\n');
diff --git a/add-patch.c b/add-patch.c
index f899389e2c..bd94bd3a7c 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -2,7 +2,7 @@
#include "add-interactive.h"
#include "strbuf.h"
#include "run-command.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "pathspec.h"
#include "color.h"
#include "diff.h"
@@ -266,6 +266,20 @@ struct add_p_state {
const char *revision;
};
+static void add_p_state_clear(struct add_p_state *s)
+{
+ size_t i;
+
+ strbuf_release(&s->answer);
+ strbuf_release(&s->buf);
+ strbuf_release(&s->plain);
+ strbuf_release(&s->colored);
+ for (i = 0; i < s->file_diff_nr; i++)
+ free(s->file_diff[i].hunk);
+ free(s->file_diff);
+ clear_add_i_state(&s->s);
+}
+
static void err(struct add_p_state *s, const char *fmt, ...)
{
va_list args;
@@ -286,12 +300,12 @@ static void setup_child_process(struct add_p_state *s,
va_start(ap, cp);
while ((arg = va_arg(ap, const char *)))
- argv_array_push(&cp->args, arg);
+ strvec_push(&cp->args, arg);
va_end(ap);
cp->git_cmd = 1;
- argv_array_pushf(&cp->env_array,
- INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
+ strvec_pushf(&cp->env_array,
+ INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
}
static int parse_range(const char **p,
@@ -370,7 +384,7 @@ static int is_octal(const char *p, size_t len)
static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
{
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
const char *diff_algorithm = s->s.interactive_diff_algorithm;
struct strbuf *plain = &s->plain, *colored = NULL;
struct child_process cp = CHILD_PROCESS_INIT;
@@ -380,32 +394,32 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
struct hunk *hunk = NULL;
int res;
- argv_array_pushv(&args, s->mode->diff_cmd);
+ strvec_pushv(&args, s->mode->diff_cmd);
if (diff_algorithm)
- argv_array_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
+ strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
if (s->revision) {
struct object_id oid;
- argv_array_push(&args,
- /* could be on an unborn branch */
- !strcmp("HEAD", s->revision) &&
- get_oid("HEAD", &oid) ?
- empty_tree_oid_hex() : s->revision);
+ strvec_push(&args,
+ /* could be on an unborn branch */
+ !strcmp("HEAD", s->revision) &&
+ get_oid("HEAD", &oid) ?
+ empty_tree_oid_hex() : s->revision);
}
- color_arg_index = args.argc;
+ color_arg_index = args.nr;
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
- argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
+ strvec_pushl(&args, "--no-color", "-p", "--", NULL);
for (i = 0; i < ps->nr; i++)
- argv_array_push(&args, ps->items[i].original);
+ strvec_push(&args, ps->items[i].original);
setup_child_process(s, &cp, NULL);
- cp.argv = args.argv;
+ cp.argv = args.v;
res = capture_command(&cp, plain, 0);
if (res) {
- argv_array_clear(&args);
+ strvec_clear(&args);
return error(_("could not parse diff"));
}
if (!plain->len) {
- argv_array_clear(&args);
+ strvec_clear(&args);
return 0;
}
strbuf_complete_line(plain);
@@ -415,11 +429,11 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
const char *diff_filter = s->s.interactive_diff_filter;
setup_child_process(s, &colored_cp, NULL);
- xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
- colored_cp.argv = args.argv;
+ xsnprintf((char *)args.v[color_arg_index], 8, "--color");
+ colored_cp.argv = args.v;
colored = &s->colored;
res = capture_command(&colored_cp, colored, 0);
- argv_array_clear(&args);
+ strvec_clear(&args);
if (res)
return error(_("could not parse colored diff"));
@@ -444,24 +458,22 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
colored_p = colored->buf;
colored_pend = colored_p + colored->len;
}
- argv_array_clear(&args);
+ strvec_clear(&args);
/* parse files and hunks */
p = plain->buf;
pend = p + plain->len;
while (p != pend) {
char *eol = memchr(p, '\n', pend - p);
- const char *deleted = NULL, *added = NULL, *mode_change = NULL;
+ const char *deleted = NULL, *mode_change = NULL;
if (!eol)
eol = pend;
if (starts_with(p, "diff ")) {
- s->file_diff_nr++;
- ALLOC_GROW(s->file_diff, s->file_diff_nr,
+ ALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,
file_diff_alloc);
file_diff = s->file_diff + s->file_diff_nr - 1;
- memset(file_diff, 0, sizeof(*file_diff));
hunk = &file_diff->head;
hunk->start = p - plain->buf;
if (colored_p)
@@ -470,12 +482,11 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
} else if (p == plain->buf)
BUG("diff starts with unexpected line:\n"
"%.*s\n", (int)(eol - p), p);
- else if (file_diff->deleted || file_diff->added)
+ else if (file_diff->deleted)
; /* keep the rest of the file in a single "hunk" */
else if (starts_with(p, "@@ ") ||
(hunk == &file_diff->head &&
- (skip_prefix(p, "deleted file", &deleted) ||
- skip_prefix(p, "new file", &added)))) {
+ (skip_prefix(p, "deleted file", &deleted)))) {
if (marker == '-' || marker == '+')
/*
* Should not happen; previous hunk did not end
@@ -483,11 +494,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
*/
hunk->splittable_into++;
- file_diff->hunk_nr++;
- ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
+ ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
file_diff->hunk_alloc);
hunk = file_diff->hunk + file_diff->hunk_nr - 1;
- memset(hunk, 0, sizeof(*hunk));
hunk->start = p - plain->buf;
if (colored)
@@ -495,8 +504,6 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
if (deleted)
file_diff->deleted = 1;
- else if (added)
- file_diff->added = 1;
else if (parse_hunk_header(s, hunk) < 0)
return -1;
@@ -506,12 +513,15 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
*/
marker = *p;
} else if (hunk == &file_diff->head &&
+ starts_with(p, "new file")) {
+ file_diff->added = 1;
+ } else if (hunk == &file_diff->head &&
skip_prefix(p, "old mode ", &mode_change) &&
is_octal(mode_change, eol - mode_change)) {
if (file_diff->mode_change)
BUG("double mode change?\n\n%.*s",
(int)(eol - plain->buf), plain->buf);
- if (file_diff->hunk_nr++)
+ if (file_diff->hunk_nr)
BUG("mode change in the middle?\n\n%.*s",
(int)(eol - plain->buf), plain->buf);
@@ -520,9 +530,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
* is _part of_ the header "hunk".
*/
file_diff->mode_change = 1;
- ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
+ ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
file_diff->hunk_alloc);
- memset(file_diff->hunk, 0, sizeof(struct hunk));
file_diff->hunk->start = p - plain->buf;
if (colored_p)
file_diff->hunk->colored_start =
@@ -1158,7 +1167,7 @@ static int run_apply_check(struct add_p_state *s,
setup_child_process(s, &cp,
"apply", "--check", NULL);
- argv_array_pushv(&cp.args, s->mode->apply_check_args);
+ strvec_pushv(&cp.args, s->mode->apply_check_args);
if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
return error(_("'git apply --cached' failed"));
@@ -1203,7 +1212,7 @@ static int edit_hunk_loop(struct add_p_state *s,
for (;;) {
int res = edit_hunk_manually(s, hunk);
if (res == 0) {
- /* abandonded */
+ /* abandoned */
*hunk = backup;
return -1;
}
@@ -1357,8 +1366,18 @@ static int patch_update_file(struct add_p_state *s,
struct child_process cp = CHILD_PROCESS_INIT;
int colored = !!s->colored.len, quit = 0;
enum prompt_mode_type prompt_mode_type;
-
- if (!file_diff->hunk_nr)
+ enum {
+ ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
+ ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
+ ALLOW_GOTO_NEXT_HUNK = 1 << 2,
+ ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
+ ALLOW_SEARCH_AND_GOTO = 1 << 4,
+ ALLOW_SPLIT = 1 << 5,
+ ALLOW_EDIT = 1 << 6
+ } permitted = 0;
+
+ /* Empty added files have no hunks */
+ if (!file_diff->hunk_nr && !file_diff->added)
return 0;
strbuf_reset(&s->buf);
@@ -1367,21 +1386,25 @@ static int patch_update_file(struct add_p_state *s,
for (;;) {
if (hunk_index >= file_diff->hunk_nr)
hunk_index = 0;
- hunk = file_diff->hunk + hunk_index;
-
+ hunk = file_diff->hunk_nr
+ ? file_diff->hunk + hunk_index
+ : &file_diff->head;
undecided_previous = -1;
- for (i = hunk_index - 1; i >= 0; i--)
- if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
- undecided_previous = i;
- break;
- }
-
undecided_next = -1;
- for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
- if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
- undecided_next = i;
- break;
- }
+
+ if (file_diff->hunk_nr) {
+ for (i = hunk_index - 1; i >= 0; i--)
+ if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
+ undecided_previous = i;
+ break;
+ }
+
+ for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
+ if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
+ undecided_next = i;
+ break;
+ }
+ }
/* Everything decided? */
if (undecided_previous < 0 && undecided_next < 0 &&
@@ -1389,26 +1412,41 @@ static int patch_update_file(struct add_p_state *s,
break;
strbuf_reset(&s->buf);
- render_hunk(s, hunk, 0, colored, &s->buf);
- fputs(s->buf.buf, stdout);
-
- strbuf_reset(&s->buf);
- if (undecided_previous >= 0)
- strbuf_addstr(&s->buf, ",k");
- if (hunk_index)
- strbuf_addstr(&s->buf, ",K");
- if (undecided_next >= 0)
- strbuf_addstr(&s->buf, ",j");
- if (hunk_index + 1 < file_diff->hunk_nr)
- strbuf_addstr(&s->buf, ",J");
- if (file_diff->hunk_nr > 1)
- strbuf_addstr(&s->buf, ",g,/");
- if (hunk->splittable_into > 1)
- strbuf_addstr(&s->buf, ",s");
- if (hunk_index + 1 > file_diff->mode_change &&
- !file_diff->deleted)
- strbuf_addstr(&s->buf, ",e");
+ if (file_diff->hunk_nr) {
+ render_hunk(s, hunk, 0, colored, &s->buf);
+ fputs(s->buf.buf, stdout);
+ strbuf_reset(&s->buf);
+ if (undecided_previous >= 0) {
+ permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
+ strbuf_addstr(&s->buf, ",k");
+ }
+ if (hunk_index) {
+ permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
+ strbuf_addstr(&s->buf, ",K");
+ }
+ if (undecided_next >= 0) {
+ permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
+ strbuf_addstr(&s->buf, ",j");
+ }
+ if (hunk_index + 1 < file_diff->hunk_nr) {
+ permitted |= ALLOW_GOTO_NEXT_HUNK;
+ strbuf_addstr(&s->buf, ",J");
+ }
+ if (file_diff->hunk_nr > 1) {
+ permitted |= ALLOW_SEARCH_AND_GOTO;
+ strbuf_addstr(&s->buf, ",g,/");
+ }
+ if (hunk->splittable_into > 1) {
+ permitted |= ALLOW_SPLIT;
+ strbuf_addstr(&s->buf, ",s");
+ }
+ if (hunk_index + 1 > file_diff->mode_change &&
+ !file_diff->deleted) {
+ permitted |= ALLOW_EDIT;
+ strbuf_addstr(&s->buf, ",e");
+ }
+ }
if (file_diff->deleted)
prompt_mode_type = PROMPT_DELETION;
else if (file_diff->added)
@@ -1421,7 +1459,9 @@ static int patch_update_file(struct add_p_state *s,
color_fprintf(stdout, s->s.prompt_color,
"(%"PRIuMAX"/%"PRIuMAX") ",
(uintmax_t)hunk_index + 1,
- (uintmax_t)file_diff->hunk_nr);
+ (uintmax_t)(file_diff->hunk_nr
+ ? file_diff->hunk_nr
+ : 1));
color_fprintf(stdout, s->s.prompt_color,
_(s->mode->prompt_mode[prompt_mode_type]),
s->buf.buf);
@@ -1441,38 +1481,46 @@ soft_increment:
hunk->use = SKIP_HUNK;
goto soft_increment;
} else if (ch == 'a') {
- for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
- hunk = file_diff->hunk + hunk_index;
- if (hunk->use == UNDECIDED_HUNK)
- hunk->use = USE_HUNK;
+ if (file_diff->hunk_nr) {
+ for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
+ hunk = file_diff->hunk + hunk_index;
+ if (hunk->use == UNDECIDED_HUNK)
+ hunk->use = USE_HUNK;
+ }
+ } else if (hunk->use == UNDECIDED_HUNK) {
+ hunk->use = USE_HUNK;
}
} else if (ch == 'd' || ch == 'q') {
- for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
- hunk = file_diff->hunk + hunk_index;
- if (hunk->use == UNDECIDED_HUNK)
- hunk->use = SKIP_HUNK;
+ if (file_diff->hunk_nr) {
+ for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
+ hunk = file_diff->hunk + hunk_index;
+ if (hunk->use == UNDECIDED_HUNK)
+ hunk->use = SKIP_HUNK;
+ }
+ } else if (hunk->use == UNDECIDED_HUNK) {
+ hunk->use = SKIP_HUNK;
}
if (ch == 'q') {
quit = 1;
break;
}
} else if (s->answer.buf[0] == 'K') {
- if (hunk_index)
+ if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
hunk_index--;
else
err(s, _("No previous hunk"));
} else if (s->answer.buf[0] == 'J') {
- if (hunk_index + 1 < file_diff->hunk_nr)
+ if (permitted & ALLOW_GOTO_NEXT_HUNK)
hunk_index++;
else
err(s, _("No next hunk"));
} else if (s->answer.buf[0] == 'k') {
- if (undecided_previous >= 0)
+ if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
hunk_index = undecided_previous;
else
err(s, _("No previous hunk"));
} else if (s->answer.buf[0] == 'j') {
- if (undecided_next >= 0)
+ if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
hunk_index = undecided_next;
else
err(s, _("No next hunk"));
@@ -1480,7 +1528,7 @@ soft_increment:
char *pend;
unsigned long response;
- if (file_diff->hunk_nr < 2) {
+ if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
err(s, _("No other hunks to goto"));
continue;
}
@@ -1517,7 +1565,7 @@ soft_increment:
regex_t regex;
int ret;
- if (file_diff->hunk_nr < 2) {
+ if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
err(s, _("No other hunks to search"));
continue;
}
@@ -1562,7 +1610,7 @@ soft_increment:
hunk_index = i;
} else if (s->answer.buf[0] == 's') {
size_t splittable_into = hunk->splittable_into;
- if (splittable_into < 2)
+ if (!(permitted & ALLOW_SPLIT))
err(s, _("Sorry, cannot split this hunk"));
else if (!split_hunk(s, file_diff,
hunk - file_diff->hunk))
@@ -1570,7 +1618,7 @@ soft_increment:
_("Split into %d hunks."),
(int)splittable_into);
} else if (s->answer.buf[0] == 'e') {
- if (hunk_index + 1 == file_diff->mode_change)
+ if (!(permitted & ALLOW_EDIT))
err(s, _("Sorry, cannot edit this hunk"));
else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
hunk->use = USE_HUNK;
@@ -1608,7 +1656,8 @@ soft_increment:
if (file_diff->hunk[i].use == USE_HUNK)
break;
- if (i < file_diff->hunk_nr) {
+ if (i < file_diff->hunk_nr ||
+ (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
/* At least one hunk selected: apply */
strbuf_reset(&s->buf);
reassemble_patch(s, file_diff, 0, &s->buf);
@@ -1619,12 +1668,12 @@ soft_increment:
s->mode->is_reverse);
else {
setup_child_process(s, &cp, "apply", NULL);
- argv_array_pushv(&cp.args, s->mode->apply_args);
+ strvec_pushv(&cp.args, s->mode->apply_args);
if (pipe_command(&cp, s->buf.buf, s->buf.len,
NULL, 0, NULL, 0))
error(_("'git apply' failed"));
}
- if (!repo_read_index(s->s.r))
+ if (repo_read_index(s->s.r) >= 0)
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1, NULL, NULL, NULL);
}
@@ -1673,9 +1722,7 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
NULL, NULL, NULL) < 0) ||
parse_diff(&s, ps) < 0) {
- strbuf_release(&s.plain);
- strbuf_release(&s.colored);
- clear_add_i_state(&s.s);
+ add_p_state_clear(&s);
return -1;
}
@@ -1690,10 +1737,6 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
else if (binary_count == s.file_diff_nr)
fprintf(stderr, _("Only binary files changed.\n"));
- strbuf_release(&s.answer);
- strbuf_release(&s.buf);
- strbuf_release(&s.plain);
- strbuf_release(&s.colored);
- clear_add_i_state(&s.s);
+ add_p_state_clear(&s);
return 0;
}
diff --git a/apply.c b/apply.c
index 8bff604dbe..76dba93c97 100644
--- a/apply.c
+++ b/apply.c
@@ -30,8 +30,8 @@ struct gitdiff_data {
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_get_string("apply.whitespace", &apply_default_whitespace);
+ git_config_get_string("apply.ignorewhitespace", &apply_default_ignorewhitespace);
git_config(git_xmerge_config, NULL);
}
@@ -3178,7 +3178,7 @@ static int apply_binary(struct apply_state *state,
return 0; /* deletion patch */
}
- if (has_object_file(&oid)) {
+ if (has_object(the_repository, &oid, 0)) {
/* We already have the postimage */
enum object_type type;
unsigned long size;
@@ -3740,6 +3740,7 @@ static int check_preimage(struct apply_state *state,
#define EXISTS_IN_INDEX 1
#define EXISTS_IN_WORKTREE 2
+#define EXISTS_IN_INDEX_AS_ITA 3
static int check_to_create(struct apply_state *state,
const char *new_name,
@@ -3747,10 +3748,23 @@ static int check_to_create(struct apply_state *state,
{
struct stat nst;
- if (state->check_index &&
- index_name_pos(state->repo->index, new_name, strlen(new_name)) >= 0 &&
- !ok_if_exists)
- return EXISTS_IN_INDEX;
+ if (state->check_index && (!ok_if_exists || !state->cached)) {
+ int pos;
+
+ pos = index_name_pos(state->repo->index, new_name, strlen(new_name));
+ if (pos >= 0) {
+ struct cache_entry *ce = state->repo->index->cache[pos];
+
+ /* allow ITA, as they do not yet exist in the index */
+ if (!ok_if_exists && !(ce->ce_flags & CE_INTENT_TO_ADD))
+ return EXISTS_IN_INDEX;
+
+ /* ITA entries can never match working tree files */
+ if (!state->cached && (ce->ce_flags & CE_INTENT_TO_ADD))
+ return EXISTS_IN_INDEX_AS_ITA;
+ }
+ }
+
if (state->cached)
return 0;
@@ -3935,6 +3949,9 @@ static int check_patch(struct apply_state *state, struct patch *patch)
case EXISTS_IN_INDEX:
return error(_("%s: already exists in index"), new_name);
break;
+ case EXISTS_IN_INDEX_AS_ITA:
+ return error(_("%s: does not match index"), new_name);
+ break;
case EXISTS_IN_WORKTREE:
return error(_("%s: already exists in working directory"),
new_name);
diff --git a/archive.c b/archive.c
index fb39706120..0de6048bfc 100644
--- a/archive.c
+++ b/archive.c
@@ -397,10 +397,10 @@ static void parse_treeish_arg(const char **argv,
const char *colon = strchrnul(name, ':');
int refnamelen = colon - name;
- if (!dwim_ref(name, refnamelen, &oid, &ref))
+ if (!dwim_ref(name, refnamelen, &oid, &ref, 0))
die(_("no such ref: %.*s"), refnamelen, name);
} else {
- dwim_ref(name, strlen(name), &oid, &ref);
+ dwim_ref(name, strlen(name), &oid, &ref, 0);
}
if (get_oid(name, &oid))
diff --git a/argv-array.c b/argv-array.c
deleted file mode 100644
index 61ef8c0dfd..0000000000
--- a/argv-array.c
+++ /dev/null
@@ -1,109 +0,0 @@
-#include "cache.h"
-#include "argv-array.h"
-#include "strbuf.h"
-
-const char *empty_argv[] = { NULL };
-
-void argv_array_init(struct argv_array *array)
-{
- array->argv = empty_argv;
- array->argc = 0;
- array->alloc = 0;
-}
-
-static void argv_array_push_nodup(struct argv_array *array, const char *value)
-{
- if (array->argv == empty_argv)
- array->argv = NULL;
-
- ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
- array->argv[array->argc++] = value;
- array->argv[array->argc] = NULL;
-}
-
-const char *argv_array_push(struct argv_array *array, const char *value)
-{
- argv_array_push_nodup(array, xstrdup(value));
- return array->argv[array->argc - 1];
-}
-
-const char *argv_array_pushf(struct argv_array *array, const char *fmt, ...)
-{
- va_list ap;
- struct strbuf v = STRBUF_INIT;
-
- va_start(ap, fmt);
- strbuf_vaddf(&v, fmt, ap);
- va_end(ap);
-
- argv_array_push_nodup(array, strbuf_detach(&v, NULL));
- return array->argv[array->argc - 1];
-}
-
-void argv_array_pushl(struct argv_array *array, ...)
-{
- va_list ap;
- const char *arg;
-
- va_start(ap, array);
- while ((arg = va_arg(ap, const char *)))
- argv_array_push(array, arg);
- va_end(ap);
-}
-
-void argv_array_pushv(struct argv_array *array, const char **argv)
-{
- for (; *argv; argv++)
- argv_array_push(array, *argv);
-}
-
-void argv_array_pop(struct argv_array *array)
-{
- if (!array->argc)
- return;
- free((char *)array->argv[array->argc - 1]);
- array->argv[array->argc - 1] = NULL;
- array->argc--;
-}
-
-void argv_array_split(struct argv_array *array, const char *to_split)
-{
- while (isspace(*to_split))
- to_split++;
- for (;;) {
- const char *p = to_split;
-
- if (!*p)
- break;
-
- while (*p && !isspace(*p))
- p++;
- argv_array_push_nodup(array, xstrndup(to_split, p - to_split));
-
- while (isspace(*p))
- p++;
- to_split = p;
- }
-}
-
-void argv_array_clear(struct argv_array *array)
-{
- if (array->argv != empty_argv) {
- int i;
- for (i = 0; i < array->argc; i++)
- free((char *)array->argv[i]);
- free(array->argv);
- }
- argv_array_init(array);
-}
-
-const char **argv_array_detach(struct argv_array *array)
-{
- if (array->argv == empty_argv)
- return xcalloc(1, sizeof(const char *));
- else {
- const char **ret = array->argv;
- argv_array_init(array);
- return ret;
- }
-}
diff --git a/bisect.c b/bisect.c
index d5e830410f..f5b1368128 100644
--- a/bisect.c
+++ b/bisect.c
@@ -11,10 +11,11 @@
#include "log-tree.h"
#include "bisect.h"
#include "oid-array.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "commit-slab.h"
#include "commit-reach.h"
#include "object-store.h"
+#include "dir.h"
static struct oid_array good_revs;
static struct oid_array skipped_revs;
@@ -88,15 +89,16 @@ static inline void weight_set(struct commit_list *elem, int weight)
**commit_weight_at(&commit_weight, elem->item) = weight;
}
-static int count_interesting_parents(struct commit *commit)
+static int count_interesting_parents(struct commit *commit, unsigned bisect_flags)
{
struct commit_list *p;
int count;
for (count = 0, p = commit->parents; p; p = p->next) {
- if (p->item->object.flags & UNINTERESTING)
- continue;
- count++;
+ if (!(p->item->object.flags & UNINTERESTING))
+ count++;
+ if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY)
+ break;
}
return count;
}
@@ -135,7 +137,7 @@ static void show_list(const char *debug, int counted, int nr,
for (p = list; p; p = p->next) {
struct commit_list *pp;
struct commit *commit = p->item;
- unsigned flags = commit->object.flags;
+ unsigned commit_flags = commit->object.flags;
enum object_type type;
unsigned long size;
char *buf = read_object_file(&commit->object.oid, &type,
@@ -144,9 +146,9 @@ static void show_list(const char *debug, int counted, int nr,
int subject_len;
fprintf(stderr, "%c%c%c ",
- (flags & TREESAME) ? ' ' : 'T',
- (flags & UNINTERESTING) ? 'U' : ' ',
- (flags & COUNTED) ? 'C' : ' ');
+ (commit_flags & TREESAME) ? ' ' : 'T',
+ (commit_flags & UNINTERESTING) ? 'U' : ' ',
+ (commit_flags & COUNTED) ? 'C' : ' ');
if (*commit_weight_at(&commit_weight, p->item))
fprintf(stderr, "%3d", weight(p));
else
@@ -171,9 +173,9 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr)
best = list;
for (p = list; p; p = p->next) {
int distance;
- unsigned flags = p->item->object.flags;
+ unsigned commit_flags = p->item->object.flags;
- if (flags & TREESAME)
+ if (commit_flags & TREESAME)
continue;
distance = weight(p);
if (nr - distance < distance)
@@ -212,9 +214,9 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
for (p = list, cnt = 0; p; p = p->next) {
int distance;
- unsigned flags = p->item->object.flags;
+ unsigned commit_flags = p->item->object.flags;
- if (flags & TREESAME)
+ if (commit_flags & TREESAME)
continue;
distance = weight(p);
if (nr - distance < distance)
@@ -259,7 +261,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
*/
static struct commit_list *do_find_bisection(struct commit_list *list,
int nr, int *weights,
- int find_all)
+ unsigned bisect_flags)
{
int n, counted;
struct commit_list *p;
@@ -268,12 +270,12 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
for (n = 0, p = list; p; p = p->next) {
struct commit *commit = p->item;
- unsigned flags = commit->object.flags;
+ unsigned commit_flags = commit->object.flags;
*commit_weight_at(&commit_weight, p->item) = &weights[n++];
- switch (count_interesting_parents(commit)) {
+ switch (count_interesting_parents(commit, bisect_flags)) {
case 0:
- if (!(flags & TREESAME)) {
+ if (!(commit_flags & TREESAME)) {
weight_set(p, 1);
counted++;
show_list("bisection 2 count one",
@@ -314,11 +316,13 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
continue;
if (weight(p) != -2)
continue;
+ if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY)
+ BUG("shouldn't be calling count-distance in fp mode");
weight_set(p, count_distance(p));
clear_distance(list);
/* Does it happen to be at exactly half-way? */
- if (!find_all && halfway(p, nr))
+ if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
return p;
counted++;
}
@@ -328,11 +332,14 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
while (counted < nr) {
for (p = list; p; p = p->next) {
struct commit_list *q;
- unsigned flags = p->item->object.flags;
+ unsigned commit_flags = p->item->object.flags;
if (0 <= weight(p))
continue;
- for (q = p->item->parents; q; q = q->next) {
+
+ for (q = p->item->parents;
+ q;
+ q = bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY ? NULL : q->next) {
if (q->item->object.flags & UNINTERESTING)
continue;
if (0 <= weight(q))
@@ -346,7 +353,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
* add one for p itself if p is to be counted,
* otherwise inherit it from q directly.
*/
- if (!(flags & TREESAME)) {
+ if (!(commit_flags & TREESAME)) {
weight_set(p, weight(q)+1);
counted++;
show_list("bisection 2 count one",
@@ -356,21 +363,21 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
weight_set(p, weight(q));
/* Does it happen to be at exactly half-way? */
- if (!find_all && halfway(p, nr))
+ if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr))
return p;
}
}
show_list("bisection 2 counted all", counted, nr, list);
- if (!find_all)
+ if (!(bisect_flags & FIND_BISECTION_ALL))
return best_bisection(list, nr);
else
return best_bisection_sorted(list, nr);
}
void find_bisection(struct commit_list **commit_list, int *reaches,
- int *all, int find_all)
+ int *all, unsigned bisect_flags)
{
int nr, on_list;
struct commit_list *list, *p, *best, *next, *last;
@@ -386,16 +393,16 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
for (nr = on_list = 0, last = NULL, p = *commit_list;
p;
p = next) {
- unsigned flags = p->item->object.flags;
+ unsigned commit_flags = p->item->object.flags;
next = p->next;
- if (flags & UNINTERESTING) {
+ if (commit_flags & UNINTERESTING) {
free(p);
continue;
}
p->next = last;
last = p;
- if (!(flags & TREESAME))
+ if (!(commit_flags & TREESAME))
nr++;
on_list++;
}
@@ -406,9 +413,9 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
weights = xcalloc(on_list, sizeof(*weights));
/* Do the real work of finding bisection commit. */
- best = do_find_bisection(list, nr, weights, find_all);
+ best = do_find_bisection(list, nr, weights, bisect_flags);
if (best) {
- if (!find_all) {
+ if (!(bisect_flags & FIND_BISECTION_ALL)) {
list->item = best->item;
free_commit_list(list->next);
best = list;
@@ -454,9 +461,10 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
static GIT_PATH_FUNC(git_path_head_name, "head-name")
-static void read_bisect_paths(struct argv_array *array)
+static void read_bisect_paths(struct strvec *array)
{
struct strbuf str = STRBUF_INIT;
const char *filename = git_path_bisect_names();
@@ -464,7 +472,7 @@ static void read_bisect_paths(struct argv_array *array)
while (strbuf_getline_lf(&str, fp) != EOF) {
strbuf_trim(&str);
- if (sq_dequote_to_argv_array(str.buf, array))
+ if (sq_dequote_to_strvec(str.buf, array))
die(_("Badly quoted content in file '%s': %s"),
filename, str.buf);
}
@@ -632,7 +640,7 @@ static void bisect_rev_setup(struct repository *r, struct rev_info *revs,
const char *bad_format, const char *good_format,
int read_paths)
{
- struct argv_array rev_argv = ARGV_ARRAY_INIT;
+ struct strvec rev_argv = STRVEC_INIT;
int i;
repo_init_revisions(r, revs, prefix);
@@ -640,16 +648,16 @@ static void bisect_rev_setup(struct repository *r, struct rev_info *revs,
revs->commit_format = CMIT_FMT_UNSPECIFIED;
/* rev_argv.argv[0] will be ignored by setup_revisions */
- argv_array_push(&rev_argv, "bisect_rev_setup");
- argv_array_pushf(&rev_argv, bad_format, oid_to_hex(current_bad_oid));
+ strvec_push(&rev_argv, "bisect_rev_setup");
+ strvec_pushf(&rev_argv, bad_format, oid_to_hex(current_bad_oid));
for (i = 0; i < good_revs.nr; i++)
- argv_array_pushf(&rev_argv, good_format,
- oid_to_hex(good_revs.oid + i));
- argv_array_push(&rev_argv, "--");
+ strvec_pushf(&rev_argv, good_format,
+ oid_to_hex(good_revs.oid + i));
+ strvec_push(&rev_argv, "--");
if (read_paths)
read_bisect_paths(&rev_argv);
- setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
+ setup_revisions(rev_argv.nr, rev_argv.v, revs, NULL);
/* XXX leak rev_argv, as "revs" may still be pointing to it */
}
@@ -709,7 +717,7 @@ static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int
char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
enum bisect_error res = BISECT_OK;
- memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), the_hash_algo->hexsz + 1);
+ oid_to_hex_r(bisect_rev_hex, bisect_rev);
update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
argv_checkout[2] = bisect_rev_hex;
@@ -980,10 +988,13 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
* the bisection process finished successfully.
* In this case the calling function or command should not turn a
* BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND return code into an error or a non zero exit code.
- * If no_checkout is non-zero, the bisection process does not
- * checkout the trial commit but instead simply updates BISECT_HEAD.
+ *
+ * Checking BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND
+ * in bisect_helper::bisect_next() and only transforming it to 0 at
+ * the end of bisect_helper::cmd_bisect__helper() helps bypassing
+ * all the code related to finding a commit to test.
*/
-enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
+enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
{
struct rev_info revs;
struct commit_list *tried;
@@ -991,21 +1002,35 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int
enum bisect_error res = BISECT_OK;
struct object_id *bisect_rev;
char *steps_msg;
+ /*
+ * If no_checkout is non-zero, the bisection process does not
+ * checkout the trial commit but instead simply updates BISECT_HEAD.
+ */
+ int no_checkout = ref_exists("BISECT_HEAD");
+ unsigned bisect_flags = 0;
read_bisect_terms(&term_bad, &term_good);
if (read_bisect_refs())
die(_("reading bisect refs failed"));
+ if (file_exists(git_path_bisect_first_parent()))
+ bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY;
+
+ if (skipped_revs.nr)
+ bisect_flags |= FIND_BISECTION_ALL;
+
res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
if (res)
return res;
bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
+
+ revs.first_parent_only = !!(bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY);
revs.limited = 1;
bisect_common(&revs);
- find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr);
+ find_bisection(&revs.commits, &reaches, &all, bisect_flags);
revs.commits = managed_skipped(revs.commits, &tried);
if (!revs.commits) {
@@ -1064,6 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int
"Bisecting: %d revisions left to test after this %s\n",
nr), nr, steps_msg);
free(steps_msg);
+ /* Clean up objects used, as they will be reused. */
+ clear_commit_marks_all(ALL_REV_FLAGS);
return bisect_checkout(bisect_rev, no_checkout);
}
@@ -1133,6 +1160,7 @@ int bisect_clean_state(void)
unlink_or_warn(git_path_bisect_names());
unlink_or_warn(git_path_bisect_run());
unlink_or_warn(git_path_bisect_terms());
+ unlink_or_warn(git_path_bisect_first_parent());
/* Cleanup head-name if it got left by an old version of git-bisect */
unlink_or_warn(git_path_head_name());
/*
diff --git a/bisect.h b/bisect.h
index 8bad8d8391..ec24ac2d7e 100644
--- a/bisect.h
+++ b/bisect.h
@@ -12,7 +12,7 @@ struct repository;
* best commit, as chosen by `find_all`.
*/
void find_bisection(struct commit_list **list, int *reaches, int *all,
- int find_all);
+ unsigned bisect_flags);
struct commit_list *filter_skipped(struct commit_list *list,
struct commit_list **tried,
@@ -23,6 +23,9 @@ struct commit_list *filter_skipped(struct commit_list *list,
#define BISECT_SHOW_ALL (1<<0)
#define REV_LIST_QUIET (1<<1)
+#define FIND_BISECTION_ALL (1u<<0)
+#define FIND_BISECTION_FIRST_PARENT_ONLY (1u<<1)
+
struct rev_list_info {
struct rev_info *revs;
int flags;
@@ -58,9 +61,7 @@ enum bisect_error {
BISECT_INTERNAL_SUCCESS_MERGE_BASE = -11
};
-enum bisect_error bisect_next_all(struct repository *r,
- const char *prefix,
- int no_checkout);
+enum bisect_error bisect_next_all(struct repository *r, const char *prefix);
int estimate_bisect_steps(int all);
diff --git a/blame.c b/blame.c
index 82fa16d658..686845b2b4 100644
--- a/blame.c
+++ b/blame.c
@@ -1184,6 +1184,7 @@ void blame_coalesce(struct blame_scoreboard *sb)
for (ent = sb->ent; ent && (next = ent->next); ent = next) {
if (ent->suspect == next->suspect &&
ent->s_lno + ent->num_lines == next->s_lno &&
+ ent->lno + ent->num_lines == next->lno &&
ent->ignored == next->ignored &&
ent->unblamable == next->unblamable) {
ent->num_lines += next->num_lines;
@@ -1275,7 +1276,7 @@ static int maybe_changed_path(struct repository *r,
if (commit_graph_generation(origin->commit) == GENERATION_NUMBER_INFINITY)
return 1;
- filter = get_bloom_filter(r, origin->commit, 0);
+ filter = get_bloom_filter(r, origin->commit);
if (!filter)
return 1;
@@ -1352,8 +1353,8 @@ static struct blame_origin *find_origin(struct repository *r,
else {
int compute_diff = 1;
if (origin->commit->parents &&
- !oidcmp(&parent->object.oid,
- &origin->commit->parents->item->object.oid))
+ oideq(&parent->object.oid,
+ &origin->commit->parents->item->object.oid))
compute_diff = maybe_changed_path(r, origin, bd);
if (compute_diff)
@@ -2891,16 +2892,18 @@ void setup_blame_bloom_data(struct blame_scoreboard *sb,
const char *path)
{
struct blame_bloom_data *bd;
+ struct bloom_filter_settings *bs;
if (!sb->repo->objects->commit_graph)
return;
- if (!sb->repo->objects->commit_graph->bloom_filter_settings)
+ bs = get_bloom_filter_settings(sb->repo);
+ if (!bs)
return;
bd = xmalloc(sizeof(struct blame_bloom_data));
- bd->settings = sb->repo->objects->commit_graph->bloom_filter_settings;
+ bd->settings = bs;
bd->alloc = 4;
bd->nr = 0;
diff --git a/bloom.c b/bloom.c
index 6a7f2f2bdc..68c73200a5 100644
--- a/bloom.c
+++ b/bloom.c
@@ -38,7 +38,7 @@ static int load_bloom_filter_from_graph(struct commit_graph *g,
while (graph_pos < g->num_commits_in_base)
g = g->base_graph;
- /* The commit graph commit 'c' lives in doesn't carry bloom filters. */
+ /* The commit graph commit 'c' lives in doesn't carry Bloom filters. */
if (!g->chunk_bloom_indexes)
return 0;
@@ -177,39 +177,46 @@ static int pathmap_cmp(const void *hashmap_cmp_fn_data,
return strcmp(e1->path, e2->path);
}
-struct bloom_filter *get_bloom_filter(struct repository *r,
- struct commit *c,
- int compute_if_not_present)
+static void init_truncated_large_filter(struct bloom_filter *filter)
+{
+ filter->data = xmalloc(1);
+ filter->data[0] = 0xFF;
+ filter->len = 1;
+}
+
+struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
+ struct commit *c,
+ int compute_if_not_present,
+ const struct bloom_filter_settings *settings,
+ enum bloom_filter_computed *computed)
{
struct bloom_filter *filter;
- struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
int i;
struct diff_options diffopt;
- int max_changes = 512;
- if (bloom_filters.slab_size == 0)
+ if (computed)
+ *computed = BLOOM_NOT_COMPUTED;
+
+ if (!bloom_filters.slab_size)
return NULL;
filter = bloom_filter_slab_at(&bloom_filters, c);
if (!filter->data) {
load_commit_graph_info(r, c);
- if (commit_graph_position(c) != COMMIT_NOT_FROM_GRAPH &&
- r->objects->commit_graph->chunk_bloom_indexes) {
- if (load_bloom_filter_from_graph(r->objects->commit_graph, filter, c))
- return filter;
- else
- return NULL;
- }
+ if (commit_graph_position(c) != COMMIT_NOT_FROM_GRAPH)
+ load_bloom_filter_from_graph(r->objects->commit_graph, filter, c);
}
- if (filter->data || !compute_if_not_present)
+ if (filter->data && filter->len)
return filter;
+ if (!compute_if_not_present)
+ return NULL;
repo_diff_setup(r, &diffopt);
diffopt.flags.recursive = 1;
diffopt.detect_rename = 0;
- diffopt.max_changes = max_changes;
+ diffopt.max_changes = settings->max_changed_paths;
diff_setup_done(&diffopt);
/* ensure commit is parsed so we have parent information */
@@ -221,7 +228,7 @@ struct bloom_filter *get_bloom_filter(struct repository *r,
diff_tree_oid(NULL, &c->object.oid, "", &diffopt);
diffcore_std(&diffopt);
- if (diffopt.num_changes <= max_changes) {
+ if (diff_queued_diff.nr <= settings->max_changed_paths) {
struct hashmap pathmap;
struct pathmap_hash_entry *e;
struct hashmap_iter iter;
@@ -258,23 +265,41 @@ struct bloom_filter *get_bloom_filter(struct repository *r,
diff_free_filepair(diff_queued_diff.queue[i]);
}
- filter->len = (hashmap_get_size(&pathmap) * settings.bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD;
+ if (hashmap_get_size(&pathmap) > settings->max_changed_paths) {
+ init_truncated_large_filter(filter);
+ if (computed)
+ *computed |= BLOOM_TRUNC_LARGE;
+ goto cleanup;
+ }
+
+ filter->len = (hashmap_get_size(&pathmap) * settings->bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD;
+ if (!filter->len) {
+ if (computed)
+ *computed |= BLOOM_TRUNC_EMPTY;
+ filter->len = 1;
+ }
filter->data = xcalloc(filter->len, sizeof(unsigned char));
hashmap_for_each_entry(&pathmap, &iter, e, entry) {
struct bloom_key key;
- fill_bloom_key(e->path, strlen(e->path), &key, &settings);
- add_key_to_filter(&key, filter, &settings);
+ fill_bloom_key(e->path, strlen(e->path), &key, settings);
+ add_key_to_filter(&key, filter, settings);
}
+ cleanup:
hashmap_free_entries(&pathmap, struct pathmap_hash_entry, entry);
} else {
for (i = 0; i < diff_queued_diff.nr; i++)
diff_free_filepair(diff_queued_diff.queue[i]);
- filter->data = NULL;
- filter->len = 0;
+ init_truncated_large_filter(filter);
+
+ if (computed)
+ *computed |= BLOOM_TRUNC_LARGE;
}
+ if (computed)
+ *computed |= BLOOM_COMPUTED;
+
free(diff_queued_diff.queue);
DIFF_QUEUE_CLEAR(&diff_queued_diff);
diff --git a/bloom.h b/bloom.h
index d8fbb0fbf1..adde6dfe21 100644
--- a/bloom.h
+++ b/bloom.h
@@ -28,9 +28,18 @@ struct bloom_filter_settings {
* that contain n*b bits.
*/
uint32_t bits_per_entry;
+
+ /*
+ * The maximum number of changed paths per commit
+ * before declaring a Bloom filter to be too-large.
+ *
+ * Not written to the commit-graph file.
+ */
+ uint32_t max_changed_paths;
};
-#define DEFAULT_BLOOM_FILTER_SETTINGS { 1, 7, 10 }
+#define DEFAULT_BLOOM_MAX_CHANGES 512
+#define DEFAULT_BLOOM_FILTER_SETTINGS { 1, 7, 10, DEFAULT_BLOOM_MAX_CHANGES }
#define BITS_PER_WORD 8
#define BLOOMDATA_CHUNK_HEADER_SIZE 3 * sizeof(uint32_t)
@@ -80,9 +89,21 @@ void add_key_to_filter(const struct bloom_key *key,
void init_bloom_filters(void);
-struct bloom_filter *get_bloom_filter(struct repository *r,
- struct commit *c,
- int compute_if_not_present);
+enum bloom_filter_computed {
+ BLOOM_NOT_COMPUTED = (1 << 0),
+ BLOOM_COMPUTED = (1 << 1),
+ BLOOM_TRUNC_LARGE = (1 << 2),
+ BLOOM_TRUNC_EMPTY = (1 << 3),
+};
+
+struct bloom_filter *get_or_compute_bloom_filter(struct repository *r,
+ struct commit *c,
+ int compute_if_not_present,
+ const struct bloom_filter_settings *settings,
+ enum bloom_filter_computed *computed);
+
+#define get_bloom_filter(r, c) get_or_compute_bloom_filter( \
+ (r), (c), 0, NULL, NULL)
int bloom_filter_contains(const struct bloom_filter *filter,
const struct bloom_key *key,
diff --git a/branch.c b/branch.c
index 7095f78058..9c9dae1eae 100644
--- a/branch.c
+++ b/branch.c
@@ -281,7 +281,7 @@ void create_branch(struct repository *r,
die(_("Not a valid object name: '%s'."), start_name);
}
- switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref)) {
+ switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
case 0:
/* Not branching from any existing branch */
if (explicit_tracking)
diff --git a/builtin.h b/builtin.h
index a5ae15bfe5..53fb290963 100644
--- a/builtin.h
+++ b/builtin.h
@@ -119,6 +119,7 @@ int cmd_archive(int argc, const char **argv, const char *prefix);
int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
int cmd_blame(int argc, const char **argv, const char *prefix);
int cmd_branch(int argc, const char **argv, const char *prefix);
+int cmd_bugreport(int argc, const char **argv, const char *prefix);
int cmd_bundle(int argc, const char **argv, const char *prefix);
int cmd_cat_file(int argc, const char **argv, const char *prefix);
int cmd_checkout(int argc, const char **argv, const char *prefix);
@@ -138,6 +139,9 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix);
int cmd_config(int argc, const char **argv, const char *prefix);
int cmd_count_objects(int argc, const char **argv, const char *prefix);
int cmd_credential(int argc, const char **argv, const char *prefix);
+int cmd_credential_cache(int argc, const char **argv, const char *prefix);
+int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix);
+int cmd_credential_store(int argc, const char **argv, const char *prefix);
int cmd_describe(int argc, const char **argv, const char *prefix);
int cmd_diff_files(int argc, const char **argv, const char *prefix);
int cmd_diff_index(int argc, const char **argv, const char *prefix);
@@ -146,6 +150,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix);
int cmd_difftool(int argc, const char **argv, const char *prefix);
int cmd_env__helper(int argc, const char **argv, const char *prefix);
int cmd_fast_export(int argc, const char **argv, const char *prefix);
+int cmd_fast_import(int argc, const char **argv, const char *prefix);
int cmd_fetch(int argc, const char **argv, const char *prefix);
int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
@@ -167,6 +172,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix);
int cmd_ls_remote(int argc, const char **argv, const char *prefix);
int cmd_mailinfo(int argc, const char **argv, const char *prefix);
int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+int cmd_maintenance(int argc, const char **argv, const char *prefix);
int cmd_merge(int argc, const char **argv, const char *prefix);
int cmd_merge_base(int argc, const char **argv, const char *prefix);
int cmd_merge_index(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 298e0114f9..26b6ced09e 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -18,7 +18,7 @@
#include "diffcore.h"
#include "revision.h"
#include "bulk-checkin.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "submodule.h"
#include "add-interactive.h"
@@ -188,13 +188,19 @@ int run_add_interactive(const char *revision, const char *patch_mode,
const struct pathspec *pathspec)
{
int status, i;
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
int use_builtin_add_i =
git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
- if (use_builtin_add_i < 0)
- git_config_get_bool("add.interactive.usebuiltin",
- &use_builtin_add_i);
+ if (use_builtin_add_i < 0) {
+ int experimental;
+ if (!git_config_get_bool("add.interactive.usebuiltin",
+ &use_builtin_add_i))
+ ; /* ok */
+ else if (!git_config_get_bool("feature.experimental", &experimental) &&
+ experimental)
+ use_builtin_add_i = 1;
+ }
if (use_builtin_add_i == 1) {
enum add_p_mode mode;
@@ -218,18 +224,18 @@ int run_add_interactive(const char *revision, const char *patch_mode,
return !!run_add_p(the_repository, mode, revision, pathspec);
}
- argv_array_push(&argv, "add--interactive");
+ strvec_push(&argv, "add--interactive");
if (patch_mode)
- argv_array_push(&argv, patch_mode);
+ strvec_push(&argv, patch_mode);
if (revision)
- argv_array_push(&argv, revision);
- argv_array_push(&argv, "--");
+ strvec_push(&argv, revision);
+ strvec_push(&argv, "--");
for (i = 0; i < pathspec->nr; i++)
/* pass original pathspec, to be re-parsed */
- argv_array_push(&argv, pathspec->items[i].original);
+ strvec_push(&argv, pathspec->items[i].original);
- status = run_command_v_opt(argv.argv, RUN_GIT_CMD);
- argv_array_clear(&argv);
+ status = run_command_v_opt(argv.v, RUN_GIT_CMD);
+ strvec_clear(&argv);
return status;
}
@@ -534,11 +540,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
die_in_unpopulated_submodule(&the_index, prefix);
die_path_inside_submodule(&the_index, &pathspec);
+ dir_init(&dir);
if (add_new_files) {
int baselen;
/* Set up the default git porcelain excludes */
- memset(&dir, 0, sizeof(dir));
if (!ignored_too) {
dir.flags |= DIR_COLLECT_IGNORED;
setup_standard_excludes(&dir);
@@ -611,7 +617,7 @@ finish:
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write new index file"));
+ dir_clear(&dir);
UNLEAK(pathspec);
- UNLEAK(dir);
return exit_status;
}
diff --git a/builtin/am.c b/builtin/am.c
index 69e50de018..7259186408 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -98,6 +98,8 @@ struct am_state {
char *author_name;
char *author_email;
char *author_date;
+ char *committer_name;
+ char *committer_email;
char *msg;
size_t msg_len;
@@ -116,7 +118,7 @@ struct am_state {
int keep; /* enum keep_type */
int message_id;
int scissors; /* enum scissors_type */
- struct argv_array git_apply_opts;
+ struct strvec git_apply_opts;
const char *resolvemsg;
int committer_date_is_author_date;
int ignore_date;
@@ -130,6 +132,8 @@ struct am_state {
*/
static void am_state_init(struct am_state *state)
{
+ const char *committer;
+ struct ident_split id;
int gpgsign;
memset(state, 0, sizeof(*state));
@@ -146,10 +150,18 @@ static void am_state_init(struct am_state *state)
state->scissors = SCISSORS_UNSET;
- argv_array_init(&state->git_apply_opts);
+ strvec_init(&state->git_apply_opts);
if (!git_config_get_bool("commit.gpgsign", &gpgsign))
state->sign_commit = gpgsign ? "" : NULL;
+
+ committer = git_committer_info(IDENT_STRICT);
+ if (split_ident_line(&id, committer, strlen(committer)) < 0)
+ die(_("invalid committer: %s"), committer);
+ state->committer_name =
+ xmemdupz(id.name_begin, id.name_end - id.name_begin);
+ state->committer_email =
+ xmemdupz(id.mail_begin, id.mail_end - id.mail_end);
}
/**
@@ -161,8 +173,10 @@ static void am_state_release(struct am_state *state)
free(state->author_name);
free(state->author_email);
free(state->author_date);
+ free(state->committer_name);
+ free(state->committer_email);
free(state->msg);
- argv_array_clear(&state->git_apply_opts);
+ strvec_clear(&state->git_apply_opts);
}
/**
@@ -398,8 +412,8 @@ static void am_load(struct am_state *state)
state->scissors = SCISSORS_UNSET;
read_state_file(&sb, state, "apply-opt", 1);
- argv_array_clear(&state->git_apply_opts);
- if (sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) < 0)
+ strvec_clear(&state->git_apply_opts);
+ if (sq_dequote_to_strvec(sb.buf, &state->git_apply_opts) < 0)
die(_("could not parse %s"), am_path(state, "apply-opt"));
state->rebasing = !!file_exists(am_path(state, "rebasing"));
@@ -452,8 +466,8 @@ static int run_post_rewrite_hook(const struct am_state *state)
if (!hook)
return 0;
- argv_array_push(&cp.args, hook);
- argv_array_push(&cp.args, "rebase");
+ strvec_push(&cp.args, hook);
+ strvec_push(&cp.args, "rebase");
cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
cp.stdout_to_stderr = 1;
@@ -651,16 +665,16 @@ static int split_mail_mbox(struct am_state *state, const char **paths,
int ret;
cp.git_cmd = 1;
- argv_array_push(&cp.args, "mailsplit");
- argv_array_pushf(&cp.args, "-d%d", state->prec);
- argv_array_pushf(&cp.args, "-o%s", state->dir);
- argv_array_push(&cp.args, "-b");
+ strvec_push(&cp.args, "mailsplit");
+ strvec_pushf(&cp.args, "-d%d", state->prec);
+ strvec_pushf(&cp.args, "-o%s", state->dir);
+ strvec_push(&cp.args, "-b");
if (keep_cr)
- argv_array_push(&cp.args, "--keep-cr");
+ strvec_push(&cp.args, "--keep-cr");
if (mboxrd)
- argv_array_push(&cp.args, "--mboxrd");
- argv_array_push(&cp.args, "--");
- argv_array_pushv(&cp.args, paths);
+ strvec_push(&cp.args, "--mboxrd");
+ strvec_push(&cp.args, "--");
+ strvec_pushv(&cp.args, paths);
ret = capture_command(&cp, &last, 8);
if (ret)
@@ -787,7 +801,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
const char *series_dir;
char *series_dir_buf;
FILE *fp;
- struct argv_array patches = ARGV_ARRAY_INIT;
+ struct strvec patches = STRVEC_INIT;
struct strbuf sb = STRBUF_INIT;
int ret;
@@ -805,16 +819,16 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths,
if (*sb.buf == '#')
continue; /* skip comment lines */
- argv_array_push(&patches, mkpath("%s/%s", series_dir, sb.buf));
+ strvec_push(&patches, mkpath("%s/%s", series_dir, sb.buf));
}
fclose(fp);
strbuf_release(&sb);
free(series_dir_buf);
- ret = split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr);
+ ret = split_mail_conv(stgit_patch_to_mail, state, patches.v, keep_cr);
- argv_array_clear(&patches);
+ strvec_clear(&patches);
return ret;
}
@@ -1002,7 +1016,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
}
write_state_text(state, "scissors", str);
- sq_quote_argv(&sb, state->git_apply_opts.argv);
+ sq_quote_argv(&sb, state->git_apply_opts.v);
write_state_text(state, "apply-opt", sb.buf);
if (state->rebasing)
@@ -1390,8 +1404,8 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
*/
static int run_apply(const struct am_state *state, const char *index_file)
{
- struct argv_array apply_paths = ARGV_ARRAY_INIT;
- struct argv_array apply_opts = ARGV_ARRAY_INIT;
+ struct strvec apply_paths = STRVEC_INIT;
+ struct strvec apply_opts = STRVEC_INIT;
struct apply_state apply_state;
int res, opts_left;
int force_apply = 0;
@@ -1400,10 +1414,10 @@ static int run_apply(const struct am_state *state, const char *index_file)
if (init_apply_state(&apply_state, the_repository, NULL))
BUG("init_apply_state() failed");
- argv_array_push(&apply_opts, "apply");
- argv_array_pushv(&apply_opts, state->git_apply_opts.argv);
+ strvec_push(&apply_opts, "apply");
+ strvec_pushv(&apply_opts, state->git_apply_opts.v);
- opts_left = apply_parse_options(apply_opts.argc, apply_opts.argv,
+ opts_left = apply_parse_options(apply_opts.nr, apply_opts.v,
&apply_state, &force_apply, &options,
NULL);
@@ -1426,12 +1440,12 @@ static int run_apply(const struct am_state *state, const char *index_file)
if (check_apply_state(&apply_state, force_apply))
BUG("check_apply_state() failed");
- argv_array_push(&apply_paths, am_path(state, "patch"));
+ strvec_push(&apply_paths, am_path(state, "patch"));
- res = apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options);
+ res = apply_all_patches(&apply_state, apply_paths.nr, apply_paths.v, options);
- argv_array_clear(&apply_paths);
- argv_array_clear(&apply_opts);
+ strvec_clear(&apply_paths);
+ strvec_clear(&apply_opts);
clear_apply_state(&apply_state);
if (res)
@@ -1454,10 +1468,10 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
- argv_array_push(&cp.args, "apply");
- argv_array_pushv(&cp.args, state->git_apply_opts.argv);
- argv_array_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
- argv_array_push(&cp.args, am_path(state, "patch"));
+ strvec_push(&cp.args, "apply");
+ strvec_pushv(&cp.args, state->git_apply_opts.v);
+ strvec_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
+ strvec_push(&cp.args, am_path(state, "patch"));
if (run_command(&cp))
return -1;
@@ -1556,7 +1570,7 @@ static void do_commit(const struct am_state *state)
struct object_id tree, parent, commit;
const struct object_id *old_oid;
struct commit_list *parents = NULL;
- const char *reflog_msg, *author;
+ const char *reflog_msg, *author, *committer = NULL;
struct strbuf sb = STRBUF_INIT;
if (run_hook_le(NULL, "pre-applypatch", NULL))
@@ -1580,11 +1594,15 @@ static void do_commit(const struct am_state *state)
IDENT_STRICT);
if (state->committer_date_is_author_date)
- setenv("GIT_COMMITTER_DATE",
- state->ignore_date ? "" : state->author_date, 1);
-
- if (commit_tree(state->msg, state->msg_len, &tree, parents, &commit,
- author, state->sign_commit))
+ committer = fmt_ident(state->committer_name,
+ state->author_email, WANT_COMMITTER_IDENT,
+ state->ignore_date ? NULL
+ : state->author_date,
+ IDENT_STRICT);
+
+ if (commit_tree_extended(state->msg, state->msg_len, &tree, parents,
+ &commit, author, committer, state->sign_commit,
+ NULL))
die(_("failed to write commit object"));
reflog_msg = getenv("GIT_REFLOG_ACTION");
@@ -1676,7 +1694,7 @@ static int do_interactive(struct am_state *state)
if (!pager)
pager = "cat";
prepare_pager_args(&cp, pager);
- argv_array_push(&cp.args, am_path(state, "patch"));
+ strvec_push(&cp.args, am_path(state, "patch"));
run_command(&cp);
}
}
@@ -1795,7 +1813,7 @@ next:
if (!state->rebasing) {
am_destroy(state);
close_object_store(the_repository->objects);
- run_auto_gc(state->quiet);
+ run_auto_maintenance(state->quiet);
}
}
@@ -2346,7 +2364,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
if (state.signoff == SIGNOFF_EXPLICIT)
am_append_signoff(&state);
} else {
- struct argv_array paths = ARGV_ARRAY_INIT;
+ struct strvec paths = STRVEC_INIT;
int i;
/*
@@ -2371,17 +2389,17 @@ int cmd_am(int argc, const char **argv, const char *prefix)
for (i = 0; i < argc; i++) {
if (is_absolute_path(argv[i]) || !prefix)
- argv_array_push(&paths, argv[i]);
+ strvec_push(&paths, argv[i]);
else
- argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
+ strvec_push(&paths, mkpath("%s/%s", prefix, argv[i]));
}
- if (state.interactive && !paths.argc)
+ if (state.interactive && !paths.nr)
die(_("interactive mode requires patches on the command line"));
- am_setup(&state, patch_format, paths.argv, keep_cr);
+ am_setup(&state, patch_format, paths.v, keep_cr);
- argv_array_clear(&paths);
+ strvec_clear(&paths);
}
switch (resume.mode) {
diff --git a/builtin/annotate.c b/builtin/annotate.c
index da413ae0d1..58ff977a23 100644
--- a/builtin/annotate.c
+++ b/builtin/annotate.c
@@ -5,18 +5,18 @@
*/
#include "git-compat-util.h"
#include "builtin.h"
-#include "argv-array.h"
+#include "strvec.h"
int cmd_annotate(int argc, const char **argv, const char *prefix)
{
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
int i;
- argv_array_pushl(&args, "annotate", "-c", NULL);
+ strvec_pushl(&args, "annotate", "-c", NULL);
for (i = 1; i < argc; i++) {
- argv_array_push(&args, argv[i]);
+ strvec_push(&args, argv[i]);
}
- return cmd_blame(args.argc, args.argv, prefix);
+ return cmd_blame(args.nr, args.v, prefix);
}
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index ec4996282e..7512b880f0 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -4,22 +4,23 @@
#include "bisect.h"
#include "refs.h"
#include "dir.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "run-command.h"
#include "prompt.h"
#include "quote.h"
+#include "revision.h"
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
-static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_head_name, "head-name")
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
+static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
static const char * const git_bisect_helper_usage[] = {
- N_("git bisect--helper --next-all [--no-checkout]"),
+ N_("git bisect--helper --next-all"),
N_("git bisect--helper --write-terms <bad_term> <good_term>"),
N_("git bisect--helper --bisect-clean-state"),
N_("git bisect--helper --bisect-reset [<commit>]"),
@@ -27,11 +28,19 @@ static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
- N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]"
- "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"),
+ N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
+ " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
+ N_("git bisect--helper --bisect-next"),
+ N_("git bisect--helper --bisect-auto-next"),
+ N_("git bisect--helper --bisect-autostart"),
NULL
};
+struct add_bisect_ref_data {
+ struct rev_info *revs;
+ unsigned int object_flags;
+};
+
struct bisect_terms {
char *term_good;
char *term_bad;
@@ -55,6 +64,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
static const char vocab_bad[] = "bad|new";
static const char vocab_good[] = "good|old";
+static int bisect_autostart(struct bisect_terms *terms);
+
/*
* Check whether the string `term` belongs to the set of strings
* included in the variable arguments.
@@ -74,6 +85,52 @@ static int one_of(const char *term, ...)
return res;
}
+static int write_in_file(const char *path, const char *mode, const char *format, va_list args)
+{
+ FILE *fp = NULL;
+ int res = 0;
+
+ if (strcmp(mode, "w") && strcmp(mode, "a"))
+ BUG("write-in-file does not support '%s' mode", mode);
+ fp = fopen(path, mode);
+ if (!fp)
+ return error_errno(_("cannot open file '%s' in mode '%s'"), path, mode);
+ res = vfprintf(fp, format, args);
+
+ if (res < 0) {
+ int saved_errno = errno;
+ fclose(fp);
+ errno = saved_errno;
+ return error_errno(_("could not write to file '%s'"), path);
+ }
+
+ return fclose(fp);
+}
+
+static int write_to_file(const char *path, const char *format, ...)
+{
+ int res;
+ va_list args;
+
+ va_start(args, format);
+ res = write_in_file(path, "w", format, args);
+ va_end(args);
+
+ return res;
+}
+
+static int append_to_file(const char *path, const char *format, ...)
+{
+ int res;
+ va_list args;
+
+ va_start(args, format);
+ res = write_in_file(path, "a", format, args);
+ va_end(args);
+
+ return res;
+}
+
static int check_term_format(const char *term, const char *orig_term)
{
int res;
@@ -104,7 +161,6 @@ static int check_term_format(const char *term, const char *orig_term)
static int write_terms(const char *bad, const char *good)
{
- FILE *fp = NULL;
int res;
if (!strcmp(bad, good))
@@ -113,13 +169,9 @@ static int write_terms(const char *bad, const char *good)
if (check_term_format(bad, "bad") || check_term_format(good, "good"))
return -1;
- fp = fopen(git_path_bisect_terms(), "w");
- if (!fp)
- return error_errno(_("could not open the file BISECT_TERMS"));
+ res = write_to_file(git_path_bisect_terms(), "%s\n%s\n", bad, good);
- res = fprintf(fp, "%s\n%s\n", bad, good);
- res |= fclose(fp);
- return (res < 0) ? -1 : 0;
+ return res;
}
static int is_expected_rev(const char *expected_hex)
@@ -164,19 +216,19 @@ static int bisect_reset(const char *commit)
strbuf_addstr(&branch, commit);
}
- if (!file_exists(git_path_bisect_head())) {
- struct argv_array argv = ARGV_ARRAY_INIT;
+ if (!ref_exists("BISECT_HEAD")) {
+ struct strvec argv = STRVEC_INIT;
- argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
- if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ strvec_pushl(&argv, "checkout", branch.buf, "--", NULL);
+ if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
error(_("could not check out original"
" HEAD '%s'. Try 'git bisect"
" reset <commit>'."), branch.buf);
strbuf_release(&branch);
- argv_array_clear(&argv);
+ strvec_clear(&argv);
return -1;
}
- argv_array_clear(&argv);
+ strvec_clear(&argv);
}
strbuf_release(&branch);
@@ -421,9 +473,146 @@ finish:
return res;
}
-static int bisect_start(struct bisect_terms *terms, int no_checkout,
- const char **argv, int argc)
+static int add_bisect_ref(const char *refname, const struct object_id *oid,
+ int flags, void *cb)
+{
+ struct add_bisect_ref_data *data = cb;
+
+ add_pending_oid(data->revs, refname, oid, data->object_flags);
+
+ return 0;
+}
+
+static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs)
+{
+ int res = 0;
+ struct add_bisect_ref_data cb = { revs };
+ char *good = xstrfmt("%s-*", terms->term_good);
+
+ /*
+ * We cannot use terms->term_bad directly in
+ * for_each_glob_ref_in() and we have to append a '*' to it,
+ * otherwise for_each_glob_ref_in() will append '/' and '*'.
+ */
+ char *bad = xstrfmt("%s*", terms->term_bad);
+
+ /*
+ * It is important to reset the flags used by revision walks
+ * as the previous call to bisect_next_all() in turn
+ * sets up a revision walk.
+ */
+ reset_revision_walk();
+ init_revisions(revs, NULL);
+ setup_revisions(0, NULL, revs, NULL);
+ for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
+ cb.object_flags = UNINTERESTING;
+ for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb);
+ if (prepare_revision_walk(revs))
+ res = error(_("revision walk setup failed\n"));
+
+ free(good);
+ free(bad);
+ return res;
+}
+
+static int bisect_skipped_commits(struct bisect_terms *terms)
+{
+ int res;
+ FILE *fp = NULL;
+ struct rev_info revs;
+ struct commit *commit;
+ struct pretty_print_context pp = {0};
+ struct strbuf commit_name = STRBUF_INIT;
+
+ res = prepare_revs(terms, &revs);
+ if (res)
+ return res;
+
+ fp = fopen(git_path_bisect_log(), "a");
+ if (!fp)
+ return error_errno(_("could not open '%s' for appending"),
+ git_path_bisect_log());
+
+ if (fprintf(fp, "# only skipped commits left to test\n") < 0)
+ return error_errno(_("failed to write to '%s'"), git_path_bisect_log());
+
+ while ((commit = get_revision(&revs)) != NULL) {
+ strbuf_reset(&commit_name);
+ format_commit_message(commit, "%s",
+ &commit_name, &pp);
+ fprintf(fp, "# possible first %s commit: [%s] %s\n",
+ terms->term_bad, oid_to_hex(&commit->object.oid),
+ commit_name.buf);
+ }
+
+ /*
+ * Reset the flags used by revision walks in case
+ * there is another revision walk after this one.
+ */
+ reset_revision_walk();
+
+ strbuf_release(&commit_name);
+ fclose(fp);
+ return 0;
+}
+
+static int bisect_successful(struct bisect_terms *terms)
+{
+ struct object_id oid;
+ struct commit *commit;
+ struct pretty_print_context pp = {0};
+ struct strbuf commit_name = STRBUF_INIT;
+ char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad);
+ int res;
+
+ read_ref(bad_ref, &oid);
+ commit = lookup_commit_reference_by_name(bad_ref);
+ format_commit_message(commit, "%s", &commit_name, &pp);
+
+ res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n",
+ terms->term_bad, oid_to_hex(&commit->object.oid),
+ commit_name.buf);
+
+ strbuf_release(&commit_name);
+ free(bad_ref);
+ return res;
+}
+
+static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix)
{
+ enum bisect_error res;
+
+ if (bisect_autostart(terms))
+ return BISECT_FAILED;
+
+ if (bisect_next_check(terms, terms->term_good))
+ return BISECT_FAILED;
+
+ /* Perform all bisection computation */
+ res = bisect_next_all(the_repository, prefix);
+
+ if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
+ res = bisect_successful(terms);
+ return res ? res : BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+ } else if (res == BISECT_ONLY_SKIPPED_LEFT) {
+ res = bisect_skipped_commits(terms);
+ return res ? res : BISECT_ONLY_SKIPPED_LEFT;
+ }
+ return res;
+}
+
+static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix)
+{
+ if (bisect_next_check(terms, NULL))
+ return BISECT_OK;
+
+ return bisect_next(terms, prefix);
+}
+
+static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
+{
+ int no_checkout = 0;
+ int first_parent_only = 0;
int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
int flags, pathspec_pos, res = 0;
struct string_list revs = STRING_LIST_INIT_DUP;
@@ -453,6 +642,8 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
break;
} else if (!strcmp(arg, "--no-checkout")) {
no_checkout = 1;
+ } else if (!strcmp(arg, "--first-parent")) {
+ first_parent_only = 1;
} else if (!strcmp(arg, "--term-good") ||
!strcmp(arg, "--term-old")) {
i++;
@@ -481,14 +672,13 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
terms->term_bad = xstrdup(arg);
} else if (starts_with(arg, "--")) {
return error(_("unrecognized option: '%s'"), arg);
- } else {
- char *commit_id = xstrfmt("%s^{commit}", arg);
- if (get_oid(commit_id, &oid) && has_double_dash)
- die(_("'%s' does not appear to be a valid "
- "revision"), arg);
-
+ } else if (!get_oidf(&oid, "%s^{commit}", arg)) {
string_list_append(&revs, oid_to_hex(&oid));
- free(commit_id);
+ } else if (has_double_dash) {
+ die(_("'%s' does not appear to be a valid "
+ "revision"), arg);
+ } else {
+ break;
}
}
pathspec_pos = i;
@@ -526,11 +716,11 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
strbuf_read_file(&start_head, git_path_bisect_start(), 0);
strbuf_trim(&start_head);
if (!no_checkout) {
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
- argv_array_pushl(&argv, "checkout", start_head.buf,
- "--", NULL);
- if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ strvec_pushl(&argv, "checkout", start_head.buf,
+ "--", NULL);
+ if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
res = error(_("checking out '%s' failed."
" Try 'git bisect start "
"<valid-branch>'."),
@@ -577,6 +767,9 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
*/
write_file(git_path_bisect_start(), "%s\n", start_head.buf);
+ if (first_parent_only)
+ write_file(git_path_bisect_first_parent(), "\n");
+
if (no_checkout) {
if (get_oid(start_head.buf, &oid) < 0) {
res = error(_("invalid ref: '%s'"), start_head.buf);
@@ -618,6 +811,38 @@ finish:
return res;
}
+static inline int file_is_not_empty(const char *path)
+{
+ return !is_empty_or_missing_file(path);
+}
+
+static int bisect_autostart(struct bisect_terms *terms)
+{
+ int res;
+ const char *yesno;
+
+ if (file_is_not_empty(git_path_bisect_start()))
+ return 0;
+
+ fprintf_ln(stderr, _("You need to start by \"git bisect "
+ "start\"\n"));
+
+ if (!isatty(STDIN_FILENO))
+ return -1;
+
+ /*
+ * TRANSLATORS: Make sure to include [Y] and [n] in your
+ * translation. The program will only accept English input
+ * at this point.
+ */
+ yesno = git_prompt(_("Do you want me to do it for you "
+ "[Y/n]? "), PROMPT_ECHO);
+ res = tolower(*yesno) == 'n' ?
+ -1 : bisect_start(terms, empty_strvec, 0);
+
+ return res;
+}
+
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
enum {
@@ -630,9 +855,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
CHECK_AND_SET_TERMS,
BISECT_NEXT_CHECK,
BISECT_TERMS,
- BISECT_START
+ BISECT_START,
+ BISECT_AUTOSTART,
+ BISECT_NEXT,
+ BISECT_AUTO_NEXT
} cmdmode = 0;
- int no_checkout = 0, res = 0, nolog = 0;
+ int res = 0, nolog = 0;
struct option options[] = {
OPT_CMDMODE(0, "next-all", &cmdmode,
N_("perform 'git bisect next'"), NEXT_ALL),
@@ -654,8 +882,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
N_("print out the bisect terms"), BISECT_TERMS),
OPT_CMDMODE(0, "bisect-start", &cmdmode,
N_("start the bisect session"), BISECT_START),
- OPT_BOOL(0, "no-checkout", &no_checkout,
- N_("update BISECT_HEAD instead of checking out the current commit")),
+ OPT_CMDMODE(0, "bisect-next", &cmdmode,
+ N_("find the next bisection commit"), BISECT_NEXT),
+ OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
+ N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT),
+ OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
+ N_("start the bisection if it has not yet been started"), BISECT_AUTOSTART),
OPT_BOOL(0, "no-log", &nolog,
N_("no log for BISECT_WRITE")),
OPT_END()
@@ -671,7 +903,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
switch (cmdmode) {
case NEXT_ALL:
- res = bisect_next_all(the_repository, prefix, no_checkout);
+ res = bisect_next_all(the_repository, prefix);
break;
case WRITE_TERMS:
if (argc != 2)
@@ -713,10 +945,28 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
break;
case BISECT_START:
set_terms(&terms, "bad", "good");
- res = bisect_start(&terms, no_checkout, argv, argc);
+ res = bisect_start(&terms, argv, argc);
+ break;
+ case BISECT_NEXT:
+ if (argc)
+ return error(_("--bisect-next requires 0 arguments"));
+ get_terms(&terms);
+ res = bisect_next(&terms, prefix);
+ break;
+ case BISECT_AUTO_NEXT:
+ if (argc)
+ return error(_("--bisect-auto-next requires 0 arguments"));
+ get_terms(&terms);
+ res = bisect_auto_next(&terms, prefix);
+ break;
+ case BISECT_AUTOSTART:
+ if (argc)
+ return error(_("--bisect-autostart does not accept arguments"));
+ set_terms(&terms, "bad", "good");
+ res = bisect_autostart(&terms);
break;
default:
- return error("BUG: unknown subcommand '%d'", cmdmode);
+ BUG("unknown subcommand %d", cmdmode);
}
free_terms(&terms);
@@ -724,8 +974,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
* Handle early success
* From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
*/
- if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE)
+ if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND))
res = BISECT_OK;
- return abs(res);
+ return -res;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 94ef57c1cc..bb0f29300e 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -27,6 +27,7 @@
#include "object-store.h"
#include "blame.h"
#include "refs.h"
+#include "tag.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
@@ -803,6 +804,26 @@ static int is_a_rev(const char *name)
return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
}
+static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata)
+{
+ struct repository *r = ((struct blame_scoreboard *)cbdata)->repo;
+ struct object_id oid;
+
+ oidcpy(&oid, oid_ret);
+ while (1) {
+ struct object *obj;
+ int kind = oid_object_info(r, &oid, NULL);
+ if (kind == OBJ_COMMIT) {
+ oidcpy(oid_ret, &oid);
+ return 0;
+ }
+ if (kind != OBJ_TAG)
+ return -1;
+ obj = deref_tag(r, parse_object(r, &oid), NULL, 0);
+ oidcpy(&oid, &obj->oid);
+ }
+}
+
static void build_ignorelist(struct blame_scoreboard *sb,
struct string_list *ignore_revs_file_list,
struct string_list *ignore_rev_list)
@@ -815,10 +836,12 @@ static void build_ignorelist(struct blame_scoreboard *sb,
if (!strcmp(i->string, ""))
oidset_clear(&sb->ignore_list);
else
- oidset_parse_file(&sb->ignore_list, i->string);
+ oidset_parse_file_carefully(&sb->ignore_list, i->string,
+ peel_to_commit_oid, sb);
}
for_each_string_list_item(i, ignore_rev_list) {
- if (get_oid_committish(i->string, &oid))
+ if (get_oid_committish(i->string, &oid) ||
+ peel_to_commit_oid(&oid, sb))
die(_("cannot find revision %s to ignore"), i->string);
oidset_insert(&sb->ignore_list, &oid);
}
@@ -842,7 +865,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
const char *contents_from = NULL;
const struct option options[] = {
OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
- OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
+ OPT_BOOL('b', NULL, &blank_boundary, N_("Do not show object names of boundary commits (Default: off)")),
OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
diff --git a/builtin/branch.c b/builtin/branch.c
index e82301fb1b..efb30b8820 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -26,7 +26,7 @@
#include "commit-reach.h"
static const char * const builtin_branch_usage[] = {
- N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
+ N_("git branch [<options>] [-r | -a] [--merged] [--no-merged]"),
N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
@@ -688,8 +688,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
!show_current && !unset_upstream && argc == 0)
list = 1;
- if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
- filter.no_commit)
+ if (filter.with_commit || filter.no_commit ||
+ filter.reachable_from || filter.unreachable_from || filter.points_at.nr)
list = 1;
if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
diff --git a/bugreport.c b/builtin/bugreport.c
index 09579e268d..3ad4b9b62e 100644
--- a/bugreport.c
+++ b/builtin/bugreport.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "builtin.h"
#include "parse-options.h"
#include "strbuf.h"
#include "help.h"
@@ -119,7 +119,7 @@ static void get_header(struct strbuf *buf, const char *title)
strbuf_addf(buf, "\n\n[%s]\n", title);
}
-int cmd_main(int argc, const char **argv)
+int cmd_bugreport(int argc, const char **argv, const char *prefix)
{
struct strbuf buffer = STRBUF_INIT;
struct strbuf report_path = STRBUF_INIT;
@@ -127,8 +127,6 @@ int cmd_main(int argc, const char **argv)
time_t now = time(NULL);
char *option_output = NULL;
char *option_suffix = "%Y-%m-%d-%H%M";
- int nongit_ok = 0;
- const char *prefix = NULL;
const char *user_relative_path = NULL;
const struct option bugreport_options[] = {
@@ -139,8 +137,6 @@ int cmd_main(int argc, const char **argv)
OPT_END()
};
- prefix = setup_git_directory_gently(&nongit_ok);
-
argc = parse_options(argc, argv, prefix, bugreport_options,
bugreport_usage, 0);
@@ -170,15 +166,13 @@ int cmd_main(int argc, const char **argv)
get_system_info(&buffer);
get_header(&buffer, _("Enabled Hooks"));
- get_populated_hooks(&buffer, nongit_ok);
+ get_populated_hooks(&buffer, !startup_info->have_repository);
/* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (report < 0) {
- UNLEAK(report_path);
+ if (report < 0)
die(_("couldn't create a new file at '%s'"), report_path.buf);
- }
if (write_in_full(report, buffer.buf, buffer.len) < 0)
die_errno(_("unable to write to %s"), report_path.buf);
diff --git a/builtin/bundle.c b/builtin/bundle.c
index f049d27a14..ea6948110b 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -1,5 +1,5 @@
#include "builtin.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "parse-options.h"
#include "cache.h"
#include "bundle.h"
@@ -59,7 +59,8 @@ static int parse_options_cmd_bundle(int argc,
static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
int all_progress_implied = 0;
int progress = isatty(STDERR_FILENO);
- struct argv_array pack_opts;
+ struct strvec pack_opts;
+ int version = -1;
struct option options[] = {
OPT_SET_INT('q', "quiet", &progress,
@@ -71,6 +72,8 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
OPT_BOOL(0, "all-progress-implied",
&all_progress_implied,
N_("similar to --all-progress when progress meter is shown")),
+ OPT_INTEGER(0, "version", &version,
+ N_("specify bundle format version")),
OPT_END()
};
const char* bundle_file;
@@ -79,19 +82,19 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
builtin_bundle_create_usage, options, &bundle_file);
/* bundle internals use argv[1] as further parameters */
- argv_array_init(&pack_opts);
+ strvec_init(&pack_opts);
if (progress == 0)
- argv_array_push(&pack_opts, "--quiet");
+ strvec_push(&pack_opts, "--quiet");
else if (progress == 1)
- argv_array_push(&pack_opts, "--progress");
+ strvec_push(&pack_opts, "--progress");
else if (progress == 2)
- argv_array_push(&pack_opts, "--all-progress");
+ strvec_push(&pack_opts, "--all-progress");
if (progress && all_progress_implied)
- argv_array_push(&pack_opts, "--all-progress-implied");
+ strvec_push(&pack_opts, "--all-progress-implied");
if (!startup_info->have_repository)
die(_("Need a repository to create a bundle."));
- return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts);
+ return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version);
}
static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) {
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index ea5d0ae3a6..3c652748d5 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -180,7 +180,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
if (!no_index && read_cache() < 0)
die(_("index file corrupt"));
- memset(&dir, 0, sizeof(dir));
+ dir_init(&dir);
setup_standard_excludes(&dir);
if (stdin_paths) {
@@ -190,7 +190,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
maybe_flush_or_die(stdout, "ignore to stdout");
}
- clear_directory(&dir);
+ dir_clear(&dir);
return !num_ignored;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index af849c644f..0951f8fee5 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -239,6 +239,8 @@ static int checkout_merged(int pos, const struct checkout *state, int *nr_checko
mmbuffer_t result_buf;
struct object_id threeway[3];
unsigned mode = 0;
+ struct ll_merge_options ll_opts;
+ int renormalize = 0;
memset(threeway, 0, sizeof(threeway));
while (pos < active_nr) {
@@ -259,13 +261,12 @@ static int checkout_merged(int pos, const struct checkout *state, int *nr_checko
read_mmblob(&ours, &threeway[1]);
read_mmblob(&theirs, &threeway[2]);
- /*
- * NEEDSWORK: re-create conflicts from merges with
- * merge.renormalize set, too
- */
+ memset(&ll_opts, 0, sizeof(ll_opts));
+ git_config_get_bool("merge.renormalize", &renormalize);
+ ll_opts.renormalize = renormalize;
status = ll_merge(&result_buf, path, &ancestor, "base",
&ours, "ours", &theirs, "theirs",
- state->istate, NULL);
+ state->istate, &ll_opts);
free(ancestor.ptr);
free(ours.ptr);
free(theirs.ptr);
@@ -650,7 +651,7 @@ static void setup_branch_path(struct branch_info *branch)
* If this is a ref, resolve it; otherwise, look up the OID for our
* expression. Failure here is okay.
*/
- if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname))
+ if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname, 0))
repo_get_oid_committish(the_repository, branch->name, &branch->oid);
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
@@ -771,13 +772,6 @@ static int merge_working_tree(const struct checkout_opts *opts,
*/
add_files_to_cache(NULL, NULL, 0);
- /*
- * NEEDSWORK: carrying over local changes
- * when branches have different end-of-line
- * normalization (or clean+smudge rules) is
- * a pain; plumb in an option to set
- * o.renormalize?
- */
init_merge_options(&o, the_repository);
o.verbosity = 0;
work = write_in_core_index_as_tree(the_repository);
@@ -1126,8 +1120,10 @@ static void setup_new_branch_info_and_source_tree(
if (!check_refname_format(new_branch_info->path, 0) &&
!read_ref(new_branch_info->path, &branch_rev))
oidcpy(rev, &branch_rev);
- else
+ else {
+ free((char *)new_branch_info->path);
new_branch_info->path = NULL; /* not an existing branch */
+ }
new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
if (!new_branch_info->commit) {
@@ -1349,7 +1345,7 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
struct object_id oid;
char *to_free;
- if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
+ if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) {
const char *ref = to_free;
if (skip_prefix(ref, "refs/tags/", &ref))
@@ -1713,6 +1709,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
die(_("--pathspec-file-nul requires --pathspec-from-file"));
}
+ opts->pathspec.recursive = 1;
+
if (opts->pathspec.nr) {
if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
diff --git a/builtin/clean.c b/builtin/clean.c
index 5a9c29a558..687ab473c2 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -162,7 +162,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
is_nonbare_repository_dir(path)) {
if (!quiet) {
- quote_path_relative(path->buf, prefix, &quoted);
+ quote_path(path->buf, prefix, &quoted, 0);
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
quoted.buf);
}
@@ -177,7 +177,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
res = dry_run ? 0 : rmdir(path->buf);
if (res) {
int saved_errno = errno;
- quote_path_relative(path->buf, prefix, &quoted);
+ quote_path(path->buf, prefix, &quoted, 0);
errno = saved_errno;
warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
@@ -202,7 +202,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
ret = 1;
if (gone) {
- quote_path_relative(path->buf, prefix, &quoted);
+ quote_path(path->buf, prefix, &quoted, 0);
string_list_append(&dels, quoted.buf);
} else
*dir_gone = 0;
@@ -210,11 +210,11 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
} else {
res = dry_run ? 0 : unlink(path->buf);
if (!res) {
- quote_path_relative(path->buf, prefix, &quoted);
+ quote_path(path->buf, prefix, &quoted, 0);
string_list_append(&dels, quoted.buf);
} else {
int saved_errno = errno;
- quote_path_relative(path->buf, prefix, &quoted);
+ quote_path(path->buf, prefix, &quoted, 0);
errno = saved_errno;
warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
@@ -238,7 +238,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
*dir_gone = 1;
else {
int saved_errno = errno;
- quote_path_relative(path->buf, prefix, &quoted);
+ quote_path(path->buf, prefix, &quoted, 0);
errno = saved_errno;
warning_errno(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
@@ -266,7 +266,7 @@ static void pretty_print_dels(void)
struct column_options copts;
for_each_string_list_item(item, &del_list) {
- qname = quote_path_relative(item->string, NULL, &buf);
+ qname = quote_path(item->string, NULL, &buf, 0);
string_list_append(&list, qname);
}
@@ -667,7 +667,7 @@ static int filter_by_patterns_cmd(void)
if (!confirm.len)
break;
- memset(&dir, 0, sizeof(dir));
+ dir_init(&dir);
pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude");
ignore_list = strbuf_split_max(&confirm, ' ', 0);
@@ -698,7 +698,7 @@ static int filter_by_patterns_cmd(void)
}
strbuf_list_free(ignore_list);
- clear_directory(&dir);
+ dir_clear(&dir);
}
strbuf_release(&confirm);
@@ -753,7 +753,7 @@ static int ask_each_cmd(void)
for_each_string_list_item(item, &del_list) {
/* Ctrl-D should stop removing files */
if (!eof) {
- qname = quote_path_relative(item->string, NULL, &buf);
+ qname = quote_path(item->string, NULL, &buf, 0);
/* TRANSLATORS: Make sure to keep [y/N] as is */
printf(_("Remove %s [y/N]? "), qname);
if (git_read_line_interactively(&confirm) == EOF) {
@@ -923,7 +923,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
0);
- memset(&dir, 0, sizeof(dir));
+ dir_init(&dir);
if (!interactive && !dry_run && !force) {
if (config_set)
die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
@@ -1021,11 +1021,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
string_list_append(&del_list, rel);
}
- for (i = 0; i < dir.nr; i++)
- free(dir.entries[i]);
-
- for (i = 0; i < dir.ignored_nr; i++)
- free(dir.ignored[i]);
+ dir_clear(&dir);
if (interactive && del_list.nr > 0)
interactive_main_loop();
@@ -1051,19 +1047,19 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
errors++;
if (gone && !quiet) {
- qname = quote_path_relative(item->string, NULL, &buf);
+ qname = quote_path(item->string, NULL, &buf, 0);
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
}
} else {
res = dry_run ? 0 : unlink(abs_path.buf);
if (res) {
int saved_errno = errno;
- qname = quote_path_relative(item->string, NULL, &buf);
+ qname = quote_path(item->string, NULL, &buf, 0);
errno = saved_errno;
warning_errno(_(msg_warn_remove_failed), qname);
errors++;
} else if (!quiet) {
- qname = quote_path_relative(item->string, NULL, &buf);
+ qname = quote_path(item->string, NULL, &buf, 0);
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
}
}
diff --git a/builtin/clone.c b/builtin/clone.c
index bef70745c0..391aa41075 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -742,9 +742,9 @@ static void update_head(const struct ref *our, const struct ref *remote,
static int git_sparse_checkout_init(const char *repo)
{
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
int result = 0;
- argv_array_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+ strvec_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
/*
* We must apply the setting in the current process
@@ -752,12 +752,12 @@ static int git_sparse_checkout_init(const char *repo)
*/
core_apply_sparse_checkout = 1;
- if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
error(_("failed to initialize sparse-checkout"));
result = 1;
}
- argv_array_clear(&argv);
+ strvec_clear(&argv);
return result;
}
@@ -819,33 +819,33 @@ static int checkout(int submodule_progress)
oid_to_hex(&oid), "1", NULL);
if (!err && (option_recurse_submodules.nr > 0)) {
- struct argv_array args = ARGV_ARRAY_INIT;
- argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
+ struct strvec args = STRVEC_INIT;
+ strvec_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
if (option_shallow_submodules == 1)
- argv_array_push(&args, "--depth=1");
+ strvec_push(&args, "--depth=1");
if (max_jobs != -1)
- argv_array_pushf(&args, "--jobs=%d", max_jobs);
+ strvec_pushf(&args, "--jobs=%d", max_jobs);
if (submodule_progress)
- argv_array_push(&args, "--progress");
+ strvec_push(&args, "--progress");
if (option_verbosity < 0)
- argv_array_push(&args, "--quiet");
+ strvec_push(&args, "--quiet");
if (option_remote_submodules) {
- argv_array_push(&args, "--remote");
- argv_array_push(&args, "--no-fetch");
+ strvec_push(&args, "--remote");
+ strvec_push(&args, "--no-fetch");
}
if (option_single_branch >= 0)
- argv_array_push(&args, option_single_branch ?
+ strvec_push(&args, option_single_branch ?
"--single-branch" :
"--no-single-branch");
- err = run_command_v_opt(args.argv, RUN_GIT_CMD);
- argv_array_clear(&args);
+ err = run_command_v_opt(args.v, RUN_GIT_CMD);
+ strvec_clear(&args);
}
return err;
@@ -946,14 +946,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int is_bundle = 0, is_local;
const char *repo_name, *repo, *work_tree, *git_dir;
char *path, *dir, *display_repo = NULL;
- int dest_exists;
+ int dest_exists, real_dest_exists = 0;
const struct ref *refs, *remote_head;
const struct ref *remote_head_points_at;
const struct ref *our_head_points_at;
struct ref *mapped_refs;
const struct ref *ref;
struct strbuf key = STRBUF_INIT;
- struct strbuf default_refspec = STRBUF_INIT;
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
struct transport *transport = NULL;
const char *src_ref_prefix = "refs/heads/";
@@ -961,7 +960,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int err = 0, complete_refs_before_fetch = 1;
int submodule_progress;
- struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+ struct strvec ref_prefixes = STRVEC_INIT;
packet_trace_identity("clone");
argc = parse_options(argc, argv, prefix, builtin_clone_options,
@@ -1021,6 +1020,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
die(_("destination path '%s' already exists and is not "
"an empty directory."), dir);
+ if (real_git_dir) {
+ real_dest_exists = path_exists(real_git_dir);
+ if (real_dest_exists && !is_empty_dir(real_git_dir))
+ die(_("repository path '%s' already exists and is not "
+ "an empty directory."), real_git_dir);
+ }
+
+
strbuf_addf(&reflog_msg, "clone: from %s",
display_repo ? display_repo : repo);
free(display_repo);
@@ -1057,7 +1064,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
if (real_git_dir) {
- if (path_exists(real_git_dir))
+ if (real_dest_exists)
junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
junk_git_dir = real_git_dir;
} else {
@@ -1149,9 +1156,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote = remote_get(option_origin);
- strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
- branch_top.buf);
- refspec_append(&remote->fetch, default_refspec.buf);
+ refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
+ branch_top.buf);
transport = transport_get(remote, remote->url[0]);
transport_set_verbosity(transport, option_verbosity, option_progress);
@@ -1211,12 +1217,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport->smart_options->check_self_contained_and_connected = 1;
- argv_array_push(&ref_prefixes, "HEAD");
+ strvec_push(&ref_prefixes, "HEAD");
refspec_ref_prefixes(&remote->fetch, &ref_prefixes);
if (option_branch)
expand_ref_prefix(&ref_prefixes, option_branch);
if (!option_no_tags)
- argv_array_push(&ref_prefixes, "refs/tags/");
+ strvec_push(&ref_prefixes, "refs/tags/");
refs = transport_get_remote_refs(transport, &ref_prefixes);
@@ -1227,7 +1233,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* Now that we know what algorithm the remote side is using,
* let's set ours to the same thing.
*/
- initialize_repository_version(hash_algo);
+ initialize_repository_version(hash_algo, 1);
repo_set_hash_algo(the_repository, hash_algo);
mapped_refs = wanted_peer_refs(refs, &remote->fetch);
@@ -1324,9 +1330,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_release(&reflog_msg);
strbuf_release(&branch_top);
strbuf_release(&key);
- strbuf_release(&default_refspec);
junk_mode = JUNK_LEAVE_ALL;
- argv_array_clear(&ref_prefixes);
+ strvec_clear(&ref_prefixes);
return err;
}
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 16c9f6101a..988445abdf 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -13,7 +13,8 @@ static char const * const builtin_commit_graph_usage[] = {
N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
N_("git commit-graph write [--object-dir <objdir>] [--append] "
"[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
- "[--changed-paths] [--[no-]progress] <split options>"),
+ "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] "
+ "<split options>"),
NULL
};
@@ -25,7 +26,8 @@ static const char * const builtin_commit_graph_verify_usage[] = {
static const char * const builtin_commit_graph_write_usage[] = {
N_("git commit-graph write [--object-dir <objdir>] [--append] "
"[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
- "[--changed-paths] [--[no-]progress] <split options>"),
+ "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] "
+ "<split options>"),
NULL
};
@@ -106,7 +108,7 @@ static int graph_verify(int argc, const char **argv)
FREE_AND_NULL(graph_name);
if (open_ok)
- graph = load_commit_graph_one_fd_st(fd, &st, odb);
+ graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb);
else
graph = read_commit_graph_one(the_repository, odb);
@@ -119,7 +121,7 @@ static int graph_verify(int argc, const char **argv)
}
extern int read_replace_refs;
-static struct split_commit_graph_opts split_opts;
+static struct commit_graph_opts write_opts;
static int write_option_parse_split(const struct option *opt, const char *arg,
int unset)
@@ -162,6 +164,35 @@ static int read_one_commit(struct oidset *commits, struct progress *progress,
return 0;
}
+static int write_option_max_new_filters(const struct option *opt,
+ const char *arg,
+ int unset)
+{
+ int *to = opt->value;
+ if (unset)
+ *to = -1;
+ else {
+ const char *s;
+ *to = strtol(arg, (char **)&s, 10);
+ if (*s)
+ return error(_("%s expects a numerical value"),
+ optname(opt, opt->flags));
+ }
+ return 0;
+}
+
+static int git_commit_graph_write_config(const char *var, const char *value,
+ void *cb)
+{
+ if (!strcmp(var, "commitgraph.maxnewfilters"))
+ write_opts.max_new_filters = git_config_int(var, value);
+ /*
+ * No need to fall-back to 'git_default_config', since this was already
+ * called in 'cmd_commit_graph()'.
+ */
+ return 0;
+}
+
static int graph_write(int argc, const char **argv)
{
struct string_list pack_indexes = STRING_LIST_INIT_NODUP;
@@ -187,26 +218,33 @@ static int graph_write(int argc, const char **argv)
OPT_BOOL(0, "changed-paths", &opts.enable_changed_paths,
N_("enable computation for changed paths")),
OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
- OPT_CALLBACK_F(0, "split", &split_opts.flags, NULL,
+ OPT_CALLBACK_F(0, "split", &write_opts.split_flags, NULL,
N_("allow writing an incremental commit-graph file"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
write_option_parse_split),
- OPT_INTEGER(0, "max-commits", &split_opts.max_commits,
+ OPT_INTEGER(0, "max-commits", &write_opts.max_commits,
N_("maximum number of commits in a non-base split commit-graph")),
- OPT_INTEGER(0, "size-multiple", &split_opts.size_multiple,
+ OPT_INTEGER(0, "size-multiple", &write_opts.size_multiple,
N_("maximum ratio between two levels of a split commit-graph")),
- OPT_EXPIRY_DATE(0, "expire-time", &split_opts.expire_time,
+ OPT_EXPIRY_DATE(0, "expire-time", &write_opts.expire_time,
N_("only expire files older than a given date-time")),
+ OPT_CALLBACK_F(0, "max-new-filters", &write_opts.max_new_filters,
+ NULL, N_("maximum number of changed-path Bloom filters to compute"),
+ 0, write_option_max_new_filters),
OPT_END(),
};
opts.progress = isatty(2);
- split_opts.size_multiple = 2;
- split_opts.max_commits = 0;
- split_opts.expire_time = 0;
+ opts.enable_changed_paths = -1;
+ write_opts.size_multiple = 2;
+ write_opts.max_commits = 0;
+ write_opts.expire_time = 0;
+ write_opts.max_new_filters = -1;
trace2_cmd_mode("write");
+ git_config(git_commit_graph_write_config, &opts);
+
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_write_options,
builtin_commit_graph_write_usage, 0);
@@ -221,7 +259,9 @@ static int graph_write(int argc, const char **argv)
flags |= COMMIT_GRAPH_WRITE_SPLIT;
if (opts.progress)
flags |= COMMIT_GRAPH_WRITE_PROGRESS;
- if (opts.enable_changed_paths ||
+ if (!opts.enable_changed_paths)
+ flags |= COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS;
+ if (opts.enable_changed_paths == 1 ||
git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0))
flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS;
@@ -229,7 +269,7 @@ static int graph_write(int argc, const char **argv)
odb = find_odb(the_repository, opts.obj_dir);
if (opts.reachable) {
- if (write_commit_graph_reachable(odb, flags, &split_opts))
+ if (write_commit_graph_reachable(odb, flags, &write_opts))
return 1;
return 0;
}
@@ -258,7 +298,7 @@ static int graph_write(int argc, const char **argv)
opts.stdin_packs ? &pack_indexes : NULL,
opts.stdin_commits ? &commits : NULL,
flags,
- &split_opts))
+ &write_opts))
result = 1;
cleanup:
diff --git a/builtin/commit.c b/builtin/commit.c
index d1b7396052..42b964e0ca 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -847,21 +847,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
!merge_contains_scissors)
wt_status_add_cut_line(s->fp);
- status_printf_ln(s, GIT_COLOR_NORMAL,
- whence == FROM_MERGE
- ? _("\n"
- "It looks like you may be committing a merge.\n"
- "If this is not correct, please remove the file\n"
- " %s\n"
- "and try again.\n")
- : _("\n"
- "It looks like you may be committing a cherry-pick.\n"
- "If this is not correct, please remove the file\n"
- " %s\n"
- "and try again.\n"),
+ status_printf_ln(
+ s, GIT_COLOR_NORMAL,
whence == FROM_MERGE ?
- git_path_merge_head(the_repository) :
- git_path_cherry_pick_head(the_repository));
+ _("\n"
+ "It looks like you may be committing a merge.\n"
+ "If this is not correct, please run\n"
+ " git update-ref -d MERGE_HEAD\n"
+ "and try again.\n") :
+ _("\n"
+ "It looks like you may be committing a cherry-pick.\n"
+ "If this is not correct, please run\n"
+ " git update-ref -d CHERRY_PICK_HEAD\n"
+ "and try again.\n"));
}
fprintf(s->fp, "\n");
@@ -1005,15 +1003,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
if (use_editor) {
- struct argv_array env = ARGV_ARRAY_INIT;
+ struct strvec env = STRVEC_INIT;
- argv_array_pushf(&env, "GIT_INDEX_FILE=%s", index_file);
- if (launch_editor(git_path_commit_editmsg(), NULL, env.argv)) {
+ strvec_pushf(&env, "GIT_INDEX_FILE=%s", index_file);
+ if (launch_editor(git_path_commit_editmsg(), NULL, env.v)) {
fprintf(stderr,
_("Please supply the message using either -m or -F option.\n"));
exit(1);
}
- argv_array_clear(&env);
+ strvec_clear(&env);
}
if (!no_verify &&
@@ -1674,8 +1672,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid,
- parents, &oid, author_ident.buf, sign_commit,
- extra)) {
+ parents, &oid, author_ident.buf, NULL,
+ sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}
@@ -1702,7 +1700,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
git_test_write_commit_graph_or_die();
repo_rerere(the_repository, 0);
- run_auto_gc(quiet);
+ run_auto_maintenance(quiet);
run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
if (amend && !no_post_rewrite) {
commit_post_rewrite(the_repository, current_head, &oid);
diff --git a/builtin/config.c b/builtin/config.c
index 5e39f61885..963d65fd3f 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -65,6 +65,7 @@ static int show_scope;
#define TYPE_PATH 4
#define TYPE_EXPIRY_DATE 5
#define TYPE_COLOR 6
+#define TYPE_BOOL_OR_STR 7
#define OPT_CALLBACK_VALUE(s, l, v, h, i) \
{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
@@ -94,6 +95,8 @@ static int option_parse_type(const struct option *opt, const char *arg,
new_type = TYPE_INT;
else if (!strcmp(arg, "bool-or-int"))
new_type = TYPE_BOOL_OR_INT;
+ else if (!strcmp(arg, "bool-or-str"))
+ new_type = TYPE_BOOL_OR_STR;
else if (!strcmp(arg, "path"))
new_type = TYPE_PATH;
else if (!strcmp(arg, "expiry-date"))
@@ -149,6 +152,7 @@ static struct option builtin_config_options[] = {
OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
+ OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
OPT_GROUP(N_("Other")),
@@ -250,6 +254,12 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
strbuf_addstr(buf, v ? "true" : "false");
else
strbuf_addf(buf, "%d", v);
+ } else if (type == TYPE_BOOL_OR_STR) {
+ int v = git_parse_maybe_bool(value_);
+ if (v < 0)
+ strbuf_addstr(buf, value_);
+ else
+ strbuf_addstr(buf, v ? "true" : "false");
} else if (type == TYPE_PATH) {
const char *v;
if (git_config_pathname(&v, key_, value_) < 0)
@@ -411,6 +421,13 @@ static char *normalize_value(const char *key, const char *value)
else
return xstrdup(v ? "true" : "false");
}
+ if (type == TYPE_BOOL_OR_STR) {
+ int v = git_parse_maybe_bool(value);
+ if (v < 0)
+ return xstrdup(value);
+ else
+ return xstrdup(v ? "true" : "false");
+ }
if (type == TYPE_COLOR) {
char v[COLOR_MAXLEN];
if (git_config_color(v, key, value))
@@ -628,11 +645,15 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config();
}
- if (use_local_config && nongit)
- die(_("--local can only be used inside a git repository"));
+ if (nongit) {
+ if (use_local_config)
+ die(_("--local can only be used inside a git repository"));
+ if (given_config_source.blob)
+ die(_("--blob can only be used inside a git repository"));
+ if (use_worktree_config)
+ die(_("--worktree can only be used inside a git repository"));
- if (given_config_source.blob && nongit)
- die(_("--blob can only be used inside a git repository"));
+ }
if (given_config_source.file &&
!strcmp(given_config_source.file, "-")) {
diff --git a/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index ec1271f89c..c61f123a3b 100644
--- a/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -1,9 +1,12 @@
-#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+#ifndef NO_UNIX_SOCKETS
+
#include "config.h"
#include "tempfile.h"
#include "credential.h"
#include "unix-socket.h"
-#include "parse-options.h"
struct credential_cache_entry {
struct credential item;
@@ -257,7 +260,7 @@ static void init_socket_directory(const char *path)
free(path_copy);
}
-int cmd_main(int argc, const char **argv)
+int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
{
struct tempfile *socket_file;
const char *socket_path;
@@ -275,7 +278,7 @@ int cmd_main(int argc, const char **argv)
git_config_get_bool("credentialcache.ignoresighup", &ignore_sighup);
- argc = parse_options(argc, argv, NULL, options, usage, 0);
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
socket_path = argv[0];
if (!socket_path)
@@ -295,3 +298,21 @@ int cmd_main(int argc, const char **argv)
return 0;
}
+
+#else
+
+int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
+{
+ const char * const usage[] = {
+ "git credential-cache--daemon [options] <action>",
+ "",
+ "credential-cache--daemon is disabled in this build of Git",
+ NULL
+ };
+ struct option options[] = { OPT_END() };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+ die(_("credential-cache--daemon unavailable; no unix socket support"));
+}
+
+#endif /* NO_UNIX_SOCKET */
diff --git a/credential-cache.c b/builtin/credential-cache.c
index 1cccc3a0b9..9b3f709905 100644
--- a/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+#ifndef NO_UNIX_SOCKETS
+
#include "credential.h"
#include "string-list.h"
-#include "parse-options.h"
#include "unix-socket.h"
#include "run-command.h"
@@ -39,13 +42,13 @@ static int send_request(const char *socket, const struct strbuf *out)
static void spawn_daemon(const char *socket)
{
struct child_process daemon = CHILD_PROCESS_INIT;
- const char *argv[] = { NULL, NULL, NULL };
char buf[128];
int r;
- argv[0] = "git-credential-cache--daemon";
- argv[1] = socket;
- daemon.argv = argv;
+ strvec_pushl(&daemon.args,
+ "credential-cache--daemon", socket,
+ NULL);
+ daemon.git_cmd = 1;
daemon.no_stdin = 1;
daemon.out = -1;
@@ -96,7 +99,7 @@ static char *get_socket_path(void)
return socket;
}
-int cmd_main(int argc, const char **argv)
+int cmd_credential_cache(int argc, const char **argv, const char *prefix)
{
char *socket_path = NULL;
int timeout = 900;
@@ -113,7 +116,7 @@ int cmd_main(int argc, const char **argv)
OPT_END()
};
- argc = parse_options(argc, argv, NULL, options, usage, 0);
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
if (!argc)
usage_with_options(usage, options);
op = argv[0];
@@ -134,3 +137,21 @@ int cmd_main(int argc, const char **argv)
return 0;
}
+
+#else
+
+int cmd_credential_cache(int argc, const char **argv, const char *prefix)
+{
+ const char * const usage[] = {
+ "git credential-cache [options] <action>",
+ "",
+ "credential-cache is disabled in this build of Git",
+ NULL
+ };
+ struct option options[] = { OPT_END() };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+ die(_("credential-cache unavailable; no unix socket support"));
+}
+
+#endif /* NO_UNIX_SOCKETS */
diff --git a/credential-store.c b/builtin/credential-store.c
index 294e771681..5331ab151a 100644
--- a/credential-store.c
+++ b/builtin/credential-store.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "builtin.h"
#include "lockfile.h"
#include "credential.h"
#include "string-list.h"
@@ -143,7 +143,7 @@ static void lookup_credential(const struct string_list *fns, struct credential *
return; /* Found credential */
}
-int cmd_main(int argc, const char **argv)
+int cmd_credential_store(int argc, const char **argv, const char *prefix)
{
const char * const usage[] = {
"git credential-store [<options>] <action>",
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
umask(077);
- argc = parse_options(argc, (const char **)argv, NULL, options, usage, 0);
+ argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
if (argc != 1)
usage_with_options(usage, options);
op = argv[0];
diff --git a/builtin/describe.c b/builtin/describe.c
index 21d2cb9e57..7668591d57 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -12,7 +12,7 @@
#include "revision.h"
#include "diff.h"
#include "hashmap.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "run-command.h"
#include "object-store.h"
#include "list-objects.h"
@@ -501,15 +501,15 @@ static void process_object(struct object *obj, const char *path, void *data)
static void describe_blob(struct object_id oid, struct strbuf *dst)
{
struct rev_info revs;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
struct process_commit_data pcd = { null_oid, oid, dst, &revs};
- argv_array_pushl(&args, "internal: The first arg is not parsed",
- "--objects", "--in-commit-order", "--reverse", "HEAD",
- NULL);
+ strvec_pushl(&args, "internal: The first arg is not parsed",
+ "--objects", "--in-commit-order", "--reverse", "HEAD",
+ NULL);
repo_init_revisions(the_repository, &revs, NULL);
- if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
+ if (setup_revisions(args.nr, args.v, &revs, NULL) > 1)
BUG("setup_revisions could not handle all args?");
if (prepare_revision_walk(&revs))
@@ -594,26 +594,26 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
if (contains) {
struct string_list_item *item;
- struct argv_array args;
+ struct strvec args;
- argv_array_init(&args);
- argv_array_pushl(&args, "name-rev",
- "--peel-tag", "--name-only", "--no-undefined",
- NULL);
+ strvec_init(&args);
+ strvec_pushl(&args, "name-rev",
+ "--peel-tag", "--name-only", "--no-undefined",
+ NULL);
if (always)
- argv_array_push(&args, "--always");
+ strvec_push(&args, "--always");
if (!all) {
- argv_array_push(&args, "--tags");
+ strvec_push(&args, "--tags");
for_each_string_list_item(item, &patterns)
- argv_array_pushf(&args, "--refs=refs/tags/%s", item->string);
+ strvec_pushf(&args, "--refs=refs/tags/%s", item->string);
for_each_string_list_item(item, &exclude_patterns)
- argv_array_pushf(&args, "--exclude=refs/tags/%s", item->string);
+ strvec_pushf(&args, "--exclude=refs/tags/%s", item->string);
}
if (argc)
- argv_array_pushv(&args, argv);
+ strvec_pushv(&args, argv);
else
- argv_array_push(&args, "HEAD");
- return cmd_name_rev(args.argc, args.argv, prefix);
+ strvec_push(&args, "HEAD");
+ return cmd_name_rev(args.nr, args.v, prefix);
}
hashmap_init(&names, commit_name_neq, NULL, 0);
@@ -624,7 +624,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
if (argc == 0) {
if (broken) {
struct child_process cp = CHILD_PROCESS_INIT;
- argv_array_pushv(&cp.args, diff_index_args);
+ strvec_pushv(&cp.args, diff_index_args);
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.no_stdout = 1;
@@ -646,7 +646,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
} else if (dirty) {
struct lock_file index_lock = LOCK_INIT;
struct rev_info revs;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
int fd, result;
setup_work_tree();
@@ -658,8 +658,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
repo_update_index_if_able(the_repository, &index_lock);
repo_init_revisions(the_repository, &revs, prefix);
- argv_array_pushv(&args, diff_index_args);
- if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
+ strvec_pushv(&args, diff_index_args);
+ if (setup_revisions(args.nr, args.v, &revs, NULL) != 1)
BUG("malformed internal diff-index command line");
result = run_diff_index(&revs, 0);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index c280e682b2..7ac432b881 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -18,7 +18,7 @@
#include "run-command.h"
#include "exec-cmd.h"
#include "parse-options.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "strbuf.h"
#include "lockfile.h"
#include "object-store.h"
@@ -210,10 +210,10 @@ static void changed_files(struct hashmap *result, const char *index_path,
strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path);
env[0] = index_env.buf;
- argv_array_pushl(&update_index.args,
- "--git-dir", git_dir, "--work-tree", workdir,
- "update-index", "--really-refresh", "-q",
- "--unmerged", NULL);
+ strvec_pushl(&update_index.args,
+ "--git-dir", git_dir, "--work-tree", workdir,
+ "update-index", "--really-refresh", "-q",
+ "--unmerged", NULL);
update_index.no_stdin = 1;
update_index.no_stdout = 1;
update_index.no_stderr = 1;
@@ -225,9 +225,9 @@ static void changed_files(struct hashmap *result, const char *index_path,
/* Ignore any errors of update-index */
run_command(&update_index);
- argv_array_pushl(&diff_files.args,
- "--git-dir", git_dir, "--work-tree", workdir,
- "diff-files", "--name-only", "-z", NULL);
+ strvec_pushl(&diff_files.args,
+ "--git-dir", git_dir, "--work-tree", workdir,
+ "diff-files", "--name-only", "-z", NULL);
diff_files.no_stdin = 1;
diff_files.git_cmd = 1;
diff_files.use_shell = 0;
@@ -393,10 +393,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
child.clean_on_exit = 1;
child.dir = prefix;
child.out = -1;
- argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
- NULL);
+ strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
+ NULL);
for (i = 0; i < argc; i++)
- argv_array_push(&child.args, argv[i]);
+ strvec_push(&child.args, argv[i]);
if (start_command(&child))
die("could not obtain raw diff");
fp = xfdopen(child.out, "r");
@@ -667,7 +667,7 @@ finish:
static int run_file_diff(int prompt, const char *prefix,
int argc, const char **argv)
{
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
const char *env[] = {
"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
NULL
@@ -680,10 +680,10 @@ static int run_file_diff(int prompt, const char *prefix,
env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
- argv_array_push(&args, "diff");
+ strvec_push(&args, "diff");
for (i = 0; i < argc; i++)
- argv_array_push(&args, argv[i]);
- ret = run_command_v_opt_cd_env(args.argv, RUN_GIT_CMD, prefix, env);
+ strvec_push(&args, argv[i]);
+ ret = run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
exit(ret);
}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 9f37895d4c..e266d90a81 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -405,12 +405,12 @@ static char *generate_fake_oid(void *data)
{
static uint32_t counter = 1; /* avoid null oid */
const unsigned hashsz = the_hash_algo->rawsz;
- unsigned char out[GIT_MAX_RAWSZ];
+ struct object_id oid;
char *hex = xmallocz(GIT_MAX_HEXSZ);
- hashclr(out);
- put_be32(out + hashsz - 4, counter++);
- return hash_to_hex_algop_r(hex, out, the_hash_algo);
+ oidclr(&oid);
+ put_be32(oid.hash + hashsz - 4, counter++);
+ return oid_to_hex_r(hex, &oid);
}
static const char *anonymize_oid(const char *oid_hex)
@@ -943,7 +943,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
if (e->flags & UNINTERESTING)
continue;
- if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)
+ if (dwim_ref(e->name, strlen(e->name), &oid, &full_name, 0) != 1)
continue;
if (refspecs.nr) {
diff --git a/fast-import.c b/builtin/fast-import.c
index 0dfa14dc8c..1bf50a73dc 100644
--- a/fast-import.c
+++ b/builtin/fast-import.c
@@ -526,14 +526,6 @@ static unsigned int hc_str(const char *s, size_t len)
return r;
}
-static char *pool_strdup(const char *s)
-{
- size_t len = strlen(s) + 1;
- char *r = mem_pool_alloc(&fi_mem_pool, len);
- memcpy(r, s, len);
- return r;
-}
-
static void insert_mark(struct mark_set *s, uintmax_t idnum, struct object_entry *oe)
{
while ((idnum >> s->shift) >= 1024) {
@@ -615,7 +607,7 @@ static struct branch *new_branch(const char *name)
die("Branch name doesn't conform to GIT standards: %s", name);
b = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct branch));
- b->name = pool_strdup(name);
+ b->name = mem_pool_strdup(&fi_mem_pool, name);
b->table_next_branch = branch_table[hc];
b->branch_tree.versions[0].mode = S_IFDIR;
b->branch_tree.versions[1].mode = S_IFDIR;
@@ -747,7 +739,6 @@ static void start_packfile(void)
{
struct strbuf tmp_file = STRBUF_INIT;
struct packed_git *p;
- struct pack_header hdr;
int pack_fd;
pack_fd = odb_mkstemp(&tmp_file, "pack/tmp_pack_XXXXXX");
@@ -758,13 +749,8 @@ static void start_packfile(void)
p->do_not_close = 1;
pack_file = hashfd(pack_fd, p->pack_name);
- hdr.hdr_signature = htonl(PACK_SIGNATURE);
- hdr.hdr_version = htonl(2);
- hdr.hdr_entries = 0;
- hashwrite(pack_file, &hdr, sizeof(hdr));
-
pack_data = p;
- pack_size = sizeof(hdr);
+ pack_size = write_pack_header(pack_file, 0);
object_count = 0;
REALLOC_ARRAY(all_packs, pack_id + 1);
@@ -843,9 +829,9 @@ static int loosen_small_pack(const struct packed_git *p)
unpack.in = p->pack_fd;
unpack.git_cmd = 1;
unpack.stdout_to_stderr = 1;
- argv_array_push(&unpack.args, "unpack-objects");
+ strvec_push(&unpack.args, "unpack-objects");
if (!show_stats)
- argv_array_push(&unpack.args, "-q");
+ strvec_push(&unpack.args, "-q");
return run_command(&unpack);
}
@@ -2806,7 +2792,7 @@ static void parse_new_tag(const char *arg)
t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag));
memset(t, 0, sizeof(struct tag));
- t->name = pool_strdup(arg);
+ t->name = mem_pool_strdup(&fi_mem_pool, arg);
if (last_tag)
last_tag->next_tag = t;
else
@@ -3519,14 +3505,13 @@ static void parse_argv(void)
build_mark_map(&sub_marks_from, &sub_marks_to);
}
-int cmd_main(int argc, const char **argv)
+int cmd_fast_import(int argc, const char **argv, const char *prefix)
{
unsigned int i;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(fast_import_usage);
- setup_git_directory();
reset_pack_idx_option(&pack_idx_opts);
git_pack_config();
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index bbb5c96167..58b7c1fbdc 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -153,10 +153,6 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.from_promisor = 1;
continue;
}
- if (!strcmp("--no-dependents", arg)) {
- args.no_dependents = 1;
- continue;
- }
if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
parse_list_objects_filter(&args.filter_options, arg);
continue;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 82ac4be8a5..d5bf526277 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -19,7 +19,7 @@
#include "submodule-config.h"
#include "submodule.h"
#include "connected.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "utf8.h"
#include "packfile.h"
#include "list-objects-filter-options.h"
@@ -56,6 +56,7 @@ 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;
+static int write_fetch_head = 1;
static int verbosity, deepen_relative, set_upstream;
static int progress = -1;
static int enable_auto_gc = 1;
@@ -79,6 +80,7 @@ static struct list_objects_filter_options filter_options;
static struct string_list server_options = STRING_LIST_INIT_DUP;
static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
static int fetch_write_commit_graph = -1;
+static int stdin_refspecs = 0;
static int git_fetch_config(const char *k, const char *v, void *cb)
{
@@ -162,6 +164,8 @@ static struct option builtin_fetch_options[] = {
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
OPT_BOOL(0, "dry-run", &dry_run,
N_("dry run")),
+ OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
+ N_("write fetched references to the FETCH_HEAD file")),
OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
OPT_BOOL('u', "update-head-ok", &update_head_ok,
N_("allow updating of HEAD ref")),
@@ -196,12 +200,16 @@ static struct option builtin_fetch_options[] = {
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
N_("report that we have only objects reachable from this object")),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+ OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
+ N_("run 'maintenance --auto' after fetching")),
OPT_BOOL(0, "auto-gc", &enable_auto_gc,
- N_("run 'gc --auto' after fetching")),
+ N_("run 'maintenance --auto' after fetching")),
OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
N_("check for forced-updates on all updated branches")),
OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
N_("write the commit-graph after fetching")),
+ OPT_BOOL(0, "stdin", &stdin_refspecs,
+ N_("accept refspecs from stdin")),
OPT_END()
};
@@ -439,6 +447,7 @@ static struct ref *get_ref_map(struct remote *remote,
struct ref *orefs = NULL, **oref_tail = &orefs;
struct hashmap existing_refs;
+ int existing_refs_populated = 0;
if (rs->nr) {
struct refspec *fetch_refspec;
@@ -532,15 +541,18 @@ static struct ref *get_ref_map(struct remote *remote,
ref_map = ref_remove_duplicates(ref_map);
- refname_hash_init(&existing_refs);
- for_each_ref(add_one_refname, &existing_refs);
-
for (rm = ref_map; rm; rm = rm->next) {
if (rm->peer_ref) {
const char *refname = rm->peer_ref->name;
struct refname_hash_entry *peer_item;
unsigned int hash = strhash(refname);
+ if (!existing_refs_populated) {
+ refname_hash_init(&existing_refs);
+ for_each_ref(add_one_refname, &existing_refs);
+ existing_refs_populated = 1;
+ }
+
peer_item = hashmap_get_entry_from_hash(&existing_refs,
hash, refname,
struct refname_hash_entry, ent);
@@ -550,7 +562,8 @@ static struct ref *get_ref_map(struct remote *remote,
}
}
}
- hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
+ if (existing_refs_populated)
+ hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
return ref_map;
}
@@ -645,7 +658,7 @@ static void prepare_format_display(struct ref *ref_map)
struct ref *rm;
const char *format = "full";
- git_config_get_string_const("fetch.output", &format);
+ git_config_get_string_tmp("fetch.output", &format);
if (!strcasecmp(format, "full"))
compact_format = 0;
else if (!strcasecmp(format, "compact"))
@@ -893,7 +906,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
const char *what, *kind;
struct ref *rm;
char *url;
- const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository);
+ const char *filename = (!write_fetch_head
+ ? "/dev/null"
+ : git_path_fetch_head(the_repository));
int want_status;
int summary_width = transport_summary_width(ref_map);
@@ -953,8 +968,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
ref->force = rm->peer_ref->force;
}
- if (recurse_submodules != RECURSE_SUBMODULES_OFF)
+ if (recurse_submodules != RECURSE_SUBMODULES_OFF &&
+ (!rm->peer_ref || !oideq(&ref->old_oid, &ref->new_oid))) {
check_for_new_submodule_commits(&rm->old_oid);
+ }
if (!strcmp(rm->name, "HEAD")) {
kind = "";
@@ -1010,11 +1027,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
rc |= update_local_ref(ref, what, rm, &note,
summary_width);
free(ref);
- } else
+ } else if (write_fetch_head || dry_run) {
+ /*
+ * Display fetches written to FETCH_HEAD (or
+ * would be written to FETCH_HEAD, if --dry-run
+ * is set).
+ */
format_display(&note, '*',
*kind ? kind : "branch", NULL,
*what ? what : "HEAD",
"FETCH_HEAD", summary_width);
+ }
if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
@@ -1316,7 +1339,7 @@ static int do_fetch(struct transport *transport,
int autotags = (transport->remote->fetch_tags == 1);
int retcode = 0;
const struct ref *remote_refs;
- struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+ struct strvec ref_prefixes = STRVEC_INIT;
int must_list_refs = 1;
if (tags == TAGS_DEFAULT) {
@@ -1327,7 +1350,7 @@ static int do_fetch(struct transport *transport,
}
/* if not appending, truncate FETCH_HEAD */
- if (!append && !dry_run) {
+ if (!append && write_fetch_head) {
retcode = truncate_fetch_head();
if (retcode)
goto cleanup;
@@ -1354,8 +1377,8 @@ static int do_fetch(struct transport *transport,
if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
must_list_refs = 1;
- if (ref_prefixes.argc)
- argv_array_push(&ref_prefixes, "refs/tags/");
+ if (ref_prefixes.nr)
+ strvec_push(&ref_prefixes, "refs/tags/");
}
if (must_list_refs) {
@@ -1365,7 +1388,7 @@ static int do_fetch(struct transport *transport,
} else
remote_refs = NULL;
- argv_array_clear(&ref_prefixes);
+ strvec_clear(&ref_prefixes);
ref_map = get_ref_map(transport->remote, remote_refs, rs,
tags, &autotags);
@@ -1503,35 +1526,38 @@ static int add_remote_or_group(const char *name, struct string_list *list)
return 1;
}
-static void add_options_to_argv(struct argv_array *argv)
+static void add_options_to_argv(struct strvec *argv)
{
if (dry_run)
- argv_array_push(argv, "--dry-run");
+ strvec_push(argv, "--dry-run");
if (prune != -1)
- argv_array_push(argv, prune ? "--prune" : "--no-prune");
+ strvec_push(argv, prune ? "--prune" : "--no-prune");
if (prune_tags != -1)
- argv_array_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags");
+ strvec_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags");
if (update_head_ok)
- argv_array_push(argv, "--update-head-ok");
+ strvec_push(argv, "--update-head-ok");
if (force)
- argv_array_push(argv, "--force");
+ strvec_push(argv, "--force");
if (keep)
- argv_array_push(argv, "--keep");
+ strvec_push(argv, "--keep");
if (recurse_submodules == RECURSE_SUBMODULES_ON)
- argv_array_push(argv, "--recurse-submodules");
+ strvec_push(argv, "--recurse-submodules");
else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
- argv_array_push(argv, "--recurse-submodules=on-demand");
+ strvec_push(argv, "--recurse-submodules=on-demand");
if (tags == TAGS_SET)
- argv_array_push(argv, "--tags");
+ strvec_push(argv, "--tags");
else if (tags == TAGS_UNSET)
- argv_array_push(argv, "--no-tags");
+ strvec_push(argv, "--no-tags");
if (verbosity >= 2)
- argv_array_push(argv, "-v");
+ strvec_push(argv, "-v");
if (verbosity >= 1)
- argv_array_push(argv, "-v");
+ strvec_push(argv, "-v");
else if (verbosity < 0)
- argv_array_push(argv, "-q");
-
+ strvec_push(argv, "-q");
+ if (family == TRANSPORT_FAMILY_IPV4)
+ strvec_push(argv, "--ipv4");
+ else if (family == TRANSPORT_FAMILY_IPV6)
+ strvec_push(argv, "--ipv6");
}
/* Fetch multiple remotes in parallel */
@@ -1554,8 +1580,8 @@ static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
remote = state->remotes->items[state->next++].string;
*task_cb = remote;
- argv_array_pushv(&cp->args, state->argv);
- argv_array_push(&cp->args, remote);
+ strvec_pushv(&cp->args, state->argv);
+ strvec_push(&cp->args, remote);
cp->git_cmd = 1;
if (verbosity >= 0)
@@ -1592,22 +1618,22 @@ static int fetch_finished(int result, struct strbuf *out,
static int fetch_multiple(struct string_list *list, int max_children)
{
int i, result = 0;
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
- if (!append && !dry_run) {
+ if (!append && write_fetch_head) {
int errcode = truncate_fetch_head();
if (errcode)
return errcode;
}
- argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc",
- "--no-write-commit-graph", NULL);
+ strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc",
+ "--no-write-commit-graph", NULL);
add_options_to_argv(&argv);
if (max_children != 1 && list->nr != 1) {
- struct parallel_fetch_state state = { argv.argv, list, 0, 0 };
+ struct parallel_fetch_state state = { argv.v, list, 0, 0 };
- argv_array_push(&argv, "--end-of-options");
+ strvec_push(&argv, "--end-of-options");
result = run_processes_parallel_tr2(max_children,
&fetch_next_remote,
&fetch_failed_to_start,
@@ -1620,17 +1646,17 @@ static int fetch_multiple(struct string_list *list, int max_children)
} else
for (i = 0; i < list->nr; i++) {
const char *name = list->items[i].string;
- argv_array_push(&argv, name);
+ strvec_push(&argv, name);
if (verbosity >= 0)
printf(_("Fetching %s\n"), name);
- if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
error(_("Could not fetch %s"), name);
result = 1;
}
- argv_array_pop(&argv);
+ strvec_pop(&argv);
}
- argv_array_clear(&argv);
+ strvec_clear(&argv);
return !!result;
}
@@ -1675,7 +1701,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
return;
}
-static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)
+static int fetch_one(struct remote *remote, int argc, const char **argv,
+ int prune_tags_ok, int use_stdin_refspecs)
{
struct refspec rs = REFSPEC_INIT_FETCH;
int i;
@@ -1718,20 +1745,24 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru
for (i = 0; i < argc; i++) {
if (!strcmp(argv[i], "tag")) {
- char *tag;
i++;
if (i >= argc)
die(_("You need to specify a tag name."));
- tag = xstrfmt("refs/tags/%s:refs/tags/%s",
- argv[i], argv[i]);
- refspec_append(&rs, tag);
- free(tag);
+ refspec_appendf(&rs, "refs/tags/%s:refs/tags/%s",
+ argv[i], argv[i]);
} else {
refspec_append(&rs, argv[i]);
}
}
+ if (use_stdin_refspecs) {
+ struct strbuf line = STRBUF_INIT;
+ while (strbuf_getline_lf(&line, stdin) != EOF)
+ refspec_append(&rs, line.buf);
+ strbuf_release(&line);
+ }
+
if (server_options.nr)
gtransport->server_options = &server_options;
@@ -1766,12 +1797,18 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
free(anon);
}
- fetch_config_from_gitmodules(&submodule_fetch_jobs_config,
- &recurse_submodules);
git_config(git_fetch_config, NULL);
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
+ if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+ int *sfjc = submodule_fetch_jobs_config == -1
+ ? &submodule_fetch_jobs_config : NULL;
+ int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT
+ ? &recurse_submodules : NULL;
+
+ fetch_config_from_gitmodules(sfjc, rs);
+ }
if (deepen_relative) {
if (deepen_relative < 0)
@@ -1795,6 +1832,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (depth || deepen_since || deepen_not.nr)
deepen = 1;
+ /* FETCH_HEAD never gets updated in --dry-run mode */
+ if (dry_run)
+ write_fetch_head = 0;
+
if (all) {
if (argc == 1)
die(_("fetch --all does not take a repository argument"));
@@ -1828,7 +1869,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (remote) {
if (filter_options.choice || has_promisor_remote())
fetch_one_setup_partial(remote);
- result = fetch_one(remote, argc, argv, prune_tags_ok);
+ result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
} else {
int max_children = max_jobs;
@@ -1836,6 +1877,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
die(_("--filter can only be used with the remote "
"configured in extensions.partialclone"));
+ if (stdin_refspecs)
+ die(_("--stdin can only be used when fetching "
+ "from one remote"));
+
if (max_children < 0)
max_children = fetch_parallel_config;
@@ -1844,7 +1889,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
}
if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
- struct argv_array options = ARGV_ARRAY_INIT;
+ struct strvec options = STRVEC_INIT;
int max_children = max_jobs;
if (max_children < 0)
@@ -1860,7 +1905,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
recurse_submodules_default,
verbosity < 0,
max_children);
- argv_array_clear(&options);
+ strvec_clear(&options);
}
string_list_clear(&list, 0);
@@ -1882,7 +1927,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
close_object_store(the_repository->objects);
if (enable_auto_gc)
- run_auto_gc(verbosity < 0);
+ run_auto_maintenance(verbosity < 0);
return result;
}
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 57489e4eab..9d1ecda2b8 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -9,7 +9,7 @@
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
N_("git for-each-ref [--points-at <object>]"),
- N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
+ N_("git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"),
N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
NULL
};
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 37aa07da78..fbf26cafcf 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -168,7 +168,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
return 0;
if (!(obj->flags & HAS_OBJ)) {
- if (parent && !has_object_file(&obj->oid)) {
+ if (parent && !has_object(the_repository, &obj->oid, 1)) {
printf_ln(_("broken link from %7s %s\n"
" to %7s %s"),
printable_type(&parent->oid, parent->type),
diff --git a/builtin/gc.c b/builtin/gc.c
index 8e0b9cf41b..090959350e 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -18,7 +18,7 @@
#include "parse-options.h"
#include "run-command.h"
#include "sigchain.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "commit.h"
#include "commit-graph.h"
#include "packfile.h"
@@ -28,6 +28,7 @@
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
+#include "refs.h"
#define FAILED_RUN "failed to run %s"
@@ -50,12 +51,12 @@ static const char *prune_worktrees_expire = "3.months.ago";
static unsigned long big_pack_threshold;
static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
-static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
-static struct argv_array reflog = ARGV_ARRAY_INIT;
-static struct argv_array repack = ARGV_ARRAY_INIT;
-static struct argv_array prune = ARGV_ARRAY_INIT;
-static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
-static struct argv_array rerere = ARGV_ARRAY_INIT;
+static struct strvec pack_refs_cmd = STRVEC_INIT;
+static struct strvec reflog = STRVEC_INIT;
+static struct strvec repack = STRVEC_INIT;
+static struct strvec prune = STRVEC_INIT;
+static struct strvec prune_worktrees = STRVEC_INIT;
+static struct strvec rerere = STRVEC_INIT;
static struct tempfile *pidfile;
static struct lock_file log_lock;
@@ -311,18 +312,18 @@ static uint64_t estimate_repack_memory(struct packed_git *pack)
static int keep_one_pack(struct string_list_item *item, void *data)
{
- argv_array_pushf(&repack, "--keep-pack=%s", basename(item->string));
+ strvec_pushf(&repack, "--keep-pack=%s", basename(item->string));
return 0;
}
static void add_repack_all_option(struct string_list *keep_pack)
{
if (prune_expire && !strcmp(prune_expire, "now"))
- argv_array_push(&repack, "-a");
+ strvec_push(&repack, "-a");
else {
- argv_array_push(&repack, "-A");
+ strvec_push(&repack, "-A");
if (prune_expire)
- argv_array_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
+ strvec_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
}
if (keep_pack)
@@ -331,7 +332,7 @@ static void add_repack_all_option(struct string_list *keep_pack)
static void add_repack_incremental_option(void)
{
- argv_array_push(&repack, "--no-write-bitmap-index");
+ strvec_push(&repack, "--no-write-bitmap-index");
}
static int need_to_gc(void)
@@ -514,11 +515,11 @@ static void gc_before_repack(void)
if (done++)
return;
- if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
- die(FAILED_RUN, pack_refs_cmd.argv[0]);
+ if (pack_refs && run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD))
+ die(FAILED_RUN, pack_refs_cmd.v[0]);
- if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
- die(FAILED_RUN, reflog.argv[0]);
+ if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD))
+ die(FAILED_RUN, reflog.v[0]);
}
int cmd_gc(int argc, const char **argv, const char *prefix)
@@ -552,12 +553,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_gc_usage, builtin_gc_options);
- argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
- argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
- argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
- argv_array_pushl(&prune, "prune", "--expire", NULL);
- argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
- argv_array_pushl(&rerere, "rerere", "gc", NULL);
+ strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
+ strvec_pushl(&reflog, "reflog", "expire", "--all", NULL);
+ strvec_pushl(&repack, "repack", "-d", "-l", NULL);
+ strvec_pushl(&prune, "prune", "--expire", NULL);
+ strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
+ strvec_pushl(&rerere, "rerere", "gc", NULL);
/* default expiry time, overwritten in gc_config */
gc_config();
@@ -576,14 +577,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
die(_("failed to parse prune expiry value %s"), prune_expire);
if (aggressive) {
- argv_array_push(&repack, "-f");
+ strvec_push(&repack, "-f");
if (aggressive_depth > 0)
- argv_array_pushf(&repack, "--depth=%d", aggressive_depth);
+ strvec_pushf(&repack, "--depth=%d", aggressive_depth);
if (aggressive_window > 0)
- argv_array_pushf(&repack, "--window=%d", aggressive_window);
+ strvec_pushf(&repack, "--window=%d", aggressive_window);
}
if (quiet)
- argv_array_push(&repack, "-q");
+ strvec_push(&repack, "-q");
if (auto_gc) {
/*
@@ -653,29 +654,29 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (!repository_format_precious_objects) {
close_object_store(the_repository->objects);
- if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
- die(FAILED_RUN, repack.argv[0]);
+ if (run_command_v_opt(repack.v, RUN_GIT_CMD))
+ die(FAILED_RUN, repack.v[0]);
if (prune_expire) {
- argv_array_push(&prune, prune_expire);
+ strvec_push(&prune, prune_expire);
if (quiet)
- argv_array_push(&prune, "--no-progress");
+ strvec_push(&prune, "--no-progress");
if (has_promisor_remote())
- argv_array_push(&prune,
- "--exclude-promisor-objects");
- if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
- die(FAILED_RUN, prune.argv[0]);
+ strvec_push(&prune,
+ "--exclude-promisor-objects");
+ if (run_command_v_opt(prune.v, RUN_GIT_CMD))
+ die(FAILED_RUN, prune.v[0]);
}
}
if (prune_worktrees_expire) {
- argv_array_push(&prune_worktrees, prune_worktrees_expire);
- if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD))
- die(FAILED_RUN, prune_worktrees.argv[0]);
+ strvec_push(&prune_worktrees, prune_worktrees_expire);
+ if (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD))
+ die(FAILED_RUN, prune_worktrees.v[0]);
}
- if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
- die(FAILED_RUN, rerere.argv[0]);
+ if (run_command_v_opt(rerere.v, RUN_GIT_CMD))
+ die(FAILED_RUN, rerere.v[0]);
report_garbage = report_pack_garbage;
reprepare_packed_git(the_repository);
@@ -699,3 +700,339 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
return 0;
}
+
+static const char * const builtin_maintenance_run_usage[] = {
+ N_("git maintenance run [--auto] [--[no-]quiet] [--task=<task>]"),
+ NULL
+};
+
+struct maintenance_run_opts {
+ int auto_flag;
+ int quiet;
+};
+
+/* Remember to update object flag allocation in object.h */
+#define SEEN (1u<<0)
+
+struct cg_auto_data {
+ int num_not_in_graph;
+ int limit;
+};
+
+static int dfs_on_ref(const char *refname,
+ const struct object_id *oid, int flags,
+ void *cb_data)
+{
+ struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
+ int result = 0;
+ struct object_id peeled;
+ struct commit_list *stack = NULL;
+ struct commit *commit;
+
+ if (!peel_ref(refname, &peeled))
+ oid = &peeled;
+ if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
+ return 0;
+
+ commit = lookup_commit(the_repository, oid);
+ if (!commit)
+ return 0;
+ if (parse_commit(commit))
+ return 0;
+
+ commit_list_append(commit, &stack);
+
+ while (!result && stack) {
+ struct commit_list *parent;
+
+ commit = pop_commit(&stack);
+
+ for (parent = commit->parents; parent; parent = parent->next) {
+ if (parse_commit(parent->item) ||
+ commit_graph_position(parent->item) != COMMIT_NOT_FROM_GRAPH ||
+ parent->item->object.flags & SEEN)
+ continue;
+
+ parent->item->object.flags |= SEEN;
+ data->num_not_in_graph++;
+
+ if (data->num_not_in_graph >= data->limit) {
+ result = 1;
+ break;
+ }
+
+ commit_list_append(parent->item, &stack);
+ }
+ }
+
+ free_commit_list(stack);
+ return result;
+}
+
+static int should_write_commit_graph(void)
+{
+ int result;
+ struct cg_auto_data data;
+
+ data.num_not_in_graph = 0;
+ data.limit = 100;
+ git_config_get_int("maintenance.commit-graph.auto",
+ &data.limit);
+
+ if (!data.limit)
+ return 0;
+ if (data.limit < 0)
+ return 1;
+
+ result = for_each_ref(dfs_on_ref, &data);
+
+ clear_commit_marks_all(SEEN);
+
+ return result;
+}
+
+static int run_write_commit_graph(struct maintenance_run_opts *opts)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ child.git_cmd = 1;
+ strvec_pushl(&child.args, "commit-graph", "write",
+ "--split", "--reachable", NULL);
+
+ if (opts->quiet)
+ strvec_push(&child.args, "--no-progress");
+
+ return !!run_command(&child);
+}
+
+static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
+{
+ close_object_store(the_repository->objects);
+ if (run_write_commit_graph(opts)) {
+ error(_("failed to write commit-graph"));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int maintenance_task_gc(struct maintenance_run_opts *opts)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ child.git_cmd = 1;
+ strvec_push(&child.args, "gc");
+
+ if (opts->auto_flag)
+ strvec_push(&child.args, "--auto");
+ if (opts->quiet)
+ strvec_push(&child.args, "--quiet");
+ else
+ strvec_push(&child.args, "--no-quiet");
+
+ close_object_store(the_repository->objects);
+ return run_command(&child);
+}
+
+typedef int maintenance_task_fn(struct maintenance_run_opts *opts);
+
+/*
+ * An auto condition function returns 1 if the task should run
+ * and 0 if the task should NOT run. See needs_to_gc() for an
+ * example.
+ */
+typedef int maintenance_auto_fn(void);
+
+struct maintenance_task {
+ const char *name;
+ maintenance_task_fn *fn;
+ maintenance_auto_fn *auto_condition;
+ unsigned enabled:1;
+
+ /* -1 if not selected. */
+ int selected_order;
+};
+
+enum maintenance_task_label {
+ TASK_GC,
+ TASK_COMMIT_GRAPH,
+
+ /* Leave as final value */
+ TASK__COUNT
+};
+
+static struct maintenance_task tasks[] = {
+ [TASK_GC] = {
+ "gc",
+ maintenance_task_gc,
+ need_to_gc,
+ 1,
+ },
+ [TASK_COMMIT_GRAPH] = {
+ "commit-graph",
+ maintenance_task_commit_graph,
+ should_write_commit_graph,
+ },
+};
+
+static int compare_tasks_by_selection(const void *a_, const void *b_)
+{
+ const struct maintenance_task *a, *b;
+
+ a = (const struct maintenance_task *)&a_;
+ b = (const struct maintenance_task *)&b_;
+
+ return b->selected_order - a->selected_order;
+}
+
+static int maintenance_run_tasks(struct maintenance_run_opts *opts)
+{
+ int i, found_selected = 0;
+ int result = 0;
+ struct lock_file lk;
+ struct repository *r = the_repository;
+ char *lock_path = xstrfmt("%s/maintenance", r->objects->odb->path);
+
+ if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) {
+ /*
+ * Another maintenance command is running.
+ *
+ * If --auto was provided, then it is likely due to a
+ * recursive process stack. Do not report an error in
+ * that case.
+ */
+ if (!opts->auto_flag && !opts->quiet)
+ warning(_("lock file '%s' exists, skipping maintenance"),
+ lock_path);
+ free(lock_path);
+ return 0;
+ }
+ free(lock_path);
+
+ for (i = 0; !found_selected && i < TASK__COUNT; i++)
+ found_selected = tasks[i].selected_order >= 0;
+
+ if (found_selected)
+ QSORT(tasks, TASK__COUNT, compare_tasks_by_selection);
+
+ for (i = 0; i < TASK__COUNT; i++) {
+ if (found_selected && tasks[i].selected_order < 0)
+ continue;
+
+ if (!found_selected && !tasks[i].enabled)
+ continue;
+
+ if (opts->auto_flag &&
+ (!tasks[i].auto_condition ||
+ !tasks[i].auto_condition()))
+ continue;
+
+ trace2_region_enter("maintenance", tasks[i].name, r);
+ if (tasks[i].fn(opts)) {
+ error(_("task '%s' failed"), tasks[i].name);
+ result = 1;
+ }
+ trace2_region_leave("maintenance", tasks[i].name, r);
+ }
+
+ rollback_lock_file(&lk);
+ return result;
+}
+
+static void initialize_task_config(void)
+{
+ int i;
+ struct strbuf config_name = STRBUF_INIT;
+ gc_config();
+
+ for (i = 0; i < TASK__COUNT; i++) {
+ int config_value;
+
+ strbuf_setlen(&config_name, 0);
+ strbuf_addf(&config_name, "maintenance.%s.enabled",
+ tasks[i].name);
+
+ if (!git_config_get_bool(config_name.buf, &config_value))
+ tasks[i].enabled = config_value;
+ }
+
+ strbuf_release(&config_name);
+}
+
+static int task_option_parse(const struct option *opt,
+ const char *arg, int unset)
+{
+ int i, num_selected = 0;
+ struct maintenance_task *task = NULL;
+
+ BUG_ON_OPT_NEG(unset);
+
+ for (i = 0; i < TASK__COUNT; i++) {
+ if (tasks[i].selected_order >= 0)
+ num_selected++;
+ if (!strcasecmp(tasks[i].name, arg)) {
+ task = &tasks[i];
+ }
+ }
+
+ if (!task) {
+ error(_("'%s' is not a valid task"), arg);
+ return 1;
+ }
+
+ if (task->selected_order >= 0) {
+ error(_("task '%s' cannot be selected multiple times"), arg);
+ return 1;
+ }
+
+ task->selected_order = num_selected + 1;
+
+ return 0;
+}
+
+static int maintenance_run(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ struct maintenance_run_opts opts;
+ struct option builtin_maintenance_run_options[] = {
+ OPT_BOOL(0, "auto", &opts.auto_flag,
+ N_("run tasks based on the state of the repository")),
+ OPT_BOOL(0, "quiet", &opts.quiet,
+ N_("do not report progress or other information over stderr")),
+ OPT_CALLBACK_F(0, "task", NULL, N_("task"),
+ N_("run a specific task"),
+ PARSE_OPT_NONEG, task_option_parse),
+ OPT_END()
+ };
+ memset(&opts, 0, sizeof(opts));
+
+ opts.quiet = !isatty(2);
+ initialize_task_config();
+
+ for (i = 0; i < TASK__COUNT; i++)
+ tasks[i].selected_order = -1;
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_maintenance_run_options,
+ builtin_maintenance_run_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (argc != 0)
+ usage_with_options(builtin_maintenance_run_usage,
+ builtin_maintenance_run_options);
+ return maintenance_run_tasks(&opts);
+}
+
+static const char builtin_maintenance_usage[] = N_("git maintenance run [<options>]");
+
+int cmd_maintenance(int argc, const char **argv, const char *prefix)
+{
+ if (argc < 2 ||
+ (argc == 2 && !strcmp(argv[1], "-h")))
+ usage(builtin_maintenance_usage);
+
+ if (!strcmp(argv[1], "run"))
+ return maintenance_run(argc - 1, argv + 1, prefix);
+
+ die(_("invalid subcommand: %s"), argv[1]);
+}
diff --git a/builtin/grep.c b/builtin/grep.c
index a5056f395a..c8037388c6 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -319,7 +319,7 @@ static void grep_source_name(struct grep_opt *opt, const char *filename,
}
if (opt->relative && opt->prefix_length)
- quote_path_relative(filename + tree_name_len, opt->prefix, out);
+ quote_path(filename + tree_name_len, opt->prefix, out, 0);
else
quote_c_style(filename + tree_name_len, out, NULL, 0);
@@ -397,7 +397,7 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
int i, status;
for (i = 0; i < path_list->nr; i++)
- argv_array_push(&child.args, path_list->items[i].string);
+ strvec_push(&child.args, path_list->items[i].string);
child.dir = prefix;
child.use_shell = 1;
@@ -466,7 +466,7 @@ static int grep_submodule(struct grep_opt *opt,
struct strbuf base = STRBUF_INIT;
obj_read_lock();
- object = parse_object_or_die(oid, oid_to_hex(oid));
+ object = parse_object_or_die(oid, NULL);
obj_read_unlock();
data = read_object_with_reference(&subrepo,
&object->oid, tree_type,
@@ -693,7 +693,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
struct dir_struct dir;
int i, hit = 0;
- memset(&dir, 0, sizeof(dir));
+ dir_init(&dir);
if (!use_index)
dir.flags |= DIR_NO_GITLINKS;
if (exc_std)
@@ -705,6 +705,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
if (hit && opt->status_only)
break;
}
+ dir_clear(&dir);
return hit;
}
diff --git a/builtin/help.c b/builtin/help.c
index 299206eb57..bb339f0fc8 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -579,7 +579,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
}
if (show_guides)
- list_common_guides_help();
+ list_guides_help();
if (show_all || show_guides) {
printf("%s\n", _(git_more_info_string));
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index f865666db9..8acd078aa0 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -33,19 +33,61 @@ struct object_stat {
};
struct base_data {
+ /* Initialized by make_base(). */
struct base_data *base;
- struct base_data *child;
struct object_entry *obj;
- void *data;
- unsigned long size;
int ref_first, ref_last;
int ofs_first, ofs_last;
+ /*
+ * Threads should increment retain_data if they are about to call
+ * patch_delta() using this struct's data as a base, and decrement this
+ * when they are done. While retain_data is nonzero, this struct's data
+ * will not be freed even if the delta base cache limit is exceeded.
+ */
+ int retain_data;
+ /*
+ * The number of direct children that have not been fully processed
+ * (entered work_head, entered done_head, left done_head). When this
+ * number reaches zero, this struct base_data can be freed.
+ */
+ int children_remaining;
+
+ /* Not initialized by make_base(). */
+ struct list_head list;
+ void *data;
+ unsigned long size;
};
+/*
+ * Stack of struct base_data that have unprocessed children.
+ * threaded_second_pass() uses this as a source of work (the other being the
+ * objects array).
+ *
+ * Guarded by work_mutex.
+ */
+static LIST_HEAD(work_head);
+
+/*
+ * Stack of struct base_data that have children, all of whom have been
+ * processed or are being processed, and at least one child is being processed.
+ * These struct base_data must be kept around until the last child is
+ * processed.
+ *
+ * Guarded by work_mutex.
+ */
+static LIST_HEAD(done_head);
+
+/*
+ * All threads share one delta base cache.
+ *
+ * base_cache_used is guarded by work_mutex, and base_cache_limit is read-only
+ * in a thread.
+ */
+static size_t base_cache_used;
+static size_t base_cache_limit;
+
struct thread_local {
pthread_t thread;
- struct base_data *base_cache;
- size_t base_cache_used;
int pack_fd;
};
@@ -364,56 +406,42 @@ static void set_thread_data(struct thread_local *data)
pthread_setspecific(key, data);
}
-static struct base_data *alloc_base_data(void)
-{
- struct base_data *base = xcalloc(1, sizeof(struct base_data));
- base->ref_last = -1;
- base->ofs_last = -1;
- return base;
-}
-
static void free_base_data(struct base_data *c)
{
if (c->data) {
FREE_AND_NULL(c->data);
- get_thread_data()->base_cache_used -= c->size;
+ base_cache_used -= c->size;
}
}
static void prune_base_data(struct base_data *retain)
{
- struct base_data *b;
- struct thread_local *data = get_thread_data();
- for (b = data->base_cache;
- data->base_cache_used > delta_base_cache_limit && b;
- b = b->child) {
- if (b->data && b != retain)
- free_base_data(b);
- }
-}
+ struct list_head *pos;
-static void link_base_data(struct base_data *base, struct base_data *c)
-{
- if (base)
- base->child = c;
- else
- get_thread_data()->base_cache = c;
+ if (base_cache_used <= base_cache_limit)
+ return;
- c->base = base;
- c->child = NULL;
- if (c->data)
- get_thread_data()->base_cache_used += c->size;
- prune_base_data(c);
-}
+ list_for_each_prev(pos, &done_head) {
+ struct base_data *b = list_entry(pos, struct base_data, list);
+ if (b->retain_data || b == retain)
+ continue;
+ if (b->data) {
+ free_base_data(b);
+ if (base_cache_used <= base_cache_limit)
+ return;
+ }
+ }
-static void unlink_base_data(struct base_data *c)
-{
- struct base_data *base = c->base;
- if (base)
- base->child = NULL;
- else
- get_thread_data()->base_cache = NULL;
- free_base_data(c);
+ list_for_each_prev(pos, &work_head) {
+ struct base_data *b = list_entry(pos, struct base_data, list);
+ if (b->retain_data || b == retain)
+ continue;
+ if (b->data) {
+ free_base_data(b);
+ if (base_cache_used <= base_cache_limit)
+ return;
+ }
+ }
}
static int is_delta_type(enum object_type type)
@@ -614,7 +642,7 @@ static int compare_ofs_delta_bases(off_t offset1, off_t offset2,
0;
}
-static int find_ofs_delta(const off_t offset, enum object_type type)
+static int find_ofs_delta(const off_t offset)
{
int first = 0, last = nr_ofs_deltas;
@@ -624,7 +652,8 @@ static int find_ofs_delta(const off_t offset, enum object_type type)
int cmp;
cmp = compare_ofs_delta_bases(offset, delta->offset,
- type, objects[delta->obj_no].type);
+ OBJ_OFS_DELTA,
+ objects[delta->obj_no].type);
if (!cmp)
return next;
if (cmp < 0) {
@@ -637,10 +666,9 @@ static int find_ofs_delta(const off_t offset, enum object_type type)
}
static void find_ofs_delta_children(off_t offset,
- int *first_index, int *last_index,
- enum object_type type)
+ int *first_index, int *last_index)
{
- int first = find_ofs_delta(offset, type);
+ int first = find_ofs_delta(offset);
int last = first;
int end = nr_ofs_deltas - 1;
@@ -668,7 +696,7 @@ static int compare_ref_delta_bases(const struct object_id *oid1,
return oidcmp(oid1, oid2);
}
-static int find_ref_delta(const struct object_id *oid, enum object_type type)
+static int find_ref_delta(const struct object_id *oid)
{
int first = 0, last = nr_ref_deltas;
@@ -678,7 +706,8 @@ static int find_ref_delta(const struct object_id *oid, enum object_type type)
int cmp;
cmp = compare_ref_delta_bases(oid, &delta->oid,
- type, objects[delta->obj_no].type);
+ OBJ_REF_DELTA,
+ objects[delta->obj_no].type);
if (!cmp)
return next;
if (cmp < 0) {
@@ -691,10 +720,9 @@ static int find_ref_delta(const struct object_id *oid, enum object_type type)
}
static void find_ref_delta_children(const struct object_id *oid,
- int *first_index, int *last_index,
- enum object_type type)
+ int *first_index, int *last_index)
{
- int first = find_ref_delta(oid, type);
+ int first = find_ref_delta(oid);
int last = first;
int end = nr_ref_deltas - 1;
@@ -866,15 +894,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
}
/*
- * This function is part of find_unresolved_deltas(). There are two
- * walkers going in the opposite ways.
- *
- * The first one in find_unresolved_deltas() traverses down from
- * parent node to children, deflating nodes along the way. However,
- * memory for deflated nodes is limited by delta_base_cache_limit, so
- * at some point parent node's deflated content may be freed.
- *
- * The second walker is this function, which goes from current node up
+ * Walk from current node up
* to top parent if necessary to deflate the node. In normal
* situation, its parent node would be already deflated, so it just
* needs to apply delta.
@@ -902,7 +922,7 @@ static void *get_base_data(struct base_data *c)
if (!delta_nr) {
c->data = get_data_from_pack(obj);
c->size = obj->size;
- get_thread_data()->base_cache_used += c->size;
+ base_cache_used += c->size;
prune_base_data(c);
}
for (; delta_nr > 0; delta_nr--) {
@@ -918,7 +938,7 @@ static void *get_base_data(struct base_data *c)
free(raw);
if (!c->data)
bad_object(obj->idx.offset, _("failed to apply delta"));
- get_thread_data()->base_cache_used += c->size;
+ base_cache_used += c->size;
prune_base_data(c);
}
free(delta);
@@ -926,10 +946,27 @@ static void *get_base_data(struct base_data *c)
return c->data;
}
-static void resolve_delta(struct object_entry *delta_obj,
- struct base_data *base, struct base_data *result)
+static struct base_data *make_base(struct object_entry *obj,
+ struct base_data *parent)
+{
+ struct base_data *base = xcalloc(1, sizeof(struct base_data));
+ base->base = parent;
+ base->obj = obj;
+ find_ref_delta_children(&obj->idx.oid,
+ &base->ref_first, &base->ref_last);
+ find_ofs_delta_children(obj->idx.offset,
+ &base->ofs_first, &base->ofs_last);
+ base->children_remaining = base->ref_last - base->ref_first +
+ base->ofs_last - base->ofs_first + 2;
+ return base;
+}
+
+static struct base_data *resolve_delta(struct object_entry *delta_obj,
+ struct base_data *base)
{
- void *base_data, *delta_data;
+ void *delta_data, *result_data;
+ struct base_data *result;
+ unsigned long result_size;
if (show_stat) {
int i = delta_obj - objects;
@@ -942,115 +979,26 @@ static void resolve_delta(struct object_entry *delta_obj,
obj_stat[i].base_object_no = j;
}
delta_data = get_data_from_pack(delta_obj);
- base_data = get_base_data(base);
- result->obj = delta_obj;
- result->data = patch_delta(base_data, base->size,
- delta_data, delta_obj->size, &result->size);
+ assert(base->data);
+ result_data = patch_delta(base->data, base->size,
+ delta_data, delta_obj->size, &result_size);
free(delta_data);
- if (!result->data)
+ if (!result_data)
bad_object(delta_obj->idx.offset, _("failed to apply delta"));
- hash_object_file(the_hash_algo, result->data, result->size,
+ hash_object_file(the_hash_algo, result_data, result_size,
type_name(delta_obj->real_type), &delta_obj->idx.oid);
- sha1_object(result->data, NULL, result->size, delta_obj->real_type,
+ sha1_object(result_data, NULL, result_size, delta_obj->real_type,
&delta_obj->idx.oid);
+
+ result = make_base(delta_obj, base);
+ result->data = result_data;
+ result->size = result_size;
+
counter_lock();
nr_resolved_deltas++;
counter_unlock();
-}
-/*
- * Standard boolean compare-and-swap: atomically check whether "*type" is
- * "want"; if so, swap in "set" and return true. Otherwise, leave it untouched
- * and return false.
- */
-static int compare_and_swap_type(signed char *type,
- enum object_type want,
- enum object_type set)
-{
- enum object_type old;
-
- type_cas_lock();
- old = *type;
- if (old == want)
- *type = set;
- type_cas_unlock();
-
- return old == want;
-}
-
-static struct base_data *find_unresolved_deltas_1(struct base_data *base,
- struct base_data *prev_base)
-{
- if (base->ref_last == -1 && base->ofs_last == -1) {
- find_ref_delta_children(&base->obj->idx.oid,
- &base->ref_first, &base->ref_last,
- OBJ_REF_DELTA);
-
- find_ofs_delta_children(base->obj->idx.offset,
- &base->ofs_first, &base->ofs_last,
- OBJ_OFS_DELTA);
-
- if (base->ref_last == -1 && base->ofs_last == -1) {
- free(base->data);
- return NULL;
- }
-
- link_base_data(prev_base, base);
- }
-
- if (base->ref_first <= base->ref_last) {
- struct object_entry *child = objects + ref_deltas[base->ref_first].obj_no;
- struct base_data *result = alloc_base_data();
-
- if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
- base->obj->real_type))
- die("REF_DELTA at offset %"PRIuMAX" already resolved (duplicate base %s?)",
- (uintmax_t)child->idx.offset,
- oid_to_hex(&base->obj->idx.oid));
-
- resolve_delta(child, base, result);
- if (base->ref_first == base->ref_last && base->ofs_last == -1)
- free_base_data(base);
-
- base->ref_first++;
- return result;
- }
-
- if (base->ofs_first <= base->ofs_last) {
- struct object_entry *child = objects + ofs_deltas[base->ofs_first].obj_no;
- struct base_data *result = alloc_base_data();
-
- assert(child->real_type == OBJ_OFS_DELTA);
- child->real_type = base->obj->real_type;
- resolve_delta(child, base, result);
- if (base->ofs_first == base->ofs_last)
- free_base_data(base);
-
- base->ofs_first++;
- return result;
- }
-
- unlink_base_data(base);
- return NULL;
-}
-
-static void find_unresolved_deltas(struct base_data *base)
-{
- struct base_data *new_base, *prev_base = NULL;
- for (;;) {
- new_base = find_unresolved_deltas_1(base, prev_base);
-
- if (new_base) {
- prev_base = base;
- base = new_base;
- } else {
- free(base);
- base = prev_base;
- if (!base)
- return;
- prev_base = base->base;
- }
- }
+ return result;
}
static int compare_ofs_delta_entry(const void *a, const void *b)
@@ -1071,34 +1019,131 @@ static int compare_ref_delta_entry(const void *a, const void *b)
return oidcmp(&delta_a->oid, &delta_b->oid);
}
-static void resolve_base(struct object_entry *obj)
-{
- struct base_data *base_obj = alloc_base_data();
- base_obj->obj = obj;
- base_obj->data = NULL;
- find_unresolved_deltas(base_obj);
-}
-
static void *threaded_second_pass(void *data)
{
- set_thread_data(data);
+ if (data)
+ set_thread_data(data);
for (;;) {
- int i;
- counter_lock();
- display_progress(progress, nr_resolved_deltas);
- counter_unlock();
+ struct base_data *parent = NULL;
+ struct object_entry *child_obj;
+ struct base_data *child;
+
work_lock();
- while (nr_dispatched < nr_objects &&
- is_delta_type(objects[nr_dispatched].type))
- nr_dispatched++;
- if (nr_dispatched >= nr_objects) {
- work_unlock();
- break;
+ if (list_empty(&work_head)) {
+ /*
+ * Take an object from the object array.
+ */
+ while (nr_dispatched < nr_objects &&
+ is_delta_type(objects[nr_dispatched].type))
+ nr_dispatched++;
+ if (nr_dispatched >= nr_objects) {
+ work_unlock();
+ break;
+ }
+ child_obj = &objects[nr_dispatched++];
+ } else {
+ /*
+ * Peek at the top of the stack, and take a child from
+ * it.
+ */
+ parent = list_first_entry(&work_head, struct base_data,
+ list);
+
+ if (parent->ref_first <= parent->ref_last) {
+ int offset = ref_deltas[parent->ref_first++].obj_no;
+ child_obj = objects + offset;
+ if (child_obj->real_type != OBJ_REF_DELTA)
+ die("REF_DELTA at offset %"PRIuMAX" already resolved (duplicate base %s?)",
+ (uintmax_t) child_obj->idx.offset,
+ oid_to_hex(&parent->obj->idx.oid));
+ child_obj->real_type = parent->obj->real_type;
+ } else {
+ child_obj = objects +
+ ofs_deltas[parent->ofs_first++].obj_no;
+ assert(child_obj->real_type == OBJ_OFS_DELTA);
+ child_obj->real_type = parent->obj->real_type;
+ }
+
+ if (parent->ref_first > parent->ref_last &&
+ parent->ofs_first > parent->ofs_last) {
+ /*
+ * This parent has run out of children, so move
+ * it to done_head.
+ */
+ list_del(&parent->list);
+ list_add(&parent->list, &done_head);
+ }
+
+ /*
+ * Ensure that the parent has data, since we will need
+ * it later.
+ *
+ * NEEDSWORK: If parent data needs to be reloaded, this
+ * prolongs the time that the current thread spends in
+ * the mutex. A mitigating factor is that parent data
+ * needs to be reloaded only if the delta base cache
+ * limit is exceeded, so in the typical case, this does
+ * not happen.
+ */
+ get_base_data(parent);
+ parent->retain_data++;
}
- i = nr_dispatched++;
work_unlock();
- resolve_base(&objects[i]);
+ if (parent) {
+ child = resolve_delta(child_obj, parent);
+ if (!child->children_remaining)
+ FREE_AND_NULL(child->data);
+ } else {
+ child = make_base(child_obj, NULL);
+ if (child->children_remaining) {
+ /*
+ * Since this child has its own delta children,
+ * we will need this data in the future.
+ * Inflate now so that future iterations will
+ * have access to this object's data while
+ * outside the work mutex.
+ */
+ child->data = get_data_from_pack(child_obj);
+ child->size = child_obj->size;
+ }
+ }
+
+ work_lock();
+ if (parent)
+ parent->retain_data--;
+ if (child->data) {
+ /*
+ * This child has its own children, so add it to
+ * work_head.
+ */
+ list_add(&child->list, &work_head);
+ base_cache_used += child->size;
+ prune_base_data(NULL);
+ } else {
+ /*
+ * This child does not have its own children. It may be
+ * the last descendant of its ancestors; free those
+ * that we can.
+ */
+ struct base_data *p = parent;
+
+ while (p) {
+ struct base_data *next_p;
+
+ p->children_remaining--;
+ if (p->children_remaining)
+ break;
+
+ next_p = p->base;
+ free_base_data(p);
+ list_del(&p->list);
+ free(p);
+
+ p = next_p;
+ }
+ }
+ work_unlock();
}
return NULL;
}
@@ -1199,6 +1244,7 @@ static void resolve_deltas(void)
nr_ref_deltas + nr_ofs_deltas);
nr_dispatched = 0;
+ base_cache_limit = delta_base_cache_limit * nr_threads;
if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
init_thread();
for (i = 0; i < nr_threads; i++) {
@@ -1213,15 +1259,7 @@ static void resolve_deltas(void)
cleanup_thread();
return;
}
-
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *obj = &objects[i];
-
- if (is_delta_type(obj->type))
- continue;
- resolve_base(obj);
- display_progress(progress, nr_resolved_deltas);
- }
+ threaded_second_pass(&nothread_data);
}
/*
@@ -1376,22 +1414,28 @@ static void fix_unresolved_deltas(struct hashfile *f)
for (i = 0; i < nr_ref_deltas; i++) {
struct ref_delta_entry *d = sorted_by_pos[i];
enum object_type type;
- struct base_data *base_obj = alloc_base_data();
+ void *data;
+ unsigned long size;
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
continue;
- base_obj->data = read_object_file(&d->oid, &type,
- &base_obj->size);
- if (!base_obj->data)
+ data = read_object_file(&d->oid, &type, &size);
+ if (!data)
continue;
if (check_object_signature(the_repository, &d->oid,
- base_obj->data, base_obj->size,
+ data, size,
type_name(type)))
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
- base_obj->obj = append_obj_to_pack(f, d->oid.hash,
- base_obj->data, base_obj->size, type);
- find_unresolved_deltas(base_obj);
+
+ /*
+ * Add this as an object to the objects array and call
+ * threaded_second_pass() (which will pick up the added
+ * object).
+ */
+ append_obj_to_pack(f, d->oid.hash, data, size, type);
+ threaded_second_pass(NULL);
+
display_progress(progress, nr_resolved_deltas);
}
free(sorted_by_pos);
@@ -1798,9 +1842,22 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (HAVE_THREADS && !nr_threads) {
nr_threads = online_cpus();
- /* An experiment showed that more threads does not mean faster */
- if (nr_threads > 3)
- nr_threads = 3;
+ /*
+ * Experiments show that going above 20 threads doesn't help,
+ * no matter how many cores you have. Below that, we tend to
+ * max at half the number of online_cpus(), presumably because
+ * half of those are hyperthreads rather than full cores. We'll
+ * never reduce the level below "3", though, to match a
+ * historical value that nobody complained about.
+ */
+ if (nr_threads < 4)
+ ; /* too few cores to consider capping */
+ else if (nr_threads < 6)
+ nr_threads = 3; /* historic cap */
+ else if (nr_threads < 40)
+ nr_threads /= 2;
+ else
+ nr_threads = 20; /* hard cap */
}
curr_pack = open_pack_file(pack_name);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index cee64823cb..01bc648d41 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -9,6 +9,7 @@
#include "builtin.h"
#include "exec-cmd.h"
#include "parse-options.h"
+#include "worktree.h"
#ifndef DEFAULT_GIT_TEMPLATE_DIR
#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
@@ -178,16 +179,11 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
return 1;
}
-void initialize_repository_version(int hash_algo)
+void initialize_repository_version(int hash_algo, int reinit)
{
char repo_version_string[10];
int repo_version = GIT_REPO_VERSION;
-#ifndef ENABLE_SHA256
- if (hash_algo != GIT_HASH_SHA1)
- die(_("The hash algorithm %s is not supported in this build."), hash_algos[hash_algo].name);
-#endif
-
if (hash_algo != GIT_HASH_SHA1)
repo_version = GIT_REPO_VERSION_READ;
@@ -199,6 +195,8 @@ void initialize_repository_version(int hash_algo)
if (hash_algo != GIT_HASH_SHA1)
git_config_set("extensions.objectformat",
hash_algos[hash_algo].name);
+ else if (reinit)
+ git_config_set_gently("extensions.objectformat", NULL);
}
static int create_default_files(const char *template_path,
@@ -281,7 +279,7 @@ static int create_default_files(const char *template_path,
free(ref);
}
- initialize_repository_version(fmt->hash_algo);
+ initialize_repository_version(fmt->hash_algo, 0);
/* Check filemode trustability */
path = git_path_buf(&buf, "config");
@@ -369,6 +367,7 @@ static void separate_git_dir(const char *git_dir, const char *git_link)
if (rename(src, git_dir))
die_errno(_("unable to move %s to %s"), src, git_dir);
+ repair_worktrees(NULL, NULL);
}
write_file(git_link, "gitdir: %s", git_dir);
@@ -568,6 +567,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
+ if (real_git_dir && is_bare_repository_cfg == 1)
+ die(_("--separate-git-dir and --bare are mutually exclusive"));
+
if (real_git_dir && !is_absolute_path(real_git_dir))
real_git_dir = real_pathdup(real_git_dir, 1);
@@ -642,6 +644,30 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
if (!git_dir)
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+ /*
+ * When --separate-git-dir is used inside a linked worktree, take
+ * care to ensure that the common .git/ directory is relocated, not
+ * the worktree-specific .git/worktrees/<id>/ directory.
+ */
+ if (real_git_dir) {
+ int err;
+ const char *p;
+ struct strbuf sb = STRBUF_INIT;
+
+ p = read_gitfile_gently(git_dir, &err);
+ if (p && get_common_dir(&sb, p)) {
+ struct strbuf mainwt = STRBUF_INIT;
+
+ strbuf_addbuf(&mainwt, &sb);
+ strbuf_strip_suffix(&mainwt, "/.git");
+ if (chdir(mainwt.buf) < 0)
+ die_errno(_("cannot chdir to %s"), mainwt.buf);
+ strbuf_release(&mainwt);
+ git_dir = strbuf_detach(&sb, NULL);
+ }
+ strbuf_release(&sb);
+ }
+
if (is_bare_repository_cfg < 0)
is_bare_repository_cfg = guess_repository_type(git_dir);
@@ -663,6 +689,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
get_git_work_tree());
}
else {
+ if (real_git_dir)
+ die(_("--separate-git-dir incompatible with bare repository"));
if (work_tree)
set_git_work_tree(work_tree);
}
diff --git a/builtin/log.c b/builtin/log.c
index d104d5c688..b8824d898f 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -33,7 +33,6 @@
#include "commit-slab.h"
#include "repository.h"
#include "commit-reach.h"
-#include "interdiff.h"
#include "range-diff.h"
#define MAIL_DEFAULT_WRAP 72
@@ -599,8 +598,8 @@ static int show_tree_object(const struct object_id *oid,
static void show_setup_revisions_tweak(struct rev_info *rev,
struct setup_revision_opt *opt)
{
- if (rev->ignore_merges) {
- /* There was no "-m" on the command line */
+ if (rev->ignore_merges < 0) {
+ /* There was no "-m" variant on the command line */
rev->ignore_merges = 0;
if (!rev->first_parent_only && !rev->combine_merges) {
/* No "--first-parent", "-c", or "--cc" */
@@ -732,8 +731,7 @@ static void log_setup_revisions_tweak(struct rev_info *rev,
if (!rev->diffopt.output_format && rev->combine_merges)
rev->diffopt.output_format = DIFF_FORMAT_PATCH;
- /* Turn -m on when --cc/-c was given */
- if (rev->combine_merges)
+ if (rev->first_parent_only && rev->ignore_merges < 0)
rev->ignore_merges = 0;
}
@@ -1062,7 +1060,7 @@ static char *find_branch_name(struct rev_info *rev)
return NULL;
ref = rev->cmdline.rev[positive].name;
tip_oid = &rev->cmdline.rev[positive].item->oid;
- if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) &&
+ if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref, 0) &&
skip_prefix(full_ref, "refs/heads/", &v) &&
oideq(tip_oid, &branch_oid))
branch = xstrdup(v);
@@ -1128,18 +1126,18 @@ do_pp:
static int get_notes_refs(struct string_list_item *item, void *arg)
{
- argv_array_pushf(arg, "--notes=%s", item->string);
+ strvec_pushf(arg, "--notes=%s", item->string);
return 0;
}
-static void get_notes_args(struct argv_array *arg, struct rev_info *rev)
+static void get_notes_args(struct strvec *arg, struct rev_info *rev)
{
if (!rev->show_notes) {
- argv_array_push(arg, "--no-notes");
+ strvec_push(arg, "--no-notes");
} else if (rev->notes_opt.use_default_notes > 0 ||
(rev->notes_opt.use_default_notes == -1 &&
!rev->notes_opt.extra_notes_refs.nr)) {
- argv_array_push(arg, "--notes");
+ strvec_push(arg, "--notes");
} else {
for_each_string_list(&rev->notes_opt.extra_notes_refs, get_notes_refs, arg);
}
@@ -1208,7 +1206,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
if (rev->idiff_oid1) {
fprintf_ln(rev->diffopt.file, "%s", rev->idiff_title);
- show_interdiff(rev, 0);
+ show_interdiff(rev->idiff_oid1, rev->idiff_oid2, 0,
+ &rev->diffopt);
}
if (rev->rdiff1) {
@@ -1217,7 +1216,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
* can be added later if deemed desirable.
*/
struct diff_options opts;
- struct argv_array other_arg = ARGV_ARRAY_INIT;
+ struct strvec other_arg = STRVEC_INIT;
diff_setup(&opts);
opts.file = rev->diffopt.file;
opts.use_color = rev->diffopt.use_color;
@@ -1226,7 +1225,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
get_notes_args(&other_arg, rev);
show_range_diff(rev->rdiff1, rev->rdiff2,
rev->creation_factor, 1, &opts, &other_arg);
- argv_array_clear(&other_arg);
+ strvec_clear(&other_arg);
}
}
@@ -1596,16 +1595,20 @@ static void infer_range_diff_ranges(struct strbuf *r1,
struct commit *head)
{
const char *head_oid = oid_to_hex(&head->object.oid);
+ int prev_is_range = !!strstr(prev, "..");
- if (!strstr(prev, "..")) {
+ if (prev_is_range)
+ strbuf_addstr(r1, prev);
+ else
strbuf_addf(r1, "%s..%s", head_oid, prev);
+
+ if (origin)
+ strbuf_addf(r2, "%s..%s", oid_to_hex(&origin->object.oid), head_oid);
+ else if (prev_is_range)
+ die(_("failed to infer range-diff origin of current series"));
+ else {
+ warning(_("using '%s' as range-diff origin of current series"), prev);
strbuf_addf(r2, "%s..%s", prev, head_oid);
- } else if (!origin) {
- die(_("failed to infer range-diff ranges"));
- } else {
- strbuf_addstr(r1, prev);
- strbuf_addf(r2, "%s..%s",
- oid_to_hex(&origin->object.oid), head_oid);
}
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 30a4c10334..c8eae899b8 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -584,7 +584,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(ls_files_usage, builtin_ls_files_options);
- memset(&dir, 0, sizeof(dir));
+ dir_init(&dir);
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
@@ -688,6 +688,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
return bad ? 1 : 0;
}
- UNLEAK(dir);
+ dir_clear(&dir);
return 0;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 3a4dd12903..092917eca2 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -45,7 +45,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
int show_symref_target = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
- struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+ struct strvec ref_prefixes = STRVEC_INIT;
int i;
struct string_list server_options = STRING_LIST_INIT_DUP;
@@ -83,6 +83,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
PARSE_OPT_STOP_AT_NON_OPTION);
dest = argv[0];
+ UNLEAK(sorting);
+
if (argc > 1) {
int i;
pattern = xcalloc(argc, sizeof(const char *));
@@ -92,9 +94,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
}
if (flags & REF_TAGS)
- argv_array_push(&ref_prefixes, "refs/tags/");
+ strvec_push(&ref_prefixes, "refs/tags/");
if (flags & REF_HEADS)
- argv_array_push(&ref_prefixes, "refs/heads/");
+ strvec_push(&ref_prefixes, "refs/heads/");
remote = remote_get(dest);
if (!remote) {
@@ -107,7 +109,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (get_url) {
printf("%s\n", *remote->url);
- UNLEAK(sorting);
return 0;
}
@@ -122,10 +123,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
repo_set_hash_algo(the_repository, hash_algo);
}
- if (transport_disconnect(transport)) {
- UNLEAK(sorting);
+ if (transport_disconnect(transport))
return 1;
- }
if (!dest && !quiet)
fprintf(stderr, "From %s\n", *remote->url);
@@ -150,7 +149,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
status = 0; /* we found something */
}
- UNLEAK(sorting);
ref_array_clear(&ref_array);
return status;
}
diff --git a/builtin/merge.c b/builtin/merge.c
index 7da707bf55..9d5359edc2 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -72,7 +72,6 @@ static const char **xopts;
static size_t xopts_nr, xopts_alloc;
static const char *branch;
static char *branch_mergeoptions;
-static int option_renormalize;
static int verbosity;
static int allow_rerere_auto;
static int abort_current_merge;
@@ -457,7 +456,7 @@ static void finish(struct commit *head_commit,
* user should see them.
*/
close_object_store(the_repository->objects);
- run_auto_gc(verbosity < 0);
+ run_auto_maintenance(verbosity < 0);
}
}
if (new_head && show_diffstat) {
@@ -501,7 +500,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
- if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref) > 0) {
+ if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref, 0) > 0) {
if (starts_with(found_ref, "refs/heads/")) {
strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
oid_to_hex(&branch_head), remote);
@@ -621,8 +620,6 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return git_config_string(&pull_octopus, k, v);
else if (!strcmp(k, "commit.cleanup"))
return git_config_string(&cleanup_arg, k, v);
- else if (!strcmp(k, "merge.renormalize"))
- option_renormalize = git_config_bool(k, v);
else if (!strcmp(k, "merge.ff")) {
int boolval = git_parse_maybe_bool(v);
if (0 <= boolval) {
@@ -721,7 +718,6 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
if (!strcmp(strategy, "subtree"))
o.subtree_shift = "";
- o.renormalize = option_renormalize;
o.show_rename_progress =
show_progress == -1 ? isatty(2) : show_progress;
@@ -1352,7 +1348,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
die(_("You have not concluded your merge (MERGE_HEAD exists)."));
}
- if (file_exists(git_path_cherry_pick_head(the_repository))) {
+ if (ref_exists("CHERRY_PICK_HEAD")) {
if (advice_resolve_conflict)
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
"Please, commit your changes before you merge."));
diff --git a/builtin/mv.c b/builtin/mv.c
index be15ba7044..7dac714af9 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -132,6 +132,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
struct lock_file lock_file = LOCK_INIT;
+ struct cache_entry *ce;
git_config(git_default_config, NULL);
@@ -220,9 +221,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}
argc += last - first;
}
- } else if (cache_name_pos(src, length) < 0)
+ } else if (!(ce = cache_file_exists(src, length, ignore_case))) {
bad = _("not under version control");
- else if (lstat(dst, &st) == 0 &&
+ } else if (ce_stage(ce)) {
+ bad = _("conflicted");
+ } else if (lstat(dst, &st) == 0 &&
(!ignore_case || strcasecmp(src, dst))) {
bad = _("destination exists");
if (force) {
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index a9dcd25e46..725dd04519 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -521,7 +521,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
struct option opts[] = {
- OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
+ OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
N_("only use refs matching <pattern>")),
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 7016b28485..5617c01b5a 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -27,7 +27,7 @@
#include "delta-islands.h"
#include "reachable.h"
#include "oid-array.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "list.h"
#include "packfile.h"
#include "object-store.h"
@@ -35,6 +35,7 @@
#include "midx.h"
#include "trace2.h"
#include "shallow.h"
+#include "promisor-remote.h"
#define IN_PACK(obj) oe_in_pack(&to_pack, obj)
#define SIZE(obj) oe_size(&to_pack, obj)
@@ -1704,9 +1705,30 @@ static int can_reuse_delta(const struct object_id *base_oid,
return 0;
}
-static void check_object(struct object_entry *entry)
+static void prefetch_to_pack(uint32_t object_index_start) {
+ struct oid_array to_fetch = OID_ARRAY_INIT;
+ uint32_t i;
+
+ for (i = object_index_start; i < to_pack.nr_objects; i++) {
+ struct object_entry *entry = to_pack.objects + i;
+
+ if (!oid_object_info_extended(the_repository,
+ &entry->idx.oid,
+ NULL,
+ OBJECT_INFO_FOR_PREFETCH))
+ continue;
+ oid_array_append(&to_fetch, &entry->idx.oid);
+ }
+ promisor_remote_get_direct(the_repository,
+ to_fetch.oid, to_fetch.nr);
+ oid_array_clear(&to_fetch);
+}
+
+static void check_object(struct object_entry *entry, uint32_t object_index)
{
unsigned long canonical_size;
+ enum object_type type;
+ struct object_info oi = {.typep = &type, .sizep = &canonical_size};
if (IN_PACK(entry)) {
struct packed_git *p = IN_PACK(entry);
@@ -1840,8 +1862,18 @@ static void check_object(struct object_entry *entry)
unuse_pack(&w_curs);
}
- oe_set_type(entry,
- oid_object_info(the_repository, &entry->idx.oid, &canonical_size));
+ if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
+ OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) {
+ if (has_promisor_remote()) {
+ prefetch_to_pack(object_index);
+ if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
+ OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0)
+ type = -1;
+ } else {
+ type = -1;
+ }
+ }
+ oe_set_type(entry, type);
if (entry->type_valid) {
SET_SIZE(entry, canonical_size);
} else {
@@ -2061,7 +2093,7 @@ static void get_object_details(void)
for (i = 0; i < to_pack.nr_objects; i++) {
struct object_entry *entry = sorted_by_offset[i];
- check_object(entry);
+ check_object(entry, i);
if (entry->type_valid &&
oe_size_greater_than(&to_pack, entry, big_file_threshold))
entry->no_try_delta = 1;
@@ -3016,7 +3048,7 @@ static void show_object__ma_allow_any(struct object *obj, const char *name, void
* Quietly ignore ALL missing objects. This avoids problems with
* staging them now and getting an odd error later.
*/
- if (!has_object_file(&obj->oid))
+ if (!has_object(the_repository, &obj->oid, 0))
return;
show_object(obj, name, data);
@@ -3030,7 +3062,7 @@ static void show_object__ma_allow_promisor(struct object *obj, const char *name,
* Quietly ignore EXPECTED missing objects. This avoids problems with
* staging them now and getting an odd error later.
*/
- if (!has_object_file(&obj->oid) && is_promisor_object(&obj->oid))
+ if (!has_object(the_repository, &obj->oid, 0) && is_promisor_object(&obj->oid))
return;
show_object(obj, name, data);
@@ -3325,7 +3357,7 @@ static void get_object_list(int ac, const char **av)
if (starts_with(line, "--shallow ")) {
struct object_id oid;
if (get_oid_hex(line + 10, &oid))
- die("not an SHA-1 '%s'", line + 10);
+ die("not an object name '%s'", line + 10);
register_shallow(the_repository, &oid);
use_bitmap_index = 0;
continue;
@@ -3439,7 +3471,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
int use_internal_rev_list = 0;
int shallow = 0;
int all_progress_implied = 0;
- struct argv_array rp = ARGV_ARRAY_INIT;
+ struct strvec rp = STRVEC_INIT;
int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
int rev_list_index = 0;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
@@ -3575,36 +3607,36 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
cache_max_small_delta_size = (1U << OE_Z_DELTA_BITS) - 1;
}
- argv_array_push(&rp, "pack-objects");
+ strvec_push(&rp, "pack-objects");
if (thin) {
use_internal_rev_list = 1;
- argv_array_push(&rp, shallow
+ strvec_push(&rp, shallow
? "--objects-edge-aggressive"
: "--objects-edge");
} else
- argv_array_push(&rp, "--objects");
+ strvec_push(&rp, "--objects");
if (rev_list_all) {
use_internal_rev_list = 1;
- argv_array_push(&rp, "--all");
+ strvec_push(&rp, "--all");
}
if (rev_list_reflog) {
use_internal_rev_list = 1;
- argv_array_push(&rp, "--reflog");
+ strvec_push(&rp, "--reflog");
}
if (rev_list_index) {
use_internal_rev_list = 1;
- argv_array_push(&rp, "--indexed-objects");
+ strvec_push(&rp, "--indexed-objects");
}
if (rev_list_unpacked) {
use_internal_rev_list = 1;
- argv_array_push(&rp, "--unpacked");
+ strvec_push(&rp, "--unpacked");
}
if (exclude_promisor_objects) {
use_internal_rev_list = 1;
fetch_if_missing = 0;
- argv_array_push(&rp, "--exclude-promisor-objects");
+ strvec_push(&rp, "--exclude-promisor-objects");
}
if (unpack_unreachable || keep_unreachable || pack_loose_unreachable)
use_internal_rev_list = 1;
@@ -3666,7 +3698,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
write_bitmap_index = 0;
if (use_delta_islands)
- argv_array_push(&rp, "--topo-order");
+ strvec_push(&rp, "--topo-order");
if (progress && all_progress_implied)
progress = 2;
@@ -3704,8 +3736,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
- get_object_list(rp.argc, rp.argv);
- argv_array_clear(&rp);
+ get_object_list(rp.nr, rp.v);
+ strvec_clear(&rp);
}
cleanup_preferred_base();
if (include_tag && nr_result)
diff --git a/builtin/pull.c b/builtin/pull.c
index 8159c5d7c9..425950f469 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -87,8 +87,8 @@ static char *opt_verify_signatures;
static int opt_autostash = -1;
static int config_autostash;
static int check_trust_level = 1;
-static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
-static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
+static struct strvec opt_strategies = STRVEC_INIT;
+static struct strvec opt_strategy_opts = STRVEC_INIT;
static char *opt_gpg_sign;
static int opt_allow_unrelated_histories;
@@ -110,7 +110,7 @@ static char *opt_ipv4;
static char *opt_ipv6;
static int opt_show_forced_updates = -1;
static char *set_upstream;
-static struct argv_array opt_fetch = ARGV_ARRAY_INIT;
+static struct strvec opt_fetch = STRVEC_INIT;
static struct option pull_options[] = {
/* Shared options */
@@ -251,25 +251,25 @@ static struct option pull_options[] = {
/**
* Pushes "-q" or "-v" switches into arr to match the opt_verbosity level.
*/
-static void argv_push_verbosity(struct argv_array *arr)
+static void argv_push_verbosity(struct strvec *arr)
{
int verbosity;
for (verbosity = opt_verbosity; verbosity > 0; verbosity--)
- argv_array_push(arr, "-v");
+ strvec_push(arr, "-v");
for (verbosity = opt_verbosity; verbosity < 0; verbosity++)
- argv_array_push(arr, "-q");
+ strvec_push(arr, "-q");
}
/**
* Pushes "-f" switches into arr to match the opt_force level.
*/
-static void argv_push_force(struct argv_array *arr)
+static void argv_push_force(struct strvec *arr)
{
int force = opt_force;
while (force-- > 0)
- argv_array_push(arr, "-f");
+ strvec_push(arr, "-f");
}
/**
@@ -344,8 +344,7 @@ static enum rebase_type config_get_rebase(void)
if (!git_config_get_value("pull.rebase", &value))
return parse_config_rebase("pull.rebase", value, 1);
- if (opt_verbosity >= 0 &&
- (!opt_ff || strcmp(opt_ff, "--ff-only"))) {
+ if (opt_verbosity >= 0 && !opt_ff) {
warning(_("Pulling without specifying how to reconcile divergent branches is\n"
"discouraged. You can squelch this message by running one of the following\n"
"commands sometime before your next pull:\n"
@@ -524,75 +523,75 @@ static void parse_repo_refspecs(int argc, const char **argv, const char **repo,
*/
static int run_fetch(const char *repo, const char **refspecs)
{
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
int ret;
- argv_array_pushl(&args, "fetch", "--update-head-ok", NULL);
+ strvec_pushl(&args, "fetch", "--update-head-ok", NULL);
/* Shared options */
argv_push_verbosity(&args);
if (opt_progress)
- argv_array_push(&args, opt_progress);
+ strvec_push(&args, opt_progress);
/* Options passed to git-fetch */
if (opt_all)
- argv_array_push(&args, opt_all);
+ strvec_push(&args, opt_all);
if (opt_append)
- argv_array_push(&args, opt_append);
+ strvec_push(&args, opt_append);
if (opt_upload_pack)
- argv_array_push(&args, opt_upload_pack);
+ strvec_push(&args, opt_upload_pack);
argv_push_force(&args);
if (opt_tags)
- argv_array_push(&args, opt_tags);
+ strvec_push(&args, opt_tags);
if (opt_prune)
- argv_array_push(&args, opt_prune);
+ strvec_push(&args, opt_prune);
if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
switch (recurse_submodules) {
case RECURSE_SUBMODULES_ON:
- argv_array_push(&args, "--recurse-submodules=on");
+ strvec_push(&args, "--recurse-submodules=on");
break;
case RECURSE_SUBMODULES_OFF:
- argv_array_push(&args, "--recurse-submodules=no");
+ strvec_push(&args, "--recurse-submodules=no");
break;
case RECURSE_SUBMODULES_ON_DEMAND:
- argv_array_push(&args, "--recurse-submodules=on-demand");
+ strvec_push(&args, "--recurse-submodules=on-demand");
break;
default:
BUG("submodule recursion option not understood");
}
if (max_children)
- argv_array_push(&args, max_children);
+ strvec_push(&args, max_children);
if (opt_dry_run)
- argv_array_push(&args, "--dry-run");
+ strvec_push(&args, "--dry-run");
if (opt_keep)
- argv_array_push(&args, opt_keep);
+ strvec_push(&args, opt_keep);
if (opt_depth)
- argv_array_push(&args, opt_depth);
+ strvec_push(&args, opt_depth);
if (opt_unshallow)
- argv_array_push(&args, opt_unshallow);
+ strvec_push(&args, opt_unshallow);
if (opt_update_shallow)
- argv_array_push(&args, opt_update_shallow);
+ strvec_push(&args, opt_update_shallow);
if (opt_refmap)
- argv_array_push(&args, opt_refmap);
+ strvec_push(&args, opt_refmap);
if (opt_ipv4)
- argv_array_push(&args, opt_ipv4);
+ strvec_push(&args, opt_ipv4);
if (opt_ipv6)
- argv_array_push(&args, opt_ipv6);
+ strvec_push(&args, opt_ipv6);
if (opt_show_forced_updates > 0)
- argv_array_push(&args, "--show-forced-updates");
+ strvec_push(&args, "--show-forced-updates");
else if (opt_show_forced_updates == 0)
- argv_array_push(&args, "--no-show-forced-updates");
+ strvec_push(&args, "--no-show-forced-updates");
if (set_upstream)
- argv_array_push(&args, set_upstream);
- argv_array_pushv(&args, opt_fetch.argv);
+ strvec_push(&args, set_upstream);
+ strvec_pushv(&args, opt_fetch.v);
if (repo) {
- argv_array_push(&args, repo);
- argv_array_pushv(&args, refspecs);
+ strvec_push(&args, repo);
+ strvec_pushv(&args, refspecs);
} else if (*refspecs)
BUG("refspecs without repo?");
- ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
- argv_array_clear(&args);
+ ret = run_command_v_opt(args.v, RUN_GIT_CMD);
+ strvec_clear(&args);
return ret;
}
@@ -637,8 +636,8 @@ static int rebase_submodules(void)
cp.git_cmd = 1;
cp.no_stdin = 1;
- argv_array_pushl(&cp.args, "submodule", "update",
- "--recursive", "--rebase", NULL);
+ strvec_pushl(&cp.args, "submodule", "update",
+ "--recursive", "--rebase", NULL);
argv_push_verbosity(&cp.args);
return run_command(&cp);
@@ -650,8 +649,8 @@ static int update_submodules(void)
cp.git_cmd = 1;
cp.no_stdin = 1;
- argv_array_pushl(&cp.args, "submodule", "update",
- "--recursive", "--checkout", NULL);
+ strvec_pushl(&cp.args, "submodule", "update",
+ "--recursive", "--checkout", NULL);
argv_push_verbosity(&cp.args);
return run_command(&cp);
@@ -663,48 +662,48 @@ static int update_submodules(void)
static int run_merge(void)
{
int ret;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
- argv_array_pushl(&args, "merge", NULL);
+ strvec_pushl(&args, "merge", NULL);
/* Shared options */
argv_push_verbosity(&args);
if (opt_progress)
- argv_array_push(&args, opt_progress);
+ strvec_push(&args, opt_progress);
/* Options passed to git-merge */
if (opt_diffstat)
- argv_array_push(&args, opt_diffstat);
+ strvec_push(&args, opt_diffstat);
if (opt_log)
- argv_array_push(&args, opt_log);
+ strvec_push(&args, opt_log);
if (opt_signoff)
- argv_array_push(&args, opt_signoff);
+ strvec_push(&args, opt_signoff);
if (opt_squash)
- argv_array_push(&args, opt_squash);
+ strvec_push(&args, opt_squash);
if (opt_commit)
- argv_array_push(&args, opt_commit);
+ strvec_push(&args, opt_commit);
if (opt_edit)
- argv_array_push(&args, opt_edit);
+ strvec_push(&args, opt_edit);
if (cleanup_arg)
- argv_array_pushf(&args, "--cleanup=%s", cleanup_arg);
+ strvec_pushf(&args, "--cleanup=%s", cleanup_arg);
if (opt_ff)
- argv_array_push(&args, opt_ff);
+ strvec_push(&args, opt_ff);
if (opt_verify_signatures)
- argv_array_push(&args, opt_verify_signatures);
- argv_array_pushv(&args, opt_strategies.argv);
- argv_array_pushv(&args, opt_strategy_opts.argv);
+ strvec_push(&args, opt_verify_signatures);
+ strvec_pushv(&args, opt_strategies.v);
+ strvec_pushv(&args, opt_strategy_opts.v);
if (opt_gpg_sign)
- argv_array_push(&args, opt_gpg_sign);
+ strvec_push(&args, opt_gpg_sign);
if (opt_autostash == 0)
- argv_array_push(&args, "--no-autostash");
+ strvec_push(&args, "--no-autostash");
else if (opt_autostash == 1)
- argv_array_push(&args, "--autostash");
+ strvec_push(&args, "--autostash");
if (opt_allow_unrelated_histories > 0)
- argv_array_push(&args, "--allow-unrelated-histories");
+ strvec_push(&args, "--allow-unrelated-histories");
- argv_array_push(&args, "FETCH_HEAD");
- ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
- argv_array_clear(&args);
+ strvec_push(&args, "FETCH_HEAD");
+ ret = run_command_v_opt(args.v, RUN_GIT_CMD);
+ strvec_clear(&args);
return ret;
}
@@ -801,8 +800,8 @@ static int get_rebase_fork_point(struct object_id *fork_point, const char *repo,
if (!remote_branch)
return -1;
- argv_array_pushl(&cp.args, "merge-base", "--fork-point",
- remote_branch, curr_branch->name, NULL);
+ strvec_pushl(&cp.args, "merge-base", "--fork-point",
+ remote_branch, curr_branch->name, NULL);
cp.no_stdin = 1;
cp.no_stderr = 1;
cp.git_cmd = 1;
@@ -862,48 +861,48 @@ static int run_rebase(const struct object_id *curr_head,
{
int ret;
struct object_id oct_merge_base;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point))
if (!is_null_oid(fork_point) && oideq(&oct_merge_base, fork_point))
fork_point = NULL;
- argv_array_push(&args, "rebase");
+ strvec_push(&args, "rebase");
/* Shared options */
argv_push_verbosity(&args);
/* Options passed to git-rebase */
if (opt_rebase == REBASE_MERGES)
- argv_array_push(&args, "--rebase-merges");
+ strvec_push(&args, "--rebase-merges");
else if (opt_rebase == REBASE_PRESERVE)
- argv_array_push(&args, "--preserve-merges");
+ strvec_push(&args, "--preserve-merges");
else if (opt_rebase == REBASE_INTERACTIVE)
- argv_array_push(&args, "--interactive");
+ strvec_push(&args, "--interactive");
if (opt_diffstat)
- argv_array_push(&args, opt_diffstat);
- argv_array_pushv(&args, opt_strategies.argv);
- argv_array_pushv(&args, opt_strategy_opts.argv);
+ strvec_push(&args, opt_diffstat);
+ strvec_pushv(&args, opt_strategies.v);
+ strvec_pushv(&args, opt_strategy_opts.v);
if (opt_gpg_sign)
- argv_array_push(&args, opt_gpg_sign);
+ strvec_push(&args, opt_gpg_sign);
if (opt_autostash == 0)
- argv_array_push(&args, "--no-autostash");
+ strvec_push(&args, "--no-autostash");
else if (opt_autostash == 1)
- argv_array_push(&args, "--autostash");
+ strvec_push(&args, "--autostash");
if (opt_verify_signatures &&
!strcmp(opt_verify_signatures, "--verify-signatures"))
warning(_("ignoring --verify-signatures for rebase"));
- argv_array_push(&args, "--onto");
- argv_array_push(&args, oid_to_hex(merge_head));
+ strvec_push(&args, "--onto");
+ strvec_push(&args, oid_to_hex(merge_head));
if (fork_point && !is_null_oid(fork_point))
- argv_array_push(&args, oid_to_hex(fork_point));
+ strvec_push(&args, oid_to_hex(fork_point));
else
- argv_array_push(&args, oid_to_hex(merge_head));
+ strvec_push(&args, oid_to_hex(merge_head));
- ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
- argv_array_clear(&args);
+ ret = run_command_v_opt(args.v, RUN_GIT_CMD);
+ strvec_clear(&args);
return ret;
}
diff --git a/builtin/push.c b/builtin/push.c
index bc94078e72..0eeb2c8dd5 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -61,26 +61,27 @@ static struct refspec rs = REFSPEC_INIT_PUSH;
static struct string_list push_options_config = STRING_LIST_INIT_DUP;
-static const char *map_refspec(const char *ref,
- struct remote *remote, struct ref *local_refs)
+static void refspec_append_mapped(struct refspec *refspec, const char *ref,
+ struct remote *remote, struct ref *local_refs)
{
const char *branch_name;
struct ref *matched = NULL;
/* Does "ref" uniquely name our ref? */
- if (count_refspec_match(ref, local_refs, &matched) != 1)
- return ref;
+ if (count_refspec_match(ref, local_refs, &matched) != 1) {
+ refspec_append(refspec, ref);
+ return;
+ }
if (remote->push.nr) {
struct refspec_item query;
memset(&query, 0, sizeof(struct refspec_item));
query.src = matched->name;
if (!query_refspecs(&remote->push, &query) && query.dst) {
- struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%s%s:%s",
- query.force ? "+" : "",
- query.src, query.dst);
- return strbuf_detach(&buf, NULL);
+ refspec_appendf(refspec, "%s%s:%s",
+ query.force ? "+" : "",
+ query.src, query.dst);
+ return;
}
}
@@ -88,14 +89,13 @@ static const char *map_refspec(const char *ref,
skip_prefix(matched->name, "refs/heads/", &branch_name)) {
struct branch *branch = branch_get(branch_name);
if (branch->merge_nr == 1 && branch->merge[0]->src) {
- struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%s:%s",
- ref, branch->merge[0]->src);
- return strbuf_detach(&buf, NULL);
+ refspec_appendf(refspec, "%s:%s",
+ ref, branch->merge[0]->src);
+ return;
}
}
- return ref;
+ refspec_append(refspec, ref);
}
static void set_refspecs(const char **refs, int nr, const char *repo)
@@ -107,30 +107,26 @@ static void set_refspecs(const char **refs, int nr, const char *repo)
for (i = 0; i < nr; i++) {
const char *ref = refs[i];
if (!strcmp("tag", ref)) {
- struct strbuf tagref = STRBUF_INIT;
if (nr <= ++i)
die(_("tag shorthand without <tag>"));
ref = refs[i];
if (deleterefs)
- strbuf_addf(&tagref, ":refs/tags/%s", ref);
+ refspec_appendf(&rs, ":refs/tags/%s", ref);
else
- strbuf_addf(&tagref, "refs/tags/%s", ref);
- ref = strbuf_detach(&tagref, NULL);
+ refspec_appendf(&rs, "refs/tags/%s", ref);
} else if (deleterefs) {
- struct strbuf delref = STRBUF_INIT;
if (strchr(ref, ':'))
die(_("--delete only accepts plain target ref names"));
- strbuf_addf(&delref, ":%s", ref);
- ref = strbuf_detach(&delref, NULL);
+ refspec_appendf(&rs, ":%s", ref);
} else if (!strchr(ref, ':')) {
if (!remote) {
/* lazily grab remote and local_refs */
remote = remote_get(repo);
local_refs = get_local_heads();
}
- ref = map_refspec(ref, remote, local_refs);
- }
- refspec_append(&rs, ref);
+ refspec_append_mapped(&rs, ref, remote, local_refs);
+ } else
+ refspec_append(&rs, ref);
}
}
@@ -192,8 +188,6 @@ static const char message_detached_head_die[] =
static void setup_push_upstream(struct remote *remote, struct branch *branch,
int triangular, int simple)
{
- struct strbuf refspec = STRBUF_INIT;
-
if (!branch)
die(_(message_detached_head_die), remote->name);
if (!branch->merge_nr || !branch->merge || !branch->remote_name)
@@ -219,18 +213,14 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,
die_push_simple(branch, remote);
}
- strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src);
- refspec_append(&rs, refspec.buf);
+ refspec_appendf(&rs, "%s:%s", branch->refname, branch->merge[0]->src);
}
static void setup_push_current(struct remote *remote, struct branch *branch)
{
- struct strbuf refspec = STRBUF_INIT;
-
if (!branch)
die(_(message_detached_head_die), remote->name);
- strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname);
- refspec_append(&rs, refspec.buf);
+ refspec_appendf(&rs, "%s:%s", branch->refname, branch->refname);
}
static int is_workflow_triangular(struct remote *remote)
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index d8a4670629..24c4162f74 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -15,7 +15,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
{
int creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
struct diff_options diffopt = { NULL };
- struct argv_array other_arg = ARGV_ARRAY_INIT;
+ struct strvec other_arg = STRVEC_INIT;
int simple_color = -1;
struct option range_diff_options[] = {
OPT_INTEGER(0, "creation-factor", &creation_factor,
@@ -84,7 +84,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
res = show_range_diff(range1.buf, range2.buf, creation_factor,
simple_color < 1, &diffopt, &other_arg);
- argv_array_clear(&other_arg);
+ strvec_clear(&other_arg);
strbuf_release(&range1);
strbuf_release(&range2);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 37ba76ac3d..eeca53382f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -8,7 +8,7 @@
#include "builtin.h"
#include "run-command.h"
#include "exec-cmd.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "dir.h"
#include "packfile.h"
#include "refs.h"
@@ -84,7 +84,7 @@ struct rebase_options {
REBASE_FORCE = 1<<3,
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
} flags;
- struct argv_array git_am_opts;
+ struct strvec git_am_opts;
const char *action;
int signoff;
int allow_rerere_autoupdate;
@@ -92,6 +92,8 @@ struct rebase_options {
int autosquash;
char *gpg_sign_opt;
int autostash;
+ int committer_date_is_author_date;
+ int ignore_date;
char *cmd;
int allow_empty_message;
int rebase_merges, rebase_cousins;
@@ -108,7 +110,7 @@ struct rebase_options {
.keep_empty = 1, \
.default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
- .git_am_opts = ARGV_ARRAY_INIT, \
+ .git_am_opts = STRVEC_INIT, \
.git_format_patch_opt = STRBUF_INIT \
}
@@ -130,8 +132,12 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
+ replay.committer_date_is_author_date =
+ opts->committer_date_is_author_date;
+ replay.ignore_date = opts->ignore_date;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
replay.strategy = opts->strategy;
+
if (opts->strategy_opts)
parse_strategy_opts(&replay, opts->strategy_opts);
@@ -323,7 +329,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
int ret;
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
- struct argv_array make_script_args = ARGV_ARRAY_INIT;
+ struct strvec make_script_args = STRVEC_INIT;
struct todo_list todo_list = TODO_LIST_INIT;
struct replay_opts replay = get_replay_opts(opts);
struct string_list commands = STRING_LIST_INIT_DUP;
@@ -345,13 +351,13 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
write_file(path_squash_onto(), "%s\n",
oid_to_hex(opts->squash_onto));
- argv_array_pushl(&make_script_args, "", revisions, NULL);
+ strvec_pushl(&make_script_args, "", revisions, NULL);
if (opts->restrict_revision)
- argv_array_pushf(&make_script_args, "^%s",
- oid_to_hex(&opts->restrict_revision->object.oid));
+ strvec_pushf(&make_script_args, "^%s",
+ oid_to_hex(&opts->restrict_revision->object.oid));
ret = sequencer_make_script(the_repository, &todo_list.buf,
- make_script_args.argc, make_script_args.argv,
+ make_script_args.nr, make_script_args.v,
flags);
if (ret)
@@ -372,7 +378,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
free(revisions);
free(shortrevisions);
todo_list_release(&todo_list);
- argv_array_clear(&make_script_args);
+ strvec_clear(&make_script_args);
return ret;
}
@@ -420,7 +426,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.git_cmd = 1;
- argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
+ strvec_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
ret = run_command(&cmd);
break;
@@ -728,10 +734,10 @@ static int finish_rebase(struct rebase_options *opts)
apply_autostash(state_dir_path("autostash", opts));
close_object_store(the_repository->objects);
/*
- * We ignore errors in 'gc --auto', since the
+ * We ignore errors in 'git maintenance run --auto', since the
* user should see them.
*/
- run_auto_gc(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
+ run_auto_maintenance(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
if (opts->type == REBASE_MERGE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
@@ -811,13 +817,13 @@ static int run_am(struct rebase_options *opts)
char *rebased_patches;
am.git_cmd = 1;
- argv_array_push(&am.args, "am");
+ strvec_push(&am.args, "am");
if (opts->action && !strcmp("continue", opts->action)) {
- argv_array_push(&am.args, "--resolved");
- argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+ strvec_push(&am.args, "--resolved");
+ strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
if (opts->gpg_sign_opt)
- argv_array_push(&am.args, opts->gpg_sign_opt);
+ strvec_push(&am.args, opts->gpg_sign_opt);
status = run_command(&am);
if (status)
return status;
@@ -825,8 +831,8 @@ static int run_am(struct rebase_options *opts)
return move_to_original_branch(opts);
}
if (opts->action && !strcmp("skip", opts->action)) {
- argv_array_push(&am.args, "--skip");
- argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+ strvec_push(&am.args, "--skip");
+ strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
status = run_command(&am);
if (status)
return status;
@@ -834,7 +840,7 @@ static int run_am(struct rebase_options *opts)
return move_to_original_branch(opts);
}
if (opts->action && !strcmp("show-current-patch", opts->action)) {
- argv_array_push(&am.args, "--show-current-patch");
+ strvec_push(&am.args, "--show-current-patch");
return run_command(&am);
}
@@ -852,29 +858,29 @@ static int run_am(struct rebase_options *opts)
status = error_errno(_("could not open '%s' for writing"),
rebased_patches);
free(rebased_patches);
- argv_array_clear(&am.args);
+ strvec_clear(&am.args);
return status;
}
format_patch.git_cmd = 1;
- argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
- "--full-index", "--cherry-pick", "--right-only",
- "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
- "--no-cover-letter", "--pretty=mboxrd", "--topo-order",
- "--no-base", NULL);
+ strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
+ "--full-index", "--cherry-pick", "--right-only",
+ "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
+ "--no-cover-letter", "--pretty=mboxrd", "--topo-order",
+ "--no-base", NULL);
if (opts->git_format_patch_opt.len)
- argv_array_split(&format_patch.args,
- opts->git_format_patch_opt.buf);
- argv_array_push(&format_patch.args, revisions.buf);
+ strvec_split(&format_patch.args,
+ opts->git_format_patch_opt.buf);
+ strvec_push(&format_patch.args, revisions.buf);
if (opts->restrict_revision)
- argv_array_pushf(&format_patch.args, "^%s",
- oid_to_hex(&opts->restrict_revision->object.oid));
+ strvec_pushf(&format_patch.args, "^%s",
+ oid_to_hex(&opts->restrict_revision->object.oid));
status = run_command(&format_patch);
if (status) {
unlink(rebased_patches);
free(rebased_patches);
- argv_array_clear(&am.args);
+ strvec_clear(&am.args);
reset_head(the_repository, &opts->orig_head, "checkout",
opts->head_name, 0,
@@ -896,20 +902,20 @@ static int run_am(struct rebase_options *opts)
status = error_errno(_("could not open '%s' for reading"),
rebased_patches);
free(rebased_patches);
- argv_array_clear(&am.args);
+ strvec_clear(&am.args);
return status;
}
- argv_array_pushv(&am.args, opts->git_am_opts.argv);
- argv_array_push(&am.args, "--rebasing");
- argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
- argv_array_push(&am.args, "--patch-format=mboxrd");
+ strvec_pushv(&am.args, opts->git_am_opts.v);
+ strvec_push(&am.args, "--rebasing");
+ strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+ strvec_push(&am.args, "--patch-format=mboxrd");
if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
- argv_array_push(&am.args, "--rerere-autoupdate");
+ strvec_push(&am.args, "--rerere-autoupdate");
else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
- argv_array_push(&am.args, "--no-rerere-autoupdate");
+ strvec_push(&am.args, "--no-rerere-autoupdate");
if (opts->gpg_sign_opt)
- argv_array_push(&am.args, opts->gpg_sign_opt);
+ strvec_push(&am.args, opts->gpg_sign_opt);
status = run_command(&am);
unlink(rebased_patches);
free(rebased_patches);
@@ -969,7 +975,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
add_var(&script_snippet, "revisions", opts->revisions);
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
+ sq_quote_argv_pretty(&buf, opts->git_am_opts.v);
add_var(&script_snippet, "git_am_opt", buf.buf);
strbuf_release(&buf);
add_var(&script_snippet, "verbose",
@@ -1289,6 +1295,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct strbuf revisions = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
struct object_id merge_base;
+ int ignore_whitespace = 0;
enum action action = ACTION_NONE;
const char *gpg_sign = NULL;
struct string_list exec = STRING_LIST_INIT_NODUP;
@@ -1318,16 +1325,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
OPT_BOOL(0, "signoff", &options.signoff,
N_("add a Signed-off-by: line to each commit")),
- OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
- NULL, N_("passed to 'git am'"),
- PARSE_OPT_NOARG),
- OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
- &options.git_am_opts, NULL,
- N_("passed to 'git am'"), PARSE_OPT_NOARG),
- OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL,
- N_("passed to 'git am'"), PARSE_OPT_NOARG),
+ OPT_BOOL(0, "committer-date-is-author-date",
+ &options.committer_date_is_author_date,
+ N_("make committer date match author date")),
+ OPT_BOOL(0, "reset-author-date", &options.ignore_date,
+ N_("ignore author date and use current date")),
+ OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date,
+ N_("synonym of --reset-author-date")),
OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
N_("passed to 'git apply'"), 0),
+ OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
+ N_("ignore changes in whitespace")),
OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
N_("action"), N_("passed to 'git apply'"), 0),
OPT_BIT('f', "force-rebase", &options.flags,
@@ -1624,12 +1632,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.autosquash) {
allow_preemptive_ff = 0;
}
+ if (options.committer_date_is_author_date || options.ignore_date)
+ options.flags |= REBASE_FORCE;
- for (i = 0; i < options.git_am_opts.argc; i++) {
- const char *option = options.git_am_opts.argv[i], *p;
- if (!strcmp(option, "--committer-date-is-author-date") ||
- !strcmp(option, "--ignore-date") ||
- !strcmp(option, "--whitespace=fix") ||
+ for (i = 0; i < options.git_am_opts.nr; i++) {
+ const char *option = options.git_am_opts.v[i], *p;
+ if (!strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
allow_preemptive_ff = 0;
else if (skip_prefix(option, "-C", &p)) {
@@ -1649,7 +1657,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
exit(1);
if (!(options.flags & REBASE_NO_QUIET))
- argv_array_push(&options.git_am_opts, "-q");
+ strvec_push(&options.git_am_opts, "-q");
if (options.empty != EMPTY_UNSPECIFIED)
imply_merge(&options, "--empty");
@@ -1682,6 +1690,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
imply_merge(&options, "--rebase-merges");
}
+ if (options.type == REBASE_APPLY) {
+ if (ignore_whitespace)
+ strvec_push(&options.git_am_opts,
+ "--ignore-whitespace");
+ if (options.committer_date_is_author_date)
+ strvec_push(&options.git_am_opts,
+ "--committer-date-is-author-date");
+ if (options.ignore_date)
+ strvec_push(&options.git_am_opts, "--ignore-date");
+ } else {
+ /* REBASE_MERGE and PRESERVE_MERGES */
+ if (ignore_whitespace) {
+ string_list_append(&strategy_options,
+ "ignore-space-change");
+ }
+ }
+
if (strategy_options.nr) {
int i;
@@ -1721,10 +1746,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
- if (options.git_am_opts.argc || options.type == REBASE_APPLY) {
+ if (options.git_am_opts.nr || options.type == REBASE_APPLY) {
/* all am options except -q are compatible only with --apply */
- for (i = options.git_am_opts.argc - 1; i >= 0; i--)
- if (strcmp(options.git_am_opts.argv[i], "-q"))
+ for (i = options.git_am_opts.nr - 1; i >= 0; i--)
+ if (strcmp(options.git_am_opts.v[i], "-q"))
break;
if (i >= 0) {
@@ -1776,7 +1801,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
"'--preserve-merges'");
- argv_array_push(&options.git_am_opts, "--signoff");
+ strvec_push(&options.git_am_opts, "--signoff");
options.flags |= REBASE_FORCE;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d43663bb0a..bb9909c52e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -15,7 +15,7 @@
#include "string-list.h"
#include "oid-array.h"
#include "connected.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "version.h"
#include "tag.h"
#include "gpg-interface.h"
@@ -57,6 +57,7 @@ static int advertise_push_options;
static int unpack_limit = 100;
static off_t max_input_size;
static int report_status;
+static int report_status_v2;
static int use_sideband;
static int use_atomic;
static int use_push_options;
@@ -97,6 +98,17 @@ static int keepalive_in_sec = 5;
static struct tmp_objdir *tmp_objdir;
+static struct proc_receive_ref {
+ unsigned int want_add:1,
+ want_delete:1,
+ want_modify:1,
+ negative_ref:1;
+ char *ref_prefix;
+ struct proc_receive_ref *next;
+} *proc_receive_ref;
+
+static void proc_receive_ref_append(const char *prefix);
+
static enum deny_action parse_deny_action(const char *var, const char *value)
{
if (value) {
@@ -229,6 +241,13 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "receive.procreceiverefs") == 0) {
+ if (!value)
+ return config_error_nonbool(var);
+ proc_receive_ref_append(value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -240,7 +259,7 @@ static void show_ref(const char *path, const struct object_id *oid)
struct strbuf cap = STRBUF_INIT;
strbuf_addstr(&cap,
- "report-status delete-refs side-band-64k quiet");
+ "report-status report-status-v2 delete-refs side-band-64k quiet");
if (advertise_atomic_push)
strbuf_addstr(&cap, " atomic");
if (prefer_ofs_delta)
@@ -310,17 +329,94 @@ static void write_head_info(void)
packet_flush(1);
}
+#define RUN_PROC_RECEIVE_SCHEDULED 1
+#define RUN_PROC_RECEIVE_RETURNED 2
struct command {
struct command *next;
const char *error_string;
+ struct ref_push_report *report;
unsigned int skip_update:1,
- did_not_exist:1;
+ did_not_exist:1,
+ run_proc_receive:2;
int index;
struct object_id old_oid;
struct object_id new_oid;
char ref_name[FLEX_ARRAY]; /* more */
};
+static void proc_receive_ref_append(const char *prefix)
+{
+ struct proc_receive_ref *ref_pattern;
+ char *p;
+ int len;
+
+ ref_pattern = xcalloc(1, sizeof(struct proc_receive_ref));
+ p = strchr(prefix, ':');
+ if (p) {
+ while (prefix < p) {
+ if (*prefix == 'a')
+ ref_pattern->want_add = 1;
+ else if (*prefix == 'd')
+ ref_pattern->want_delete = 1;
+ else if (*prefix == 'm')
+ ref_pattern->want_modify = 1;
+ else if (*prefix == '!')
+ ref_pattern->negative_ref = 1;
+ prefix++;
+ }
+ prefix++;
+ } else {
+ ref_pattern->want_add = 1;
+ ref_pattern->want_delete = 1;
+ ref_pattern->want_modify = 1;
+ }
+ len = strlen(prefix);
+ while (len && prefix[len - 1] == '/')
+ len--;
+ ref_pattern->ref_prefix = xmemdupz(prefix, len);
+ if (!proc_receive_ref) {
+ proc_receive_ref = ref_pattern;
+ } else {
+ struct proc_receive_ref *end;
+
+ end = proc_receive_ref;
+ while (end->next)
+ end = end->next;
+ end->next = ref_pattern;
+ }
+}
+
+static int proc_receive_ref_matches(struct command *cmd)
+{
+ struct proc_receive_ref *p;
+
+ if (!proc_receive_ref)
+ return 0;
+
+ for (p = proc_receive_ref; p; p = p->next) {
+ const char *match = p->ref_prefix;
+ const char *remains;
+
+ if (!p->want_add && is_null_oid(&cmd->old_oid))
+ continue;
+ else if (!p->want_delete && is_null_oid(&cmd->new_oid))
+ continue;
+ else if (!p->want_modify &&
+ !is_null_oid(&cmd->old_oid) &&
+ !is_null_oid(&cmd->new_oid))
+ continue;
+
+ if (skip_prefix(cmd->ref_name, match, &remains) &&
+ (!*remains || *remains == '/')) {
+ if (!p->negative_ref)
+ return 1;
+ } else if (p->negative_ref) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@ -667,31 +763,32 @@ static void prepare_push_cert_sha1(struct child_process *proc)
nonce_status = check_nonce(push_cert.buf, bogs);
}
if (!is_null_oid(&push_cert_oid)) {
- argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT=%s",
- oid_to_hex(&push_cert_oid));
- argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s",
- sigcheck.signer ? sigcheck.signer : "");
- argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s",
- sigcheck.key ? sigcheck.key : "");
- argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_STATUS=%c",
- sigcheck.result);
+ strvec_pushf(&proc->env_array, "GIT_PUSH_CERT=%s",
+ oid_to_hex(&push_cert_oid));
+ strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s",
+ sigcheck.signer ? sigcheck.signer : "");
+ strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s",
+ sigcheck.key ? sigcheck.key : "");
+ strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_STATUS=%c",
+ sigcheck.result);
if (push_cert_nonce) {
- argv_array_pushf(&proc->env_array,
- "GIT_PUSH_CERT_NONCE=%s",
- push_cert_nonce);
- argv_array_pushf(&proc->env_array,
- "GIT_PUSH_CERT_NONCE_STATUS=%s",
- nonce_status);
+ strvec_pushf(&proc->env_array,
+ "GIT_PUSH_CERT_NONCE=%s",
+ push_cert_nonce);
+ strvec_pushf(&proc->env_array,
+ "GIT_PUSH_CERT_NONCE_STATUS=%s",
+ nonce_status);
if (nonce_status == NONCE_SLOP)
- argv_array_pushf(&proc->env_array,
- "GIT_PUSH_CERT_NONCE_SLOP=%ld",
- nonce_stamp_slop);
+ strvec_pushf(&proc->env_array,
+ "GIT_PUSH_CERT_NONCE_SLOP=%ld",
+ nonce_stamp_slop);
}
}
}
struct receive_hook_feed_state {
struct command *cmd;
+ struct ref_push_report *report;
int skip_broken;
struct strbuf buf;
const struct string_list *push_options;
@@ -720,16 +817,16 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
if (feed_state->push_options) {
int i;
for (i = 0; i < feed_state->push_options->nr; i++)
- argv_array_pushf(&proc.env_array,
- "GIT_PUSH_OPTION_%d=%s", i,
- feed_state->push_options->items[i].string);
- argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d",
- feed_state->push_options->nr);
+ strvec_pushf(&proc.env_array,
+ "GIT_PUSH_OPTION_%d=%s", i,
+ feed_state->push_options->items[i].string);
+ strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d",
+ feed_state->push_options->nr);
} else
- argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
+ strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
if (tmp_objdir)
- argv_array_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir));
+ strvec_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir));
if (use_sideband) {
memset(&muxer, 0, sizeof(muxer));
@@ -779,11 +876,31 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
cmd = cmd->next;
if (!cmd)
return -1; /* EOF */
+ if (!bufp)
+ return 0; /* OK, can feed something. */
strbuf_reset(&state->buf);
- strbuf_addf(&state->buf, "%s %s %s\n",
- oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
- cmd->ref_name);
- state->cmd = cmd->next;
+ if (!state->report)
+ state->report = cmd->report;
+ if (state->report) {
+ struct object_id *old_oid;
+ struct object_id *new_oid;
+ const char *ref_name;
+
+ old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
+ new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
+ ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
+ strbuf_addf(&state->buf, "%s %s %s\n",
+ oid_to_hex(old_oid), oid_to_hex(new_oid),
+ ref_name);
+ state->report = state->report->next;
+ if (!state->report)
+ state->cmd = cmd->next;
+ } else {
+ strbuf_addf(&state->buf, "%s %s %s\n",
+ oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
+ cmd->ref_name);
+ state->cmd = cmd->next;
+ }
if (bufp) {
*bufp = state->buf.buf;
*sizep = state->buf.len;
@@ -802,6 +919,7 @@ static int run_receive_hook(struct command *commands,
strbuf_init(&state.buf, 0);
state.cmd = commands;
state.skip_broken = skip_broken;
+ state.report = NULL;
if (feed_receive_hook(&state, NULL, NULL))
return 0;
state.cmd = commands;
@@ -840,6 +958,268 @@ static int run_update_hook(struct command *cmd)
return finish_command(&proc);
}
+static struct command *find_command_by_refname(struct command *list,
+ const char *refname)
+{
+ for (; list; list = list->next)
+ if (!strcmp(list->ref_name, refname))
+ return list;
+ return NULL;
+}
+
+static int read_proc_receive_report(struct packet_reader *reader,
+ struct command *commands,
+ struct strbuf *errmsg)
+{
+ struct command *cmd;
+ struct command *hint = NULL;
+ struct ref_push_report *report = NULL;
+ int new_report = 0;
+ int code = 0;
+ int once = 0;
+
+ for (;;) {
+ struct object_id old_oid, new_oid;
+ const char *head;
+ const char *refname;
+ char *p;
+
+ if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+ break;
+
+ head = reader->line;
+ p = strchr(head, ' ');
+ if (!p) {
+ strbuf_addf(errmsg, "proc-receive reported incomplete status line: '%s'\n", head);
+ code = -1;
+ continue;
+ }
+ *p++ = '\0';
+ if (!strcmp(head, "option")) {
+ const char *key, *val;
+
+ if (!hint || !(report || new_report)) {
+ if (!once++)
+ strbuf_addstr(errmsg, "proc-receive reported 'option' without a matching 'ok/ng' directive\n");
+ code = -1;
+ continue;
+ }
+ if (new_report) {
+ if (!hint->report) {
+ hint->report = xcalloc(1, sizeof(struct ref_push_report));
+ report = hint->report;
+ } else {
+ report = hint->report;
+ while (report->next)
+ report = report->next;
+ report->next = xcalloc(1, sizeof(struct ref_push_report));
+ report = report->next;
+ }
+ new_report = 0;
+ }
+ key = p;
+ p = strchr(key, ' ');
+ if (p)
+ *p++ = '\0';
+ val = p;
+ if (!strcmp(key, "refname"))
+ report->ref_name = xstrdup_or_null(val);
+ else if (!strcmp(key, "old-oid") && val &&
+ !parse_oid_hex(val, &old_oid, &val))
+ report->old_oid = oiddup(&old_oid);
+ else if (!strcmp(key, "new-oid") && val &&
+ !parse_oid_hex(val, &new_oid, &val))
+ report->new_oid = oiddup(&new_oid);
+ else if (!strcmp(key, "forced-update"))
+ report->forced_update = 1;
+ else if (!strcmp(key, "fall-through"))
+ /* Fall through, let 'receive-pack' to execute it. */
+ hint->run_proc_receive = 0;
+ continue;
+ }
+
+ report = NULL;
+ new_report = 0;
+ refname = p;
+ p = strchr(refname, ' ');
+ if (p)
+ *p++ = '\0';
+ if (strcmp(head, "ok") && strcmp(head, "ng")) {
+ strbuf_addf(errmsg, "proc-receive reported bad status '%s' on ref '%s'\n",
+ head, refname);
+ code = -1;
+ continue;
+ }
+
+ /* first try searching at our hint, falling back to all refs */
+ if (hint)
+ hint = find_command_by_refname(hint, refname);
+ if (!hint)
+ hint = find_command_by_refname(commands, refname);
+ if (!hint) {
+ strbuf_addf(errmsg, "proc-receive reported status on unknown ref: %s\n",
+ refname);
+ code = -1;
+ continue;
+ }
+ if (!hint->run_proc_receive) {
+ strbuf_addf(errmsg, "proc-receive reported status on unexpected ref: %s\n",
+ refname);
+ code = -1;
+ continue;
+ }
+ hint->run_proc_receive |= RUN_PROC_RECEIVE_RETURNED;
+ if (!strcmp(head, "ng")) {
+ if (p)
+ hint->error_string = xstrdup(p);
+ else
+ hint->error_string = "failed";
+ code = -1;
+ continue;
+ }
+ new_report = 1;
+ }
+
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (cmd->run_proc_receive && !cmd->error_string &&
+ !(cmd->run_proc_receive & RUN_PROC_RECEIVE_RETURNED)) {
+ cmd->error_string = "proc-receive failed to report status";
+ code = -1;
+ }
+ return code;
+}
+
+static int run_proc_receive_hook(struct command *commands,
+ const struct string_list *push_options)
+{
+ struct child_process proc = CHILD_PROCESS_INIT;
+ struct async muxer;
+ struct command *cmd;
+ const char *argv[2];
+ struct packet_reader reader;
+ struct strbuf cap = STRBUF_INIT;
+ struct strbuf errmsg = STRBUF_INIT;
+ int hook_use_push_options = 0;
+ int version = 0;
+ int code;
+
+ argv[0] = find_hook("proc-receive");
+ if (!argv[0]) {
+ rp_error("cannot find hook 'proc-receive'");
+ return -1;
+ }
+ argv[1] = NULL;
+
+ proc.argv = argv;
+ proc.in = -1;
+ proc.out = -1;
+ proc.trace2_hook_name = "proc-receive";
+
+ if (use_sideband) {
+ memset(&muxer, 0, sizeof(muxer));
+ muxer.proc = copy_to_sideband;
+ muxer.in = -1;
+ code = start_async(&muxer);
+ if (code)
+ return code;
+ proc.err = muxer.in;
+ } else {
+ proc.err = 0;
+ }
+
+ code = start_command(&proc);
+ if (code) {
+ if (use_sideband)
+ finish_async(&muxer);
+ return code;
+ }
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ /* Version negotiaton */
+ packet_reader_init(&reader, proc.out, NULL, 0,
+ PACKET_READ_CHOMP_NEWLINE |
+ PACKET_READ_GENTLE_ON_EOF);
+ if (use_atomic)
+ strbuf_addstr(&cap, " atomic");
+ if (use_push_options)
+ strbuf_addstr(&cap, " push-options");
+ if (cap.len) {
+ packet_write_fmt(proc.in, "version=1%c%s\n", '\0', cap.buf + 1);
+ strbuf_release(&cap);
+ } else {
+ packet_write_fmt(proc.in, "version=1\n");
+ }
+ packet_flush(proc.in);
+
+ for (;;) {
+ int linelen;
+
+ if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
+ break;
+
+ if (reader.pktlen > 8 && starts_with(reader.line, "version=")) {
+ version = atoi(reader.line + 8);
+ linelen = strlen(reader.line);
+ if (linelen < reader.pktlen) {
+ const char *feature_list = reader.line + linelen + 1;
+ if (parse_feature_request(feature_list, "push-options"))
+ hook_use_push_options = 1;
+ }
+ }
+ }
+
+ if (version != 1) {
+ strbuf_addf(&errmsg, "proc-receive version '%d' is not supported",
+ version);
+ code = -1;
+ goto cleanup;
+ }
+
+ /* Send commands */
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (!cmd->run_proc_receive || cmd->skip_update || cmd->error_string)
+ continue;
+ packet_write_fmt(proc.in, "%s %s %s",
+ oid_to_hex(&cmd->old_oid),
+ oid_to_hex(&cmd->new_oid),
+ cmd->ref_name);
+ }
+ packet_flush(proc.in);
+
+ /* Send push options */
+ if (hook_use_push_options) {
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, push_options)
+ packet_write_fmt(proc.in, "%s", item->string);
+ packet_flush(proc.in);
+ }
+
+ /* Read result from proc-receive */
+ code = read_proc_receive_report(&reader, commands, &errmsg);
+
+cleanup:
+ close(proc.in);
+ close(proc.out);
+ if (use_sideband)
+ finish_async(&muxer);
+ if (finish_command(&proc))
+ code = -1;
+ if (errmsg.len >0) {
+ char *p = errmsg.buf;
+
+ p += errmsg.len - 1;
+ if (*p == '\n')
+ *p = '\0';
+ rp_error("%s", errmsg.buf);
+ strbuf_release(&errmsg);
+ }
+ sigchain_pop(SIGPIPE);
+
+ return code;
+}
+
static char *refuse_unconfigured_deny_msg =
N_("By default, updating the current branch in a non-bare repository\n"
"is denied, because it will make the index and work tree inconsistent\n"
@@ -931,7 +1311,7 @@ static int head_has_history(void)
}
static const char *push_to_deploy(unsigned char *sha1,
- struct argv_array *env,
+ struct strvec *env,
const char *work_tree)
{
const char *update_refresh[] = {
@@ -950,7 +1330,7 @@ static const char *push_to_deploy(unsigned char *sha1,
struct child_process child = CHILD_PROCESS_INIT;
child.argv = update_refresh;
- child.env = env->argv;
+ child.env = env->v;
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -961,7 +1341,7 @@ static const char *push_to_deploy(unsigned char *sha1,
/* run_command() does not clean up completely; reinitialize */
child_process_init(&child);
child.argv = diff_files;
- child.env = env->argv;
+ child.env = env->v;
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -974,7 +1354,7 @@ static const char *push_to_deploy(unsigned char *sha1,
child_process_init(&child);
child.argv = diff_index;
- child.env = env->argv;
+ child.env = env->v;
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
@@ -985,7 +1365,7 @@ static const char *push_to_deploy(unsigned char *sha1,
read_tree[3] = hash_to_hex(sha1);
child_process_init(&child);
child.argv = read_tree;
- child.env = env->argv;
+ child.env = env->v;
child.dir = work_tree;
child.no_stdin = 1;
child.no_stdout = 1;
@@ -1000,11 +1380,11 @@ 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 *hash,
- struct argv_array *env,
+ struct strvec *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,
+ strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
+ if (run_hook_le(env->v, push_to_checkout_hook,
hash_to_hex(hash), NULL))
return "push-to-checkout hook declined";
else
@@ -1014,7 +1394,7 @@ static const char *push_to_checkout(unsigned char *hash,
static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
{
const char *retval, *work_tree, *git_dir = NULL;
- struct argv_array env = ARGV_ARRAY_INIT;
+ struct strvec env = STRVEC_INIT;
if (worktree && worktree->path)
work_tree = worktree->path;
@@ -1030,14 +1410,14 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
if (!git_dir)
git_dir = get_git_dir();
- argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
+ strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
if (!find_hook(push_to_checkout_hook))
retval = push_to_deploy(sha1, &env, work_tree);
else
retval = push_to_checkout(sha1, &env, work_tree);
- argv_array_clear(&env);
+ strvec_clear(&env);
return retval;
}
@@ -1205,11 +1585,11 @@ static void run_update_post_hook(struct command *commands)
for (cmd = commands; cmd; cmd = cmd->next) {
if (cmd->error_string || cmd->did_not_exist)
continue;
- if (!proc.args.argc)
- argv_array_push(&proc.args, hook);
- argv_array_push(&proc.args, cmd->ref_name);
+ if (!proc.args.nr)
+ strvec_push(&proc.args, hook);
+ strvec_push(&proc.args, cmd->ref_name);
}
- if (!proc.args.argc)
+ if (!proc.args.nr)
return;
proc.no_stdin = 1;
@@ -1415,7 +1795,7 @@ static void execute_commands_non_atomic(struct command *commands,
struct strbuf err = STRBUF_INIT;
for (cmd = commands; cmd; cmd = cmd->next) {
- if (!should_process_cmd(cmd))
+ if (!should_process_cmd(cmd) || cmd->run_proc_receive)
continue;
transaction = ref_transaction_begin(&err);
@@ -1455,7 +1835,7 @@ static void execute_commands_atomic(struct command *commands,
}
for (cmd = commands; cmd; cmd = cmd->next) {
- if (!should_process_cmd(cmd))
+ if (!should_process_cmd(cmd) || cmd->run_proc_receive)
continue;
cmd->error_string = update(cmd, si);
@@ -1491,6 +1871,7 @@ static void execute_commands(struct command *commands,
struct iterate_data data;
struct async muxer;
int err_fd = 0;
+ int run_proc_receive = 0;
if (unpacker_error) {
for (cmd = commands; cmd; cmd = cmd->next)
@@ -1520,6 +1901,22 @@ static void execute_commands(struct command *commands,
reject_updates_to_hidden(commands);
+ /*
+ * Try to find commands that have special prefix in their reference names,
+ * and mark them to run an external "proc-receive" hook later.
+ */
+ if (proc_receive_ref) {
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (!should_process_cmd(cmd))
+ continue;
+
+ if (proc_receive_ref_matches(cmd)) {
+ cmd->run_proc_receive = RUN_PROC_RECEIVE_SCHEDULED;
+ run_proc_receive = 1;
+ }
+ }
+ }
+
if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string)
@@ -1546,6 +1943,14 @@ static void execute_commands(struct command *commands,
free(head_name_to_free);
head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
+ if (run_proc_receive &&
+ run_proc_receive_hook(commands, push_options))
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (!cmd->error_string &&
+ !(cmd->run_proc_receive & RUN_PROC_RECEIVE_RETURNED) &&
+ (cmd->run_proc_receive || use_atomic))
+ cmd->error_string = "fail to run proc-receive hook";
+
if (use_atomic)
execute_commands_atomic(commands, si);
else
@@ -1629,6 +2034,8 @@ static struct command *read_head_info(struct packet_reader *reader,
int len = 0;
if (parse_feature_request(feature_list, "report-status"))
report_status = 1;
+ if (parse_feature_request(feature_list, "report-status-v2"))
+ report_status_v2 = 1;
if (parse_feature_request(feature_list, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
if (parse_feature_request(feature_list, "quiet"))
@@ -1715,10 +2122,10 @@ static const char *parse_pack_header(struct pack_header *hdr)
static const char *pack_lockfile;
-static void push_header_arg(struct argv_array *args, struct pack_header *hdr)
+static void push_header_arg(struct strvec *args, struct pack_header *hdr)
{
- argv_array_pushf(args, "--pack_header=%"PRIu32",%"PRIu32,
- ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
+ strvec_pushf(args, "--pack_header=%"PRIu32",%"PRIu32,
+ ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
}
static const char *unpack(int err_fd, struct shallow_info *si)
@@ -1742,8 +2149,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)
if (si->nr_ours || si->nr_theirs) {
alt_shallow_file = setup_temporary_shallow(si->shallow);
- argv_array_push(&child.args, "--shallow-file");
- argv_array_push(&child.args, alt_shallow_file);
+ strvec_push(&child.args, "--shallow-file");
+ strvec_push(&child.args, alt_shallow_file);
}
tmp_objdir = tmp_objdir_create();
@@ -1762,16 +2169,16 @@ static const char *unpack(int err_fd, struct shallow_info *si)
tmp_objdir_add_as_alternate(tmp_objdir);
if (ntohl(hdr.hdr_entries) < unpack_limit) {
- argv_array_push(&child.args, "unpack-objects");
+ strvec_push(&child.args, "unpack-objects");
push_header_arg(&child.args, &hdr);
if (quiet)
- argv_array_push(&child.args, "-q");
+ strvec_push(&child.args, "-q");
if (fsck_objects)
- argv_array_pushf(&child.args, "--strict%s",
- fsck_msg_types.buf);
+ strvec_pushf(&child.args, "--strict%s",
+ fsck_msg_types.buf);
if (max_input_size)
- argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
- (uintmax_t)max_input_size);
+ strvec_pushf(&child.args, "--max-input-size=%"PRIuMAX,
+ (uintmax_t)max_input_size);
child.no_stdout = 1;
child.err = err_fd;
child.git_cmd = 1;
@@ -1781,28 +2188,28 @@ static const char *unpack(int err_fd, struct shallow_info *si)
} else {
char hostname[HOST_NAME_MAX + 1];
- argv_array_pushl(&child.args, "index-pack", "--stdin", NULL);
+ strvec_pushl(&child.args, "index-pack", "--stdin", NULL);
push_header_arg(&child.args, &hdr);
if (xgethostname(hostname, sizeof(hostname)))
xsnprintf(hostname, sizeof(hostname), "localhost");
- argv_array_pushf(&child.args,
- "--keep=receive-pack %"PRIuMAX" on %s",
- (uintmax_t)getpid(),
- hostname);
+ strvec_pushf(&child.args,
+ "--keep=receive-pack %"PRIuMAX" on %s",
+ (uintmax_t)getpid(),
+ hostname);
if (!quiet && err_fd)
- argv_array_push(&child.args, "--show-resolving-progress");
+ strvec_push(&child.args, "--show-resolving-progress");
if (use_sideband)
- argv_array_push(&child.args, "--report-end-of-input");
+ strvec_push(&child.args, "--report-end-of-input");
if (fsck_objects)
- argv_array_pushf(&child.args, "--strict%s",
- fsck_msg_types.buf);
+ strvec_pushf(&child.args, "--strict%s",
+ fsck_msg_types.buf);
if (!reject_thin)
- argv_array_push(&child.args, "--fix-thin");
+ strvec_push(&child.args, "--fix-thin");
if (max_input_size)
- argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
- (uintmax_t)max_input_size);
+ strvec_pushf(&child.args, "--max-input-size=%"PRIuMAX,
+ (uintmax_t)max_input_size);
child.out = -1;
child.err = err_fd;
child.git_cmd = 1;
@@ -1947,6 +2354,51 @@ static void report(struct command *commands, const char *unpack_status)
strbuf_release(&buf);
}
+static void report_v2(struct command *commands, const char *unpack_status)
+{
+ struct command *cmd;
+ struct strbuf buf = STRBUF_INIT;
+ struct ref_push_report *report;
+
+ packet_buf_write(&buf, "unpack %s\n",
+ unpack_status ? unpack_status : "ok");
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ int count = 0;
+
+ if (cmd->error_string) {
+ packet_buf_write(&buf, "ng %s %s\n",
+ cmd->ref_name,
+ cmd->error_string);
+ continue;
+ }
+ packet_buf_write(&buf, "ok %s\n",
+ cmd->ref_name);
+ for (report = cmd->report; report; report = report->next) {
+ if (count++ > 0)
+ packet_buf_write(&buf, "ok %s\n",
+ cmd->ref_name);
+ if (report->ref_name)
+ packet_buf_write(&buf, "option refname %s\n",
+ report->ref_name);
+ if (report->old_oid)
+ packet_buf_write(&buf, "option old-oid %s\n",
+ oid_to_hex(report->old_oid));
+ if (report->new_oid)
+ packet_buf_write(&buf, "option new-oid %s\n",
+ oid_to_hex(report->new_oid));
+ if (report->forced_update)
+ packet_buf_write(&buf, "option forced-update\n");
+ }
+ }
+ packet_buf_flush(&buf);
+
+ if (use_sideband)
+ send_sideband(1, 1, buf.buf, buf.len, use_sideband);
+ else
+ write_or_die(1, buf.buf, buf.len);
+ strbuf_release(&buf);
+}
+
static int delete_only(struct command *commands)
{
struct command *cmd;
@@ -2055,7 +2507,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
&push_options);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
- if (report_status)
+ if (report_status_v2)
+ report_v2(commands, unpack_status);
+ else if (report_status)
report(commands, unpack_status);
run_receive_hook(commands, "post-receive", 1,
&push_options);
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index 6a9127a33c..fd3538d4f0 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -117,12 +117,12 @@ static char *strip_escapes(const char *str, const char *service,
}
}
-static void parse_argv(struct argv_array *out, const char *arg, const char *service)
+static void parse_argv(struct strvec *out, const char *arg, const char *service)
{
while (*arg) {
char *expanded = strip_escapes(arg, service, &arg);
if (expanded)
- argv_array_push(out, expanded);
+ strvec_push(out, expanded);
free(expanded);
}
}
diff --git a/builtin/remote.c b/builtin/remote.c
index e8377994e5..64b4b551eb 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -10,7 +10,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-store.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "commit-reach.h"
static const char * const builtin_remote_usage[] = {
@@ -478,6 +478,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
struct refspec_item refspec;
+ memset(&refspec, 0, sizeof(refspec));
refspec.force = 0;
refspec.pattern = 1;
refspec.src = refspec.dst = "refs/heads/*";
@@ -1355,7 +1356,7 @@ static int set_head(int argc, const char **argv)
result |= error(_("Not a valid ref: %s"), buf2.buf);
else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
result |= error(_("Could not setup %s"), buf.buf);
- if (opt_a)
+ else if (opt_a)
printf("%s/HEAD set to %s\n", argv[0], head_name);
free(head_name);
}
@@ -1451,35 +1452,35 @@ static int update(int argc, const char **argv)
N_("prune remotes after fetching")),
OPT_END()
};
- struct argv_array fetch_argv = ARGV_ARRAY_INIT;
+ struct strvec fetch_argv = STRVEC_INIT;
int default_defined = 0;
int retval;
argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
PARSE_OPT_KEEP_ARGV0);
- argv_array_push(&fetch_argv, "fetch");
+ strvec_push(&fetch_argv, "fetch");
if (prune != -1)
- argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune");
+ strvec_push(&fetch_argv, prune ? "--prune" : "--no-prune");
if (verbose)
- argv_array_push(&fetch_argv, "-v");
- argv_array_push(&fetch_argv, "--multiple");
+ strvec_push(&fetch_argv, "-v");
+ strvec_push(&fetch_argv, "--multiple");
if (argc < 2)
- argv_array_push(&fetch_argv, "default");
+ strvec_push(&fetch_argv, "default");
for (i = 1; i < argc; i++)
- argv_array_push(&fetch_argv, argv[i]);
+ strvec_push(&fetch_argv, argv[i]);
- if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) {
+ if (strcmp(fetch_argv.v[fetch_argv.nr-1], "default") == 0) {
git_config(get_remote_default, &default_defined);
if (!default_defined) {
- argv_array_pop(&fetch_argv);
- argv_array_push(&fetch_argv, "--all");
+ strvec_pop(&fetch_argv);
+ strvec_push(&fetch_argv, "--all");
}
}
- retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD);
- argv_array_clear(&fetch_argv);
+ retval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD);
+ strvec_clear(&fetch_argv);
return retval;
}
diff --git a/builtin/repack.c b/builtin/repack.c
index df287739d9..01e7767c79 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -7,7 +7,7 @@
#include "sigchain.h"
#include "strbuf.h"
#include "string-list.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "midx.h"
#include "packfile.h"
#include "prune-packed.h"
@@ -133,7 +133,11 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list,
static void remove_redundant_pack(const char *dir_name, const char *base_name)
{
struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%s/%s.pack", dir_name, base_name);
+ struct multi_pack_index *m = get_local_multi_pack_index(the_repository);
+ strbuf_addf(&buf, "%s.pack", base_name);
+ if (m && midx_contains_pack(m, buf.buf))
+ clear_midx_file(the_repository);
+ strbuf_insertf(&buf, 0, "%s/", dir_name);
unlink_pack_path(buf.buf, 1);
strbuf_release(&buf);
}
@@ -153,28 +157,28 @@ struct pack_objects_args {
static void prepare_pack_objects(struct child_process *cmd,
const struct pack_objects_args *args)
{
- argv_array_push(&cmd->args, "pack-objects");
+ strvec_push(&cmd->args, "pack-objects");
if (args->window)
- argv_array_pushf(&cmd->args, "--window=%s", args->window);
+ strvec_pushf(&cmd->args, "--window=%s", args->window);
if (args->window_memory)
- argv_array_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
+ strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
if (args->depth)
- argv_array_pushf(&cmd->args, "--depth=%s", args->depth);
+ strvec_pushf(&cmd->args, "--depth=%s", args->depth);
if (args->threads)
- argv_array_pushf(&cmd->args, "--threads=%s", args->threads);
+ strvec_pushf(&cmd->args, "--threads=%s", args->threads);
if (args->max_pack_size)
- argv_array_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
+ strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
if (args->no_reuse_delta)
- argv_array_pushf(&cmd->args, "--no-reuse-delta");
+ strvec_pushf(&cmd->args, "--no-reuse-delta");
if (args->no_reuse_object)
- argv_array_pushf(&cmd->args, "--no-reuse-object");
+ strvec_pushf(&cmd->args, "--no-reuse-object");
if (args->local)
- argv_array_push(&cmd->args, "--local");
+ strvec_push(&cmd->args, "--local");
if (args->quiet)
- argv_array_push(&cmd->args, "--quiet");
+ strvec_push(&cmd->args, "--quiet");
if (delta_base_offset)
- argv_array_push(&cmd->args, "--delta-base-offset");
- argv_array_push(&cmd->args, packtmp);
+ strvec_push(&cmd->args, "--delta-base-offset");
+ strvec_push(&cmd->args, packtmp);
cmd->git_cmd = 1;
cmd->out = -1;
}
@@ -286,7 +290,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int keep_unreachable = 0;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
int no_update_server_info = 0;
- int midx_cleared = 0;
struct pack_objects_args po_args = {NULL};
struct option builtin_repack_options[] = {
@@ -361,24 +364,24 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
prepare_pack_objects(&cmd, &po_args);
- argv_array_push(&cmd.args, "--keep-true-parents");
+ strvec_push(&cmd.args, "--keep-true-parents");
if (!pack_kept_objects)
- argv_array_push(&cmd.args, "--honor-pack-keep");
+ strvec_push(&cmd.args, "--honor-pack-keep");
for (i = 0; i < keep_pack_list.nr; i++)
- argv_array_pushf(&cmd.args, "--keep-pack=%s",
- keep_pack_list.items[i].string);
- argv_array_push(&cmd.args, "--non-empty");
- argv_array_push(&cmd.args, "--all");
- argv_array_push(&cmd.args, "--reflog");
- argv_array_push(&cmd.args, "--indexed-objects");
+ strvec_pushf(&cmd.args, "--keep-pack=%s",
+ keep_pack_list.items[i].string);
+ strvec_push(&cmd.args, "--non-empty");
+ strvec_push(&cmd.args, "--all");
+ strvec_push(&cmd.args, "--reflog");
+ strvec_push(&cmd.args, "--indexed-objects");
if (has_promisor_remote())
- argv_array_push(&cmd.args, "--exclude-promisor-objects");
+ strvec_push(&cmd.args, "--exclude-promisor-objects");
if (write_bitmaps > 0)
- argv_array_push(&cmd.args, "--write-bitmap-index");
+ strvec_push(&cmd.args, "--write-bitmap-index");
else if (write_bitmaps < 0)
- argv_array_push(&cmd.args, "--write-bitmap-index-quiet");
+ strvec_push(&cmd.args, "--write-bitmap-index-quiet");
if (use_delta_islands)
- argv_array_push(&cmd.args, "--delta-islands");
+ strvec_push(&cmd.args, "--delta-islands");
if (pack_everything & ALL_INTO_ONE) {
get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
@@ -387,23 +390,23 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (existing_packs.nr && delete_redundant) {
if (unpack_unreachable) {
- argv_array_pushf(&cmd.args,
- "--unpack-unreachable=%s",
- unpack_unreachable);
- argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
+ strvec_pushf(&cmd.args,
+ "--unpack-unreachable=%s",
+ unpack_unreachable);
+ strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
} else if (pack_everything & LOOSEN_UNREACHABLE) {
- argv_array_push(&cmd.args,
- "--unpack-unreachable");
+ strvec_push(&cmd.args,
+ "--unpack-unreachable");
} else if (keep_unreachable) {
- argv_array_push(&cmd.args, "--keep-unreachable");
- argv_array_push(&cmd.args, "--pack-loose-unreachable");
+ strvec_push(&cmd.args, "--keep-unreachable");
+ strvec_push(&cmd.args, "--pack-loose-unreachable");
} else {
- argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
+ strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
}
}
} else {
- argv_array_push(&cmd.args, "--unpacked");
- argv_array_push(&cmd.args, "--incremental");
+ strvec_push(&cmd.args, "--unpacked");
+ strvec_push(&cmd.args, "--incremental");
}
cmd.no_stdin = 1;
@@ -439,11 +442,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
char *fname, *fname_old;
- if (!midx_cleared) {
- clear_midx_file(the_repository);
- midx_cleared = 1;
- }
-
fname = mkpathdup("%s/pack-%s%s", packdir,
item->string, exts[ext].name);
if (!file_exists(fname)) {
diff --git a/builtin/replace.c b/builtin/replace.c
index b36d17a657..cd48765911 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -228,13 +228,13 @@ static int export_object(const struct object_id *oid, enum object_type type,
if (fd < 0)
return error_errno(_("unable to open %s for writing"), filename);
- argv_array_push(&cmd.args, "--no-replace-objects");
- argv_array_push(&cmd.args, "cat-file");
+ strvec_push(&cmd.args, "--no-replace-objects");
+ strvec_push(&cmd.args, "cat-file");
if (raw)
- argv_array_push(&cmd.args, type_name(type));
+ strvec_push(&cmd.args, type_name(type));
else
- argv_array_push(&cmd.args, "-p");
- argv_array_push(&cmd.args, oid_to_hex(oid));
+ strvec_push(&cmd.args, "-p");
+ strvec_push(&cmd.args, oid_to_hex(oid));
cmd.git_cmd = 1;
cmd.out = fd;
@@ -502,7 +502,7 @@ static int convert_graft_file(int force)
const char *graft_file = get_graft_file(the_repository);
FILE *fp = fopen_or_warn(graft_file, "r");
struct strbuf buf = STRBUF_INIT, err = STRBUF_INIT;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
if (!fp)
return -1;
@@ -512,10 +512,10 @@ static int convert_graft_file(int force)
if (*buf.buf == '#')
continue;
- argv_array_split(&args, buf.buf);
- if (args.argc && create_graft(args.argc, args.argv, force, 1))
+ strvec_split(&args, buf.buf);
+ if (args.nr && create_graft(args.nr, args.v, force, 1))
strbuf_addf(&err, "\n\t%s", buf.buf);
- argv_array_clear(&args);
+ strvec_clear(&args);
}
fclose(fp);
diff --git a/builtin/reset.c b/builtin/reset.c
index 8ae69d6f2b..c635b062c3 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -423,7 +423,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
char *ref = NULL;
int err;
- dwim_ref(rev, strlen(rev), &dummy, &ref);
+ dwim_ref(rev, strlen(rev), &dummy, &ref, 0);
if (ref && !starts_with(ref, "refs/"))
ref = NULL;
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index f520111eda..25c6c3b38d 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -637,8 +637,15 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (bisect_list) {
int reaches, all;
+ unsigned bisect_flags = 0;
- find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
+ if (bisect_find_all)
+ bisect_flags |= FIND_BISECTION_ALL;
+
+ if (revs.first_parent_only)
+ bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY;
+
+ find_bisection(&revs.commits, &reaches, &all, bisect_flags);
if (bisect_show_vars)
return show_bisect_vars(&info, reaches, all);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 669dd2fd6f..ed200c8af1 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -136,7 +136,7 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
struct object_id discard;
char *full;
- switch (dwim_ref(name, strlen(name), &discard, &full)) {
+ switch (dwim_ref(name, strlen(name), &discard, &full, 0)) {
case 0:
/*
* Not found -- not a ref. We could
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 2b9610f121..7af148d733 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -29,10 +29,12 @@ static struct send_pack_args args;
static void print_helper_status(struct ref *ref)
{
struct strbuf buf = STRBUF_INIT;
+ struct ref_push_report *report;
for (; ref; ref = ref->next) {
const char *msg = NULL;
const char *res;
+ int count = 0;
switch(ref->status) {
case REF_STATUS_NONE:
@@ -94,6 +96,23 @@ static void print_helper_status(struct ref *ref)
}
strbuf_addch(&buf, '\n');
+ if (ref->status == REF_STATUS_OK) {
+ for (report = ref->report; report; report = report->next) {
+ if (count++ > 0)
+ strbuf_addf(&buf, "ok %s\n", ref->name);
+ if (report->ref_name)
+ strbuf_addf(&buf, "option refname %s\n",
+ report->ref_name);
+ if (report->old_oid)
+ strbuf_addf(&buf, "option old-oid %s\n",
+ oid_to_hex(report->old_oid));
+ if (report->new_oid)
+ strbuf_addf(&buf, "option new-oid %s\n",
+ oid_to_hex(report->new_oid));
+ if (report->forced_update)
+ strbuf_addstr(&buf, "option forced-update\n");
+ }
+ }
write_or_die(1, buf.buf, buf.len);
}
strbuf_release(&buf);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 7e52ee9126..d6d2dabeca 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -4,7 +4,7 @@
#include "refs.h"
#include "builtin.h"
#include "color.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "parse-options.h"
#include "dir.h"
#include "commit-slab.h"
@@ -20,7 +20,7 @@ static const char* show_branch_usage[] = {
static int showbranch_use_color = -1;
-static struct argv_array default_args = ARGV_ARRAY_INIT;
+static struct strvec default_args = STRVEC_INIT;
/*
* TODO: convert this use of commit->object.flags to commit-slab
@@ -561,9 +561,9 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
* default_arg is now passed to parse_options(), so we need to
* mimic the real argv a bit better.
*/
- if (!default_args.argc)
- argv_array_push(&default_args, "show-branch");
- argv_array_push(&default_args, value);
+ if (!default_args.nr)
+ strvec_push(&default_args, "show-branch");
+ strvec_push(&default_args, value);
return 0;
}
@@ -684,9 +684,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
git_config(git_show_branch_config, NULL);
/* If nothing is specified, try the default first */
- if (ac == 1 && default_args.argc) {
- ac = default_args.argc;
- av = default_args.argv;
+ if (ac == 1 && default_args.nr) {
+ ac = default_args.nr;
+ av = default_args.v;
}
ac = parse_options(ac, av, prefix, builtin_show_branch_options,
@@ -741,7 +741,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
die(Q_("only %d entry can be shown at one time.",
"only %d entries can be shown at one time.",
MAX_REVS), MAX_REVS);
- if (!dwim_ref(*av, strlen(*av), &oid, &ref))
+ if (!dwim_ref(*av, strlen(*av), &oid, &ref, 0))
die(_("no such ref %s"), *av);
/* Has the base been specified? */
diff --git a/builtin/stash.c b/builtin/stash.c
index 0c52a3b849..3f811f3050 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -7,7 +7,7 @@
#include "cache-tree.h"
#include "unpack-trees.h"
#include "merge-recursive.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "run-command.h"
#include "dir.h"
#include "rerere.h"
@@ -185,7 +185,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
end_of_rev = strchrnul(revision, '@');
strbuf_add(&symbolic, revision, end_of_rev - revision);
- ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
+ ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0);
strbuf_release(&symbolic);
switch (ret) {
case 0: /* Not found, but valid ref */
@@ -277,8 +277,8 @@ static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
* however it should be done together with apply_cached.
*/
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
- argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
+ strvec_pushl(&cp.args, "diff-tree", "--binary", NULL);
+ strvec_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
}
@@ -293,7 +293,7 @@ static int apply_cached(struct strbuf *out)
* buffer.
*/
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "apply", "--cached", NULL);
+ strvec_pushl(&cp.args, "apply", "--cached", NULL);
return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
}
@@ -306,7 +306,7 @@ static int reset_head(void)
* API for resetting.
*/
cp.git_cmd = 1;
- argv_array_push(&cp.args, "reset");
+ strvec_push(&cp.args, "reset");
return run_command(&cp);
}
@@ -335,9 +335,9 @@ static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
* converted together with update_index.
*/
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
- "--diff-filter=A", NULL);
- argv_array_push(&cp.args, c_tree_hex);
+ strvec_pushl(&cp.args, "diff-index", "--cached", "--name-only",
+ "--diff-filter=A", NULL);
+ strvec_push(&cp.args, c_tree_hex);
return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
}
@@ -350,7 +350,7 @@ static int update_index(struct strbuf *out)
* function exposed in order to remove this forking.
*/
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
+ strvec_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
}
@@ -365,10 +365,10 @@ static int restore_untracked(struct object_id *u_tree)
* run_command to fork processes that will not interfere.
*/
cp.git_cmd = 1;
- argv_array_push(&cp.args, "read-tree");
- argv_array_push(&cp.args, oid_to_hex(u_tree));
- argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
- stash_index_path.buf);
+ strvec_push(&cp.args, "read-tree");
+ strvec_push(&cp.args, oid_to_hex(u_tree));
+ strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
if (run_command(&cp)) {
remove_path(stash_index_path.buf);
return -1;
@@ -376,9 +376,9 @@ static int restore_untracked(struct object_id *u_tree)
child_process_init(&cp);
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
- argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
- stash_index_path.buf);
+ strvec_pushl(&cp.args, "checkout-index", "--all", NULL);
+ strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
res = run_command(&cp);
remove_path(stash_index_path.buf);
@@ -499,11 +499,11 @@ 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");
+ strvec_pushf(&cp.env_array, GIT_WORK_TREE_ENVIRONMENT"=%s",
+ absolute_path(get_git_work_tree()));
+ strvec_pushf(&cp.env_array, GIT_DIR_ENVIRONMENT"=%s",
+ absolute_path(get_git_dir()));
+ strvec_push(&cp.args, "status");
run_command(&cp);
}
@@ -546,9 +546,9 @@ static int do_drop_stash(struct stash_info *info, int quiet)
*/
cp_reflog.git_cmd = 1;
- argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
- "--rewrite", NULL);
- argv_array_push(&cp_reflog.args, info->revision.buf);
+ strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
+ "--rewrite", NULL);
+ strvec_push(&cp_reflog.args, info->revision.buf);
ret = run_command(&cp_reflog);
if (!ret) {
if (!quiet)
@@ -566,8 +566,8 @@ static int do_drop_stash(struct stash_info *info, int quiet)
cp.git_cmd = 1;
/* Even though --quiet is specified, rev-parse still outputs the hash */
cp.no_stdout = 1;
- argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
- argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
+ strvec_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
+ strvec_pushf(&cp.args, "%s@{0}", ref_stash);
ret = run_command(&cp);
/* do_clear_stash if we just dropped the last stash entry */
@@ -663,9 +663,9 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
return -1;
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "checkout", "-b", NULL);
- argv_array_push(&cp.args, branch);
- argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
+ strvec_pushl(&cp.args, "checkout", "-b", NULL);
+ strvec_push(&cp.args, branch);
+ strvec_push(&cp.args, oid_to_hex(&info.b_commit));
ret = run_command(&cp);
if (!ret)
ret = do_apply_stash(prefix, &info, 1, 0);
@@ -692,11 +692,11 @@ static int list_stash(int argc, const char **argv, const char *prefix)
return 0;
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
- "--first-parent", "-m", NULL);
- argv_array_pushv(&cp.args, argv);
- argv_array_push(&cp.args, ref_stash);
- argv_array_push(&cp.args, "--");
+ strvec_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
+ "--first-parent", "-m", NULL);
+ strvec_pushv(&cp.args, argv);
+ strvec_push(&cp.args, ref_stash);
+ strvec_push(&cp.args, "--");
return run_command(&cp);
}
@@ -727,8 +727,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
int ret = 0;
struct stash_info info;
struct rev_info rev;
- struct argv_array stash_args = ARGV_ARRAY_INIT;
- struct argv_array revision_args = ARGV_ARRAY_INIT;
+ struct strvec stash_args = STRVEC_INIT;
+ struct strvec revision_args = STRVEC_INIT;
struct option options[] = {
OPT_END()
};
@@ -737,16 +737,16 @@ static int show_stash(int argc, const char **argv, const char *prefix)
git_config(git_diff_ui_config, NULL);
init_revisions(&rev, prefix);
- argv_array_push(&revision_args, argv[0]);
+ strvec_push(&revision_args, argv[0]);
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-')
- argv_array_push(&stash_args, argv[i]);
+ strvec_push(&stash_args, argv[i]);
else
- argv_array_push(&revision_args, argv[i]);
+ strvec_push(&revision_args, argv[i]);
}
- ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
- argv_array_clear(&stash_args);
+ ret = get_stash_info(&info, stash_args.nr, stash_args.v);
+ strvec_clear(&stash_args);
if (ret)
return -1;
@@ -754,7 +754,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
* The config settings are applied only if there are not passed
* any options.
*/
- if (revision_args.argc == 1) {
+ if (revision_args.nr == 1) {
if (show_stat)
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
@@ -767,7 +767,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
}
}
- argc = setup_revisions(revision_args.argc, revision_args.argv, &rev, NULL);
+ argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
if (argc > 1) {
free_stash_info(&info);
usage_with_options(git_stash_show_usage, options);
@@ -842,12 +842,12 @@ static int store_stash(int argc, const char **argv, const char *prefix)
return do_store_stash(&obj, stash_msg, quiet);
}
-static void add_pathspecs(struct argv_array *args,
+static void add_pathspecs(struct strvec *args,
const struct pathspec *ps) {
int i;
for (i = 0; i < ps->nr; i++)
- argv_array_push(args, ps->items[i].original);
+ strvec_push(args, ps->items[i].original);
}
/*
@@ -864,7 +864,7 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked,
int found = 0;
struct dir_struct dir;
- memset(&dir, 0, sizeof(dir));
+ dir_init(&dir);
if (include_untracked != INCLUDE_ALL_FILES)
setup_standard_excludes(&dir);
@@ -875,12 +875,9 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked,
strbuf_addstr(untracked_files, ent->name);
/* NUL-terminate: will be fed to update-index -z */
strbuf_addch(untracked_files, '\0');
- free(ent);
}
- free(dir.entries);
- free(dir.ignored);
- clear_directory(&dir);
+ dir_clear(&dir);
return found;
}
@@ -960,9 +957,9 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
struct index_state istate = { NULL };
cp_upd_index.git_cmd = 1;
- argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
- "--remove", "--stdin", NULL);
- argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+ strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+ "--remove", "--stdin", NULL);
+ strvec_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
stash_index_path.buf);
strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
@@ -1003,9 +1000,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
remove_path(stash_index_path.buf);
cp_read_tree.git_cmd = 1;
- argv_array_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
- argv_array_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
- stash_index_path.buf);
+ strvec_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
+ strvec_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
if (run_command(&cp_read_tree)) {
ret = -1;
goto done;
@@ -1034,8 +1031,8 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
}
cp_diff_tree.git_cmd = 1;
- argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
- oid_to_hex(&info->w_tree), "--", NULL);
+ strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
+ oid_to_hex(&info->w_tree), "--", NULL);
if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
ret = -1;
goto done;
@@ -1088,11 +1085,11 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
}
cp_upd_index.git_cmd = 1;
- argv_array_pushl(&cp_upd_index.args, "update-index",
- "--ignore-skip-worktree-entries",
- "-z", "--add", "--remove", "--stdin", NULL);
- argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
- stash_index_path.buf);
+ strvec_pushl(&cp_upd_index.args, "update-index",
+ "--ignore-skip-worktree-entries",
+ "-z", "--add", "--remove", "--stdin", NULL);
+ strvec_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,
NULL, 0, NULL, 0)) {
@@ -1342,10 +1339,10 @@ 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, "clean", "--force",
- "--quiet", "-d", NULL);
+ strvec_pushl(&cp.args, "clean", "--force",
+ "--quiet", "-d", NULL);
if (include_untracked == INCLUDE_ALL_FILES)
- argv_array_push(&cp.args, "-x");
+ strvec_push(&cp.args, "-x");
if (run_command(&cp)) {
ret = -1;
goto done;
@@ -1359,12 +1356,12 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
struct strbuf out = STRBUF_INIT;
cp_add.git_cmd = 1;
- argv_array_push(&cp_add.args, "add");
+ strvec_push(&cp_add.args, "add");
if (!include_untracked)
- argv_array_push(&cp_add.args, "-u");
+ strvec_push(&cp_add.args, "-u");
if (include_untracked == INCLUDE_ALL_FILES)
- argv_array_push(&cp_add.args, "--force");
- argv_array_push(&cp_add.args, "--");
+ strvec_push(&cp_add.args, "--force");
+ strvec_push(&cp_add.args, "--");
add_pathspecs(&cp_add.args, ps);
if (run_command(&cp_add)) {
ret = -1;
@@ -1372,9 +1369,9 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
}
cp_diff.git_cmd = 1;
- argv_array_pushl(&cp_diff.args, "diff-index", "-p",
- "--cached", "--binary", "HEAD", "--",
- NULL);
+ strvec_pushl(&cp_diff.args, "diff-index", "-p",
+ "--cached", "--binary", "HEAD", "--",
+ NULL);
add_pathspecs(&cp_diff.args, ps);
if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {
ret = -1;
@@ -1382,8 +1379,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
}
cp_apply.git_cmd = 1;
- argv_array_pushl(&cp_apply.args, "apply", "--index",
- "-R", NULL);
+ strvec_pushl(&cp_apply.args, "apply", "--index",
+ "-R", NULL);
if (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,
NULL, 0)) {
ret = -1;
@@ -1392,8 +1389,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
} else {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "reset", "--hard", "-q",
- "--no-recurse-submodules", NULL);
+ strvec_pushl(&cp.args, "reset", "--hard", "-q",
+ "--no-recurse-submodules", NULL);
if (run_command(&cp)) {
ret = -1;
goto done;
@@ -1404,10 +1401,10 @@ 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, "checkout", "--no-overlay",
- oid_to_hex(&info.i_tree), "--", NULL);
+ strvec_pushl(&cp.args, "checkout", "--no-overlay",
+ oid_to_hex(&info.i_tree), "--", NULL);
if (!ps->nr)
- argv_array_push(&cp.args, ":/");
+ strvec_push(&cp.args, ":/");
else
add_pathspecs(&cp.args, ps);
if (run_command(&cp)) {
@@ -1420,7 +1417,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, "apply", "-R", NULL);
+ strvec_pushl(&cp.args, "apply", "-R", NULL);
if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {
if (!quiet)
@@ -1434,7 +1431,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", "-q", "--", NULL);
+ strvec_pushl(&cp.args, "reset", "-q", "--", NULL);
add_pathspecs(&cp.args, ps);
if (run_command(&cp)) {
ret = -1;
@@ -1560,7 +1557,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
{
pid_t pid = getpid();
const char *index_file;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
struct option options[] = {
OPT_END()
@@ -1609,7 +1606,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
git_stash_usage, options);
/* Assume 'stash push' */
- argv_array_push(&args, "push");
- argv_array_pushv(&args, argv);
- return !!push_stash(args.argc, args.argv, prefix, 1);
+ strvec_push(&args, "push");
+ strvec_pushv(&args, argv);
+ return !!push_stash(args.nr, args.v, prefix, 1);
}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a1c75607c7..de5ad73bb8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -294,9 +294,9 @@ static char *compute_rev_name(const char *sub_path, const char* object_id)
cp.git_cmd = 1;
cp.no_stderr = 1;
- argv_array_push(&cp.args, "describe");
- argv_array_pushv(&cp.args, *d);
- argv_array_push(&cp.args, object_id);
+ strvec_push(&cp.args, "describe");
+ strvec_pushv(&cp.args, *d);
+ strvec_push(&cp.args, object_id);
if (!capture_command(&cp, &sb, 0)) {
strbuf_strip_suffix(&sb, "\n");
@@ -495,12 +495,12 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
char *toplevel = xgetcwd();
struct strbuf sb = STRBUF_INIT;
- argv_array_pushf(&cp.env_array, "name=%s", sub->name);
- argv_array_pushf(&cp.env_array, "sm_path=%s", path);
- argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
- argv_array_pushf(&cp.env_array, "sha1=%s",
- oid_to_hex(ce_oid));
- argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
+ strvec_pushf(&cp.env_array, "name=%s", sub->name);
+ strvec_pushf(&cp.env_array, "sm_path=%s", path);
+ strvec_pushf(&cp.env_array, "displaypath=%s", displaypath);
+ strvec_pushf(&cp.env_array, "sha1=%s",
+ oid_to_hex(ce_oid));
+ strvec_pushf(&cp.env_array, "toplevel=%s", toplevel);
/*
* Since the path variable was accessible from the script
@@ -509,15 +509,15 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
* on windows. And since environment variables are
* case-insensitive in windows, it interferes with the
* existing PATH variable. Hence, to avoid that, we expose
- * path via the args argv_array and not via env_array.
+ * path via the args strvec and not via env_array.
*/
sq_quote_buf(&sb, path);
- argv_array_pushf(&cp.args, "path=%s; %s",
- sb.buf, info->argv[0]);
+ strvec_pushf(&cp.args, "path=%s; %s",
+ sb.buf, info->argv[0]);
strbuf_release(&sb);
free(toplevel);
} else {
- argv_array_pushv(&cp.args, info->argv);
+ strvec_pushv(&cp.args, info->argv);
}
if (!info->quiet)
@@ -534,16 +534,16 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
cpr.dir = path;
prepare_submodule_repo_env(&cpr.env_array);
- argv_array_pushl(&cpr.args, "--super-prefix", NULL);
- argv_array_pushf(&cpr.args, "%s/", displaypath);
- argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
- NULL);
+ strvec_pushl(&cpr.args, "--super-prefix", NULL);
+ strvec_pushf(&cpr.args, "%s/", displaypath);
+ strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
+ NULL);
if (info->quiet)
- argv_array_push(&cpr.args, "--quiet");
+ strvec_push(&cpr.args, "--quiet");
- argv_array_push(&cpr.args, "--");
- argv_array_pushv(&cpr.args, info->argv);
+ strvec_push(&cpr.args, "--");
+ strvec_pushv(&cpr.args, info->argv);
if (run_command(&cpr))
die(_("run_command returned non-zero status while "
@@ -612,7 +612,6 @@ struct init_cb {
const char *prefix;
unsigned int flags;
};
-
#define INIT_CB_INIT { NULL, 0 }
static void init_submodule(const char *path, const char *prefix,
@@ -742,7 +741,6 @@ struct status_cb {
const char *prefix;
unsigned int flags;
};
-
#define STATUS_CB_INIT { NULL, 0 }
static void print_status(unsigned int flags, char state, const char *path,
@@ -779,7 +777,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
unsigned int flags)
{
char *displaypath;
- struct argv_array diff_files_args = ARGV_ARRAY_INIT;
+ struct strvec diff_files_args = STRVEC_INIT;
struct rev_info rev;
int diff_files_result;
struct strbuf buf = STRBUF_INIT;
@@ -809,17 +807,17 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
}
strbuf_release(&buf);
- argv_array_pushl(&diff_files_args, "diff-files",
- "--ignore-submodules=dirty", "--quiet", "--",
- path, NULL);
+ strvec_pushl(&diff_files_args, "diff-files",
+ "--ignore-submodules=dirty", "--quiet", "--",
+ path, NULL);
git_config(git_diff_basic_config, NULL);
repo_init_revisions(the_repository, &rev, NULL);
rev.abbrev = 0;
- diff_files_args.argc = setup_revisions(diff_files_args.argc,
- diff_files_args.argv,
- &rev, NULL);
+ diff_files_args.nr = setup_revisions(diff_files_args.nr,
+ diff_files_args.v,
+ &rev, NULL);
diff_files_result = run_diff_files(&rev, 0);
if (!diff_result_code(&rev.diffopt, diff_files_result)) {
@@ -849,23 +847,23 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
cpr.dir = path;
prepare_submodule_repo_env(&cpr.env_array);
- argv_array_push(&cpr.args, "--super-prefix");
- argv_array_pushf(&cpr.args, "%s/", displaypath);
- argv_array_pushl(&cpr.args, "submodule--helper", "status",
- "--recursive", NULL);
+ strvec_push(&cpr.args, "--super-prefix");
+ strvec_pushf(&cpr.args, "%s/", displaypath);
+ strvec_pushl(&cpr.args, "submodule--helper", "status",
+ "--recursive", NULL);
if (flags & OPT_CACHED)
- argv_array_push(&cpr.args, "--cached");
+ strvec_push(&cpr.args, "--cached");
if (flags & OPT_QUIET)
- argv_array_push(&cpr.args, "--quiet");
+ strvec_push(&cpr.args, "--quiet");
if (run_command(&cpr))
die(_("failed to recurse into submodule '%s'"), path);
}
cleanup:
- argv_array_clear(&diff_files_args);
+ strvec_clear(&diff_files_args);
free(displaypath);
}
@@ -929,11 +927,437 @@ static int module_name(int argc, const char **argv, const char *prefix)
return 0;
}
+struct module_cb {
+ unsigned int mod_src;
+ unsigned int mod_dst;
+ struct object_id oid_src;
+ struct object_id oid_dst;
+ char status;
+ const char *sm_path;
+};
+#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
+
+struct module_cb_list {
+ struct module_cb **entries;
+ int alloc, nr;
+};
+#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
+
+struct summary_cb {
+ int argc;
+ const char **argv;
+ const char *prefix;
+ unsigned int cached: 1;
+ unsigned int for_status: 1;
+ unsigned int files: 1;
+ int summary_limit;
+};
+#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
+
+enum diff_cmd {
+ DIFF_INDEX,
+ DIFF_FILES
+};
+
+static char *verify_submodule_committish(const char *sm_path,
+ const char *committish)
+{
+ struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
+ struct strbuf result = STRBUF_INIT;
+
+ cp_rev_parse.git_cmd = 1;
+ cp_rev_parse.dir = sm_path;
+ prepare_submodule_repo_env(&cp_rev_parse.env_array);
+ strvec_pushl(&cp_rev_parse.args, "rev-parse", "-q", "--short", NULL);
+ strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
+ strvec_push(&cp_rev_parse.args, "--");
+
+ if (capture_command(&cp_rev_parse, &result, 0))
+ return NULL;
+
+ strbuf_trim_trailing_newline(&result);
+ return strbuf_detach(&result, NULL);
+}
+
+static void print_submodule_summary(struct summary_cb *info, char *errmsg,
+ int total_commits, const char *displaypath,
+ const char *src_abbrev, const char *dst_abbrev,
+ struct module_cb *p)
+{
+ if (p->status == 'T') {
+ if (S_ISGITLINK(p->mod_dst))
+ printf(_("* %s %s(blob)->%s(submodule)"),
+ displaypath, src_abbrev, dst_abbrev);
+ else
+ printf(_("* %s %s(submodule)->%s(blob)"),
+ displaypath, src_abbrev, dst_abbrev);
+ } else {
+ printf("* %s %s...%s",
+ displaypath, src_abbrev, dst_abbrev);
+ }
+
+ if (total_commits < 0)
+ printf(":\n");
+ else
+ printf(" (%d):\n", total_commits);
+
+ if (errmsg) {
+ printf(_("%s"), errmsg);
+ } else if (total_commits > 0) {
+ struct child_process cp_log = CHILD_PROCESS_INIT;
+
+ cp_log.git_cmd = 1;
+ cp_log.dir = p->sm_path;
+ prepare_submodule_repo_env(&cp_log.env_array);
+ strvec_pushl(&cp_log.args, "log", NULL);
+
+ if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) {
+ if (info->summary_limit > 0)
+ strvec_pushf(&cp_log.args, "-%d",
+ info->summary_limit);
+
+ strvec_pushl(&cp_log.args, "--pretty= %m %s",
+ "--first-parent", NULL);
+ strvec_pushf(&cp_log.args, "%s...%s",
+ src_abbrev, dst_abbrev);
+ } else if (S_ISGITLINK(p->mod_dst)) {
+ strvec_pushl(&cp_log.args, "--pretty= > %s",
+ "-1", dst_abbrev, NULL);
+ } else {
+ strvec_pushl(&cp_log.args, "--pretty= < %s",
+ "-1", src_abbrev, NULL);
+ }
+ run_command(&cp_log);
+ }
+ printf("\n");
+}
+
+static void generate_submodule_summary(struct summary_cb *info,
+ struct module_cb *p)
+{
+ char *displaypath, *src_abbrev = NULL, *dst_abbrev;
+ int missing_src = 0, missing_dst = 0;
+ char *errmsg = NULL;
+ int total_commits = -1;
+
+ if (!info->cached && oideq(&p->oid_dst, &null_oid)) {
+ if (S_ISGITLINK(p->mod_dst)) {
+ struct ref_store *refs = get_submodule_ref_store(p->sm_path);
+ if (refs)
+ refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
+ } else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) {
+ struct stat st;
+ int fd = open(p->sm_path, O_RDONLY);
+
+ if (fd < 0 || fstat(fd, &st) < 0 ||
+ index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+ p->sm_path, 0))
+ error(_("couldn't hash object from '%s'"), p->sm_path);
+ } else {
+ /* for a submodule removal (mode:0000000), don't warn */
+ if (p->mod_dst)
+ warning(_("unexpected mode %o\n"), p->mod_dst);
+ }
+ }
+
+ if (S_ISGITLINK(p->mod_src)) {
+ if (p->status != 'D')
+ src_abbrev = verify_submodule_committish(p->sm_path,
+ oid_to_hex(&p->oid_src));
+ if (!src_abbrev) {
+ missing_src = 1;
+ /*
+ * As `rev-parse` failed, we fallback to getting
+ * the abbreviated hash using oid_src. We do
+ * this as we might still need the abbreviated
+ * hash in cases like a submodule type change, etc.
+ */
+ src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
+ }
+ } else {
+ /*
+ * The source does not point to a submodule.
+ * So, we fallback to getting the abbreviation using
+ * oid_src as we might still need the abbreviated
+ * hash in cases like submodule add, etc.
+ */
+ src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
+ }
+
+ if (S_ISGITLINK(p->mod_dst)) {
+ dst_abbrev = verify_submodule_committish(p->sm_path,
+ oid_to_hex(&p->oid_dst));
+ if (!dst_abbrev) {
+ missing_dst = 1;
+ /*
+ * As `rev-parse` failed, we fallback to getting
+ * the abbreviated hash using oid_dst. We do
+ * this as we might still need the abbreviated
+ * hash in cases like a submodule type change, etc.
+ */
+ dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
+ }
+ } else {
+ /*
+ * The destination does not point to a submodule.
+ * So, we fallback to getting the abbreviation using
+ * oid_dst as we might still need the abbreviated
+ * hash in cases like a submodule removal, etc.
+ */
+ dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
+ }
+
+ displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
+
+ if (!missing_src && !missing_dst) {
+ struct child_process cp_rev_list = CHILD_PROCESS_INIT;
+ struct strbuf sb_rev_list = STRBUF_INIT;
+
+ strvec_pushl(&cp_rev_list.args, "rev-list",
+ "--first-parent", "--count", NULL);
+ if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
+ strvec_pushf(&cp_rev_list.args, "%s...%s",
+ src_abbrev, dst_abbrev);
+ else
+ strvec_push(&cp_rev_list.args, S_ISGITLINK(p->mod_src) ?
+ src_abbrev : dst_abbrev);
+ strvec_push(&cp_rev_list.args, "--");
+
+ cp_rev_list.git_cmd = 1;
+ cp_rev_list.dir = p->sm_path;
+ prepare_submodule_repo_env(&cp_rev_list.env_array);
+
+ if (!capture_command(&cp_rev_list, &sb_rev_list, 0))
+ total_commits = atoi(sb_rev_list.buf);
+
+ strbuf_release(&sb_rev_list);
+ } else {
+ /*
+ * Don't give error msg for modification whose dst is not
+ * submodule, i.e., deleted or changed to blob
+ */
+ if (S_ISGITLINK(p->mod_dst)) {
+ struct strbuf errmsg_str = STRBUF_INIT;
+ if (missing_src && missing_dst) {
+ strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commits %s and %s\n",
+ displaypath, oid_to_hex(&p->oid_src),
+ oid_to_hex(&p->oid_dst));
+ } else {
+ strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commit %s\n",
+ displaypath, missing_src ?
+ oid_to_hex(&p->oid_src) :
+ oid_to_hex(&p->oid_dst));
+ }
+ errmsg = strbuf_detach(&errmsg_str, NULL);
+ }
+ }
+
+ print_submodule_summary(info, errmsg, total_commits,
+ displaypath, src_abbrev,
+ dst_abbrev, p);
+
+ free(displaypath);
+ free(src_abbrev);
+ free(dst_abbrev);
+}
+
+static void prepare_submodule_summary(struct summary_cb *info,
+ struct module_cb_list *list)
+{
+ int i;
+ for (i = 0; i < list->nr; i++) {
+ const struct submodule *sub;
+ struct module_cb *p = list->entries[i];
+ struct strbuf sm_gitdir = STRBUF_INIT;
+
+ if (p->status == 'D' || p->status == 'T') {
+ generate_submodule_summary(info, p);
+ continue;
+ }
+
+ if (info->for_status && p->status != 'A' &&
+ (sub = submodule_from_path(the_repository,
+ &null_oid, p->sm_path))) {
+ char *config_key = NULL;
+ const char *value;
+ int ignore_all = 0;
+
+ config_key = xstrfmt("submodule.%s.ignore",
+ sub->name);
+ if (!git_config_get_string_tmp(config_key, &value))
+ ignore_all = !strcmp(value, "all");
+ else if (sub->ignore)
+ ignore_all = !strcmp(sub->ignore, "all");
+
+ free(config_key);
+ if (ignore_all)
+ continue;
+ }
+
+ /* Also show added or modified modules which are checked out */
+ strbuf_addstr(&sm_gitdir, p->sm_path);
+ if (is_nonbare_repository_dir(&sm_gitdir))
+ generate_submodule_summary(info, p);
+ strbuf_release(&sm_gitdir);
+ }
+}
+
+static void submodule_summary_callback(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
+{
+ int i;
+ struct module_cb_list *list = data;
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ struct module_cb *temp;
+
+ if (!S_ISGITLINK(p->one->mode) && !S_ISGITLINK(p->two->mode))
+ continue;
+ temp = (struct module_cb*)malloc(sizeof(struct module_cb));
+ temp->mod_src = p->one->mode;
+ temp->mod_dst = p->two->mode;
+ temp->oid_src = p->one->oid;
+ temp->oid_dst = p->two->oid;
+ temp->status = p->status;
+ temp->sm_path = xstrdup(p->one->path);
+
+ ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
+ list->entries[list->nr++] = temp;
+ }
+}
+
+static const char *get_diff_cmd(enum diff_cmd diff_cmd)
+{
+ switch (diff_cmd) {
+ case DIFF_INDEX: return "diff-index";
+ case DIFF_FILES: return "diff-files";
+ default: BUG("bad diff_cmd value %d", diff_cmd);
+ }
+}
+
+static int compute_summary_module_list(struct object_id *head_oid,
+ struct summary_cb *info,
+ enum diff_cmd diff_cmd)
+{
+ struct strvec diff_args = STRVEC_INIT;
+ struct rev_info rev;
+ struct module_cb_list list = MODULE_CB_LIST_INIT;
+
+ strvec_push(&diff_args, get_diff_cmd(diff_cmd));
+ if (info->cached)
+ strvec_push(&diff_args, "--cached");
+ strvec_pushl(&diff_args, "--ignore-submodules=dirty", "--raw", NULL);
+ if (head_oid)
+ strvec_push(&diff_args, oid_to_hex(head_oid));
+ strvec_push(&diff_args, "--");
+ if (info->argc)
+ strvec_pushv(&diff_args, info->argv);
+
+ git_config(git_diff_basic_config, NULL);
+ init_revisions(&rev, info->prefix);
+ rev.abbrev = 0;
+ precompose_argv(diff_args.nr, diff_args.v);
+ setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
+ rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = submodule_summary_callback;
+ rev.diffopt.format_callback_data = &list;
+
+ if (!info->cached) {
+ if (diff_cmd == DIFF_INDEX)
+ setup_work_tree();
+ if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+ perror("read_cache_preload");
+ return -1;
+ }
+ } else if (read_cache() < 0) {
+ perror("read_cache");
+ return -1;
+ }
+
+ if (diff_cmd == DIFF_INDEX)
+ run_diff_index(&rev, info->cached);
+ else
+ run_diff_files(&rev, 0);
+ prepare_submodule_summary(info, &list);
+ strvec_clear(&diff_args);
+ return 0;
+}
+
+static int module_summary(int argc, const char **argv, const char *prefix)
+{
+ struct summary_cb info = SUMMARY_CB_INIT;
+ int cached = 0;
+ int for_status = 0;
+ int files = 0;
+ int summary_limit = -1;
+ enum diff_cmd diff_cmd = DIFF_INDEX;
+ struct object_id head_oid;
+ int ret;
+
+ struct option module_summary_options[] = {
+ OPT_BOOL(0, "cached", &cached,
+ N_("use the commit stored in the index instead of the submodule HEAD")),
+ OPT_BOOL(0, "files", &files,
+ N_("to compare the commit in the index with that in the submodule HEAD")),
+ OPT_BOOL(0, "for-status", &for_status,
+ N_("skip submodules with 'ignore_config' value set to 'all'")),
+ OPT_INTEGER('n', "summary-limit", &summary_limit,
+ N_("limit the summary size")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper summary [<options>] [commit] [--] [<path>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_summary_options,
+ git_submodule_helper_usage, 0);
+
+ if (!summary_limit)
+ return 0;
+
+ if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
+ if (argc) {
+ argv++;
+ argc--;
+ }
+ } else if (!argc || !strcmp(argv[0], "HEAD")) {
+ /* before the first commit: compare with an empty tree */
+ oidcpy(&head_oid, the_hash_algo->empty_tree);
+ if (argc) {
+ argv++;
+ argc--;
+ }
+ } else {
+ if (get_oid("HEAD", &head_oid))
+ die(_("could not fetch a revision for HEAD"));
+ }
+
+ if (files) {
+ if (cached)
+ die(_("--cached and --files are mutually exclusive"));
+ diff_cmd = DIFF_FILES;
+ }
+
+ info.argc = argc;
+ info.argv = argv;
+ info.prefix = prefix;
+ info.cached = !!cached;
+ info.files = !!files;
+ info.for_status = !!for_status;
+ info.summary_limit = summary_limit;
+
+ ret = compute_summary_module_list((diff_cmd == DIFF_INDEX) ? &head_oid : NULL,
+ &info, diff_cmd);
+ return ret;
+}
+
struct sync_cb {
const char *prefix;
unsigned int flags;
};
-
#define SYNC_CB_INIT { NULL, 0 }
static void sync_submodule(const char *path, const char *prefix,
@@ -995,8 +1419,8 @@ static void sync_submodule(const char *path, const char *prefix,
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.dir = path;
- argv_array_pushl(&cp.args, "submodule--helper",
- "print-default-remote", NULL);
+ strvec_pushl(&cp.args, "submodule--helper",
+ "print-default-remote", NULL);
strbuf_reset(&sb);
if (capture_command(&cp, &sb, 0))
@@ -1021,13 +1445,13 @@ static void sync_submodule(const char *path, const char *prefix,
cpr.dir = path;
prepare_submodule_repo_env(&cpr.env_array);
- argv_array_push(&cpr.args, "--super-prefix");
- argv_array_pushf(&cpr.args, "%s/", displaypath);
- argv_array_pushl(&cpr.args, "submodule--helper", "sync",
- "--recursive", NULL);
+ strvec_push(&cpr.args, "--super-prefix");
+ strvec_pushf(&cpr.args, "%s/", displaypath);
+ strvec_pushl(&cpr.args, "submodule--helper", "sync",
+ "--recursive", NULL);
if (flags & OPT_QUIET)
- argv_array_push(&cpr.args, "--quiet");
+ strvec_push(&cpr.args, "--quiet");
if (run_command(&cpr))
die(_("failed to recurse into submodule '%s'"),
@@ -1127,8 +1551,8 @@ static void deinit_submodule(const char *path, const char *prefix,
if (!(flags & OPT_FORCE)) {
struct child_process cp_rm = CHILD_PROCESS_INIT;
cp_rm.git_cmd = 1;
- argv_array_pushl(&cp_rm.args, "rm", "-qn",
- path, NULL);
+ strvec_pushl(&cp_rm.args, "rm", "-qn",
+ path, NULL);
if (run_command(&cp_rm))
die(_("Submodule work tree '%s' contains local "
@@ -1156,8 +1580,8 @@ static void deinit_submodule(const char *path, const char *prefix,
displaypath);
cp_config.git_cmd = 1;
- argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
- argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
+ strvec_pushl(&cp_config.args, "config", "--get-regexp", NULL);
+ strvec_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
/* remove the .git/config entries (unless the user already did it) */
if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
@@ -1239,32 +1663,32 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
{
struct child_process cp = CHILD_PROCESS_INIT;
- argv_array_push(&cp.args, "clone");
- argv_array_push(&cp.args, "--no-checkout");
+ strvec_push(&cp.args, "clone");
+ strvec_push(&cp.args, "--no-checkout");
if (quiet)
- argv_array_push(&cp.args, "--quiet");
+ strvec_push(&cp.args, "--quiet");
if (progress)
- argv_array_push(&cp.args, "--progress");
+ strvec_push(&cp.args, "--progress");
if (depth && *depth)
- argv_array_pushl(&cp.args, "--depth", depth, NULL);
+ strvec_pushl(&cp.args, "--depth", depth, NULL);
if (reference->nr) {
struct string_list_item *item;
for_each_string_list_item(item, reference)
- argv_array_pushl(&cp.args, "--reference",
- item->string, NULL);
+ strvec_pushl(&cp.args, "--reference",
+ item->string, NULL);
}
if (dissociate)
- argv_array_push(&cp.args, "--dissociate");
+ strvec_push(&cp.args, "--dissociate");
if (gitdir && *gitdir)
- argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
+ strvec_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
if (single_branch >= 0)
- argv_array_push(&cp.args, single_branch ?
+ strvec_push(&cp.args, single_branch ?
"--single-branch" :
"--no-single-branch");
- argv_array_push(&cp.args, "--");
- argv_array_push(&cp.args, url);
- argv_array_push(&cp.args, path);
+ strvec_push(&cp.args, "--");
+ strvec_push(&cp.args, url);
+ strvec_push(&cp.args, path);
cp.git_cmd = 1;
prepare_submodule_repo_env(&cp.env_array);
@@ -1511,7 +1935,7 @@ static void determine_submodule_update_strategy(struct repository *r,
if (parse_submodule_update_strategy(update, out) < 0)
die(_("Invalid update mode '%s' for submodule path '%s'"),
update, path);
- } else if (!repo_config_get_string_const(r, key, &val)) {
+ } else if (!repo_config_get_string_tmp(r, key, &val)) {
if (parse_submodule_update_strategy(val, out) < 0)
die(_("Invalid update mode '%s' configured for submodule path '%s'"),
val, path);
@@ -1667,7 +2091,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
}
key = xstrfmt("submodule.%s.update", sub->name);
- if (!repo_config_get_string_const(the_repository, key, &update_string)) {
+ if (!repo_config_get_string_tmp(the_repository, key, &update_string)) {
update_type = parse_submodule_update_type(update_string);
} else {
update_type = sub->update_strategy.type;
@@ -1690,7 +2114,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.url", sub->name);
- if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
+ if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
if (starts_with_dot_slash(sub->url) ||
starts_with_dot_dot_slash(sub->url)) {
url = compute_submodule_clone_url(sub->url);
@@ -1717,38 +2141,38 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
child->no_stdin = 1;
child->stdout_to_stderr = 1;
child->err = -1;
- argv_array_push(&child->args, "submodule--helper");
- argv_array_push(&child->args, "clone");
+ strvec_push(&child->args, "submodule--helper");
+ strvec_push(&child->args, "clone");
if (suc->progress)
- argv_array_push(&child->args, "--progress");
+ strvec_push(&child->args, "--progress");
if (suc->quiet)
- argv_array_push(&child->args, "--quiet");
+ strvec_push(&child->args, "--quiet");
if (suc->prefix)
- argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+ strvec_pushl(&child->args, "--prefix", suc->prefix, NULL);
if (suc->recommend_shallow && sub->recommend_shallow == 1)
- argv_array_push(&child->args, "--depth=1");
+ strvec_push(&child->args, "--depth=1");
if (suc->require_init)
- argv_array_push(&child->args, "--require-init");
- argv_array_pushl(&child->args, "--path", sub->path, NULL);
- argv_array_pushl(&child->args, "--name", sub->name, NULL);
- argv_array_pushl(&child->args, "--url", url, NULL);
+ strvec_push(&child->args, "--require-init");
+ strvec_pushl(&child->args, "--path", sub->path, NULL);
+ strvec_pushl(&child->args, "--name", sub->name, NULL);
+ strvec_pushl(&child->args, "--url", url, NULL);
if (suc->references.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &suc->references)
- argv_array_pushl(&child->args, "--reference", item->string, NULL);
+ strvec_pushl(&child->args, "--reference", item->string, NULL);
}
if (suc->dissociate)
- argv_array_push(&child->args, "--dissociate");
+ strvec_push(&child->args, "--dissociate");
if (suc->depth)
- argv_array_push(&child->args, suc->depth);
+ strvec_push(&child->args, suc->depth);
if (suc->single_branch >= 0)
- argv_array_push(&child->args, suc->single_branch ?
+ strvec_push(&child->args, suc->single_branch ?
"--single-branch" :
"--no-single-branch");
cleanup:
- strbuf_reset(&displaypath_sb);
- strbuf_reset(&sb);
+ strbuf_release(&displaypath_sb);
+ strbuf_release(&sb);
if (need_free_url)
free((void*)url);
@@ -1976,7 +2400,7 @@ static const char *remote_submodule_branch(const char *path)
return NULL;
key = xstrfmt("submodule.%s.branch", sub->name);
- if (repo_config_get_string_const(the_repository, key, &branch))
+ if (repo_config_get_string_tmp(the_repository, key, &branch))
branch = sub->branch;
free(key);
@@ -2101,7 +2525,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
{
const struct submodule *sub;
const char *path;
- char *cw;
+ const char *cw;
struct repository subrepo;
if (argc != 2)
@@ -2116,7 +2540,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
if (repo_submodule_init(&subrepo, the_repository, sub))
die(_("could not get a repository handle for submodule '%s'"), path);
- if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
+ if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) {
char *cfg_file, *abs_path;
const char *rel_path;
struct strbuf sb = STRBUF_INIT;
@@ -2344,6 +2768,7 @@ static struct cmd_struct commands[] = {
{"print-default-remote", print_default_remote, 0},
{"sync", module_sync, SUPPORT_SUPER_PREFIX},
{"deinit", module_deinit, 0},
+ {"summary", module_summary, SUPPORT_SUPER_PREFIX},
{"remote-branch", resolve_remote_submodule_branch, 0},
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
diff --git a/builtin/tag.c b/builtin/tag.c
index 5cbd80dc3e..ecf011776d 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -26,7 +26,7 @@ static const char * const git_tag_usage[] = {
"\t\t<tagname> [<head>]"),
N_("git tag -d <tagname>..."),
N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
- "\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
+ "\t\t[--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
N_("git tag -v [--format=<format>] <tagname>..."),
NULL
};
@@ -457,8 +457,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (argc == 0)
cmdmode = 'l';
else if (filter.with_commit || filter.no_commit ||
- filter.points_at.nr || filter.merge_commit ||
- filter.lines != -1)
+ filter.reachable_from || filter.unreachable_from ||
+ filter.points_at.nr || filter.lines != -1)
cmdmode = 'l';
}
@@ -509,7 +509,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
die(_("--no-contains option is only allowed in list mode"));
if (filter.points_at.nr)
die(_("--points-at option is only allowed in list mode"));
- if (filter.merge_commit)
+ if (filter.reachable_from || filter.unreachable_from)
die(_("--merged and --no-merged options are only allowed in list mode"));
if (cmdmode == 'd')
return for_each_tag_name(argv, delete_tag, NULL);
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index b74dd9a69d..8a2df4459c 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -4,7 +4,7 @@
#include "builtin.h"
#include "parse-options.h"
#include "quote.h"
-#include "argv-array.h"
+#include "strvec.h"
static const char * const git_update_ref_usage[] = {
N_("git update-ref [<options>] -d <refname> [<old-val>]"),
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 018879737a..24654b4c9b 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -7,7 +7,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
-#include "argv-array.h"
+#include "strvec.h"
static const char upload_archive_usage[] =
"git upload-archive <repo>";
@@ -19,7 +19,7 @@ static const char deadchild[] =
int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
{
- struct argv_array sent_argv = ARGV_ARRAY_INIT;
+ struct strvec sent_argv = STRVEC_INIT;
const char *arg_cmd = "argument ";
if (argc != 2 || !strcmp(argv[1], "-h"))
@@ -31,21 +31,21 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
init_archivers();
/* put received options in sent_argv[] */
- argv_array_push(&sent_argv, "git-upload-archive");
+ strvec_push(&sent_argv, "git-upload-archive");
for (;;) {
char *buf = packet_read_line(0, NULL);
if (!buf)
break; /* got a flush */
- if (sent_argv.argc > MAX_ARGS)
+ if (sent_argv.nr > MAX_ARGS)
die("Too many options (>%d)", MAX_ARGS - 1);
if (!starts_with(buf, arg_cmd))
die("'argument' token or flush expected");
- argv_array_push(&sent_argv, buf + strlen(arg_cmd));
+ strvec_push(&sent_argv, buf + strlen(arg_cmd));
}
/* parse all options sent by the client */
- return write_archive(sent_argv.argc, sent_argv.argv, prefix,
+ return write_archive(sent_argv.nr, sent_argv.v, prefix,
the_repository, NULL, 1);
}
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index c2a1a5c504..05c5213594 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -7,21 +7,26 @@
#define VERIFY_PACK_VERBOSE 01
#define VERIFY_PACK_STAT_ONLY 02
-static int verify_one_pack(const char *path, unsigned int flags)
+static int verify_one_pack(const char *path, unsigned int flags, const char *hash_algo)
{
struct child_process index_pack = CHILD_PROCESS_INIT;
- const char *argv[] = {"index-pack", NULL, NULL, NULL };
+ struct strvec *argv = &index_pack.args;
struct strbuf arg = STRBUF_INIT;
int verbose = flags & VERIFY_PACK_VERBOSE;
int stat_only = flags & VERIFY_PACK_STAT_ONLY;
int err;
+ strvec_push(argv, "index-pack");
+
if (stat_only)
- argv[1] = "--verify-stat-only";
+ strvec_push(argv, "--verify-stat-only");
else if (verbose)
- argv[1] = "--verify-stat";
+ strvec_push(argv, "--verify-stat");
else
- argv[1] = "--verify";
+ strvec_push(argv, "--verify");
+
+ if (hash_algo)
+ strvec_pushf(argv, "--object-format=%s", hash_algo);
/*
* In addition to "foo.pack" we accept "foo.idx" and "foo";
@@ -31,9 +36,8 @@ static int verify_one_pack(const char *path, unsigned int flags)
if (strbuf_strip_suffix(&arg, ".idx") ||
!ends_with(arg.buf, ".pack"))
strbuf_addstr(&arg, ".pack");
- argv[2] = arg.buf;
+ strvec_push(argv, arg.buf);
- index_pack.argv = argv;
index_pack.git_cmd = 1;
err = run_command(&index_pack);
@@ -60,12 +64,15 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
{
int err = 0;
unsigned int flags = 0;
+ const char *object_format = NULL;
int i;
const struct option verify_pack_options[] = {
OPT_BIT('v', "verbose", &flags, N_("verbose"),
VERIFY_PACK_VERBOSE),
OPT_BIT('s', "stat-only", &flags, N_("show statistics only"),
VERIFY_PACK_STAT_ONLY),
+ OPT_STRING(0, "object-format", &object_format, N_("hash"),
+ N_("specify the hash algorithm to use")),
OPT_END()
};
@@ -75,7 +82,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
if (argc < 1)
usage_with_options(verify_pack_usage, verify_pack_options);
for (i = 0; i < argc; i++) {
- if (verify_one_pack(argv[i], flags))
+ if (verify_one_pack(argv[i], flags, object_format))
err = 1;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index f0cbdef718..99abaeec6c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -4,7 +4,7 @@
#include "builtin.h"
#include "dir.h"
#include "parse-options.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "branch.h"
#include "refs.h"
#include "run-command.h"
@@ -316,7 +316,7 @@ static int add_worktree(const char *path, const char *refname,
struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
const char *name;
struct child_process cp = CHILD_PROCESS_INIT;
- struct argv_array child_env = ARGV_ARRAY_INIT;
+ struct strvec child_env = STRVEC_INIT;
unsigned int counter = 0;
int len, ret;
struct strbuf symref = STRBUF_INIT;
@@ -408,32 +408,32 @@ static int add_worktree(const char *path, const char *refname,
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
- argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
- argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
+ strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
+ strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
if (!is_branch)
- argv_array_pushl(&cp.args, "update-ref", "HEAD",
- oid_to_hex(&commit->object.oid), NULL);
+ strvec_pushl(&cp.args, "update-ref", "HEAD",
+ oid_to_hex(&commit->object.oid), NULL);
else {
- argv_array_pushl(&cp.args, "symbolic-ref", "HEAD",
- symref.buf, NULL);
+ strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
+ symref.buf, NULL);
if (opts->quiet)
- argv_array_push(&cp.args, "--quiet");
+ strvec_push(&cp.args, "--quiet");
}
- cp.env = child_env.argv;
+ cp.env = child_env.v;
ret = run_command(&cp);
if (ret)
goto done;
if (opts->checkout) {
cp.argv = NULL;
- argv_array_clear(&cp.args);
- argv_array_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
+ strvec_clear(&cp.args);
+ strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
- argv_array_push(&cp.args, "--quiet");
- cp.env = child_env.argv;
+ strvec_push(&cp.args, "--quiet");
+ cp.env = child_env.v;
ret = run_command(&cp);
if (ret)
goto done;
@@ -465,15 +465,15 @@ done:
cp.env = env;
cp.argv = NULL;
cp.trace2_hook_name = "post-checkout";
- argv_array_pushl(&cp.args, absolute_path(hook),
- oid_to_hex(&null_oid),
- oid_to_hex(&commit->object.oid),
- "1", NULL);
+ strvec_pushl(&cp.args, absolute_path(hook),
+ oid_to_hex(&null_oid),
+ oid_to_hex(&commit->object.oid),
+ "1", NULL);
ret = run_command(&cp);
}
}
- argv_array_clear(&child_env);
+ strvec_clear(&child_env);
strbuf_release(&sb);
strbuf_release(&symref);
strbuf_release(&sb_repo);
@@ -555,7 +555,7 @@ static int add(int ac, const char **av, const char *prefix)
N_("create a new branch")),
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
N_("create or reset a branch")),
- OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
+ OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
@@ -619,15 +619,15 @@ static int add(int ac, const char **av, const char *prefix)
if (new_branch) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
- argv_array_push(&cp.args, "branch");
+ strvec_push(&cp.args, "branch");
if (new_branch_force)
- argv_array_push(&cp.args, "--force");
+ strvec_push(&cp.args, "--force");
if (opts.quiet)
- argv_array_push(&cp.args, "--quiet");
- argv_array_push(&cp.args, new_branch);
- argv_array_push(&cp.args, branch);
+ strvec_push(&cp.args, "--quiet");
+ strvec_push(&cp.args, new_branch);
+ strvec_push(&cp.args, branch);
if (opt_track)
- argv_array_push(&cp.args, opt_track);
+ strvec_push(&cp.args, opt_track);
if (run_command(&cp))
return -1;
branch = new_branch;
@@ -924,7 +924,6 @@ static int move_worktree(int ac, const char **av, const char *prefix)
static void check_clean_worktree(struct worktree *wt,
const char *original_path)
{
- struct argv_array child_env = ARGV_ARRAY_INIT;
struct child_process cp;
char buf[1];
int ret;
@@ -935,15 +934,14 @@ static void check_clean_worktree(struct worktree *wt,
*/
validate_no_submodules(wt);
- argv_array_pushf(&child_env, "%s=%s/.git",
- GIT_DIR_ENVIRONMENT, wt->path);
- argv_array_pushf(&child_env, "%s=%s",
- GIT_WORK_TREE_ENVIRONMENT, wt->path);
- memset(&cp, 0, sizeof(cp));
- argv_array_pushl(&cp.args, "status",
- "--porcelain", "--ignore-submodules=none",
- NULL);
- cp.env = child_env.argv;
+ child_process_init(&cp);
+ strvec_pushf(&cp.env_array, "%s=%s/.git",
+ GIT_DIR_ENVIRONMENT, wt->path);
+ strvec_pushf(&cp.env_array, "%s=%s",
+ GIT_WORK_TREE_ENVIRONMENT, wt->path);
+ strvec_pushl(&cp.args, "status",
+ "--porcelain", "--ignore-submodules=none",
+ NULL);
cp.git_cmd = 1;
cp.dir = wt->path;
cp.out = -1;
@@ -1030,6 +1028,34 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
return ret;
}
+static void report_repair(int iserr, const char *path, const char *msg, void *cb_data)
+{
+ if (!iserr) {
+ printf_ln(_("repair: %s: %s"), msg, path);
+ } else {
+ int *exit_status = (int *)cb_data;
+ fprintf_ln(stderr, _("error: %s: %s"), msg, path);
+ *exit_status = 1;
+ }
+}
+
+static int repair(int ac, const char **av, const char *prefix)
+{
+ const char **p;
+ const char *self[] = { ".", NULL };
+ struct option options[] = {
+ OPT_END()
+ };
+ int rc = 0;
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ repair_worktrees(report_repair, &rc);
+ p = ac > 0 ? av : self;
+ for (; *p; p++)
+ repair_worktree_at_path(*p, report_repair, &rc);
+ return rc;
+}
+
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
@@ -1056,5 +1082,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
return move_worktree(ac - 1, av + 1, prefix);
if (!strcmp(av[1], "remove"))
return remove_worktree(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "repair"))
+ return repair(ac - 1, av + 1, prefix);
usage_with_options(worktree_usage, options);
}
diff --git a/bundle.c b/bundle.c
index 2a0d744d3f..cb0e5931ac 100644
--- a/bundle.c
+++ b/bundle.c
@@ -10,9 +10,18 @@
#include "list-objects.h"
#include "run-command.h"
#include "refs.h"
-#include "argv-array.h"
+#include "strvec.h"
-static const char bundle_signature[] = "# v2 git bundle\n";
+
+static const char v2_bundle_signature[] = "# v2 git bundle\n";
+static const char v3_bundle_signature[] = "# v3 git bundle\n";
+static struct {
+ int version;
+ const char *signature;
+} bundle_sigs[] = {
+ { 2, v2_bundle_signature },
+ { 3, v3_bundle_signature },
+};
static void add_to_ref_list(const struct object_id *oid, const char *name,
struct ref_list *list)
@@ -23,15 +32,30 @@ static void add_to_ref_list(const struct object_id *oid, const char *name,
list->nr++;
}
-static const struct git_hash_algo *detect_hash_algo(struct strbuf *buf)
+static int parse_capability(struct bundle_header *header, const char *capability)
+{
+ const char *arg;
+ if (skip_prefix(capability, "object-format=", &arg)) {
+ int algo = hash_algo_by_name(arg);
+ if (algo == GIT_HASH_UNKNOWN)
+ return error(_("unrecognized bundle hash algorithm: %s"), arg);
+ header->hash_algo = &hash_algos[algo];
+ return 0;
+ }
+ return error(_("unknown capability '%s'"), capability);
+}
+
+static int parse_bundle_signature(struct bundle_header *header, const char *line)
{
- size_t len = strcspn(buf->buf, " \n");
- int algo;
+ int i;
- algo = hash_algo_by_length(len / 2);
- if (algo == GIT_HASH_UNKNOWN)
- return NULL;
- return &hash_algos[algo];
+ for (i = 0; i < ARRAY_SIZE(bundle_sigs); i++) {
+ if (!strcmp(line, bundle_sigs[i].signature)) {
+ header->version = bundle_sigs[i].version;
+ return 0;
+ }
+ }
+ return -1;
}
static int parse_bundle_header(int fd, struct bundle_header *header,
@@ -42,14 +66,16 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
/* The bundle header begins with the signature */
if (strbuf_getwholeline_fd(&buf, fd, '\n') ||
- strcmp(buf.buf, bundle_signature)) {
+ parse_bundle_signature(header, buf.buf)) {
if (report_path)
- error(_("'%s' does not look like a v2 bundle file"),
+ error(_("'%s' does not look like a v2 or v3 bundle file"),
report_path);
status = -1;
goto abort;
}
+ header->hash_algo = the_hash_algo;
+
/* The bundle header ends with an empty line */
while (!strbuf_getwholeline_fd(&buf, fd, '\n') &&
buf.len && buf.buf[0] != '\n') {
@@ -57,19 +83,19 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
int is_prereq = 0;
const char *p;
- if (*buf.buf == '-') {
- is_prereq = 1;
- strbuf_remove(&buf, 0, 1);
- }
strbuf_rtrim(&buf);
- if (!header->hash_algo) {
- header->hash_algo = detect_hash_algo(&buf);
- if (!header->hash_algo) {
- error(_("unknown hash algorithm length"));
+ if (header->version == 3 && *buf.buf == '@') {
+ if (parse_capability(header, buf.buf + 1)) {
status = -1;
break;
}
+ continue;
+ }
+
+ if (*buf.buf == '-') {
+ is_prereq = 1;
+ strbuf_remove(&buf, 0, 1);
}
/*
@@ -269,16 +295,16 @@ out:
/* Write the pack data to bundle_fd */
-static int write_pack_data(int bundle_fd, struct rev_info *revs, struct argv_array *pack_options)
+static int write_pack_data(int bundle_fd, struct rev_info *revs, struct strvec *pack_options)
{
struct child_process pack_objects = CHILD_PROCESS_INIT;
int i;
- argv_array_pushl(&pack_objects.args,
- "pack-objects",
- "--stdout", "--thin", "--delta-base-offset",
- NULL);
- argv_array_pushv(&pack_objects.args, pack_options->argv);
+ strvec_pushl(&pack_objects.args,
+ "pack-objects",
+ "--stdout", "--thin", "--delta-base-offset",
+ NULL);
+ strvec_pushv(&pack_objects.args, pack_options->v);
pack_objects.in = -1;
pack_objects.out = bundle_fd;
pack_objects.git_cmd = 1;
@@ -321,11 +347,11 @@ static int compute_and_write_prerequisites(int bundle_fd,
FILE *rls_fout;
int i;
- argv_array_pushl(&rls.args,
- "rev-list", "--boundary", "--pretty=oneline",
- NULL);
+ strvec_pushl(&rls.args,
+ "rev-list", "--boundary", "--pretty=oneline",
+ NULL);
for (i = 1; i < argc; i++)
- argv_array_push(&rls.args, argv[i]);
+ strvec_push(&rls.args, argv[i]);
rls.out = -1;
rls.git_cmd = 1;
if (start_command(&rls))
@@ -377,7 +403,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
if (e->item->flags & UNINTERESTING)
continue;
- if (dwim_ref(e->name, strlen(e->name), &oid, &ref) != 1)
+ if (dwim_ref(e->name, strlen(e->name), &oid, &ref, 0) != 1)
goto skip_write_ref;
if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
flag = 0;
@@ -449,13 +475,14 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
}
int create_bundle(struct repository *r, const char *path,
- int argc, const char **argv, struct argv_array *pack_options)
+ int argc, const char **argv, struct strvec *pack_options, int version)
{
struct lock_file lock = LOCK_INIT;
int bundle_fd = -1;
int bundle_to_stdout;
int ref_count = 0;
struct rev_info revs;
+ int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3;
bundle_to_stdout = !strcmp(path, "-");
if (bundle_to_stdout)
@@ -464,8 +491,22 @@ int create_bundle(struct repository *r, const char *path,
bundle_fd = hold_lock_file_for_update(&lock, path,
LOCK_DIE_ON_ERROR);
- /* write signature */
- write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
+ if (version == -1)
+ version = min_version;
+
+ if (version < 2 || version > 3) {
+ die(_("unsupported bundle version %d"), version);
+ } else if (version < min_version) {
+ die(_("cannot write bundle version %d with algorithm %s"), version, the_hash_algo->name);
+ } else if (version == 2) {
+ write_or_die(bundle_fd, v2_bundle_signature, strlen(v2_bundle_signature));
+ } else {
+ const char *capability = "@object-format=";
+ write_or_die(bundle_fd, v3_bundle_signature, strlen(v3_bundle_signature));
+ write_or_die(bundle_fd, capability, strlen(capability));
+ write_or_die(bundle_fd, the_hash_algo->name, strlen(the_hash_algo->name));
+ write_or_die(bundle_fd, "\n", 1);
+ }
/* init revs to list objects for pack-objects later */
save_commit_buffer = 0;
diff --git a/bundle.h b/bundle.h
index 2dc9442024..f9e2d1c8ef 100644
--- a/bundle.h
+++ b/bundle.h
@@ -1,7 +1,7 @@
#ifndef BUNDLE_H
#define BUNDLE_H
-#include "argv-array.h"
+#include "strvec.h"
#include "cache.h"
struct ref_list {
@@ -13,6 +13,7 @@ struct ref_list {
};
struct bundle_header {
+ unsigned version;
struct ref_list prerequisites;
struct ref_list references;
const struct git_hash_algo *hash_algo;
@@ -21,7 +22,8 @@ struct bundle_header {
int is_bundle(const char *path, int quiet);
int read_bundle_header(const char *path, struct bundle_header *header);
int create_bundle(struct repository *r, const char *path,
- int argc, const char **argv, struct argv_array *pack_options);
+ int argc, const char **argv, struct strvec *pack_options,
+ int version);
int verify_bundle(struct repository *r, struct bundle_header *header, int verbose);
#define BUNDLE_VERBOSE 1
int unbundle(struct repository *r, struct bundle_header *header,
diff --git a/cache.h b/cache.h
index 654426460c..c0072d43b1 100644
--- a/cache.h
+++ b/cache.h
@@ -629,7 +629,7 @@ int path_inside_repo(const char *prefix, const char *path);
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo,
const char *initial_branch, unsigned int flags);
-void initialize_repository_version(int hash_algo);
+void initialize_repository_version(int hash_algo, int reinit);
void sanitize_stdfds(void);
int daemonize(void);
@@ -921,8 +921,8 @@ extern int assume_unchanged;
extern int prefer_symlink_refs;
extern int warn_ambiguous_refs;
extern int warn_on_object_refname_ambiguity;
-extern const char *apply_default_whitespace;
-extern const char *apply_default_ignorewhitespace;
+extern char *apply_default_whitespace;
+extern char *apply_default_ignorewhitespace;
extern const char *git_attributes_file;
extern const char *git_hooks_path;
extern int zlib_compression_level;
@@ -1044,6 +1044,7 @@ struct repository_format {
int hash_algo;
char *work_tree;
struct string_list unknown_extensions;
+ struct string_list v1_only_extensions;
};
/*
@@ -1057,6 +1058,7 @@ struct repository_format {
.is_bare = -1, \
.hash_algo = GIT_HASH_SHA1, \
.unknown_extensions = STRING_LIST_INIT_DUP, \
+ .v1_only_extensions = STRING_LIST_INIT_DUP, \
}
/*
@@ -1555,21 +1557,32 @@ int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
*
* If the input was ok but there are not N branch switches in the
* reflog, it returns 0.
- *
- * If "allowed" is non-zero, it is a treated as a bitfield of allowable
- * expansions: local branches ("refs/heads/"), remote branches
- * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
- * allowed, even ones to refs outside of those namespaces.
*/
#define INTERPRET_BRANCH_LOCAL (1<<0)
#define INTERPRET_BRANCH_REMOTE (1<<1)
#define INTERPRET_BRANCH_HEAD (1<<2)
+struct interpret_branch_name_options {
+ /*
+ * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+ * expansions: local branches ("refs/heads/"), remote branches
+ * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+ * allowed, even ones to refs outside of those namespaces.
+ */
+ unsigned allowed;
+
+ /*
+ * If ^{upstream} or ^{push} (or equivalent) is requested, and the
+ * branch in question does not have such a reference, return -1 instead
+ * of die()-ing.
+ */
+ unsigned nonfatal_dangling_mark : 1;
+};
int repo_interpret_branch_name(struct repository *r,
const char *str, int len,
struct strbuf *buf,
- unsigned allowed);
-#define interpret_branch_name(str, len, buf, allowed) \
- repo_interpret_branch_name(the_repository, str, len, buf, allowed)
+ const struct interpret_branch_name_options *options);
+#define interpret_branch_name(str, len, buf, options) \
+ repo_interpret_branch_name(the_repository, str, len, buf, options)
int validate_headref(const char *ref);
diff --git a/checkout.c b/checkout.c
index c72e9f9773..6586e30ca5 100644
--- a/checkout.c
+++ b/checkout.c
@@ -47,7 +47,7 @@ const char *unique_tracking_name(const char *name, struct object_id *oid,
{
struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
const char *default_remote = NULL;
- if (!git_config_get_string_const("checkout.defaultremote", &default_remote))
+ if (!git_config_get_string_tmp("checkout.defaultremote", &default_remote))
cb_data.default_remote = default_remote;
cb_data.src_ref = xstrfmt("refs/heads/%s", name);
cb_data.dst_oid = oid;
@@ -55,7 +55,6 @@ const char *unique_tracking_name(const char *name, struct object_id *oid,
if (dwim_remotes_matched)
*dwim_remotes_matched = cb_data.num_matches;
free(cb_data.src_ref);
- free((char *)default_remote);
if (cb_data.num_matches == 1) {
free(cb_data.default_dst_ref);
free(cb_data.default_dst_oid);
diff --git a/ci/lib.sh b/ci/lib.sh
index ff24c547c8..821e3660d6 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -178,15 +178,16 @@ fi
export DEVELOPER=1
export DEFAULT_TEST_TARGET=prove
export GIT_TEST_CLONE_2GB=true
+export SKIP_DASHED_BUILT_INS=YesPlease
case "$jobname" in
linux-clang|linux-gcc)
if [ "$jobname" = linux-gcc ]
then
export CC=gcc-8
- MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python3"
else
- MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python2"
fi
export GIT_TEST_HTTPD=true
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 17e25aade9..6c27b886b8 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -24,6 +24,12 @@ linux-gcc)
export GIT_TEST_ADD_I_USE_BUILTIN=1
make test
;;
+linux-clang)
+ export GIT_TEST_DEFAULT_HASH=sha1
+ make test
+ export GIT_TEST_DEFAULT_HASH=sha256
+ make test
+ ;;
linux-gcc-4.8)
# Don't run the tests; we only care about whether Git can be
# built with GCC 4.8, as it errors out on some undesired (C99)
diff --git a/column.c b/column.c
index 4a38eed322..1261e18a72 100644
--- a/column.c
+++ b/column.c
@@ -107,7 +107,7 @@ static void display_plain(const struct string_list *list,
printf("%s%s%s", indent, list->items[i].string, nl);
}
-/* Print a cell to stdout with all necessary leading/traling space */
+/* Print a cell to stdout with all necessary leading/trailing space */
static int display_cell(struct column_data *data, int initial_width,
const char *empty_cell, int x, int y)
{
@@ -358,7 +358,7 @@ static struct child_process column_process = CHILD_PROCESS_INIT;
int run_column_filter(int colopts, const struct column_options *opts)
{
- struct argv_array *argv;
+ struct strvec *argv;
if (fd_out != -1)
return -1;
@@ -366,14 +366,14 @@ int run_column_filter(int colopts, const struct column_options *opts)
child_process_init(&column_process);
argv = &column_process.args;
- argv_array_push(argv, "column");
- argv_array_pushf(argv, "--raw-mode=%d", colopts);
+ strvec_push(argv, "column");
+ strvec_pushf(argv, "--raw-mode=%d", colopts);
if (opts && opts->width)
- argv_array_pushf(argv, "--width=%d", opts->width);
+ strvec_pushf(argv, "--width=%d", opts->width);
if (opts && opts->indent)
- argv_array_pushf(argv, "--indent=%s", opts->indent);
+ strvec_pushf(argv, "--indent=%s", opts->indent);
if (opts && opts->padding)
- argv_array_pushf(argv, "--padding=%d", opts->padding);
+ strvec_pushf(argv, "--padding=%d", opts->padding);
fflush(stdout);
column_process.in = -1;
diff --git a/command-list.txt b/command-list.txt
index 89aa60cde7..0e3204e7d1 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -117,6 +117,7 @@ git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
git-mailinfo purehelpers
git-mailsplit purehelpers
+git-maintenance mainporcelain
git-merge mainporcelain history
git-merge-base plumbinginterrogators
git-merge-file plumbingmanipulators
@@ -195,6 +196,7 @@ git-write-tree plumbingmanipulators
gitattributes guide
gitcli guide
gitcore-tutorial guide
+gitcredentials guide
gitcvs-migration guide
gitdiffcore guide
giteveryday guide
@@ -204,6 +206,7 @@ githooks guide
gitignore guide
gitmodules guide
gitnamespaces guide
+gitremote-helpers guide
gitrepository-layout guide
gitrevisions guide
gitsubmodules guide
diff --git a/commit-graph.c b/commit-graph.c
index 1af68c297d..cb042bdba8 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1,7 +1,5 @@
-#include "cache.h"
-#include "config.h"
-#include "dir.h"
#include "git-compat-util.h"
+#include "config.h"
#include "lockfile.h"
#include "pack.h"
#include "packfile.h"
@@ -19,6 +17,8 @@
#include "bloom.h"
#include "commit-slab.h"
#include "shallow.h"
+#include "json-writer.h"
+#include "trace2.h"
void git_test_write_commit_graph_or_die(void)
{
@@ -172,14 +172,21 @@ static char *get_split_graph_filename(struct object_directory *odb,
oid_hex);
}
-static char *get_chain_filename(struct object_directory *odb)
+char *get_commit_graph_chain_filename(struct object_directory *odb)
{
return xstrfmt("%s/info/commit-graphs/commit-graph-chain", odb->path);
}
static uint8_t oid_version(void)
{
- return 1;
+ switch (hash_algo_by_ptr(the_hash_algo)) {
+ case GIT_HASH_SHA1:
+ return 1;
+ case GIT_HASH_SHA256:
+ return 2;
+ default:
+ die(_("invalid hash version"));
+ }
}
static struct commit_graph *alloc_commit_graph(void)
@@ -224,7 +231,8 @@ int open_commit_graph(const char *graph_file, int *fd, struct stat *st)
return 1;
}
-struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st,
+struct commit_graph *load_commit_graph_one_fd_st(struct repository *r,
+ int fd, struct stat *st,
struct object_directory *odb)
{
void *graph_map;
@@ -240,7 +248,7 @@ struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st,
}
graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- ret = parse_commit_graph(graph_map, graph_size);
+ ret = parse_commit_graph(r, graph_map, graph_size);
if (ret)
ret->odb = odb;
@@ -280,13 +288,13 @@ static int verify_commit_graph_lite(struct commit_graph *g)
return 0;
}
-struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
+struct commit_graph *parse_commit_graph(struct repository *r,
+ void *graph_map, size_t graph_size)
{
const unsigned char *data, *chunk_lookup;
uint32_t i;
struct commit_graph *graph;
- uint64_t last_chunk_offset;
- uint32_t last_chunk_id;
+ uint64_t next_chunk_offset;
uint32_t graph_signature;
unsigned char graph_version, hash_version;
@@ -319,6 +327,8 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
return NULL;
}
+ prepare_repo_settings(r);
+
graph = alloc_commit_graph();
graph->hash_len = the_hash_algo->rawsz;
@@ -326,24 +336,26 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
graph->data = graph_map;
graph->data_len = graph_size;
- last_chunk_id = 0;
- last_chunk_offset = 8;
+ if (graph_size < GRAPH_HEADER_SIZE +
+ (graph->num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH +
+ GRAPH_FANOUT_SIZE + the_hash_algo->rawsz) {
+ error(_("commit-graph file is too small to hold %u chunks"),
+ graph->num_chunks);
+ free(graph);
+ return NULL;
+ }
+
chunk_lookup = data + 8;
+ next_chunk_offset = get_be64(chunk_lookup + 4);
for (i = 0; i < graph->num_chunks; i++) {
uint32_t chunk_id;
- uint64_t chunk_offset;
+ uint64_t chunk_offset = next_chunk_offset;
int chunk_repeated = 0;
- if (data + graph_size - chunk_lookup <
- GRAPH_CHUNKLOOKUP_WIDTH) {
- error(_("commit-graph chunk lookup table entry missing; file may be incomplete"));
- goto free_and_return;
- }
-
chunk_id = get_be32(chunk_lookup + 0);
- chunk_offset = get_be64(chunk_lookup + 4);
chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
+ next_chunk_offset = get_be64(chunk_lookup + 4);
if (chunk_offset > graph_size - the_hash_algo->rawsz) {
error(_("commit-graph improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
@@ -362,8 +374,11 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
case GRAPH_CHUNKID_OIDLOOKUP:
if (graph->chunk_oid_lookup)
chunk_repeated = 1;
- else
+ else {
graph->chunk_oid_lookup = data + chunk_offset;
+ graph->num_commits = (next_chunk_offset - chunk_offset)
+ / graph->hash_len;
+ }
break;
case GRAPH_CHUNKID_DATA:
@@ -390,14 +405,14 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
case GRAPH_CHUNKID_BLOOMINDEXES:
if (graph->chunk_bloom_indexes)
chunk_repeated = 1;
- else
+ else if (r->settings.commit_graph_read_changed_paths)
graph->chunk_bloom_indexes = data + chunk_offset;
break;
case GRAPH_CHUNKID_BLOOMDATA:
if (graph->chunk_bloom_data)
chunk_repeated = 1;
- else {
+ else if (r->settings.commit_graph_read_changed_paths) {
uint32_t hash_version;
graph->chunk_bloom_data = data + chunk_offset;
hash_version = get_be32(data + chunk_offset);
@@ -409,6 +424,7 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
graph->bloom_filter_settings->hash_version = hash_version;
graph->bloom_filter_settings->num_hashes = get_be32(data + chunk_offset + 4);
graph->bloom_filter_settings->bits_per_entry = get_be32(data + chunk_offset + 8);
+ graph->bloom_filter_settings->max_changed_paths = DEFAULT_BLOOM_MAX_CHANGES;
}
break;
}
@@ -417,15 +433,6 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
error(_("commit-graph chunk id %08x appears multiple times"), chunk_id);
goto free_and_return;
}
-
- if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
- {
- graph->num_commits = (chunk_offset - last_chunk_offset)
- / graph->hash_len;
- }
-
- last_chunk_id = chunk_id;
- last_chunk_offset = chunk_offset;
}
if (graph->chunk_bloom_indexes && graph->chunk_bloom_data) {
@@ -450,7 +457,8 @@ free_and_return:
return NULL;
}
-static struct commit_graph *load_commit_graph_one(const char *graph_file,
+static struct commit_graph *load_commit_graph_one(struct repository *r,
+ const char *graph_file,
struct object_directory *odb)
{
@@ -462,7 +470,7 @@ static struct commit_graph *load_commit_graph_one(const char *graph_file,
if (!open_ok)
return NULL;
- g = load_commit_graph_one_fd_st(fd, &st, odb);
+ g = load_commit_graph_one_fd_st(r, fd, &st, odb);
if (g)
g->filename = xstrdup(graph_file);
@@ -474,7 +482,7 @@ static struct commit_graph *load_commit_graph_v1(struct repository *r,
struct object_directory *odb)
{
char *graph_name = get_commit_graph_filename(odb);
- struct commit_graph *g = load_commit_graph_one(graph_name, odb);
+ struct commit_graph *g = load_commit_graph_one(r, graph_name, odb);
free(graph_name);
return g;
@@ -521,7 +529,7 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
struct stat st;
struct object_id *oids;
int i = 0, valid = 1, count;
- char *chain_name = get_chain_filename(odb);
+ char *chain_name = get_commit_graph_chain_filename(odb);
FILE *fp;
int stat_res;
@@ -555,7 +563,7 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
valid = 0;
for (odb = r->objects->odb; odb; odb = odb->next) {
char *graph_name = get_split_graph_filename(odb, line.buf);
- struct commit_graph *g = load_commit_graph_one(graph_name, odb);
+ struct commit_graph *g = load_commit_graph_one(r, graph_name, odb);
free(graph_name);
@@ -624,10 +632,6 @@ static int prepare_commit_graph(struct repository *r)
return !!r->objects->commit_graph;
r->objects->commit_graph_attempted = 1;
- if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))
- die("dying as requested by the '%s' variable on commit-graph load!",
- GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD);
-
prepare_repo_settings(r);
if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
@@ -669,6 +673,17 @@ int generation_numbers_enabled(struct repository *r)
return !!first_generation;
}
+struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
+{
+ struct commit_graph *g = r->objects->commit_graph;
+ while (g) {
+ if (g->bloom_filter_settings)
+ return g->bloom_filter_settings;
+ g = g->base_graph;
+ }
+ return NULL;
+}
+
static void close_commit_graph_one(struct commit_graph *g)
{
if (!g)
@@ -856,6 +871,14 @@ static int parse_commit_in_graph_one(struct repository *r,
int parse_commit_in_graph(struct repository *r, struct commit *item)
{
+ static int checked_env = 0;
+
+ if (!checked_env &&
+ git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE, 0))
+ die("dying as requested by the '%s' variable on commit-graph parse!",
+ GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE);
+ checked_env = 1;
+
if (!prepare_commit_graph(r))
return 0;
return parse_commit_in_graph_one(r, r->objects->commit_graph, item);
@@ -946,12 +969,18 @@ struct write_commit_graph_context {
changed_paths:1,
order_by_pack:1;
- const struct split_commit_graph_opts *split_opts;
+ const struct commit_graph_opts *opts;
size_t total_bloom_filter_data_size;
+ const struct bloom_filter_settings *bloom_settings;
+
+ int count_bloom_filter_computed;
+ int count_bloom_filter_not_computed;
+ int count_bloom_filter_trunc_empty;
+ int count_bloom_filter_trunc_large;
};
-static void write_graph_chunk_fanout(struct hashfile *f,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_fanout(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
int i, count = 0;
struct commit **list = ctx->commits.list;
@@ -972,17 +1001,21 @@ static void write_graph_chunk_fanout(struct hashfile *f,
hashwrite_be32(f, count);
}
+
+ return 0;
}
-static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_oids(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
int count;
for (count = 0; count < ctx->commits.nr; count++, list++) {
display_progress(ctx->progress, ++ctx->progress_cnt);
- hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
+ hashwrite(f, (*list)->object.oid.hash, the_hash_algo->rawsz);
}
+
+ return 0;
}
static const unsigned char *commit_to_sha1(size_t index, void *table)
@@ -991,8 +1024,8 @@ static const unsigned char *commit_to_sha1(size_t index, void *table)
return commits[index]->object.oid.hash;
}
-static void write_graph_chunk_data(struct hashfile *f, int hash_len,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_data(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
@@ -1009,7 +1042,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
die(_("unable to parse commit %s"),
oid_to_hex(&(*list)->object.oid));
tree = get_commit_tree_oid(*list);
- hashwrite(f, tree->hash, hash_len);
+ hashwrite(f, tree->hash, the_hash_algo->rawsz);
parent = (*list)->parents;
@@ -1089,10 +1122,12 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
list++;
}
+
+ return 0;
}
-static void write_graph_chunk_extra_edges(struct hashfile *f,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_extra_edges(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
@@ -1141,41 +1176,68 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
list++;
}
+
+ return 0;
}
-static void write_graph_chunk_bloom_indexes(struct hashfile *f,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_bloom_indexes(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
uint32_t cur_pos = 0;
while (list < last) {
- struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
- cur_pos += filter->len;
+ struct bloom_filter *filter = get_bloom_filter(ctx->r, *list);
+ size_t len = filter ? filter->len : 0;
+ cur_pos += len;
display_progress(ctx->progress, ++ctx->progress_cnt);
hashwrite_be32(f, cur_pos);
list++;
}
+
+ return 0;
+}
+
+static void trace2_bloom_filter_settings(struct write_commit_graph_context *ctx)
+{
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ jw_object_intmax(&jw, "hash_version", ctx->bloom_settings->hash_version);
+ jw_object_intmax(&jw, "num_hashes", ctx->bloom_settings->num_hashes);
+ jw_object_intmax(&jw, "bits_per_entry", ctx->bloom_settings->bits_per_entry);
+ jw_object_intmax(&jw, "max_changed_paths", ctx->bloom_settings->max_changed_paths);
+ jw_end(&jw);
+
+ trace2_data_json("bloom", ctx->r, "settings", &jw);
+
+ jw_release(&jw);
}
-static void write_graph_chunk_bloom_data(struct hashfile *f,
- struct write_commit_graph_context *ctx,
- const struct bloom_filter_settings *settings)
+static int write_graph_chunk_bloom_data(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
- hashwrite_be32(f, settings->hash_version);
- hashwrite_be32(f, settings->num_hashes);
- hashwrite_be32(f, settings->bits_per_entry);
+ trace2_bloom_filter_settings(ctx);
+
+ hashwrite_be32(f, ctx->bloom_settings->hash_version);
+ hashwrite_be32(f, ctx->bloom_settings->num_hashes);
+ hashwrite_be32(f, ctx->bloom_settings->bits_per_entry);
while (list < last) {
- struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
+ struct bloom_filter *filter = get_bloom_filter(ctx->r, *list);
+ size_t len = filter ? filter->len : 0;
+
display_progress(ctx->progress, ++ctx->progress_cnt);
- hashwrite(f, filter->data, filter->len * sizeof(unsigned char));
+ if (len)
+ hashwrite(f, filter->data, len * sizeof(unsigned char));
list++;
}
+
+ return 0;
}
static int oid_compare(const void *_a, const void *_b)
@@ -1231,8 +1293,8 @@ static void close_reachable(struct write_commit_graph_context *ctx)
{
int i;
struct commit *commit;
- enum commit_graph_split_flags flags = ctx->split_opts ?
- ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
+ enum commit_graph_split_flags flags = ctx->opts ?
+ ctx->opts->split_flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
if (ctx->report_progress)
ctx->progress = start_delayed_progress(
@@ -1336,11 +1398,24 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
stop_progress(&ctx->progress);
}
+static void trace2_bloom_filter_write_statistics(struct write_commit_graph_context *ctx)
+{
+ trace2_data_intmax("commit-graph", ctx->r, "filter-computed",
+ ctx->count_bloom_filter_computed);
+ trace2_data_intmax("commit-graph", ctx->r, "filter-not-computed",
+ ctx->count_bloom_filter_not_computed);
+ trace2_data_intmax("commit-graph", ctx->r, "filter-trunc-empty",
+ ctx->count_bloom_filter_trunc_empty);
+ trace2_data_intmax("commit-graph", ctx->r, "filter-trunc-large",
+ ctx->count_bloom_filter_trunc_large);
+}
+
static void compute_bloom_filters(struct write_commit_graph_context *ctx)
{
int i;
struct progress *progress = NULL;
struct commit **sorted_commits;
+ int max_new_filters;
init_bloom_filters();
@@ -1357,13 +1432,34 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx)
else
QSORT(sorted_commits, ctx->commits.nr, commit_gen_cmp);
+ max_new_filters = ctx->opts && ctx->opts->max_new_filters >= 0 ?
+ ctx->opts->max_new_filters : ctx->commits.nr;
+
for (i = 0; i < ctx->commits.nr; i++) {
+ enum bloom_filter_computed computed = 0;
struct commit *c = sorted_commits[i];
- struct bloom_filter *filter = get_bloom_filter(ctx->r, c, 1);
- ctx->total_bloom_filter_data_size += sizeof(unsigned char) * filter->len;
+ struct bloom_filter *filter = get_or_compute_bloom_filter(
+ ctx->r,
+ c,
+ ctx->count_bloom_filter_computed < max_new_filters,
+ ctx->bloom_settings,
+ &computed);
+ if (computed & BLOOM_COMPUTED) {
+ ctx->count_bloom_filter_computed++;
+ if (computed & BLOOM_TRUNC_EMPTY)
+ ctx->count_bloom_filter_trunc_empty++;
+ if (computed & BLOOM_TRUNC_LARGE)
+ ctx->count_bloom_filter_trunc_large++;
+ } else if (computed & BLOOM_NOT_COMPUTED)
+ ctx->count_bloom_filter_not_computed++;
+ ctx->total_bloom_filter_data_size += filter
+ ? sizeof(unsigned char) * filter->len : 0;
display_progress(progress, i + 1);
}
+ if (trace2_is_enabled())
+ trace2_bloom_filter_write_statistics(ctx);
+
free(sorted_commits);
stop_progress(&progress);
}
@@ -1392,7 +1488,7 @@ static int add_ref_to_set(const char *refname,
int write_commit_graph_reachable(struct object_directory *odb,
enum commit_graph_write_flags flags,
- const struct split_commit_graph_opts *split_opts)
+ const struct commit_graph_opts *opts)
{
struct oidset commits = OIDSET_INIT;
struct refs_cb_data data;
@@ -1409,7 +1505,7 @@ int write_commit_graph_reachable(struct object_directory *odb,
stop_progress(&data.progress);
result = write_commit_graph(odb, NULL, &commits,
- flags, split_opts);
+ flags, opts);
oidset_clear(&commits);
return result;
@@ -1524,8 +1620,8 @@ 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;
- enum commit_graph_split_flags flags = ctx->split_opts ?
- ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
+ enum commit_graph_split_flags flags = ctx->opts ?
+ ctx->opts->split_flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
ctx->num_extra_edges = 0;
if (ctx->report_progress)
@@ -1586,19 +1682,27 @@ static int write_graph_chunk_base(struct hashfile *f,
return 0;
}
+typedef int (*chunk_write_fn)(struct hashfile *f,
+ struct write_commit_graph_context *ctx);
+
+struct chunk_info {
+ uint32_t id;
+ uint64_t size;
+ chunk_write_fn write_fn;
+};
+
static int write_commit_graph_file(struct write_commit_graph_context *ctx)
{
uint32_t i;
int fd;
struct hashfile *f;
struct lock_file lk = LOCK_INIT;
- uint32_t chunk_ids[MAX_NUM_CHUNKS + 1];
- uint64_t chunk_offsets[MAX_NUM_CHUNKS + 1];
+ struct chunk_info chunks[MAX_NUM_CHUNKS + 1];
const unsigned hashsz = the_hash_algo->rawsz;
struct strbuf progress_title = STRBUF_INIT;
int num_chunks = 3;
+ uint64_t chunk_offset;
struct object_id file_hash;
- const struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
if (ctx->split) {
struct strbuf tmp_file = STRBUF_INIT;
@@ -1619,7 +1723,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
}
if (ctx->split) {
- char *lock_name = get_chain_filename(ctx->odb);
+ char *lock_name = get_commit_graph_chain_filename(ctx->odb);
hold_lock_file_for_update_mode(&lk, lock_name,
LOCK_DIE_ON_ERROR, 0444);
@@ -1644,51 +1748,41 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
}
- chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
- chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
- chunk_ids[2] = GRAPH_CHUNKID_DATA;
+ chunks[0].id = GRAPH_CHUNKID_OIDFANOUT;
+ chunks[0].size = GRAPH_FANOUT_SIZE;
+ chunks[0].write_fn = write_graph_chunk_fanout;
+ chunks[1].id = GRAPH_CHUNKID_OIDLOOKUP;
+ chunks[1].size = hashsz * ctx->commits.nr;
+ chunks[1].write_fn = write_graph_chunk_oids;
+ chunks[2].id = GRAPH_CHUNKID_DATA;
+ chunks[2].size = (hashsz + 16) * ctx->commits.nr;
+ chunks[2].write_fn = write_graph_chunk_data;
if (ctx->num_extra_edges) {
- chunk_ids[num_chunks] = GRAPH_CHUNKID_EXTRAEDGES;
+ chunks[num_chunks].id = GRAPH_CHUNKID_EXTRAEDGES;
+ chunks[num_chunks].size = 4 * ctx->num_extra_edges;
+ chunks[num_chunks].write_fn = write_graph_chunk_extra_edges;
num_chunks++;
}
if (ctx->changed_paths) {
- chunk_ids[num_chunks] = GRAPH_CHUNKID_BLOOMINDEXES;
+ chunks[num_chunks].id = GRAPH_CHUNKID_BLOOMINDEXES;
+ chunks[num_chunks].size = sizeof(uint32_t) * ctx->commits.nr;
+ chunks[num_chunks].write_fn = write_graph_chunk_bloom_indexes;
num_chunks++;
- chunk_ids[num_chunks] = GRAPH_CHUNKID_BLOOMDATA;
+ chunks[num_chunks].id = GRAPH_CHUNKID_BLOOMDATA;
+ chunks[num_chunks].size = sizeof(uint32_t) * 3
+ + ctx->total_bloom_filter_data_size;
+ chunks[num_chunks].write_fn = write_graph_chunk_bloom_data;
num_chunks++;
}
if (ctx->num_commit_graphs_after > 1) {
- chunk_ids[num_chunks] = GRAPH_CHUNKID_BASE;
+ chunks[num_chunks].id = GRAPH_CHUNKID_BASE;
+ chunks[num_chunks].size = hashsz * (ctx->num_commit_graphs_after - 1);
+ chunks[num_chunks].write_fn = write_graph_chunk_base;
num_chunks++;
}
- chunk_ids[num_chunks] = 0;
-
- chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
- chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
- chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
- chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
-
- num_chunks = 3;
- if (ctx->num_extra_edges) {
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- 4 * ctx->num_extra_edges;
- num_chunks++;
- }
- if (ctx->changed_paths) {
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- sizeof(uint32_t) * ctx->commits.nr;
- num_chunks++;
-
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- sizeof(uint32_t) * 3 + ctx->total_bloom_filter_data_size;
- num_chunks++;
- }
- if (ctx->num_commit_graphs_after > 1) {
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- hashsz * (ctx->num_commit_graphs_after - 1);
- num_chunks++;
- }
+ chunks[num_chunks].id = 0;
+ chunks[num_chunks].size = 0;
hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1697,13 +1791,16 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
hashwrite_u8(f, num_chunks);
hashwrite_u8(f, ctx->num_commit_graphs_after - 1);
+ chunk_offset = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
for (i = 0; i <= num_chunks; i++) {
uint32_t chunk_write[3];
- chunk_write[0] = htonl(chunk_ids[i]);
- chunk_write[1] = htonl(chunk_offsets[i] >> 32);
- chunk_write[2] = htonl(chunk_offsets[i] & 0xffffffff);
+ chunk_write[0] = htonl(chunks[i].id);
+ chunk_write[1] = htonl(chunk_offset >> 32);
+ chunk_write[2] = htonl(chunk_offset & 0xffffffff);
hashwrite(f, chunk_write, 12);
+
+ chunk_offset += chunks[i].size;
}
if (ctx->report_progress) {
@@ -1716,19 +1813,19 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
progress_title.buf,
num_chunks * ctx->commits.nr);
}
- write_graph_chunk_fanout(f, ctx);
- write_graph_chunk_oids(f, hashsz, ctx);
- write_graph_chunk_data(f, hashsz, ctx);
- if (ctx->num_extra_edges)
- write_graph_chunk_extra_edges(f, ctx);
- if (ctx->changed_paths) {
- write_graph_chunk_bloom_indexes(f, ctx);
- write_graph_chunk_bloom_data(f, ctx, &bloom_settings);
- }
- if (ctx->num_commit_graphs_after > 1 &&
- write_graph_chunk_base(f, ctx)) {
- return -1;
+
+ for (i = 0; i < num_chunks; i++) {
+ uint64_t start_offset = f->total + f->offset;
+
+ if (chunks[i].write_fn(f, ctx))
+ return -1;
+
+ if (f->total + f->offset != start_offset + chunks[i].size)
+ BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
+ chunks[i].size, chunks[i].id,
+ f->total + f->offset - start_offset);
}
+
stop_progress(&ctx->progress);
strbuf_release(&progress_title);
@@ -1809,13 +1906,13 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
int max_commits = 0;
int size_mult = 2;
- if (ctx->split_opts) {
- max_commits = ctx->split_opts->max_commits;
+ if (ctx->opts) {
+ max_commits = ctx->opts->max_commits;
- if (ctx->split_opts->size_multiple)
- size_mult = ctx->split_opts->size_multiple;
+ if (ctx->opts->size_multiple)
+ size_mult = ctx->opts->size_multiple;
- flags = ctx->split_opts->flags;
+ flags = ctx->opts->split_flags;
}
g = ctx->r->objects->commit_graph;
@@ -1993,10 +2090,10 @@ static void expire_commit_graphs(struct write_commit_graph_context *ctx)
size_t dirnamelen;
timestamp_t expire_time = time(NULL);
- if (ctx->split_opts && ctx->split_opts->expire_time)
- expire_time = ctx->split_opts->expire_time;
+ if (ctx->opts && ctx->opts->expire_time)
+ expire_time = ctx->opts->expire_time;
if (!ctx->split) {
- char *chain_file_name = get_chain_filename(ctx->odb);
+ char *chain_file_name = get_commit_graph_chain_filename(ctx->odb);
unlink(chain_file_name);
free(chain_file_name);
ctx->num_commit_graphs_after = 0;
@@ -2045,12 +2142,13 @@ int write_commit_graph(struct object_directory *odb,
struct string_list *pack_indexes,
struct oidset *commits,
enum commit_graph_write_flags flags,
- const struct split_commit_graph_opts *split_opts)
+ const struct commit_graph_opts *opts)
{
struct write_commit_graph_context *ctx;
uint32_t i, count_distinct = 0;
int res = 0;
int replace = 0;
+ struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
if (!commit_graph_compatible(the_repository))
return 0;
@@ -2061,10 +2159,32 @@ int write_commit_graph(struct object_directory *odb,
ctx->append = flags & COMMIT_GRAPH_WRITE_APPEND ? 1 : 0;
ctx->report_progress = flags & COMMIT_GRAPH_WRITE_PROGRESS ? 1 : 0;
ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
- ctx->split_opts = split_opts;
- ctx->changed_paths = flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS ? 1 : 0;
+ ctx->opts = opts;
ctx->total_bloom_filter_data_size = 0;
+ bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY",
+ bloom_settings.bits_per_entry);
+ bloom_settings.num_hashes = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_NUM_HASHES",
+ bloom_settings.num_hashes);
+ bloom_settings.max_changed_paths = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS",
+ bloom_settings.max_changed_paths);
+ ctx->bloom_settings = &bloom_settings;
+
+ if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
+ ctx->changed_paths = 1;
+ if (!(flags & COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS)) {
+ struct commit_graph *g;
+ prepare_commit_graph_one(ctx->r, ctx->odb);
+
+ g = ctx->r->objects->commit_graph;
+
+ /* We have changed-paths already. Keep them in the next graph */
+ if (g && g->chunk_bloom_data) {
+ ctx->changed_paths = 1;
+ ctx->bloom_settings = g->bloom_filter_settings;
+ }
+ }
+
if (ctx->split) {
struct commit_graph *g;
prepare_commit_graph(ctx->r);
@@ -2087,15 +2207,15 @@ int write_commit_graph(struct object_directory *odb,
}
}
- if (ctx->split_opts)
- replace = ctx->split_opts->flags & COMMIT_GRAPH_SPLIT_REPLACE;
+ if (ctx->opts)
+ replace = ctx->opts->split_flags & COMMIT_GRAPH_SPLIT_REPLACE;
}
ctx->approx_nr_objects = approximate_object_count();
ctx->oids.alloc = ctx->approx_nr_objects / 32;
- if (ctx->split && split_opts && ctx->oids.alloc > split_opts->max_commits)
- ctx->oids.alloc = split_opts->max_commits;
+ if (ctx->split && opts && ctx->oids.alloc > opts->max_commits)
+ ctx->oids.alloc = opts->max_commits;
if (ctx->append) {
prepare_commit_graph_one(ctx->r, ctx->odb);
diff --git a/commit-graph.h b/commit-graph.h
index 28f89cdf3e..f8e92500c6 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -2,14 +2,11 @@
#define COMMIT_GRAPH_H
#include "git-compat-util.h"
-#include "repository.h"
-#include "string-list.h"
-#include "cache.h"
#include "object-store.h"
#include "oidset.h"
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
-#define GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD "GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD"
+#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
/*
@@ -23,8 +20,12 @@ void git_test_write_commit_graph_or_die(void);
struct commit;
struct bloom_filter_settings;
+struct repository;
+struct raw_object_store;
+struct string_list;
char *get_commit_graph_filename(struct object_directory *odb);
+char *get_commit_graph_chain_filename(struct object_directory *odb);
int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
/*
@@ -75,11 +76,13 @@ struct commit_graph {
struct bloom_filter_settings *bloom_filter_settings;
};
-struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st,
+struct commit_graph *load_commit_graph_one_fd_st(struct repository *r,
+ int fd, struct stat *st,
struct object_directory *odb);
struct commit_graph *read_commit_graph_one(struct repository *r,
struct object_directory *odb);
-struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size);
+struct commit_graph *parse_commit_graph(struct repository *r,
+ void *graph_map, size_t graph_size);
/*
* Return 1 if and only if the repository has a commit-graph
@@ -87,11 +90,14 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size);
*/
int generation_numbers_enabled(struct repository *r);
+struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r);
+
enum commit_graph_write_flags {
COMMIT_GRAPH_WRITE_APPEND = (1 << 0),
COMMIT_GRAPH_WRITE_PROGRESS = (1 << 1),
COMMIT_GRAPH_WRITE_SPLIT = (1 << 2),
COMMIT_GRAPH_WRITE_BLOOM_FILTERS = (1 << 3),
+ COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS = (1 << 4),
};
enum commit_graph_split_flags {
@@ -100,11 +106,12 @@ enum commit_graph_split_flags {
COMMIT_GRAPH_SPLIT_REPLACE = 2
};
-struct split_commit_graph_opts {
+struct commit_graph_opts {
int size_multiple;
int max_commits;
timestamp_t expire_time;
- enum commit_graph_split_flags flags;
+ enum commit_graph_split_flags split_flags;
+ int max_new_filters;
};
/*
@@ -115,12 +122,12 @@ struct split_commit_graph_opts {
*/
int write_commit_graph_reachable(struct object_directory *odb,
enum commit_graph_write_flags flags,
- const struct split_commit_graph_opts *split_opts);
+ const struct commit_graph_opts *opts);
int write_commit_graph(struct object_directory *odb,
struct string_list *pack_indexes,
struct oidset *commits,
enum commit_graph_write_flags flags,
- const struct split_commit_graph_opts *split_opts);
+ const struct commit_graph_opts *opts);
#define COMMIT_GRAPH_VERIFY_SHALLOW (1 << 0)
diff --git a/commit-slab-decl.h b/commit-slab-decl.h
index bfbed1516a..98de2c970c 100644
--- a/commit-slab-decl.h
+++ b/commit-slab-decl.h
@@ -32,6 +32,7 @@ struct slabname { \
void init_ ##slabname## _with_stride(struct slabname *s, unsigned stride); \
void init_ ##slabname(struct slabname *s); \
void clear_ ##slabname(struct slabname *s); \
+void deep_clear_ ##slabname(struct slabname *s, void (*free_fn)(elemtype *ptr)); \
elemtype *slabname## _at_peek(struct slabname *s, const struct commit *c, int add_if_missing); \
elemtype *slabname## _at(struct slabname *s, const struct commit *c); \
elemtype *slabname## _peek(struct slabname *s, const struct commit *c)
diff --git a/commit-slab-impl.h b/commit-slab-impl.h
index 5c0eb91a5d..557738df27 100644
--- a/commit-slab-impl.h
+++ b/commit-slab-impl.h
@@ -38,6 +38,19 @@ scope void clear_ ##slabname(struct slabname *s) \
FREE_AND_NULL(s->slab); \
} \
\
+scope void deep_clear_ ##slabname(struct slabname *s, void (*free_fn)(elemtype *)) \
+{ \
+ unsigned int i; \
+ for (i = 0; i < s->slab_count; i++) { \
+ unsigned int j; \
+ if (!s->slab[i]) \
+ continue; \
+ for (j = 0; j < s->slab_size; j++) \
+ free_fn(&s->slab[i][j * s->stride]); \
+ } \
+ clear_ ##slabname(s); \
+} \
+ \
scope elemtype *slabname## _at_peek(struct slabname *s, \
const struct commit *c, \
int add_if_missing) \
diff --git a/commit-slab.h b/commit-slab.h
index 05b3f2804e..8e72a30536 100644
--- a/commit-slab.h
+++ b/commit-slab.h
@@ -47,6 +47,16 @@
*
* Call this function before the slab falls out of scope to avoid
* leaking memory.
+ *
+ * - void deep_clear_indegree(struct indegree *, void (*free_fn)(int*))
+ *
+ * Empties the slab, similar to clear_indegree(), but in addition it
+ * calls the given 'free_fn' for each slab entry to release any
+ * additional memory that might be owned by the entry (but not the
+ * entry itself!).
+ * Note that 'free_fn' might be called even for entries for which no
+ * indegree_at() call has been made; in this case 'free_fn' is invoked
+ * with a pointer to a zero-initialized location.
*/
#define define_commit_slab(slabname, elemtype) \
diff --git a/commit.c b/commit.c
index 7128895c3a..f53429c0ac 100644
--- a/commit.c
+++ b/commit.c
@@ -921,7 +921,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
struct commit *ret = NULL;
char *full_refname;
- switch (dwim_ref(refname, strlen(refname), &oid, &full_refname)) {
+ switch (dwim_ref(refname, strlen(refname), &oid, &full_refname, 0)) {
case 0:
die("No such ref: '%s'", refname);
case 1:
@@ -1316,8 +1316,8 @@ int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree,
int result;
append_merge_tag_headers(parents, &tail);
- result = commit_tree_extended(msg, msg_len, tree, parents, ret,
- author, sign_commit, extra);
+ result = commit_tree_extended(msg, msg_len, tree, parents, ret, author,
+ NULL, sign_commit, extra);
free_commit_extra_headers(extra);
return result;
}
@@ -1440,7 +1440,8 @@ N_("Warning: commit message did not conform to UTF-8.\n"
int commit_tree_extended(const char *msg, size_t msg_len,
const struct object_id *tree,
struct commit_list *parents, struct object_id *ret,
- const char *author, const char *sign_commit,
+ const char *author, const char *committer,
+ const char *sign_commit,
struct commit_extra_header *extra)
{
int result;
@@ -1473,7 +1474,9 @@ int commit_tree_extended(const char *msg, size_t msg_len,
if (!author)
author = git_author_info(IDENT_STRICT);
strbuf_addf(&buffer, "author %s\n", author);
- strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
+ if (!committer)
+ committer = git_committer_info(IDENT_STRICT);
+ strbuf_addf(&buffer, "committer %s\n", committer);
if (!encoding_is_utf8)
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
@@ -1630,22 +1633,22 @@ size_t ignore_non_trailer(const char *buf, size_t len)
int run_commit_hook(int editor_is_used, const char *index_file,
const char *name, ...)
{
- struct argv_array hook_env = ARGV_ARRAY_INIT;
+ struct strvec hook_env = STRVEC_INIT;
va_list args;
int ret;
- argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
+ strvec_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=:");
+ strvec_push(&hook_env, "GIT_EDITOR=:");
va_start(args, name);
- ret = run_hook_ve(hook_env.argv,name, args);
+ ret = run_hook_ve(hook_env.v, name, args);
va_end(args);
- argv_array_clear(&hook_env);
+ strvec_clear(&hook_env);
return ret;
}
diff --git a/commit.h b/commit.h
index e901538909..e6f8f7c26f 100644
--- a/commit.h
+++ b/commit.h
@@ -95,7 +95,6 @@ static inline int parse_commit_no_graph(struct commit *commit)
#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
#define parse_commit_internal(item, quiet, use) repo_parse_commit_internal(the_repository, item, quiet, use)
-#define parse_commit_gently(item, quiet) repo_parse_commit_gently(the_repository, item, quiet)
#define parse_commit(item) repo_parse_commit(the_repository, item)
#endif
@@ -270,10 +269,9 @@ int commit_tree(const char *msg, size_t msg_len,
int commit_tree_extended(const char *msg, size_t msg_len,
const struct object_id *tree,
- struct commit_list *parents,
- struct object_id *ret, const char *author,
- const char *sign_commit,
- struct commit_extra_header *);
+ struct commit_list *parents, struct object_id *ret,
+ const char *author, const char *committer,
+ const char *sign_commit, struct commit_extra_header *);
struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
diff --git a/compat/bswap.h b/compat/bswap.h
index e4e25735ce..c0bb744adc 100644
--- a/compat/bswap.h
+++ b/compat/bswap.h
@@ -145,28 +145,6 @@ static inline uint64_t git_bswap64(uint64_t x)
#endif
-/*
- * Performance might be improved if the CPU architecture is OK with
- * unaligned 32-bit loads and a fast ntohl() is available.
- * Otherwise fall back to byte loads and shifts which is portable,
- * and is faster on architectures with memory alignment issues.
- */
-
-#if !defined(NO_UNALIGNED_LOADS) && ( \
- defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64) || \
- defined(__ppc__) || defined(__ppc64__) || \
- defined(__powerpc__) || defined(__powerpc64__) || \
- defined(__s390__) || defined(__s390x__))
-
-#define get_be16(p) ntohs(*(unsigned short *)(p))
-#define get_be32(p) ntohl(*(unsigned int *)(p))
-#define get_be64(p) ntohll(*(uint64_t *)(p))
-#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
-#define put_be64(p, v) do { *(uint64_t *)(p) = htonll(v); } while (0)
-
-#else
-
static inline uint16_t get_be16(const void *ptr)
{
const unsigned char *p = ptr;
@@ -212,6 +190,4 @@ static inline void put_be64(void *ptr, uint64_t value)
p[7] = value >> 0;
}
-#endif
-
#endif /* COMPAT_BSWAP_H */
diff --git a/compat/mingw.c b/compat/mingw.c
index 8ee0b6408e..a00f331230 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -18,8 +18,8 @@ void open_in_gdb(void)
static struct child_process cp = CHILD_PROCESS_INIT;
extern char *_pgmptr;
- argv_array_pushl(&cp.args, "mintty", "gdb", NULL);
- argv_array_pushf(&cp.args, "--pid=%d", getpid());
+ strvec_pushl(&cp.args, "mintty", "gdb", NULL);
+ strvec_pushf(&cp.args, "--pid=%d", getpid());
cp.clean_on_exit = 1;
if (start_command(&cp) < 0)
die_errno("Could not start gdb");
@@ -290,6 +290,9 @@ int mingw_unlink(const char *pathname)
if (xutftowcs_path(wpathname, pathname) < 0)
return -1;
+ if (DeleteFileW(wpathname))
+ return 0;
+
/* read-only files cannot be removed */
_wchmod(wpathname, 0666);
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
diff --git a/compat/terminal.c b/compat/terminal.c
index 35bca03d14..43b73ddc75 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -86,9 +86,9 @@ static void restore_term(void)
if (stty_restore.nr == 0)
return;
- argv_array_push(&cp.args, "stty");
+ strvec_push(&cp.args, "stty");
for (i = 0; i < stty_restore.nr; i++)
- argv_array_push(&cp.args, stty_restore.items[i].string);
+ strvec_push(&cp.args, stty_restore.items[i].string);
run_command(&cp);
string_list_clear(&stty_restore, 0);
return;
@@ -107,25 +107,25 @@ static int disable_bits(DWORD bits)
if (use_stty) {
struct child_process cp = CHILD_PROCESS_INIT;
- argv_array_push(&cp.args, "stty");
+ strvec_push(&cp.args, "stty");
if (bits & ENABLE_LINE_INPUT) {
string_list_append(&stty_restore, "icanon");
- argv_array_push(&cp.args, "-icanon");
+ strvec_push(&cp.args, "-icanon");
}
if (bits & ENABLE_ECHO_INPUT) {
string_list_append(&stty_restore, "echo");
- argv_array_push(&cp.args, "-echo");
+ strvec_push(&cp.args, "-echo");
}
if (bits & ENABLE_PROCESSED_INPUT) {
string_list_append(&stty_restore, "-ignbrk");
string_list_append(&stty_restore, "intr");
string_list_append(&stty_restore, "^c");
- argv_array_push(&cp.args, "ignbrk");
- argv_array_push(&cp.args, "intr");
- argv_array_push(&cp.args, "");
+ strvec_push(&cp.args, "ignbrk");
+ strvec_push(&cp.args, "intr");
+ strvec_push(&cp.args, "");
}
if (run_command(&cp) == 0)
@@ -273,7 +273,7 @@ static int is_known_escape_sequence(const char *sequence)
hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp,
NULL, 0);
- argv_array_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
+ strvec_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
strbuf_setlen(&buf, 0);
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 42292e7c09..51fb083dbb 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -26,8 +26,8 @@ The Steps to Build Git with VS2015 or VS2017 from the command line.
Use ONE of the following forms which should match how you want to
compile git.exe.
- $ ./compat/vcbuild/vcpkg_copy_packages.bat debug
- $ ./compat/vcbuild/vcpkg_copy_packages.bat release
+ $ ./compat/vcbuild/vcpkg_copy_dlls.bat debug
+ $ ./compat/vcbuild/vcpkg_copy_dlls.bat release
3. Build git using MSVC from an SDK bash window using one of the
following commands:
diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl
index 61ad084a7b..df167d1e1a 100755
--- a/compat/vcbuild/scripts/clink.pl
+++ b/compat/vcbuild/scripts/clink.pl
@@ -66,7 +66,7 @@ while (@ARGV) {
}
push(@args, $lib);
} elsif ("$arg" eq "-lexpat") {
- push(@args, "expat.lib");
+ push(@args, "libexpat.lib");
} elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
$arg =~ s/^-L/-LIBPATH:/;
push(@lflags, $arg);
diff --git a/config.c b/config.c
index 8db9c77098..2bdff4457b 100644
--- a/config.c
+++ b/config.c
@@ -2006,18 +2006,27 @@ const struct string_list *git_configset_get_value_multi(struct config_set *cs, c
return e ? &e->value_list : NULL;
}
-int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
{
const char *value;
if (!git_configset_get_value(cs, key, &value))
- return git_config_string(dest, key, value);
+ return git_config_string((const char **)dest, key, value);
else
return 1;
}
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+ const char **dest)
{
- return git_configset_get_string_const(cs, key, (const char **)dest);
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value)) {
+ if (!value)
+ return config_error_nonbool(key);
+ *dest = value;
+ return 0;
+ } else {
+ return 1;
+ }
}
int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
@@ -2147,22 +2156,26 @@ const struct string_list *repo_config_get_value_multi(struct repository *repo,
return git_configset_get_value_multi(repo->config, key);
}
-int repo_config_get_string_const(struct repository *repo,
- const char *key, const char **dest)
+int repo_config_get_string(struct repository *repo,
+ const char *key, char **dest)
{
int ret;
git_config_check_init(repo);
- ret = git_configset_get_string_const(repo->config, key, dest);
+ ret = git_configset_get_string(repo->config, key, dest);
if (ret < 0)
git_die_config(key, NULL);
return ret;
}
-int repo_config_get_string(struct repository *repo,
- const char *key, char **dest)
+int repo_config_get_string_tmp(struct repository *repo,
+ const char *key, const char **dest)
{
+ int ret;
git_config_check_init(repo);
- return repo_config_get_string_const(repo, key, (const char **)dest);
+ ret = git_configset_get_string_tmp(repo->config, key, dest);
+ if (ret < 0)
+ git_die_config(key, NULL);
+ return ret;
}
int repo_config_get_int(struct repository *repo,
@@ -2232,14 +2245,14 @@ const struct string_list *git_config_get_value_multi(const char *key)
return repo_config_get_value_multi(the_repository, key);
}
-int git_config_get_string_const(const char *key, const char **dest)
+int git_config_get_string(const char *key, char **dest)
{
- return repo_config_get_string_const(the_repository, key, dest);
+ return repo_config_get_string(the_repository, key, dest);
}
-int git_config_get_string(const char *key, char **dest)
+int git_config_get_string_tmp(const char *key, const char **dest)
{
- return repo_config_get_string(the_repository, key, dest);
+ return repo_config_get_string_tmp(the_repository, key, dest);
}
int git_config_get_int(const char *key, int *dest)
@@ -2274,7 +2287,7 @@ int git_config_get_pathname(const char *key, const char **dest)
int git_config_get_expiry(const char *key, const char **output)
{
- int ret = git_config_get_string_const(key, output);
+ int ret = git_config_get_string(key, (char **)output);
if (ret)
return ret;
if (strcmp(*output, "now")) {
@@ -2287,11 +2300,11 @@ int git_config_get_expiry(const char *key, const char **output)
int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamp_t now)
{
- char *expiry_string;
+ const char *expiry_string;
intmax_t days;
timestamp_t when;
- if (git_config_get_string(key, &expiry_string))
+ if (git_config_get_string_tmp(key, &expiry_string))
return 1; /* no such thing */
if (git_parse_signed(expiry_string, &days, maximum_signed_value_of_type(int))) {
@@ -3115,7 +3128,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
}
while (fgets(buf, sizeof(buf), config_file)) {
- int i;
+ unsigned i;
int length;
int is_section = 0;
char *output = buf;
diff --git a/config.h b/config.h
index 060874488f..91cdfbfb41 100644
--- a/config.h
+++ b/config.h
@@ -458,8 +458,8 @@ void git_configset_clear(struct config_set *cs);
*/
int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
-int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
+int git_configset_get_string_tmp(struct config_set *cs, const char *key, const char **dest);
int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
@@ -474,10 +474,10 @@ int repo_config_get_value(struct repository *repo,
const char *key, const char **value);
const struct string_list *repo_config_get_value_multi(struct repository *repo,
const char *key);
-int repo_config_get_string_const(struct repository *repo,
- const char *key, const char **dest);
int repo_config_get_string(struct repository *repo,
const char *key, char **dest);
+int repo_config_get_string_tmp(struct repository *repo,
+ const char *key, const char **dest);
int repo_config_get_int(struct repository *repo,
const char *key, int *dest);
int repo_config_get_ulong(struct repository *repo,
@@ -529,13 +529,14 @@ void git_config_clear(void);
* error message and returns -1. When the configuration variable `key` is
* not found, returns 1 without touching `dest`.
*/
-int git_config_get_string_const(const char *key, const char **dest);
+int git_config_get_string(const char *key, char **dest);
/**
- * Similar to `git_config_get_string_const`, except that retrieved value
- * copied into the `dest` parameter is a mutable string.
+ * Similar to `git_config_get_string`, but does not allocate any new
+ * memory; on success `dest` will point to memory owned by the config
+ * machinery, which could be invalidated if it is discarded and reloaded.
*/
-int git_config_get_string(const char *key, char **dest);
+int git_config_get_string_tmp(const char *key, const char **dest);
/**
* Finds and parses the value to an integer for the configuration variable
diff --git a/config.mak.dev b/config.mak.dev
index cd4a82a9eb..89b218d11a 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -16,8 +16,6 @@ DEVELOPER_CFLAGS += -Wstrict-prototypes
DEVELOPER_CFLAGS += -Wunused
DEVELOPER_CFLAGS += -Wvla
-DEVELOPER_CFLAGS += -DENABLE_SHA256
-
ifndef COMPILER_FEATURES
COMPILER_FEATURES := $(shell ./detect-compiler $(CC))
endif
diff --git a/connect.c b/connect.c
index e0d5b9fee0..8b8f56cf6d 100644
--- a/connect.c
+++ b/connect.c
@@ -17,7 +17,7 @@
#include "alias.h"
static char *server_capabilities_v1;
-static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT;
+static struct strvec server_capabilities_v2 = STRVEC_INIT;
static const char *next_server_feature_value(const char *feature, int *len, int *offset);
static int check_ref(const char *name, unsigned int flags)
@@ -70,9 +70,9 @@ int server_supports_v2(const char *c, int die_on_error)
{
int i;
- for (i = 0; i < server_capabilities_v2.argc; i++) {
+ for (i = 0; i < server_capabilities_v2.nr; i++) {
const char *out;
- if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
+ if (skip_prefix(server_capabilities_v2.v[i], c, &out) &&
(!*out || *out == '='))
return 1;
}
@@ -87,9 +87,9 @@ int server_feature_v2(const char *c, const char **v)
{
int i;
- for (i = 0; i < server_capabilities_v2.argc; i++) {
+ for (i = 0; i < server_capabilities_v2.nr; i++) {
const char *out;
- if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
+ if (skip_prefix(server_capabilities_v2.v[i], c, &out) &&
(*out == '=')) {
*v = out + 1;
return 1;
@@ -103,9 +103,9 @@ int server_supports_feature(const char *c, const char *feature,
{
int i;
- for (i = 0; i < server_capabilities_v2.argc; i++) {
+ for (i = 0; i < server_capabilities_v2.nr; i++) {
const char *out;
- if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
+ if (skip_prefix(server_capabilities_v2.v[i], c, &out) &&
(!*out || *(out++) == '=')) {
if (parse_feature_request(out, feature))
return 1;
@@ -123,7 +123,7 @@ int server_supports_feature(const char *c, const char *feature,
static void process_capabilities_v2(struct packet_reader *reader)
{
while (packet_reader_read(reader) == PACKET_READ_NORMAL)
- argv_array_push(&server_capabilities_v2, reader->line);
+ strvec_push(&server_capabilities_v2, reader->line);
if (reader->status != PACKET_READ_FLUSH)
die(_("expected flush after capabilities"));
@@ -453,7 +453,7 @@ void check_stateless_delimiter(int stateless_rpc,
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
struct ref **list, int for_push,
- const struct argv_array *ref_prefixes,
+ const struct strvec *ref_prefixes,
const struct string_list *server_options,
int stateless_rpc)
{
@@ -488,9 +488,9 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
if (!for_push)
packet_write_fmt(fd_out, "peel\n");
packet_write_fmt(fd_out, "symrefs\n");
- for (i = 0; ref_prefixes && i < ref_prefixes->argc; i++) {
+ for (i = 0; ref_prefixes && i < ref_prefixes->nr; i++) {
packet_write_fmt(fd_out, "ref-prefix %s\n",
- ref_prefixes->argv[i]);
+ ref_prefixes->v[i]);
}
packet_flush(fd_out);
@@ -944,9 +944,9 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)
proxy = xmalloc(sizeof(*proxy));
child_process_init(proxy);
- argv_array_push(&proxy->args, git_proxy_command);
- argv_array_push(&proxy->args, host);
- argv_array_push(&proxy->args, port);
+ strvec_push(&proxy->args, git_proxy_command);
+ strvec_push(&proxy->args, host);
+ strvec_push(&proxy->args, port);
proxy->in = -1;
proxy->out = -1;
if (start_command(proxy))
@@ -1052,7 +1052,7 @@ static const char *get_ssh_command(void)
if ((ssh = getenv("GIT_SSH_COMMAND")))
return ssh;
- if (!git_config_get_string_const("core.sshcommand", &ssh))
+ if (!git_config_get_string_tmp("core.sshcommand", &ssh))
return ssh;
return NULL;
@@ -1071,7 +1071,7 @@ static void override_ssh_variant(enum ssh_variant *ssh_variant)
{
const char *variant = getenv("GIT_SSH_VARIANT");
- if (!variant && git_config_get_string_const("ssh.variant", &variant))
+ if (!variant && git_config_get_string_tmp("ssh.variant", &variant))
return;
if (!strcmp(variant, "auto"))
@@ -1199,16 +1199,16 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport,
* Append the appropriate environment variables to `env` and options to
* `args` for running ssh in Git's SSH-tunneled transport.
*/
-static void push_ssh_options(struct argv_array *args, struct argv_array *env,
+static void push_ssh_options(struct strvec *args, struct strvec *env,
enum ssh_variant variant, const char *port,
enum protocol_version version, int flags)
{
if (variant == VARIANT_SSH &&
version > 0) {
- argv_array_push(args, "-o");
- argv_array_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT);
- argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
- version);
+ strvec_push(args, "-o");
+ strvec_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT);
+ strvec_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
+ version);
}
if (flags & CONNECT_IPV4) {
@@ -1221,7 +1221,7 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env,
case VARIANT_PLINK:
case VARIANT_PUTTY:
case VARIANT_TORTOISEPLINK:
- argv_array_push(args, "-4");
+ strvec_push(args, "-4");
}
} else if (flags & CONNECT_IPV6) {
switch (variant) {
@@ -1233,12 +1233,12 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env,
case VARIANT_PLINK:
case VARIANT_PUTTY:
case VARIANT_TORTOISEPLINK:
- argv_array_push(args, "-6");
+ strvec_push(args, "-6");
}
}
if (variant == VARIANT_TORTOISEPLINK)
- argv_array_push(args, "-batch");
+ strvec_push(args, "-batch");
if (port) {
switch (variant) {
@@ -1247,15 +1247,15 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env,
case VARIANT_SIMPLE:
die(_("ssh variant 'simple' does not support setting port"));
case VARIANT_SSH:
- argv_array_push(args, "-p");
+ strvec_push(args, "-p");
break;
case VARIANT_PLINK:
case VARIANT_PUTTY:
case VARIANT_TORTOISEPLINK:
- argv_array_push(args, "-P");
+ strvec_push(args, "-P");
}
- argv_array_push(args, port);
+ strvec_push(args, port);
}
}
@@ -1293,18 +1293,18 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
detect.use_shell = conn->use_shell;
detect.no_stdin = detect.no_stdout = detect.no_stderr = 1;
- argv_array_push(&detect.args, ssh);
- argv_array_push(&detect.args, "-G");
+ strvec_push(&detect.args, ssh);
+ strvec_push(&detect.args, "-G");
push_ssh_options(&detect.args, &detect.env_array,
VARIANT_SSH, port, version, flags);
- argv_array_push(&detect.args, ssh_host);
+ strvec_push(&detect.args, ssh_host);
variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH;
}
- argv_array_push(&conn->args, ssh);
+ strvec_push(&conn->args, ssh);
push_ssh_options(&conn->args, &conn->env_array, variant, port, version, flags);
- argv_array_push(&conn->args, ssh_host);
+ strvec_push(&conn->args, ssh_host);
}
/*
@@ -1365,7 +1365,7 @@ struct child_process *git_connect(int fd[2], const char *url,
/* remove repo-local variables from the environment */
for (var = local_repo_env; *var; var++)
- argv_array_push(&conn->env_array, *var);
+ strvec_push(&conn->env_array, *var);
conn->use_shell = 1;
conn->in = conn->out = -1;
@@ -1397,11 +1397,12 @@ struct child_process *git_connect(int fd[2], const char *url,
transport_check_allowed("file");
conn->trace2_child_class = "transport/file";
if (version > 0) {
- argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d",
- version);
+ strvec_pushf(&conn->env_array,
+ GIT_PROTOCOL_ENVIRONMENT "=version=%d",
+ version);
}
}
- argv_array_push(&conn->args, cmd.buf);
+ strvec_push(&conn->args, cmd.buf);
if (start_command(conn))
die(_("unable to fork"));
diff --git a/connected.c b/connected.c
index 937b4bae38..b18299fdf0 100644
--- a/connected.c
+++ b/connected.c
@@ -22,14 +22,13 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
struct check_connected_options *opt)
{
struct child_process rev_list = CHILD_PROCESS_INIT;
+ FILE *rev_list_in;
struct check_connected_options defaults = CHECK_CONNECTED_INIT;
- char commit[GIT_MAX_HEXSZ + 1];
struct object_id oid;
int err = 0;
struct packed_git *new_pack = NULL;
struct transport *transport;
size_t base_len;
- const unsigned hexsz = the_hash_algo->hexsz;
if (!opt)
opt = &defaults;
@@ -90,23 +89,23 @@ promisor_pack_found:
no_promisor_pack_found:
if (opt->shallow_file) {
- argv_array_push(&rev_list.args, "--shallow-file");
- argv_array_push(&rev_list.args, opt->shallow_file);
+ strvec_push(&rev_list.args, "--shallow-file");
+ strvec_push(&rev_list.args, opt->shallow_file);
}
- argv_array_push(&rev_list.args,"rev-list");
- argv_array_push(&rev_list.args, "--objects");
- argv_array_push(&rev_list.args, "--stdin");
+ strvec_push(&rev_list.args,"rev-list");
+ strvec_push(&rev_list.args, "--objects");
+ strvec_push(&rev_list.args, "--stdin");
if (has_promisor_remote())
- argv_array_push(&rev_list.args, "--exclude-promisor-objects");
+ strvec_push(&rev_list.args, "--exclude-promisor-objects");
if (!opt->is_deepening_fetch) {
- argv_array_push(&rev_list.args, "--not");
- argv_array_push(&rev_list.args, "--all");
+ strvec_push(&rev_list.args, "--not");
+ strvec_push(&rev_list.args, "--all");
}
- argv_array_push(&rev_list.args, "--quiet");
- argv_array_push(&rev_list.args, "--alternate-refs");
+ strvec_push(&rev_list.args, "--quiet");
+ strvec_push(&rev_list.args, "--alternate-refs");
if (opt->progress)
- argv_array_pushf(&rev_list.args, "--progress=%s",
- _("Checking connectivity"));
+ strvec_pushf(&rev_list.args, "--progress=%s",
+ _("Checking connectivity"));
rev_list.git_cmd = 1;
rev_list.env = opt->env;
@@ -122,7 +121,8 @@ no_promisor_pack_found:
sigchain_push(SIGPIPE, SIG_IGN);
- commit[hexsz] = '\n';
+ rev_list_in = xfdopen(rev_list.in, "w");
+
do {
/*
* If index-pack already checked that:
@@ -135,16 +135,17 @@ no_promisor_pack_found:
if (new_pack && find_pack_entry_one(oid.hash, new_pack))
continue;
- 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;
+ if (fprintf(rev_list_in, "%s\n", oid_to_hex(&oid)) < 0)
break;
- }
} while (!fn(cb_data, &oid));
- if (close(rev_list.in))
+ if (ferror(rev_list_in) || fflush(rev_list_in)) {
+ if (errno != EPIPE && errno != EINVAL)
+ error_errno(_("failed write to rev-list"));
+ err = -1;
+ }
+
+ if (fclose(rev_list_in))
err = error_errno(_("failed to close rev-list's stdin"));
sigchain_pop(SIGPIPE);
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
new file mode 100644
index 0000000000..5007f173f1
--- /dev/null
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -0,0 +1,960 @@
+#
+# Copyright (c) 2020 Sibi Siddharthan
+#
+
+#[[
+
+Instructions to run CMake:
+
+cmake `relative-path-to-CMakeLists.txt` -DCMAKE_BUILD_TYPE=Release
+Eg.
+From the root of git source tree
+ `cmake contrib/buildsystems/ `
+This will build the git binaries at the root
+
+For out of source builds, say build in 'git/git-build/'
+ `mkdir git-build;cd git-build; cmake ../contrib/buildsystems/`
+This will build the git binaries in git-build directory
+
+Possible build configurations(-DCMAKE_BUILD_TYPE) with corresponding
+compiler flags
+Debug : -g
+Release: -O3
+RelWithDebInfo : -O2 -g
+MinSizeRel : -Os
+empty(default) :
+
+NOTE: -DCMAKE_BUILD_TYPE is optional. For multi-config generators like Visual Studio
+this option is ignored
+
+This process generates a Makefile(Linux/*BSD/MacOS) , Visual Studio solution(Windows) by default.
+Run `make` to build Git on Linux/*BSD/MacOS.
+Open git.sln on Windows and build Git.
+
+NOTE: By default CMake uses Makefile as the build tool on Linux and Visual Studio in Windows,
+to use another tool say `ninja` add this to the command line when configuring.
+`-G Ninja`
+
+]]
+cmake_minimum_required(VERSION 3.14)
+
+#set the source directory to root of git
+set(CMAKE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
+
+find_program(SH_EXE sh)
+if(NOT SH_EXE)
+ message(FATAL_ERROR "sh: shell interpreter was not found in your path, please install one."
+ "On Windows, you can get it as part of 'Git for Windows' install at https://gitforwindows.org/")
+endif()
+
+#Create GIT-VERSION-FILE using GIT-VERSION-GEN
+if(NOT EXISTS ${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE)
+ message("Generating GIT-VERSION-FILE")
+ execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/GIT-VERSION-GEN
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+endif()
+
+#Parse GIT-VERSION-FILE to get the version
+file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-VERSION-FILE git_version REGEX "GIT_VERSION = (.*)")
+string(REPLACE "GIT_VERSION = " "" git_version ${git_version})
+string(FIND ${git_version} "GIT" location)
+if(location EQUAL -1)
+ string(REGEX MATCH "[0-9]*\\.[0-9]*\\.[0-9]*" git_version ${git_version})
+else()
+ string(REGEX MATCH "[0-9]*\\.[0-9]*" git_version ${git_version})
+ string(APPEND git_version ".0") #for building from a snapshot
+endif()
+
+project(git
+ VERSION ${git_version}
+ LANGUAGES C)
+
+
+#TODO gitk git-gui gitweb
+#TODO Enable NLS on windows natively
+#TODO Add pcre support
+
+#macros for parsing the Makefile for sources and scripts
+macro(parse_makefile_for_sources list_var regex)
+ file(STRINGS ${CMAKE_SOURCE_DIR}/Makefile ${list_var} REGEX "^${regex} \\+=(.*)")
+ string(REPLACE "${regex} +=" "" ${list_var} ${${list_var}})
+ string(REPLACE "$(COMPAT_OBJS)" "" ${list_var} ${${list_var}}) #remove "$(COMPAT_OBJS)" This is only for libgit.
+ string(STRIP ${${list_var}} ${list_var}) #remove trailing/leading whitespaces
+ string(REPLACE ".o" ".c;" ${list_var} ${${list_var}}) #change .o to .c, ; is for converting the string into a list
+ list(TRANSFORM ${list_var} STRIP) #remove trailing/leading whitespaces for each element in list
+ list(REMOVE_ITEM ${list_var} "") #remove empty list elements
+endmacro()
+
+macro(parse_makefile_for_scripts list_var regex lang)
+ file(STRINGS ${CMAKE_SOURCE_DIR}/Makefile ${list_var} REGEX "^${regex} \\+=(.*)")
+ string(REPLACE "${regex} +=" "" ${list_var} ${${list_var}})
+ string(STRIP ${${list_var}} ${list_var}) #remove trailing/leading whitespaces
+ string(REPLACE " " ";" ${list_var} ${${list_var}}) #convert string to a list
+ if(NOT ${lang}) #exclude for SCRIPT_LIB
+ list(TRANSFORM ${list_var} REPLACE "${lang}" "") #do the replacement
+ endif()
+endmacro()
+
+include(CheckTypeSize)
+include(CheckCSourceRuns)
+include(CheckCSourceCompiles)
+include(CheckIncludeFile)
+include(CheckFunctionExists)
+include(CheckSymbolExists)
+include(CheckStructHasMember)
+include(CTest)
+
+find_package(ZLIB REQUIRED)
+find_package(CURL)
+find_package(EXPAT)
+find_package(Iconv)
+
+#Don't use libintl on Windows Visual Studio and Clang builds
+if(NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "MSVC" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")))
+ find_package(Intl)
+endif()
+
+if(NOT Intl_FOUND)
+ add_compile_definitions(NO_GETTEXT)
+ if(NOT Iconv_FOUND)
+ add_compile_definitions(NO_ICONV)
+ endif()
+endif()
+
+include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS})
+if(CURL_FOUND)
+ include_directories(SYSTEM ${CURL_INCLUDE_DIRS})
+endif()
+if(EXPAT_FOUND)
+ include_directories(SYSTEM ${EXPAT_INCLUDE_DIRS})
+endif()
+if(Iconv_FOUND)
+ include_directories(SYSTEM ${Iconv_INCLUDE_DIRS})
+endif()
+if(Intl_FOUND)
+ include_directories(SYSTEM ${Intl_INCLUDE_DIRS})
+endif()
+
+
+if(WIN32 AND NOT MSVC)#not required for visual studio builds
+ find_program(WINDRES_EXE windres)
+ if(NOT WINDRES_EXE)
+ message(FATAL_ERROR "Install windres on Windows for resource files")
+ endif()
+endif()
+
+find_program(MSGFMT_EXE msgfmt)
+if(NOT MSGFMT_EXE)
+ message(WARNING "Text Translations won't be build")
+endif()
+
+#Force all visual studio outputs to CMAKE_BINARY_DIR
+if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR})
+ add_compile_options(/MP)
+endif()
+
+#default behaviour
+include_directories(${CMAKE_SOURCE_DIR})
+add_compile_definitions(GIT_HOST_CPU="${CMAKE_SYSTEM_PROCESSOR}")
+add_compile_definitions(SHA256_BLK INTERNAL_QSORT RUNTIME_PREFIX)
+add_compile_definitions(NO_OPENSSL SHA1_DC SHA1DC_NO_STANDARD_INCLUDES
+ SHA1DC_INIT_SAFE_HASH_DEFAULT=0
+ SHA1DC_CUSTOM_INCLUDE_SHA1_C="cache.h"
+ SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="git-compat-util.h" )
+list(APPEND compat_SOURCES sha1dc_git.c sha1dc/sha1.c sha1dc/ubc_check.c block-sha1/sha1.c sha256/block/sha256.c compat/qsort_s.c)
+
+
+add_compile_definitions(PAGER_ENV="LESS=FRX LV=-c"
+ ETC_GITATTRIBUTES="etc/gitattributes"
+ ETC_GITCONFIG="etc/gitconfig"
+ GIT_EXEC_PATH="libexec/git-core"
+ GIT_LOCALE_PATH="share/locale"
+ GIT_MAN_PATH="share/man"
+ GIT_INFO_PATH="share/info"
+ GIT_HTML_PATH="share/doc/git-doc"
+ DEFAULT_HELP_FORMAT="html"
+ DEFAULT_GIT_TEMPLATE_DIR="share/git-core/templates"
+ GIT_VERSION="${PROJECT_VERSION}.GIT"
+ GIT_USER_AGENT="git/${PROJECT_VERSION}.GIT"
+ BINDIR="bin"
+ GIT_BUILT_FROM_COMMIT="")
+
+if(WIN32)
+ set(FALLBACK_RUNTIME_PREFIX /mingw64)
+ add_compile_definitions(FALLBACK_RUNTIME_PREFIX="${FALLBACK_RUNTIME_PREFIX}")
+else()
+ set(FALLBACK_RUNTIME_PREFIX /home/$ENV{USER})
+ add_compile_definitions(FALLBACK_RUNTIME_PREFIX="${FALLBACK_RUNTIME_PREFIX}")
+endif()
+
+
+#Platform Specific
+if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ include_directories(${CMAKE_SOURCE_DIR}/compat/vcbuild/include)
+ add_compile_definitions(_CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
+ endif()
+ include_directories(${CMAKE_SOURCE_DIR}/compat/win32)
+ add_compile_definitions(HAVE_ALLOCA_H NO_POSIX_GOODIES NATIVE_CRLF NO_UNIX_SOCKETS WIN32
+ _CONSOLE DETECT_MSYS_TTY STRIP_EXTENSION=".exe" NO_SYMLINK_HEAD UNRELIABLE_FSTAT
+ NOGDI OBJECT_CREATION_MODE=1 __USE_MINGW_ANSI_STDIO=0
+ USE_NED_ALLOCATOR OVERRIDE_STRDUP MMAP_PREVENTS_DELETE USE_WIN32_MMAP
+ UNICODE _UNICODE HAVE_WPGMPTR ENSURE_MSYSTEM_IS_SET)
+ list(APPEND compat_SOURCES compat/mingw.c compat/winansi.c compat/win32/path-utils.c
+ compat/win32/pthread.c compat/win32mmap.c compat/win32/syslog.c
+ compat/win32/trace2_win32_process_info.c compat/win32/dirent.c
+ compat/nedmalloc/nedmalloc.c compat/strdup.c)
+ set(NO_UNIX_SOCKETS 1)
+
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ add_compile_definitions(PROCFS_EXECUTABLE_PATH="/proc/self/exe" HAVE_DEV_TTY )
+ list(APPEND compat_SOURCES unix-socket.c)
+endif()
+
+set(EXE_EXTENSION ${CMAKE_EXECUTABLE_SUFFIX})
+
+#header checks
+check_include_file(libgen.h HAVE_LIBGEN_H)
+if(NOT HAVE_LIBGEN_H)
+ add_compile_definitions(NO_LIBGEN_H)
+ list(APPEND compat_SOURCES compat/basename.c)
+endif()
+
+check_include_file(sys/sysinfo.h HAVE_SYSINFO)
+if(HAVE_SYSINFO)
+ add_compile_definitions(HAVE_SYSINFO)
+endif()
+
+check_c_source_compiles("
+#include <alloca.h>
+
+int main(void)
+{
+ char *p = (char *) alloca(2 * sizeof(int));
+
+ if (p)
+ return 0;
+ return 0;
+}"
+HAVE_ALLOCA_H)
+if(HAVE_ALLOCA_H)
+ add_compile_definitions(HAVE_ALLOCA_H)
+endif()
+
+check_include_file(strings.h HAVE_STRINGS_H)
+if(HAVE_STRINGS_H)
+ add_compile_definitions(HAVE_STRINGS_H)
+endif()
+
+check_include_file(sys/select.h HAVE_SYS_SELECT_H)
+if(NOT HAVE_SYS_SELECT_H)
+ add_compile_definitions(NO_SYS_SELECT_H)
+endif()
+
+check_include_file(sys/poll.h HAVE_SYS_POLL_H)
+if(NOT HAVE_SYS_POLL_H)
+ add_compile_definitions(NO_SYS_POLL_H)
+endif()
+
+check_include_file(poll.h HAVE_POLL_H)
+if(NOT HAVE_POLL_H)
+ add_compile_definitions(NO_POLL_H)
+endif()
+
+check_include_file(inttypes.h HAVE_INTTYPES_H)
+if(NOT HAVE_INTTYPES_H)
+ add_compile_definitions(NO_INTTYPES_H)
+endif()
+
+check_include_file(paths.h HAVE_PATHS_H)
+if(HAVE_PATHS_H)
+ add_compile_definitions(HAVE_PATHS_H)
+endif()
+
+#function checks
+set(function_checks
+ strcasestr memmem strlcpy strtoimax strtoumax strtoull
+ setenv mkdtemp poll pread memmem)
+
+#unsetenv,hstrerror are incompatible with windows build
+if(NOT WIN32)
+ list(APPEND function_checks unsetenv hstrerror)
+endif()
+
+foreach(f ${function_checks})
+ string(TOUPPER ${f} uf)
+ check_function_exists(${f} HAVE_${uf})
+ if(NOT HAVE_${uf})
+ add_compile_definitions(NO_${uf})
+ endif()
+endforeach()
+
+if(NOT HAVE_POLL_H OR NOT HAVE_SYS_POLL_H OR NOT HAVE_POLL)
+ include_directories(${CMAKE_SOURCE_DIR}/compat/poll)
+ add_compile_definitions(NO_POLL)
+ list(APPEND compat_SOURCES compat/poll/poll.c)
+endif()
+
+if(NOT HAVE_STRCASESTR)
+ list(APPEND compat_SOURCES compat/strcasestr.c)
+endif()
+
+if(NOT HAVE_STRLCPY)
+ list(APPEND compat_SOURCES compat/strlcpy.c)
+endif()
+
+if(NOT HAVE_STRTOUMAX)
+ list(APPEND compat_SOURCES compat/strtoumax.c compat/strtoimax.c)
+endif()
+
+if(NOT HAVE_SETENV)
+ list(APPEND compat_SOURCES compat/setenv.c)
+endif()
+
+if(NOT HAVE_MKDTEMP)
+ list(APPEND compat_SOURCES compat/mkdtemp.c)
+endif()
+
+if(NOT HAVE_PREAD)
+ list(APPEND compat_SOURCES compat/pread.c)
+endif()
+
+if(NOT HAVE_MEMMEM)
+ list(APPEND compat_SOURCES compat/memmem.c)
+endif()
+
+if(NOT WIN32)
+ if(NOT HAVE_UNSETENV)
+ list(APPEND compat_SOURCES compat/unsetenv.c)
+ endif()
+
+ if(NOT HAVE_HSTRERROR)
+ list(APPEND compat_SOURCES compat/hstrerror.c)
+ endif()
+endif()
+
+check_function_exists(getdelim HAVE_GETDELIM)
+if(HAVE_GETDELIM)
+ add_compile_definitions(HAVE_GETDELIM)
+endif()
+
+check_function_exists(clock_gettime HAVE_CLOCK_GETTIME)
+check_symbol_exists(CLOCK_MONOTONIC "time.h" HAVE_CLOCK_MONOTONIC)
+if(HAVE_CLOCK_GETTIME)
+ add_compile_definitions(HAVE_CLOCK_GETTIME)
+endif()
+if(HAVE_CLOCK_MONOTONIC)
+ add_compile_definitions(HAVE_CLOCK_MONOTONIC)
+endif()
+
+#check for st_blocks in struct stat
+check_struct_has_member("struct stat" st_blocks "sys/stat.h" STRUCT_STAT_HAS_ST_BLOCKS)
+if(NOT STRUCT_STAT_HAS_ST_BLOCKS)
+ add_compile_definitions(NO_ST_BLOCKS_IN_STRUCT_STAT)
+endif()
+
+#compile checks
+check_c_source_runs("
+#include<stdio.h>
+#include<stdarg.h>
+#include<string.h>
+#include<stdlib.h>
+
+int test_vsnprintf(char *str, size_t maxsize, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = vsnprintf(str, maxsize, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+int main(void)
+{
+ char buf[6];
+
+ if (test_vsnprintf(buf, 3, \"%s\", \"12345\") != 5
+ || strcmp(buf, \"12\"))
+ return 1;
+ if (snprintf(buf, 3, \"%s\", \"12345\") != 5
+ || strcmp(buf, \"12\"))
+ return 1;
+ return 0;
+}"
+SNPRINTF_OK)
+if(NOT SNPRINTF_OK)
+ add_compile_definitions(SNPRINTF_RETURNS_BOGUS)
+ list(APPEND compat_SOURCES compat/snprintf.c)
+endif()
+
+check_c_source_runs("
+#include<stdio.h>
+
+int main(void)
+{
+ FILE *f = fopen(\".\", \"r\");
+
+ return f != NULL;
+}"
+FREAD_READS_DIRECTORIES_NO)
+if(NOT FREAD_READS_DIRECTORIES_NO)
+ add_compile_definitions(FREAD_READS_DIRECTORIES)
+ list(APPEND compat_SOURCES compat/fopen.c)
+endif()
+
+check_c_source_compiles("
+#include <regex.h>
+#ifndef REG_STARTEND
+#error oops we dont have it
+#endif
+
+int main(void)
+{
+ return 0;
+}"
+HAVE_REGEX)
+if(NOT HAVE_REGEX)
+ include_directories(${CMAKE_SOURCE_DIR}/compat/regex)
+ list(APPEND compat_SOURCES compat/regex/regex.c )
+ add_compile_definitions(NO_REGEX NO_MBSUPPORT GAWK)
+endif()
+
+
+check_c_source_compiles("
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+int main(void)
+{
+ int val, mib[2];
+ size_t len;
+
+ mib[0] = CTL_HW;
+ mib[1] = 1;
+ len = sizeof(val);
+ return sysctl(mib, 2, &val, &len, NULL, 0) ? 1 : 0;
+}"
+HAVE_BSD_SYSCTL)
+if(HAVE_BSD_SYSCTL)
+ add_compile_definitions(HAVE_BSD_SYSCTL)
+endif()
+
+set(CMAKE_REQUIRED_LIBRARIES ${Iconv_LIBRARIES})
+set(CMAKE_REQUIRED_INCLUDES ${Iconv_INCLUDE_DIRS})
+
+check_c_source_compiles("
+#include <iconv.h>
+
+extern size_t iconv(iconv_t cd,
+ char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+
+int main(void)
+{
+ return 0;
+}"
+HAVE_NEW_ICONV)
+if(HAVE_NEW_ICONV)
+ set(HAVE_OLD_ICONV 0)
+else()
+ set(HAVE_OLD_ICONV 1)
+endif()
+
+check_c_source_runs("
+#include <iconv.h>
+#if ${HAVE_OLD_ICONV}
+typedef const char *iconv_ibp;
+#else
+typedef char *iconv_ibp;
+#endif
+
+int main(void)
+{
+ int v;
+ iconv_t conv;
+ char in[] = \"a\";
+ iconv_ibp pin = in;
+ char out[20] = \"\";
+ char *pout = out;
+ size_t isz = sizeof(in);
+ size_t osz = sizeof(out);
+
+ conv = iconv_open(\"UTF-16\", \"UTF-8\");
+ iconv(conv, &pin, &isz, &pout, &osz);
+ iconv_close(conv);
+ v = (unsigned char)(out[0]) + (unsigned char)(out[1]);
+ return v != 0xfe + 0xff;
+}"
+ICONV_DOESNOT_OMIT_BOM)
+if(NOT ICONV_DOESNOT_OMIT_BOM)
+ add_compile_definitions(ICONV_OMITS_BOM)
+endif()
+
+unset(CMAKE_REQUIRED_LIBRARIES)
+unset(CMAKE_REQUIRED_INCLUDES)
+
+
+#programs
+set(PROGRAMS_BUILT
+ git git-daemon git-http-backend git-sh-i18n--envsubst
+ git-shell)
+
+if(NOT CURL_FOUND)
+ list(APPEND excluded_progs git-http-fetch git-http-push)
+ add_compile_definitions(NO_CURL)
+ message(WARNING "git-http-push and git-http-fetch will not be built")
+else()
+ list(APPEND PROGRAMS_BUILT git-http-fetch git-http-push git-imap-send git-remote-http)
+ if(CURL_VERSION_STRING VERSION_GREATER_EQUAL 7.34.0)
+ add_compile_definitions(USE_CURL_FOR_IMAP_SEND)
+ endif()
+endif()
+
+if(NOT EXPAT_FOUND)
+ list(APPEND excluded_progs git-http-push)
+ add_compile_definitions(NO_EXPAT)
+else()
+ list(APPEND PROGRAMS_BUILT git-http-push)
+ if(EXPAT_VERSION_STRING VERSION_LESS_EQUAL 1.2)
+ add_compile_definitions(EXPAT_NEEDS_XMLPARSE_H)
+ endif()
+endif()
+
+list(REMOVE_DUPLICATES excluded_progs)
+list(REMOVE_DUPLICATES PROGRAMS_BUILT)
+
+
+foreach(p ${excluded_progs})
+ list(APPEND EXCLUSION_PROGS --exclude-program ${p} )
+endforeach()
+
+#for comparing null values
+list(APPEND EXCLUSION_PROGS empty)
+set(EXCLUSION_PROGS_CACHE ${EXCLUSION_PROGS} CACHE STRING "Programs not built" FORCE)
+
+if(NOT EXISTS ${CMAKE_BINARY_DIR}/command-list.h OR NOT EXCLUSION_PROGS_CACHE STREQUAL EXCLUSION_PROGS)
+ list(REMOVE_ITEM EXCLUSION_PROGS empty)
+ message("Generating command-list.h")
+ execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/generate-cmdlist.sh ${EXCLUSION_PROGS} command-list.txt
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_FILE ${CMAKE_BINARY_DIR}/command-list.h)
+endif()
+
+if(NOT EXISTS ${CMAKE_BINARY_DIR}/config-list.h)
+ message("Generating config-list.h")
+ execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/generate-configlist.sh
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_FILE ${CMAKE_BINARY_DIR}/config-list.h)
+endif()
+
+include_directories(${CMAKE_BINARY_DIR})
+
+#build
+#libgit
+parse_makefile_for_sources(libgit_SOURCES "LIB_OBJS")
+
+list(TRANSFORM libgit_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
+list(TRANSFORM compat_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
+add_library(libgit ${libgit_SOURCES} ${compat_SOURCES})
+
+#libxdiff
+parse_makefile_for_sources(libxdiff_SOURCES "XDIFF_OBJS")
+
+list(TRANSFORM libxdiff_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
+add_library(xdiff STATIC ${libxdiff_SOURCES})
+
+if(WIN32)
+ if(NOT MSVC)#use windres when compiling with gcc and clang
+ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/git.res
+ COMMAND ${WINDRES_EXE} -O coff -DMAJOR=${PROJECT_VERSION_MAJOR} -DMINOR=${PROJECT_VERSION_MINOR}
+ -DMICRO=${PROJECT_VERSION_PATCH} -DPATCHLEVEL=0 -DGIT_VERSION="\\\"${PROJECT_VERSION}.GIT\\\""
+ -i ${CMAKE_SOURCE_DIR}/git.rc -o ${CMAKE_BINARY_DIR}/git.res
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ VERBATIM)
+ else()#MSVC use rc
+ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/git.res
+ COMMAND ${CMAKE_RC_COMPILER} /d MAJOR=${PROJECT_VERSION_MAJOR} /d MINOR=${PROJECT_VERSION_MINOR}
+ /d MICRO=${PROJECT_VERSION_PATCH} /d PATCHLEVEL=0 /d GIT_VERSION="${PROJECT_VERSION}.GIT"
+ /fo ${CMAKE_BINARY_DIR}/git.res ${CMAKE_SOURCE_DIR}/git.rc
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ VERBATIM)
+ endif()
+ add_custom_target(git-rc DEPENDS ${CMAKE_BINARY_DIR}/git.res)
+endif()
+
+#link all required libraries to common-main
+add_library(common-main OBJECT ${CMAKE_SOURCE_DIR}/common-main.c)
+
+target_link_libraries(common-main libgit xdiff ${ZLIB_LIBRARIES})
+if(Intl_FOUND)
+ target_link_libraries(common-main ${Intl_LIBRARIES})
+endif()
+if(Iconv_FOUND)
+ target_link_libraries(common-main ${Iconv_LIBRARIES})
+endif()
+if(WIN32)
+ target_link_libraries(common-main ws2_32 ntdll ${CMAKE_BINARY_DIR}/git.res)
+ add_dependencies(common-main git-rc)
+ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ target_link_options(common-main PUBLIC -municode -Wl,--nxcompat -Wl,--dynamicbase -Wl,--pic-executable,-e,mainCRTStartup)
+ elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ target_link_options(common-main PUBLIC -municode -Wl,-nxcompat -Wl,-dynamicbase -Wl,-entry:wmainCRTStartup -Wl,invalidcontinue.obj)
+ elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ target_link_options(common-main PUBLIC /IGNORE:4217 /IGNORE:4049 /NOLOGO /ENTRY:wmainCRTStartup /SUBSYSTEM:CONSOLE invalidcontinue.obj)
+ endif()
+elseif(UNIX)
+ target_link_libraries(common-main pthread rt)
+endif()
+
+#git
+parse_makefile_for_sources(git_SOURCES "BUILTIN_OBJS")
+
+list(TRANSFORM git_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
+add_executable(git ${CMAKE_SOURCE_DIR}/git.c ${git_SOURCES})
+target_link_libraries(git common-main)
+
+add_executable(git-daemon ${CMAKE_SOURCE_DIR}/daemon.c)
+target_link_libraries(git-daemon common-main)
+
+add_executable(git-http-backend ${CMAKE_SOURCE_DIR}/http-backend.c)
+target_link_libraries(git-http-backend common-main)
+
+add_executable(git-sh-i18n--envsubst ${CMAKE_SOURCE_DIR}/sh-i18n--envsubst.c)
+target_link_libraries(git-sh-i18n--envsubst common-main)
+
+add_executable(git-shell ${CMAKE_SOURCE_DIR}/shell.c)
+target_link_libraries(git-shell common-main)
+
+if(CURL_FOUND)
+ add_library(http_obj OBJECT ${CMAKE_SOURCE_DIR}/http.c)
+
+ add_executable(git-imap-send ${CMAKE_SOURCE_DIR}/imap-send.c)
+ target_link_libraries(git-imap-send http_obj common-main ${CURL_LIBRARIES})
+
+ add_executable(git-http-fetch ${CMAKE_SOURCE_DIR}/http-walker.c ${CMAKE_SOURCE_DIR}/http-fetch.c)
+ target_link_libraries(git-http-fetch http_obj common-main ${CURL_LIBRARIES})
+
+ add_executable(git-remote-http ${CMAKE_SOURCE_DIR}/http-walker.c ${CMAKE_SOURCE_DIR}/remote-curl.c)
+ target_link_libraries(git-remote-http http_obj common-main ${CURL_LIBRARIES} )
+
+ if(EXPAT_FOUND)
+ add_executable(git-http-push ${CMAKE_SOURCE_DIR}/http-push.c)
+ target_link_libraries(git-http-push http_obj common-main ${CURL_LIBRARIES} ${EXPAT_LIBRARIES})
+ endif()
+endif()
+
+set(git_builtin_extra
+ cherry cherry-pick format-patch fsck-objects
+ init merge-subtree restore show
+ stage status switch whatchanged)
+
+#Creating hardlinks
+foreach(s ${git_SOURCES} ${git_builtin_extra})
+ string(REPLACE "${CMAKE_SOURCE_DIR}/builtin/" "" s ${s})
+ string(REPLACE ".c" "" s ${s})
+ file(APPEND ${CMAKE_BINARY_DIR}/CreateLinks.cmake "file(CREATE_LINK git${EXE_EXTENSION} git-${s}${EXE_EXTENSION})\n")
+ list(APPEND git_links ${CMAKE_BINARY_DIR}/git-${s}${EXE_EXTENSION})
+endforeach()
+
+if(CURL_FOUND)
+ set(remote_exes
+ git-remote-https git-remote-ftp git-remote-ftps)
+ foreach(s ${remote_exes})
+ file(APPEND ${CMAKE_BINARY_DIR}/CreateLinks.cmake "file(CREATE_LINK git-remote-http${EXE_EXTENSION} ${s}${EXE_EXTENSION})\n")
+ list(APPEND git_http_links ${CMAKE_BINARY_DIR}/${s}${EXE_EXTENSION})
+ endforeach()
+endif()
+
+add_custom_command(OUTPUT ${git_links} ${git_http_links}
+ COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/CreateLinks.cmake
+ DEPENDS git git-remote-http)
+add_custom_target(git-links ALL DEPENDS ${git_links} ${git_http_links})
+
+
+#creating required scripts
+set(SHELL_PATH /bin/sh)
+set(PERL_PATH /usr/bin/perl)
+set(LOCALEDIR ${FALLBACK_RUNTIME_PREFIX}/share/locale)
+set(GITWEBDIR ${FALLBACK_RUNTIME_PREFIX}/share/locale)
+set(INSTLIBDIR ${FALLBACK_RUNTIME_PREFIX}/share/perl5)
+
+#shell scripts
+parse_makefile_for_scripts(git_sh_scripts "SCRIPT_SH" ".sh")
+parse_makefile_for_scripts(git_shlib_scripts "SCRIPT_LIB" "")
+set(git_shell_scripts
+ ${git_sh_scripts} ${git_shlib_scripts} git-instaweb)
+
+foreach(script ${git_shell_scripts})
+ file(STRINGS ${CMAKE_SOURCE_DIR}/${script}.sh content NEWLINE_CONSUME)
+ string(REPLACE "@SHELL_PATH@" "${SHELL_PATH}" content "${content}")
+ string(REPLACE "@@DIFF@@" "diff" content "${content}")
+ string(REPLACE "@LOCALEDIR@" "${LOCALEDIR}" content "${content}")
+ string(REPLACE "@GITWEBDIR@" "${GITWEBDIR}" content "${content}")
+ string(REPLACE "@@NO_CURL@@" "" content "${content}")
+ string(REPLACE "@@USE_GETTEXT_SCHEME@@" "" content "${content}")
+ string(REPLACE "# @@BROKEN_PATH_FIX@@" "" content "${content}")
+ string(REPLACE "@@PERL@@" "${PERL_PATH}" content "${content}")
+ string(REPLACE "@@SANE_TEXT_GREP@@" "-a" content "${content}")
+ string(REPLACE "@@PAGER_ENV@@" "LESS=FRX LV=-c" content "${content}")
+ file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
+endforeach()
+
+#perl scripts
+parse_makefile_for_scripts(git_perl_scripts "SCRIPT_PERL" ".perl")
+
+#create perl header
+file(STRINGS ${CMAKE_SOURCE_DIR}/perl/header_templates/fixed_prefix.template.pl perl_header )
+string(REPLACE "@@PATHSEP@@" ":" perl_header "${perl_header}")
+string(REPLACE "@@INSTLIBDIR@@" "${INSTLIBDIR}" perl_header "${perl_header}")
+
+foreach(script ${git_perl_scripts})
+ file(STRINGS ${CMAKE_SOURCE_DIR}/${script}.perl content NEWLINE_CONSUME)
+ string(REPLACE "#!/usr/bin/perl" "#!/usr/bin/perl\n${perl_header}\n" content "${content}")
+ string(REPLACE "@@GIT_VERSION@@" "${PROJECT_VERSION}" content "${content}")
+ file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
+endforeach()
+
+#python script
+file(STRINGS ${CMAKE_SOURCE_DIR}/git-p4.py content NEWLINE_CONSUME)
+string(REPLACE "#!/usr/bin/env python" "#!/usr/bin/python" content "${content}")
+file(WRITE ${CMAKE_BINARY_DIR}/git-p4 ${content})
+
+#perl modules
+file(GLOB_RECURSE perl_modules "${CMAKE_SOURCE_DIR}/perl/*.pm")
+
+foreach(pm ${perl_modules})
+ string(REPLACE "${CMAKE_SOURCE_DIR}/perl/" "" file_path ${pm})
+ file(STRINGS ${pm} content NEWLINE_CONSUME)
+ string(REPLACE "@@LOCALEDIR@@" "${LOCALEDIR}" content "${content}")
+ string(REPLACE "@@NO_PERL_CPAN_FALLBACKS@@" "" content "${content}")
+ file(WRITE ${CMAKE_BINARY_DIR}/perl/build/lib/${file_path} ${content})
+#test-lib.sh requires perl/build/lib to be the build directory of perl modules
+endforeach()
+
+
+#templates
+file(GLOB templates "${CMAKE_SOURCE_DIR}/templates/*")
+list(TRANSFORM templates REPLACE "${CMAKE_SOURCE_DIR}/templates/" "")
+list(REMOVE_ITEM templates ".gitignore")
+list(REMOVE_ITEM templates "Makefile")
+list(REMOVE_ITEM templates "blt")# Prevents an error when reconfiguring for in source builds
+
+list(REMOVE_ITEM templates "branches--")
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/templates/blt/branches) #create branches
+
+#templates have @.*@ replacement so use configure_file instead
+foreach(tm ${templates})
+ string(REPLACE "--" "/" blt_tm ${tm})
+ string(REPLACE "this" "" blt_tm ${blt_tm})# for this--
+ configure_file(${CMAKE_SOURCE_DIR}/templates/${tm} ${CMAKE_BINARY_DIR}/templates/blt/${blt_tm} @ONLY)
+endforeach()
+
+
+#translations
+if(MSGFMT_EXE)
+ file(GLOB po_files "${CMAKE_SOURCE_DIR}/po/*.po")
+ list(TRANSFORM po_files REPLACE "${CMAKE_SOURCE_DIR}/po/" "")
+ list(TRANSFORM po_files REPLACE ".po" "")
+ foreach(po ${po_files})
+ file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/po/build/locale/${po}/LC_MESSAGES)
+ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/po/build/locale/${po}/LC_MESSAGES/git.mo
+ COMMAND ${MSGFMT_EXE} --check --statistics -o ${CMAKE_BINARY_DIR}/po/build/locale/${po}/LC_MESSAGES/git.mo ${CMAKE_SOURCE_DIR}/po/${po}.po)
+ list(APPEND po_gen ${CMAKE_BINARY_DIR}/po/build/locale/${po}/LC_MESSAGES/git.mo)
+ endforeach()
+ add_custom_target(po-gen ALL DEPENDS ${po_gen})
+endif()
+
+
+#to help with the install
+list(TRANSFORM git_shell_scripts PREPEND "${CMAKE_BINARY_DIR}/")
+list(TRANSFORM git_perl_scripts PREPEND "${CMAKE_BINARY_DIR}/")
+
+#install
+install(TARGETS git git-shell
+ RUNTIME DESTINATION bin)
+install(PROGRAMS ${CMAKE_BINARY_DIR}/git-cvsserver
+ DESTINATION bin)
+
+list(REMOVE_ITEM PROGRAMS_BUILT git git-shell)
+install(TARGETS ${PROGRAMS_BUILT}
+ RUNTIME DESTINATION libexec/git-core)
+
+set(bin_links
+ git-receive-pack git-upload-archive git-upload-pack)
+
+foreach(b ${bin_links})
+install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/git${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/bin/${b}${EXE_EXTENSION})")
+endforeach()
+
+install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/git${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/git${EXE_EXTENSION})")
+install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/git-shell${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/git-shell${EXE_EXTENSION})")
+
+foreach(b ${git_links})
+ string(REPLACE "${CMAKE_BINARY_DIR}" "" b ${b})
+ install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/git${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/${b}${EXE_EXTENSION})")
+endforeach()
+
+foreach(b ${git_http_links})
+ string(REPLACE "${CMAKE_BINARY_DIR}" "" b ${b})
+ install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/libexec/git-core/git-remote-http${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/${b}${EXE_EXTENSION})")
+endforeach()
+
+install(PROGRAMS ${git_shell_scripts} ${git_perl_scripts} ${CMAKE_BINARY_DIR}/git-p4
+ DESTINATION libexec/git-core)
+
+install(DIRECTORY ${CMAKE_SOURCE_DIR}/mergetools DESTINATION libexec/git-core)
+install(DIRECTORY ${CMAKE_BINARY_DIR}/perl/build/lib/ DESTINATION share/perl5
+ FILES_MATCHING PATTERN "*.pm")
+install(DIRECTORY ${CMAKE_BINARY_DIR}/templates/blt/ DESTINATION share/git-core/templates)
+
+if(MSGFMT_EXE)
+ install(DIRECTORY ${CMAKE_BINARY_DIR}/po/build/locale DESTINATION share)
+endif()
+
+
+if(BUILD_TESTING)
+
+#tests-helpers
+add_executable(test-fake-ssh ${CMAKE_SOURCE_DIR}/t/helper/test-fake-ssh.c)
+target_link_libraries(test-fake-ssh common-main)
+
+#test-tool
+parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
+
+list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/")
+add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES})
+target_link_libraries(test-tool common-main)
+
+set_target_properties(test-fake-ssh test-tool
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/helper)
+
+if(MSVC)
+ set_target_properties(test-fake-ssh test-tool
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/t/helper)
+ set_target_properties(test-fake-ssh test-tool
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/t/helper)
+endif()
+
+#wrapper scripts
+set(wrapper_scripts
+ git git-upload-pack git-receive-pack git-upload-archive git-shell git-remote-ext)
+
+set(wrapper_test_scripts
+ test-fake-ssh test-tool)
+
+
+foreach(script ${wrapper_scripts})
+ file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
+ string(REPLACE "@@BUILD_DIR@@" "${CMAKE_BINARY_DIR}" content "${content}")
+ string(REPLACE "@@PROG@@" "${script}${EXE_EXTENSION}" content "${content}")
+ file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/${script} ${content})
+endforeach()
+
+foreach(script ${wrapper_test_scripts})
+ file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
+ string(REPLACE "@@BUILD_DIR@@" "${CMAKE_BINARY_DIR}" content "${content}")
+ string(REPLACE "@@PROG@@" "t/helper/${script}${EXE_EXTENSION}" content "${content}")
+ file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/${script} ${content})
+endforeach()
+
+file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)
+string(REPLACE "@@BUILD_DIR@@" "${CMAKE_BINARY_DIR}" content "${content}")
+string(REPLACE "@@PROG@@" "git-cvsserver" content "${content}")
+file(WRITE ${CMAKE_BINARY_DIR}/bin-wrappers/git-cvsserver ${content})
+
+#options for configuring test options
+option(PERL_TESTS "Perform tests that use perl" ON)
+option(PYTHON_TESTS "Perform tests that use python" ON)
+
+#GIT-BUILD-OPTIONS
+set(TEST_SHELL_PATH ${SHELL_PATH})
+set(DIFF diff)
+set(PYTHON_PATH /usr/bin/python)
+set(TAR tar)
+set(NO_CURL )
+set(NO_EXPAT )
+set(USE_LIBPCRE1 )
+set(USE_LIBPCRE2 )
+set(NO_LIBPCRE1_JIT )
+set(NO_PERL )
+set(NO_PTHREADS )
+set(NO_PYTHON )
+set(PAGER_ENV "LESS=FRX LV=-c")
+set(DC_SHA1 YesPlease)
+set(RUNTIME_PREFIX true)
+set(NO_GETTEXT )
+
+if(NOT CURL_FOUND)
+ set(NO_CURL 1)
+endif()
+
+if(NOT EXPAT_FOUND)
+ set(NO_EXPAT 1)
+endif()
+
+if(NOT Intl_FOUND)
+ set(NO_GETTEXT 1)
+endif()
+
+if(NOT PERL_TESTS)
+ set(NO_PERL 1)
+endif()
+
+if(NOT PYTHON_TESTS)
+ set(NO_PYTHON 1)
+endif()
+
+file(WRITE ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "SHELL_PATH='${SHELL_PATH}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "TEST_SHELL_PATH='${TEST_SHELL_PATH}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PERL_PATH='${PERL_PATH}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "DIFF='${DIFF}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PYTHON_PATH='${PYTHON_PATH}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "TAR='${TAR}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_CURL='${NO_CURL}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_EXPAT='${NO_EXPAT}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "USE_LIBPCRE1='${USE_LIBPCRE1}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_LIBPCRE1_JIT='${NO_LIBPCRE1_JIT}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PERL='${NO_PERL}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PTHREADS='${NO_PTHREADS}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_UNIX_SOCKETS='${NO_UNIX_SOCKETS}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PAGER_ENV='${PAGER_ENV}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "DC_SHA1='${DC_SHA1}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "X='${EXE_EXTENSION}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_GETTEXT='${NO_GETTEXT}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "RUNTIME_PREFIX='${RUNTIME_PREFIX}'\n")
+file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PYTHON='${NO_PYTHON}'\n")
+
+#Make the tests work when building out of the source tree
+get_filename_component(CACHE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../CMakeCache.txt ABSOLUTE)
+if(NOT ${CMAKE_BINARY_DIR}/CMakeCache.txt STREQUAL ${CACHE_PATH})
+ file(RELATIVE_PATH BUILD_DIR_RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}/CMakeCache.txt)
+ string(REPLACE "/CMakeCache.txt" "" BUILD_DIR_RELATIVE ${BUILD_DIR_RELATIVE})
+ #Setting the build directory in test-lib.sh before running tests
+ file(WRITE ${CMAKE_BINARY_DIR}/CTestCustom.cmake
+ "file(STRINGS ${CMAKE_SOURCE_DIR}/t/test-lib.sh GIT_BUILD_DIR_REPL REGEX \"GIT_BUILD_DIR=(.*)\")\n"
+ "file(STRINGS ${CMAKE_SOURCE_DIR}/t/test-lib.sh content NEWLINE_CONSUME)\n"
+ "string(REPLACE \"\${GIT_BUILD_DIR_REPL}\" \"GIT_BUILD_DIR=\\\"$TEST_DIRECTORY\\\"/../${BUILD_DIR_RELATIVE}\" content \"\${content}\")\n"
+ "file(WRITE ${CMAKE_SOURCE_DIR}/t/test-lib.sh \${content})")
+ #misc copies
+ file(COPY ${CMAKE_SOURCE_DIR}/t/chainlint.sed DESTINATION ${CMAKE_BINARY_DIR}/t/)
+ file(COPY ${CMAKE_SOURCE_DIR}/po/is.po DESTINATION ${CMAKE_BINARY_DIR}/po/)
+ file(COPY ${CMAKE_SOURCE_DIR}/mergetools/tkdiff DESTINATION ${CMAKE_BINARY_DIR}/mergetools/)
+ file(COPY ${CMAKE_SOURCE_DIR}/contrib/completion/git-prompt.sh DESTINATION ${CMAKE_BINARY_DIR}/contrib/completion/)
+ file(COPY ${CMAKE_SOURCE_DIR}/contrib/completion/git-completion.bash DESTINATION ${CMAKE_BINARY_DIR}/contrib/completion/)
+endif()
+
+file(GLOB test_scipts "${CMAKE_SOURCE_DIR}/t/t[0-9]*.sh")
+
+#test
+foreach(tsh ${test_scipts})
+ add_test(NAME ${tsh}
+ COMMAND ${SH_EXE} ${tsh}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/t)
+endforeach()
+
+endif()#BUILD_TESTING
diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm
index 5c666f9ac0..d2584450ba 100644
--- a/contrib/buildsystems/Generators/Vcxproj.pm
+++ b/contrib/buildsystems/Generators/Vcxproj.pm
@@ -80,6 +80,7 @@ sub createProject {
$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/g;
+ $libs_debug =~ s/libexpat\.lib/libexpatd\.lib/g;
$libs_debug =~ s/libcurl\.lib/libcurl-d\.lib/g;
}
diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl
index 070978506a..2ff9620459 100755
--- a/contrib/buildsystems/engine.pl
+++ b/contrib/buildsystems/engine.pl
@@ -349,7 +349,7 @@ sub handleLinkLine
} elsif ("$part" eq "-lcurl") {
push(@libs, "libcurl.lib");
} elsif ("$part" eq "-lexpat") {
- push(@libs, "expat.lib");
+ push(@libs, "libexpat.lib");
} elsif ("$part" eq "-liconv") {
push(@libs, "libiconv.lib");
} elsif ($part =~ /^[-\/]/) {
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index ee468ea3b0..8be4a0316e 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -39,6 +39,11 @@
# When set to "1", do not include "DWIM" suggestions in git-checkout
# and git-switch completion (e.g., completing "foo" when "origin/foo"
# exists).
+#
+# GIT_COMPLETION_SHOW_ALL
+#
+# When set to "1" suggest all options, including options which are
+# typically hidden (e.g. '--allow-empty' for 'git commit').
case "$COMP_WORDBREAKS" in
*:*) : great ;;
@@ -411,10 +416,17 @@ __gitcomp_builtin ()
local options
eval "options=\${$var-}"
+ local completion_helper
+ if [ "$GIT_COMPLETION_SHOW_ALL" = "1" ]; then
+ completion_helper="--git-completion-helper-all"
+ else
+ completion_helper="--git-completion-helper"
+ fi
+
if [ -z "$options" ]; then
# leading and trailing spaces are significant to make
# option removal work correctly.
- options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return
+ options=" $incl $(__git ${cmd/_/ } $completion_helper) " || return
for i in $excl; do
options="${options/ $i / }"
@@ -1712,8 +1724,8 @@ _git_diff ()
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
- tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc
- codecompare smerge
+ tkdiff vimdiff nvimdiff gvimdiff xxdiff araxis p4merge
+ bc codecompare smerge
"
_git_difftool ()
@@ -1772,6 +1784,10 @@ _git_format_patch ()
" "" "${cur##--thread=}"
return
;;
+ --base=*|--interdiff=*|--range-diff=*)
+ __git_complete_refs --cur="${cur#--*=}"
+ return
+ ;;
--*)
__gitcomp_builtin format-patch "$__git_format_patch_extra_options"
return
@@ -2907,6 +2923,14 @@ _git_show ()
__gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
return
;;
+ --color-moved=*)
+ __gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}"
+ return
+ ;;
+ --color-moved-ws=*)
+ __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
+ return
+ ;;
--*)
__gitcomp "--pretty= --format= --abbrev-commit --no-abbrev-commit
--oneline --show-signature --patch
diff --git a/contrib/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm
index e2589922a6..376f577737 100644
--- a/contrib/diff-highlight/DiffHighlight.pm
+++ b/contrib/diff-highlight/DiffHighlight.pm
@@ -112,7 +112,7 @@ sub handle_line {
# Since we can receive arbitrary input, there's no optimal
# place to flush. Flushing on a blank line is a heuristic that
# happens to match git-log output.
- if (!length) {
+ if (/^$/) {
$flush_cb->();
}
}
diff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl
index 28df3ee321..eb52a53d32 100755
--- a/contrib/mw-to-git/git-mw.perl
+++ b/contrib/mw-to-git/git-mw.perl
@@ -6,7 +6,7 @@
# License: GPL v2 or later
# Set of tools for git repo with a mediawiki remote.
-# Documentation & bugtracker: https://github.com/moy/Git-Mediawiki/
+# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki
use strict;
use warnings;
diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl
index d8ff2e69c4..a5624413dc 100755
--- a/contrib/mw-to-git/git-remote-mediawiki.perl
+++ b/contrib/mw-to-git/git-remote-mediawiki.perl
@@ -9,7 +9,7 @@
# License: GPL v2 or later
# Gateway between Git and MediaWiki.
-# Documentation & bugtracker: https://github.com/moy/Git-Mediawiki/
+# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki
use strict;
use MediaWiki::API;
@@ -56,38 +56,38 @@ my $url = $ARGV[1];
# Accept both space-separated and multiple keys in config file.
# Spaces should be written as _ anyway because we'll use chomp.
-my @tracked_pages = split(/[ \n]/, run_git("config --get-all remote.${remotename}.pages"));
+my @tracked_pages = split(/[ \n]/, run_git_quoted(["config", "--get-all", "remote.${remotename}.pages"]));
chomp(@tracked_pages);
# Just like @tracked_pages, but for MediaWiki categories.
-my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.${remotename}.categories"));
+my @tracked_categories = split(/[ \n]/, run_git_quoted(["config", "--get-all", "remote.${remotename}.categories"]));
chomp(@tracked_categories);
# Just like @tracked_categories, but for MediaWiki namespaces.
-my @tracked_namespaces = split(/[ \n]/, run_git("config --get-all remote.${remotename}.namespaces"));
+my @tracked_namespaces = split(/[ \n]/, run_git_quoted(["config", "--get-all", "remote.${remotename}.namespaces"]));
for (@tracked_namespaces) { s/_/ /g; }
chomp(@tracked_namespaces);
# Import media files on pull
-my $import_media = run_git("config --get --bool remote.${remotename}.mediaimport");
+my $import_media = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.mediaimport"]);
chomp($import_media);
$import_media = ($import_media eq 'true');
# Export media files on push
-my $export_media = run_git("config --get --bool remote.${remotename}.mediaexport");
+my $export_media = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.mediaexport"]);
chomp($export_media);
$export_media = !($export_media eq 'false');
-my $wiki_login = run_git("config --get remote.${remotename}.mwLogin");
+my $wiki_login = run_git_quoted(["config", "--get", "remote.${remotename}.mwLogin"]);
# Note: mwPassword is discouraged. Use the credential system instead.
-my $wiki_passwd = run_git("config --get remote.${remotename}.mwPassword");
-my $wiki_domain = run_git("config --get remote.${remotename}.mwDomain");
+my $wiki_passwd = run_git_quoted(["config", "--get", "remote.${remotename}.mwPassword"]);
+my $wiki_domain = run_git_quoted(["config", "--get", "remote.${remotename}.mwDomain"]);
chomp($wiki_login);
chomp($wiki_passwd);
chomp($wiki_domain);
# Import only last revisions (both for clone and fetch)
-my $shallow_import = run_git("config --get --bool remote.${remotename}.shallow");
+my $shallow_import = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.shallow"]);
chomp($shallow_import);
$shallow_import = ($shallow_import eq 'true');
@@ -97,9 +97,9 @@ $shallow_import = ($shallow_import eq 'true');
# Possible values:
# - by_rev: perform one query per new revision on the remote wiki
# - by_page: query each tracked page for new revision
-my $fetch_strategy = run_git("config --get remote.${remotename}.fetchStrategy");
+my $fetch_strategy = run_git_quoted(["config", "--get", "remote.${remotename}.fetchStrategy"]);
if (!$fetch_strategy) {
- $fetch_strategy = run_git('config --get mediawiki.fetchStrategy');
+ $fetch_strategy = run_git_quoted(["config", "--get", "mediawiki.fetchStrategy"]);
}
chomp($fetch_strategy);
if (!$fetch_strategy) {
@@ -123,9 +123,9 @@ my %basetimestamps;
# will get the history with information lost). If the import is
# deterministic, this means everybody gets the same sha1 for each
# MediaWiki revision.
-my $dumb_push = run_git("config --get --bool remote.${remotename}.dumbPush");
+my $dumb_push = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.dumbPush"]);
if (!$dumb_push) {
- $dumb_push = run_git('config --get --bool mediawiki.dumbPush');
+ $dumb_push = run_git_quoted(["config", "--get", "--bool", "mediawiki.dumbPush"]);
}
chomp($dumb_push);
$dumb_push = ($dumb_push eq 'true');
@@ -369,12 +369,14 @@ sub get_mw_pages {
return %pages;
}
-# usage: $out = run_git("command args");
-# $out = run_git("command args", "raw"); # don't interpret output as UTF-8.
-sub run_git {
+# usage: $out = run_git_quoted(["command", "args", ...]);
+# $out = run_git_quoted(["command", "args", ...], "raw"); # don't interpret output as UTF-8.
+# $out = run_git_quoted_nostderr(["command", "args", ...]); # discard stderr
+# $out = run_git_quoted_nostderr(["command", "args", ...], "raw"); # ditto but raw instead of UTF-8 as above
+sub _run_git {
my $args = shift;
my $encoding = (shift || 'encoding(UTF-8)');
- open(my $git, "-|:${encoding}", "git ${args}")
+ open(my $git, "-|:${encoding}", @$args)
or die "Unable to fork: $!\n";
my $res = do {
local $/ = undef;
@@ -385,6 +387,13 @@ sub run_git {
return $res;
}
+sub run_git_quoted {
+ _run_git(["git", @{$_[0]}], $_[1]);
+}
+
+sub run_git_quoted_nostderr {
+ _run_git(['sh', '-c', 'git "$@" 2>/dev/null', '--', @{$_[0]}], $_[1]);
+}
sub get_all_mediafiles {
my $pages = shift;
@@ -511,8 +520,9 @@ sub download_mw_mediafile {
}
sub get_last_local_revision {
- # Get note regarding last mediawiki revision
- my $note = run_git("notes --ref=${remotename}/mediawiki show refs/mediawiki/${remotename}/master 2>/dev/null");
+ # Get note regarding last mediawiki revision.
+ my $note = run_git_quoted_nostderr(["notes", "--ref=${remotename}/mediawiki",
+ "show", "refs/mediawiki/${remotename}/master"]);
my @note_info = split(/ /, $note);
my $lastrevision_number;
@@ -807,7 +817,10 @@ sub get_more_refs {
sub mw_import {
# multiple import commands can follow each other.
my @refs = (shift, get_more_refs('import'));
+ my $processedRefs;
foreach my $ref (@refs) {
+ next if $processedRefs->{$ref}; # skip duplicates: "import refs/heads/master" being issued twice; TODO: why?
+ $processedRefs->{$ref} = 1;
mw_import_ref($ref);
}
print {*STDOUT} "done\n";
@@ -970,7 +983,7 @@ sub mw_import_revids {
}
sub error_non_fast_forward {
- my $advice = run_git('config --bool advice.pushNonFastForward');
+ my $advice = run_git_quoted(["config", "--bool", "advice.pushNonFastForward"]);
chomp($advice);
if ($advice ne 'false') {
# Native git-push would show this after the summary.
@@ -1014,7 +1027,7 @@ sub mw_upload_file {
}
} else {
# Don't let perl try to interpret file content as UTF-8 => use "raw"
- my $content = run_git("cat-file blob ${new_sha1}", 'raw');
+ my $content = run_git_quoted(["cat-file", "blob", $new_sha1], 'raw');
if ($content ne EMPTY) {
$mediawiki = connect_maybe($mediawiki, $remotename, $url);
$mediawiki->{config}->{upload_url} =
@@ -1084,7 +1097,7 @@ sub mw_push_file {
# with this content instead:
$file_content = DELETED_CONTENT;
} else {
- $file_content = run_git("cat-file blob ${new_sha1}");
+ $file_content = run_git_quoted(["cat-file", "blob", $new_sha1]);
}
$mediawiki = connect_maybe($mediawiki, $remotename, $url);
@@ -1174,10 +1187,10 @@ sub mw_push_revision {
my $mw_revision = $last_remote_revid;
# Get sha1 of commit pointed by local HEAD
- my $HEAD_sha1 = run_git("rev-parse ${local} 2>/dev/null");
+ my $HEAD_sha1 = run_git_quoted_nostderr(["rev-parse", $local]);
chomp($HEAD_sha1);
# Get sha1 of commit pointed by remotes/$remotename/master
- my $remoteorigin_sha1 = run_git("rev-parse refs/remotes/${remotename}/master 2>/dev/null");
+ my $remoteorigin_sha1 = run_git_quoted_nostderr(["rev-parse", "refs/remotes/${remotename}/master"]);
chomp($remoteorigin_sha1);
if ($last_local_revid > 0 &&
@@ -1197,7 +1210,7 @@ sub mw_push_revision {
my $parsed_sha1 = $remoteorigin_sha1;
# Find a path from last MediaWiki commit to pushed commit
print {*STDERR} "Computing path from local to remote ...\n";
- my @local_ancestry = split(/\n/, run_git("rev-list --boundary --parents ${local} ^${parsed_sha1}"));
+ my @local_ancestry = split(/\n/, run_git_quoted(["rev-list", "--boundary", "--parents", $local, "^${parsed_sha1}"]));
my %local_ancestry;
foreach my $line (@local_ancestry) {
if (my ($child, $parents) = $line =~ /^-?([a-f0-9]+) ([a-f0-9 ]+)/) {
@@ -1221,7 +1234,7 @@ sub mw_push_revision {
# No remote mediawiki revision. Export the whole
# history (linearized with --first-parent)
print {*STDERR} "Warning: no common ancestor, pushing complete history\n";
- my $history = run_git("rev-list --first-parent --children ${local}");
+ my $history = run_git_quoted(["rev-list", "--first-parent", "--children", $local]);
my @history = split(/\n/, $history);
@history = @history[1..$#history];
foreach my $line (reverse @history) {
@@ -1233,12 +1246,12 @@ sub mw_push_revision {
foreach my $commit_info_split (@commit_pairs) {
my $sha1_child = @{$commit_info_split}[0];
my $sha1_commit = @{$commit_info_split}[1];
- my $diff_infos = run_git("diff-tree -r --raw -z ${sha1_child} ${sha1_commit}");
+ my $diff_infos = run_git_quoted(["diff-tree", "-r", "--raw", "-z", $sha1_child, $sha1_commit]);
# TODO: we could detect rename, and encode them with a #redirect on the wiki.
# TODO: for now, it's just a delete+add
my @diff_info_list = split(/\0/, $diff_infos);
# Keep the subject line of the commit message as mediawiki comment for the revision
- my $commit_msg = run_git(qq(log --no-walk --format="%s" ${sha1_commit}));
+ my $commit_msg = run_git_quoted(["log", "--no-walk", '--format="%s"', $sha1_commit]);
chomp($commit_msg);
# Push every blob
while (@diff_info_list) {
@@ -1263,7 +1276,10 @@ sub mw_push_revision {
}
}
if (!$dumb_push) {
- run_git(qq(notes --ref=${remotename}/mediawiki add -f -m "mediawiki_revision: ${mw_revision}" ${sha1_commit}));
+ run_git_quoted(["notes", "--ref=${remotename}/mediawiki",
+ "add", "-f", "-m",
+ "mediawiki_revision: ${mw_revision}",
+ $sha1_commit]);
}
}
@@ -1304,7 +1320,7 @@ sub get_mw_namespace_id {
# already cached. Namespaces are stored in form:
# "Name_of_namespace:Id_namespace", ex.: "File:6".
my @temp = split(/\n/,
- run_git("config --get-all remote.${remotename}.namespaceCache"));
+ run_git_quoted(["config", "--get-all", "remote.${remotename}.namespaceCache"]));
chomp(@temp);
foreach my $ns (@temp) {
my ($n, $id) = split(/:/, $ns);
@@ -1358,7 +1374,7 @@ sub get_mw_namespace_id {
# Store explicitly requested namespaces on disk
if (!exists $cached_mw_namespace_id{$name}) {
- run_git(qq(config --add remote.${remotename}.namespaceCache "${name}:${store_id}"));
+ run_git_quoted(["config", "--add", "remote.${remotename}.namespaceCache", "${name}:${store_id}"]);
$cached_mw_namespace_id{$name} = 1;
}
return $id;
diff --git a/contrib/mw-to-git/git-remote-mediawiki.txt b/contrib/mw-to-git/git-remote-mediawiki.txt
index 23b7ef9f62..5da825f61e 100644
--- a/contrib/mw-to-git/git-remote-mediawiki.txt
+++ b/contrib/mw-to-git/git-remote-mediawiki.txt
@@ -4,4 +4,4 @@ objects from mediawiki just as one would do with a classic git
repository thanks to remote-helpers.
For more information, visit the wiki at
-https://github.com/moy/Git-Mediawiki/wiki
+https://github.com/Git-Mediawiki/Git-Mediawiki
diff --git a/contrib/mw-to-git/t/.gitignore b/contrib/mw-to-git/t/.gitignore
index a7a40b4964..2b8dc30c6d 100644
--- a/contrib/mw-to-git/t/.gitignore
+++ b/contrib/mw-to-git/t/.gitignore
@@ -1,4 +1,4 @@
WEB/
-wiki/
+mediawiki/
trash directory.t*/
test-results/
diff --git a/contrib/mw-to-git/t/README b/contrib/mw-to-git/t/README
index 2ee34be7e4..72c4889db7 100644
--- a/contrib/mw-to-git/t/README
+++ b/contrib/mw-to-git/t/README
@@ -14,11 +14,11 @@ install the following packages (Debian/Ubuntu names, may need to be
adapted for another distribution):
* lighttpd
-* php5
-* php5-cgi
-* php5-cli
-* php5-curl
-* php5-sqlite
+* php
+* php-cgi
+* php-cli
+* php-curl
+* php-sqlite
Principles and Technical Choices
--------------------------------
diff --git a/contrib/mw-to-git/t/install-wiki/.gitignore b/contrib/mw-to-git/t/install-wiki/.gitignore
deleted file mode 100644
index b5a2a4408c..0000000000
--- a/contrib/mw-to-git/t/install-wiki/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-wikidb.sqlite
diff --git a/contrib/mw-to-git/t/install-wiki/LocalSettings.php b/contrib/mw-to-git/t/install-wiki/LocalSettings.php
deleted file mode 100644
index 745e47e881..0000000000
--- a/contrib/mw-to-git/t/install-wiki/LocalSettings.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-# This file was automatically generated by the MediaWiki 1.19.0
-# installer. If you make manual changes, please keep track in case you
-# need to recreate them later.
-#
-# See includes/DefaultSettings.php for all configurable settings
-# and their default values, but don't forget to make changes in _this_
-# file, not there.
-#
-# Further documentation for configuration settings may be found at:
-# http://www.mediawiki.org/wiki/Manual:Configuration_settings
-
-# Protect against web entry
-if ( !defined( 'MEDIAWIKI' ) ) {
- exit;
-}
-
-## Uncomment this to disable output compression
-# $wgDisableOutputCompression = true;
-
-$wgSitename = "Git-MediaWiki-Test";
-$wgMetaNamespace = "Git-MediaWiki-Test";
-
-## The URL base path to the directory containing the wiki;
-## defaults for all runtime URL paths are based off of this.
-## For more information on customizing the URLs please see:
-## http://www.mediawiki.org/wiki/Manual:Short_URL
-$wgScriptPath = "@WG_SCRIPT_PATH@";
-$wgScriptExtension = ".php";
-
-## The protocol and server name to use in fully-qualified URLs
-$wgServer = "@WG_SERVER@";
-
-## The relative URL path to the skins directory
-$wgStylePath = "$wgScriptPath/skins";
-
-## The relative URL path to the logo. Make sure you change this from the default,
-## or else you'll overwrite your logo when you upgrade!
-$wgLogo = "$wgStylePath/common/images/wiki.png";
-
-## UPO means: this is also a user preference option
-
-$wgEnableEmail = true;
-$wgEnableUserEmail = true; # UPO
-
-$wgEmergencyContact = "apache@localhost";
-$wgPasswordSender = "apache@localhost";
-
-$wgEnotifUserTalk = false; # UPO
-$wgEnotifWatchlist = false; # UPO
-$wgEmailAuthentication = true;
-
-## Database settings
-$wgDBtype = "sqlite";
-$wgDBserver = "";
-$wgDBname = "@WG_SQLITE_DATAFILE@";
-$wgDBuser = "";
-$wgDBpassword = "";
-
-# SQLite-specific settings
-$wgSQLiteDataDir = "@WG_SQLITE_DATADIR@";
-
-
-## Shared memory settings
-$wgMainCacheType = CACHE_NONE;
-$wgMemCachedServers = array();
-
-## To enable image uploads, make sure the 'images' directory
-## is writable, then set this to true:
-$wgEnableUploads = true;
-$wgUseImageMagick = true;
-$wgImageMagickConvertCommand ="@CONVERT@";
-$wgFileExtensions[] = 'txt';
-
-# InstantCommons allows wiki to use images from http://commons.wikimedia.org
-$wgUseInstantCommons = false;
-
-## If you use ImageMagick (or any other shell command) on a
-## Linux server, this will need to be set to the name of an
-## available UTF-8 locale
-$wgShellLocale = "en_US.utf8";
-
-## If you want to use image uploads under safe mode,
-## create the directories images/archive, images/thumb and
-## images/temp, and make them all writable. Then uncomment
-## this, if it's not already uncommented:
-#$wgHashedUploadDirectory = false;
-
-## Set $wgCacheDirectory to a writable directory on the web server
-## to make your wiki go slightly faster. The directory should not
-## be publicly accessible from the web.
-#$wgCacheDirectory = "$IP/cache";
-
-# Site language code, should be one of the list in ./languages/Names.php
-$wgLanguageCode = "en";
-
-$wgSecretKey = "1c912bfe3519fb70f5dc523ecc698111cd43d81a11c585b3eefb28f29c2699b7";
-#$wgSecretKey = "@SECRETKEY@";
-
-
-# Site upgrade key. Must be set to a string (default provided) to turn on the
-# web installer while LocalSettings.php is in place
-$wgUpgradeKey = "ddae7dc87cd0a645";
-
-## Default skin: you can change the default skin. Use the internal symbolic
-## names, ie 'standard', 'nostalgia', 'cologneblue', 'monobook', 'vector':
-$wgDefaultSkin = "vector";
-
-## For attaching licensing metadata to pages, and displaying an
-## appropriate copyright notice / icon. GNU Free Documentation
-## License and Creative Commons licenses are supported so far.
-$wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
-$wgRightsUrl = "";
-$wgRightsText = "";
-$wgRightsIcon = "";
-
-# Path to the GNU diff3 utility. Used for conflict resolution.
-$wgDiff3 = "/usr/bin/diff3";
-
-# Query string length limit for ResourceLoader. You should only set this if
-# your web server has a query string length limit (then set it to that limit),
-# or if you have suhosin.get.max_value_length set in php.ini (then set it to
-# that value)
-$wgResourceLoaderMaxQueryLength = -1;
-
-
-
-# End of automatically generated settings.
-# Add more configuration options below.
diff --git a/contrib/mw-to-git/t/install-wiki/db_install.php b/contrib/mw-to-git/t/install-wiki/db_install.php
deleted file mode 100644
index b033849800..0000000000
--- a/contrib/mw-to-git/t/install-wiki/db_install.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-/**
- * This script generates a SQLite database for a MediaWiki version 1.19.0
- * You must specify the login of the admin (argument 1) and its
- * password (argument 2) and the folder where the database file
- * is located (absolute path in argument 3).
- * It is used by the script install-wiki.sh in order to make easy the
- * installation of a MediaWiki.
- *
- * In order to generate a SQLite database file, MediaWiki ask the user
- * to submit some forms in its web browser. This script simulates this
- * behavior though the functions <get> and <submit>
- *
- */
-$argc = $_SERVER['argc'];
-$argv = $_SERVER['argv'];
-
-$login = $argv[2];
-$pass = $argv[3];
-$tmp = $argv[4];
-$port = $argv[5];
-
-$url = 'http://localhost:'.$port.'/wiki/mw-config/index.php';
-$db_dir = urlencode($tmp);
-$tmp_cookie = tempnam($tmp, "COOKIE_");
-/*
- * Fetches a page with cURL.
- */
-function get($page_name = "") {
- $curl = curl_init();
- $page_name_add = "";
- if ($page_name != "") {
- $page_name_add = '?page='.$page_name;
- }
- $url = $GLOBALS['url'].$page_name_add;
- $tmp_cookie = $GLOBALS['tmp_cookie'];
- curl_setopt($curl, CURLOPT_COOKIEJAR, $tmp_cookie);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($curl, CURLOPT_COOKIEFILE, $tmp_cookie);
- curl_setopt($curl, CURLOPT_HEADER, true);
- curl_setopt($curl, CURLOPT_URL, $url);
-
- $page = curl_exec($curl);
- if (!$page) {
- die("Could not get page: $url\n");
- }
- curl_close($curl);
- return $page;
-}
-
-/*
- * Submits a form with cURL.
- */
-function submit($page_name, $option = "") {
- $curl = curl_init();
- $datapost = 'submit-continue=Continue+%E2%86%92';
- if ($option != "") {
- $datapost = $option.'&'.$datapost;
- }
- $url = $GLOBALS['url'].'?page='.$page_name;
- $tmp_cookie = $GLOBALS['tmp_cookie'];
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_POST, true);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $datapost);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_COOKIEJAR, $tmp_cookie);
- curl_setopt($curl, CURLOPT_COOKIEFILE, $tmp_cookie);
-
- $page = curl_exec($curl);
- if (!$page) {
- die("Could not get page: $url\n");
- }
- curl_close($curl);
- return "$page";
-}
-
-/*
- * Here starts this script: simulates the behavior of the user
- * submitting forms to generates the database file.
- * Note this simulation was made for the MediaWiki version 1.19.0,
- * we can't assume it works with other versions.
- *
- */
-
-$page = get();
-if (!preg_match('/input type="hidden" value="([0-9]+)" name="LanguageRequestTime"/',
- $page, $matches)) {
- echo "Unexpected content for page downloaded:\n";
- echo "$page";
- die;
-};
-$timestamp = $matches[1];
-$language = "LanguageRequestTime=$timestamp&uselang=en&ContLang=en";
-$page = submit('Language', $language);
-
-submit('Welcome');
-
-$db_config = 'DBType=sqlite';
-$db_config = $db_config.'&sqlite_wgSQLiteDataDir='.$db_dir;
-$db_config = $db_config.'&sqlite_wgDBname='.$argv[1];
-submit('DBConnect', $db_config);
-
-$wiki_config = 'config_wgSitename=TEST';
-$wiki_config = $wiki_config.'&config__NamespaceType=site-name';
-$wiki_config = $wiki_config.'&config_wgMetaNamespace=MyWiki';
-$wiki_config = $wiki_config.'&config__AdminName='.$login;
-
-$wiki_config = $wiki_config.'&config__AdminPassword='.$pass;
-$wiki_config = $wiki_config.'&config__AdminPassword2='.$pass;
-
-$wiki_config = $wiki_config.'&wiki__configEmail=email%40email.org';
-$wiki_config = $wiki_config.'&config__SkipOptional=skip';
-submit('Name', $wiki_config);
-submit('Install');
-submit('Install');
-
-unlink($tmp_cookie);
-?>
diff --git a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh
index 9106833578..4c39bda7bf 100755
--- a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh
+++ b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh
@@ -28,7 +28,7 @@ test_expect_success 'Git clone creates the expected git log with one file' '
git log --format=%s HEAD^..HEAD >log.tmp
) &&
echo "this must be the same" >msg.tmp &&
- diff -b mw_dir_1/log.tmp msg.tmp
+ test_cmp msg.tmp mw_dir_1/log.tmp
'
@@ -50,8 +50,8 @@ test_expect_success 'Git clone creates the expected git log with multiple files'
echo "this must be the same" >>msgDaddy.tmp &&
echo "identical too" >msgDj.tmp &&
echo "identical" >>msgDj.tmp &&
- diff -b mw_dir_2/logDaddy.tmp msgDaddy.tmp &&
- diff -b mw_dir_2/logDj.tmp msgDj.tmp
+ test_cmp msgDaddy.tmp mw_dir_2/logDaddy.tmp &&
+ test_cmp msgDj.tmp mw_dir_2/logDj.tmp
'
@@ -135,7 +135,7 @@ test_expect_success 'Git clone works with one specific page cloned ' '
cd mw_dir_8 &&
echo "this log must stay" >msg.tmp &&
git log --format=%s >log.tmp &&
- diff -b msg.tmp log.tmp
+ test_cmp msg.tmp log.tmp
) &&
wiki_check_content mw_dir_8/Namnam.mw Namnam
'
diff --git a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
index 3ff3a09567..6187ec67fa 100755
--- a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
+++ b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh
@@ -27,12 +27,12 @@ test_git_reimport () {
# Don't bother with permissions, be administrator by default
test_expect_success 'setup config' '
- git config --global remote.origin.mwLogin WikiAdmin &&
- git config --global remote.origin.mwPassword AdminPass &&
+ git config --global remote.origin.mwLogin "$WIKI_ADMIN" &&
+ git config --global remote.origin.mwPassword "$WIKI_PASSW" &&
test_might_fail git config --global --unset remote.origin.mediaImport
'
-test_expect_success 'git push can upload media (File:) files' '
+test_expect_failure 'git push can upload media (File:) files' '
wiki_reset &&
git clone mediawiki::'"$WIKI_URL"' mw_dir &&
(
@@ -48,13 +48,14 @@ test_expect_success 'git push can upload media (File:) files' '
)
'
-test_expect_success 'git clone works on previously created wiki with media files' '
+test_expect_failure 'git clone works on previously created wiki with media files' '
test_when_finished "rm -rf mw_dir mw_dir_clone" &&
git clone -c remote.origin.mediaimport=true \
mediawiki::'"$WIKI_URL"' mw_dir_clone &&
test_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt &&
(cd mw_dir_clone && git checkout HEAD^) &&
(cd mw_dir && git checkout HEAD^) &&
+ test_path_is_file mw_dir_clone/Foo.txt &&
test_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt
'
diff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh
index 3948a00282..64e46c1671 100755
--- a/contrib/mw-to-git/t/test-gitmw-lib.sh
+++ b/contrib/mw-to-git/t/test-gitmw-lib.sh
@@ -13,7 +13,8 @@
. ./test.config
-WIKI_URL=http://"$SERVER_ADDR:$PORT/$WIKI_DIR_NAME"
+WIKI_BASE_URL=http://$SERVER_ADDR:$PORT
+WIKI_URL=$WIKI_BASE_URL/$WIKI_DIR_NAME
CURR_DIR=$(pwd)
TEST_OUTPUT_DIRECTORY=$(pwd)
TEST_DIRECTORY="$CURR_DIR"/../../../t
@@ -65,7 +66,7 @@ test_check_precond () {
GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd "../.." && pwd)
PATH="$GIT_EXEC_PATH"'/bin-wrapper:'"$PATH"
- if [ ! -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" ];
+ if ! test -d "$WIKI_DIR_INST/$WIKI_DIR_NAME"
then
skip_all='skipping gateway git-mw tests, no mediawiki found'
test_done
@@ -291,27 +292,59 @@ stop_lighttpd () {
test -f "$WEB_TMP/pid" && kill $(cat "$WEB_TMP/pid")
}
-# Create the SQLite database of the MediaWiki. If the database file already
-# exists, it will be deleted.
-# This script should be runned from the directory where $FILES_FOLDER is
-# located.
-create_db () {
- rm -f "$TMP/$DB_FILE"
-
- echo "Generating the SQLite database file. It can take some time ..."
- # Run the php script to generate the SQLite database file
- # with cURL calls.
- php "$FILES_FOLDER/$DB_INSTALL_SCRIPT" $(basename "$DB_FILE" .sqlite) \
- "$WIKI_ADMIN" "$WIKI_PASSW" "$TMP" "$PORT"
-
- if [ ! -f "$TMP/$DB_FILE" ] ; then
- error "Can't create database file $TMP/$DB_FILE. Try to run ./install-wiki.sh delete first."
+wiki_delete_db () {
+ rm -rf \
+ "$FILES_FOLDER_DB"/* || error "Couldn't delete $FILES_FOLDER_DB/"
+}
+
+wiki_delete_db_backup () {
+ rm -rf \
+ "$FILES_FOLDER_POST_INSTALL_DB"/* || error "Couldn't delete $FILES_FOLDER_POST_INSTALL_DB/"
+}
+
+# Install MediaWiki using its install.php script. If the database file
+# already exists, it will be deleted.
+install_mediawiki () {
+
+ localsettings="$WIKI_DIR_INST/$WIKI_DIR_NAME/LocalSettings.php"
+ if test -f "$localsettings"
+ then
+ error "We already installed the wiki, since $localsettings exists" \
+ "perhaps you wanted to run 'delete' first?"
fi
- # Copy the generated database file into the directory the
- # user indicated.
- cp "$TMP/$DB_FILE" "$FILES_FOLDER" ||
- error "Unable to copy $TMP/$DB_FILE to $FILES_FOLDER"
+ wiki_delete_db
+ wiki_delete_db_backup
+ mkdir \
+ "$FILES_FOLDER_DB/" \
+ "$FILES_FOLDER_POST_INSTALL_DB/"
+
+ install_script="$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php"
+ echo "Installing MediaWiki using $install_script. This may take some time ..."
+
+ php "$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php" \
+ --server $WIKI_BASE_URL \
+ --scriptpath /wiki \
+ --lang en \
+ --dbtype sqlite \
+ --dbpath $PWD/$FILES_FOLDER_DB/ \
+ --pass "$WIKI_PASSW" \
+ Git-MediaWiki-Test \
+ "$WIKI_ADMIN" ||
+ error "Couldn't run $install_script, see errors above. Try to run ./install-wiki.sh delete first."
+ cat <<-'EOF' >>$localsettings
+# Custom settings added by test-gitmw-lib.sh
+#
+# Uploading text files is needed for
+# t9363-mw-to-git-export-import.sh
+$wgEnableUploads = true;
+$wgFileExtensions[] = 'txt';
+EOF
+
+ # Copy the initially generated database file into our backup
+ # folder
+ cp -R "$FILES_FOLDER_DB/"* "$FILES_FOLDER_POST_INSTALL_DB/" ||
+ error "Unable to copy $FILES_FOLDER_DB/* to $FILES_FOLDER_POST_INSTALL_DB/*"
}
# Install a wiki in your web server directory.
@@ -320,30 +353,33 @@ wiki_install () {
start_lighttpd
fi
- SERVER_ADDR=$SERVER_ADDR:$PORT
# In this part, we change directory to $TMP in order to download,
# unpack and copy the files of MediaWiki
(
mkdir -p "$WIKI_DIR_INST/$WIKI_DIR_NAME"
- if [ ! -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" ] ; then
+ if ! test -d "$WIKI_DIR_INST/$WIKI_DIR_NAME"
+ then
error "Folder $WIKI_DIR_INST/$WIKI_DIR_NAME doesn't exist.
Please create it and launch the script again."
fi
- # Fetch MediaWiki's archive if not already present in the TMP directory
+ # Fetch MediaWiki's archive if not already present in the
+ # download directory
+ mkdir -p "$FILES_FOLDER_DOWNLOAD"
MW_FILENAME="mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz"
- cd "$TMP"
- if [ ! -f $MW_FILENAME ] ; then
+ cd "$FILES_FOLDER_DOWNLOAD"
+ if ! test -f $MW_FILENAME
+ then
echo "Downloading $MW_VERSION_MAJOR.$MW_VERSION_MINOR sources ..."
wget "http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/$MW_FILENAME" ||
error "Unable to download "\
"http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/"\
"$MW_FILENAME. "\
"Please fix your connection and launch the script again."
- echo "$MW_FILENAME downloaded in $(pwd). "\
- "You can delete it later if you want."
+ echo "$MW_FILENAME downloaded in $(pwd)/;" \
+ "you can delete it later if you want."
else
- echo "Reusing existing $MW_FILENAME downloaded in $(pwd)."
+ echo "Reusing existing $MW_FILENAME downloaded in $(pwd)/"
fi
archive_abs_path=$(pwd)/$MW_FILENAME
cd "$WIKI_DIR_INST/$WIKI_DIR_NAME/" ||
@@ -352,48 +388,12 @@ wiki_install () {
error "Unable to extract WikiMedia's files from $archive_abs_path to "\
"$WIKI_DIR_INST/$WIKI_DIR_NAME"
) || exit 1
+ echo Extracted in "$WIKI_DIR_INST/$WIKI_DIR_NAME"
- create_db
-
- # Copy the generic LocalSettings.php in the web server's directory
- # And modify parameters according to the ones set at the top
- # of this script.
- # Note that LocalSettings.php is never modified.
- if [ ! -f "$FILES_FOLDER/LocalSettings.php" ] ; then
- error "Can't find $FILES_FOLDER/LocalSettings.php " \
- "in the current folder. "\
- "Please run the script inside its folder."
- fi
- cp "$FILES_FOLDER/LocalSettings.php" \
- "$FILES_FOLDER/LocalSettings-tmp.php" ||
- error "Unable to copy $FILES_FOLDER/LocalSettings.php " \
- "to $FILES_FOLDER/LocalSettings-tmp.php"
-
- # Parse and set the LocalSettings file of the user according to the
- # CONFIGURATION VARIABLES section at the beginning of this script
- file_swap="$FILES_FOLDER/LocalSettings-swap.php"
- sed "s,@WG_SCRIPT_PATH@,/$WIKI_DIR_NAME," \
- "$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
- mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
- sed "s,@WG_SERVER@,http://$SERVER_ADDR," \
- "$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
- mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
- sed "s,@WG_SQLITE_DATADIR@,$TMP," \
- "$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
- mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
- sed "s,@WG_SQLITE_DATAFILE@,$( basename $DB_FILE .sqlite)," \
- "$FILES_FOLDER/LocalSettings-tmp.php" > "$file_swap"
- mv "$file_swap" "$FILES_FOLDER/LocalSettings-tmp.php"
-
- mv "$FILES_FOLDER/LocalSettings-tmp.php" \
- "$WIKI_DIR_INST/$WIKI_DIR_NAME/LocalSettings.php" ||
- error "Unable to move $FILES_FOLDER/LocalSettings-tmp.php" \
- "in $WIKI_DIR_INST/$WIKI_DIR_NAME"
- echo "File $FILES_FOLDER/LocalSettings.php is set in" \
- " $WIKI_DIR_INST/$WIKI_DIR_NAME"
+ install_mediawiki
echo "Your wiki has been installed. You can check it at
- http://$SERVER_ADDR/$WIKI_DIR_NAME"
+ $WIKI_URL"
}
# Reset the database of the wiki and the password of the admin
@@ -401,12 +401,18 @@ wiki_install () {
# Warning: This function must be called only in a subdirectory of t/ directory
wiki_reset () {
# Copy initial database of the wiki
- if [ ! -f "../$FILES_FOLDER/$DB_FILE" ] ; then
- error "Can't find ../$FILES_FOLDER/$DB_FILE in the current folder."
+ if ! test -d "../$FILES_FOLDER_DB"
+ then
+ error "No wiki database at ../$FILES_FOLDER_DB, not installed yet?"
+ fi
+ if ! test -d "../$FILES_FOLDER_POST_INSTALL_DB"
+ then
+ error "No wiki backup database at ../$FILES_FOLDER_POST_INSTALL_DB, failed installation?"
fi
- cp "../$FILES_FOLDER/$DB_FILE" "$TMP" ||
- error "Can't copy ../$FILES_FOLDER/$DB_FILE in $TMP"
- echo "File $FILES_FOLDER/$DB_FILE is set in $TMP"
+ wiki_delete_db
+ cp -R "../$FILES_FOLDER_POST_INSTALL_DB/"* "../$FILES_FOLDER_DB/" ||
+ error "Can't copy ../$FILES_FOLDER_POST_INSTALL_DB/* to ../$FILES_FOLDER_DB/*"
+ echo "File $FILES_FOLDER_DB/* has been reset"
}
# Delete the wiki created in the web server's directory and all its content
@@ -420,13 +426,7 @@ wiki_delete () {
rm -rf "$WIKI_DIR_INST/$WIKI_DIR_NAME" ||
error "Wiki's directory $WIKI_DIR_INST/" \
"$WIKI_DIR_NAME could not be deleted"
- # Delete the wiki's SQLite database.
- rm -f "$TMP/$DB_FILE" ||
- error "Database $TMP/$DB_FILE could not be deleted."
fi
-
- # Delete the wiki's SQLite database
- rm -f "$TMP/$DB_FILE" || error "Database $TMP/$DB_FILE could not be deleted."
- rm -f "$FILES_FOLDER/$DB_FILE"
- rm -rf "$TMP/mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz"
+ wiki_delete_db
+ wiki_delete_db_backup
}
diff --git a/contrib/mw-to-git/t/test-gitmw.pl b/contrib/mw-to-git/t/test-gitmw.pl
index 0ff76259fa..c5d687f078 100755
--- a/contrib/mw-to-git/t/test-gitmw.pl
+++ b/contrib/mw-to-git/t/test-gitmw.pl
@@ -24,9 +24,7 @@
use MediaWiki::API;
use Getopt::Long;
-use encoding 'utf8';
use DateTime::Format::ISO8601;
-use open ':encoding(utf8)';
use constant SLASH_REPLACEMENT => "%2F";
#Parsing of the config file
@@ -87,7 +85,7 @@ sub wiki_getpage {
# Replace spaces by underscore in the page name
$pagename =~ s/ /_/g;
$pagename =~ s/\//%2F/g;
- open(my $file, ">$destdir/$pagename.mw");
+ open(my $file, ">:encoding(UTF-8)", "$destdir/$pagename.mw");
print $file "$content";
close ($file);
@@ -172,7 +170,7 @@ sub wiki_getallpagename {
cmlimit => 500 },
)
|| die $mw->{error}->{code}.": ".$mw->{error}->{details};
- open(my $file, ">all.txt");
+ open(my $file, ">:encoding(UTF-8)", "all.txt");
foreach my $page (@{$mw_pages}) {
print $file "$page->{title}\n";
}
@@ -185,7 +183,7 @@ sub wiki_getallpagename {
aplimit => 500,
})
|| die $mw->{error}->{code}.": ".$mw->{error}->{details};
- open(my $file, ">all.txt");
+ open(my $file, ">:encoding(UTF-8)", "all.txt");
foreach my $page (@{$mw_pages}) {
print $file "$page->{title}\n";
}
@@ -214,12 +212,12 @@ my $fct_to_call = shift;
wiki_login($wiki_admin, $wiki_admin_pass);
-my %functions_to_call = qw(
- upload_file wiki_upload_file
- get_page wiki_getpage
- delete_page wiki_delete_page
- edit_page wiki_editpage
- getallpagename wiki_getallpagename
+my %functions_to_call = (
+ upload_file => \&wiki_upload_file,
+ get_page => \&wiki_getpage,
+ delete_page => \&wiki_delete_page,
+ edit_page => \&wiki_editpage,
+ getallpagename => \&wiki_getallpagename,
);
die "$0 ERROR: wrong argument" unless exists $functions_to_call{$fct_to_call};
-&{$functions_to_call{$fct_to_call}}(@ARGV);
+$functions_to_call{$fct_to_call}->(map { utf8::decode($_); $_ } @ARGV);
diff --git a/contrib/mw-to-git/t/test.config b/contrib/mw-to-git/t/test.config
index 5ba0684162..ed10b3e4a4 100644
--- a/contrib/mw-to-git/t/test.config
+++ b/contrib/mw-to-git/t/test.config
@@ -3,15 +3,11 @@ WIKI_DIR_NAME=wiki
# Login and password of the wiki's admin
WIKI_ADMIN=WikiAdmin
-WIKI_PASSW=AdminPass
+WIKI_PASSW=AdminPass1
# Address of the web server
SERVER_ADDR=localhost
-# SQLite database of the wiki, named DB_FILE, is located in TMP
-TMP=/tmp
-DB_FILE=wikidb.sqlite
-
# If LIGHTTPD is not set to true, the script will use the default
# web server running in WIKI_DIR_INST.
WIKI_DIR_INST=/var/www
@@ -28,10 +24,17 @@ WEB=WEB
WEB_TMP=$WEB/tmp
WEB_WWW=$WEB/www
+# Where our configuration for the wiki is located
+FILES_FOLDER=mediawiki
+FILES_FOLDER_DOWNLOAD=$FILES_FOLDER/download
+FILES_FOLDER_DB=$FILES_FOLDER/db
+FILES_FOLDER_POST_INSTALL_DB=$FILES_FOLDER/post-install-db
+
# The variables below are used by the script to install a wiki.
# You should not modify these unless you are modifying the script itself.
-# tested versions: 1.19.X -> 1.21.1
-MW_VERSION_MAJOR=1.21
-MW_VERSION_MINOR=1
-FILES_FOLDER=install-wiki
-DB_INSTALL_SCRIPT=db_install.php
+# tested versions: 1.19.X -> 1.21.1 -> 1.34.2
+#
+# See https://www.mediawiki.org/wiki/Download for what the latest
+# version is.
+MW_VERSION_MAJOR=1.34
+MW_VERSION_MINOR=2
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
index 352deda69d..0db02fe3c0 100644
--- a/contrib/subtree/git-subtree.txt
+++ b/contrib/subtree/git-subtree.txt
@@ -139,12 +139,12 @@ OPTIONS
-m <message>::
--message=<message>::
- This option is only valid for add, merge and pull (unsure).
+ This option is only valid for add, merge, pull, and split --rejoin.
Specify <message> as the commit message for the merge commit.
-OPTIONS FOR add, merge, push, pull
-----------------------------------
+OPTIONS FOR add, merge, and pull
+--------------------------------
--squash::
This option is only valid for add, merge, and pull
commands.
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 53d7accf94..57ff4b25c1 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -196,8 +196,7 @@ test_expect_success 'merge new subproj history into sub dir/ with --prefix' '
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
- check_equal "$(last_commit_message)" \
- "Merge commit '\''$(git rev-parse FETCH_HEAD)'\'' into master"
+ check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
)
'
@@ -274,8 +273,7 @@ test_expect_success 'merge new subproj history into subdir/ with a slash appende
cd "$test_count" &&
git fetch ./subproj master &&
git subtree merge --prefix=subdir/ FETCH_HEAD &&
- check_equal "$(last_commit_message)" \
- "Merge commit '\''$(git rev-parse FETCH_HEAD)'\'' into master"
+ check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
)
'
diff --git a/contrib/svn-fe/.gitignore b/contrib/svn-fe/.gitignore
deleted file mode 100644
index 02a7791585..0000000000
--- a/contrib/svn-fe/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/*.xml
-/*.1
-/*.html
-/svn-fe
diff --git a/contrib/svn-fe/Makefile b/contrib/svn-fe/Makefile
deleted file mode 100644
index e8651aaf4b..0000000000
--- a/contrib/svn-fe/Makefile
+++ /dev/null
@@ -1,105 +0,0 @@
-all:: svn-fe$X
-
-CC = cc
-RM = rm -f
-MV = mv
-
-CFLAGS = -g -O2 -Wall
-LDFLAGS =
-EXTLIBS = -lz
-
-include ../../config.mak.uname
--include ../../config.mak.autogen
--include ../../config.mak
-
-ifeq ($(uname_S),Darwin)
- ifndef NO_FINK
- ifeq ($(shell test -d /sw/lib && echo y),y)
- CFLAGS += -I/sw/include
- LDFLAGS += -L/sw/lib
- endif
- endif
- ifndef NO_DARWIN_PORTS
- ifeq ($(shell test -d /opt/local/lib && echo y),y)
- CFLAGS += -I/opt/local/include
- LDFLAGS += -L/opt/local/lib
- endif
- endif
-endif
-
-ifndef NO_OPENSSL
- EXTLIBS += -lssl
- ifdef NEEDS_CRYPTO_WITH_SSL
- EXTLIBS += -lcrypto
- endif
-endif
-
-ifndef NO_PTHREADS
- CFLAGS += $(PTHREADS_CFLAGS)
- EXTLIBS += $(PTHREAD_LIBS)
-endif
-
-ifdef HAVE_CLOCK_GETTIME
- CFLAGS += -DHAVE_CLOCK_GETTIME
- EXTLIBS += -lrt
-endif
-
-ifdef NEEDS_LIBICONV
- EXTLIBS += -liconv
-endif
-
-GIT_LIB = ../../libgit.a
-VCSSVN_LIB = ../../vcs-svn/lib.a
-XDIFF_LIB = ../../xdiff/lib.a
-
-LIBS = $(VCSSVN_LIB) $(GIT_LIB) $(XDIFF_LIB)
-
-QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
-QUIET_SUBDIR1 =
-
-ifneq ($(findstring $(MAKEFLAGS),w),w)
-PRINT_DIR = --no-print-directory
-else # "make -w"
-NO_SUBDIR = :
-endif
-
-ifneq ($(findstring $(MAKEFLAGS),s),s)
-ifndef V
- QUIET_CC = @echo ' ' CC $@;
- QUIET_LINK = @echo ' ' LINK $@;
- QUIET_SUBDIR0 = +@subdir=
- QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
- $(MAKE) $(PRINT_DIR) -C $$subdir
-endif
-endif
-
-svn-fe$X: svn-fe.o $(VCSSVN_LIB) $(XDIFF_LIB) $(GIT_LIB)
- $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(EXTLIBS) -o $@ svn-fe.o $(LIBS)
-
-svn-fe.o: svn-fe.c ../../vcs-svn/svndump.h
- $(QUIET_CC)$(CC) $(CFLAGS) -I../../vcs-svn -o $*.o -c $<
-
-svn-fe.html: svn-fe.txt
- $(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
- MAN_TXT=../contrib/svn-fe/svn-fe.txt \
- ../contrib/svn-fe/$@
-
-svn-fe.1: svn-fe.txt
- $(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
- MAN_TXT=../contrib/svn-fe/svn-fe.txt \
- ../contrib/svn-fe/$@
- $(MV) ../../Documentation/svn-fe.1 .
-
-../../vcs-svn/lib.a: FORCE
- $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) vcs-svn/lib.a
-
-../../xdiff/lib.a: FORCE
- $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) xdiff/lib.a
-
-../../libgit.a: FORCE
- $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) libgit.a
-
-clean:
- $(RM) svn-fe$X svn-fe.o svn-fe.html svn-fe.xml svn-fe.1
-
-.PHONY: all clean FORCE
diff --git a/contrib/svn-fe/svn-fe.c b/contrib/svn-fe/svn-fe.c
deleted file mode 100644
index f363505abb..0000000000
--- a/contrib/svn-fe/svn-fe.c
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * This file is in the public domain.
- * You may freely use, modify, distribute, and relicense it.
- */
-
-#include <stdlib.h>
-#include "svndump.h"
-
-int main(int argc, char **argv)
-{
- if (svndump_init(NULL))
- return 1;
- svndump_read((argc > 1) ? argv[1] : NULL, "refs/heads/master",
- "refs/notes/svn/revs");
- svndump_deinit();
- svndump_reset();
- return 0;
-}
diff --git a/contrib/svn-fe/svn-fe.txt b/contrib/svn-fe/svn-fe.txt
deleted file mode 100644
index 19333fc8df..0000000000
--- a/contrib/svn-fe/svn-fe.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-svn-fe(1)
-=========
-
-NAME
-----
-svn-fe - convert an SVN "dumpfile" to a fast-import stream
-
-SYNOPSIS
---------
-[verse]
-mkfifo backchannel &&
-svnadmin dump --deltas REPO |
- svn-fe [url] 3<backchannel |
- git fast-import --cat-blob-fd=3 3>backchannel
-
-DESCRIPTION
------------
-
-Converts a Subversion dumpfile into input suitable for
-git-fast-import(1) and similar importers. REPO is a path to a
-Subversion repository mirrored on the local disk. Remote Subversion
-repositories can be mirrored on local disk using the `svnsync`
-command.
-
-Note: this tool is very young. The details of its commandline
-interface may change in backward incompatible ways.
-
-INPUT FORMAT
-------------
-Subversion's repository dump format is documented in full in
-`notes/dump-load-format.txt` from the Subversion source tree.
-Files in this format can be generated using the 'svnadmin dump' or
-'svk admin dump' command.
-
-OUTPUT FORMAT
--------------
-The fast-import format is documented by the git-fast-import(1)
-manual page.
-
-NOTES
------
-Subversion dumps do not record a separate author and committer for
-each revision, nor do they record a separate display name and email
-address for each author. Like git-svn(1), 'svn-fe' will use the name
-
----------
-user <user@UUID>
----------
-
-as committer, where 'user' is the value of the `svn:author` property
-and 'UUID' the repository's identifier.
-
-To support incremental imports, 'svn-fe' puts a `git-svn-id` line at
-the end of each commit log message if passed a URL on the command
-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-repo --subdirectory-filter' command
-may be useful for this purpose.
-
-BUGS
-----
-Empty directories and unknown properties are silently discarded.
-
-The exit status does not reflect whether an error was detected.
-
-SEE ALSO
---------
-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
deleted file mode 100755
index 8a3cee6175..0000000000
--- a/contrib/svn-fe/svnrdump_sim.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python
-"""
-Simulates svnrdump by replaying an existing dump from a file, taking care
-of the specified revision range.
-To simulate incremental imports the environment variable SVNRMAX can be set
-to the highest revision that should be available.
-"""
-import sys
-import os
-
-if sys.hexversion < 0x02040000:
- # The limiter is the ValueError() calls. This may be too conservative
- sys.stderr.write("svnrdump-sim.py: requires Python 2.4 or later.\n")
- sys.exit(1)
-
-
-def getrevlimit():
- var = 'SVNRMAX'
- if var in os.environ:
- return os.environ[var]
- return None
-
-
-def writedump(url, lower, upper):
- if url.startswith('sim://'):
- filename = url[6:]
- if filename[-1] == '/':
- filename = filename[:-1] # remove terminating slash
- else:
- raise ValueError('sim:// url required')
- f = open(filename, 'r')
- state = 'header'
- wroterev = False
- while(True):
- l = f.readline()
- if l == '':
- break
- if state == 'header' and l.startswith('Revision-number: '):
- state = 'prefix'
- if state == 'prefix' and l == 'Revision-number: %s\n' % lower:
- state = 'selection'
- if not upper == 'HEAD' and state == 'selection' and \
- l == 'Revision-number: %s\n' % upper:
- break
-
- if state == 'header' or state == 'selection':
- if state == 'selection':
- wroterev = True
- sys.stdout.write(l)
- return wroterev
-
-if __name__ == "__main__":
- if not (len(sys.argv) in (3, 4, 5)):
- print("usage: %s dump URL -rLOWER:UPPER")
- sys.exit(1)
- if not sys.argv[1] == 'dump':
- raise NotImplementedError('only "dump" is supported.')
- url = sys.argv[2]
- r = ('0', 'HEAD')
- if len(sys.argv) == 4 and sys.argv[3][0:2] == '-r':
- r = sys.argv[3][2:].lstrip().split(':')
- if not getrevlimit() is None:
- r[1] = getrevlimit()
- if writedump(url, r[0], r[1]):
- ret = 0
- else:
- ret = 1
- sys.exit(ret)
diff --git a/convert.c b/convert.c
index 572449825c..8e6c292421 100644
--- a/convert.c
+++ b/convert.c
@@ -638,7 +638,6 @@ static int filter_buffer_or_fd(int in, int out, void *data)
struct child_process child_process = CHILD_PROCESS_INIT;
struct filter_params *params = (struct filter_params *)data;
int write_err, status;
- const char *argv[] = { NULL, NULL };
/* apply % substitution to cmd */
struct strbuf cmd = STRBUF_INIT;
@@ -656,9 +655,7 @@ static int filter_buffer_or_fd(int in, int out, void *data)
strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict);
strbuf_release(&path);
- argv[0] = cmd.buf;
-
- child_process.argv = argv;
+ strvec_push(&child_process.args, cmd.buf);
child_process.use_shell = 1;
child_process.in = -1;
child_process.out = out;
diff --git a/credential.c b/credential.c
index d8d226b97e..efc29dc5e1 100644
--- a/credential.c
+++ b/credential.c
@@ -274,11 +274,9 @@ static int run_credential_helper(struct credential *c,
int want_output)
{
struct child_process helper = CHILD_PROCESS_INIT;
- const char *argv[] = { NULL, NULL };
FILE *fp;
- argv[0] = cmd;
- helper.argv = argv;
+ strvec_push(&helper.args, cmd);
helper.use_shell = 1;
helper.in = -1;
if (want_output)
diff --git a/daemon.c b/daemon.c
index fd669ed3b4..2ab7ea82eb 100644
--- a/daemon.c
+++ b/daemon.c
@@ -296,7 +296,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
return NULL; /* Fallthrough. Deny by default */
}
-typedef int (*daemon_service_fn)(const struct argv_array *env);
+typedef int (*daemon_service_fn)(const struct strvec *env);
struct daemon_service {
const char *name;
const char *config_name;
@@ -377,7 +377,7 @@ error_return:
}
static int run_service(const char *dir, struct daemon_service *service,
- struct hostinfo *hi, const struct argv_array *env)
+ struct hostinfo *hi, const struct strvec *env)
{
const char *path;
int enabled = service->enabled;
@@ -462,7 +462,7 @@ static void copy_to_log(int fd)
static int run_service_command(struct child_process *cld)
{
- argv_array_push(&cld->args, ".");
+ strvec_push(&cld->args, ".");
cld->git_cmd = 1;
cld->err = -1;
if (start_command(cld))
@@ -476,33 +476,33 @@ static int run_service_command(struct child_process *cld)
return finish_command(cld);
}
-static int upload_pack(const struct argv_array *env)
+static int upload_pack(const struct strvec *env)
{
struct child_process cld = CHILD_PROCESS_INIT;
- argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
- argv_array_pushf(&cld.args, "--timeout=%u", timeout);
+ strvec_pushl(&cld.args, "upload-pack", "--strict", NULL);
+ strvec_pushf(&cld.args, "--timeout=%u", timeout);
- argv_array_pushv(&cld.env_array, env->argv);
+ strvec_pushv(&cld.env_array, env->v);
return run_service_command(&cld);
}
-static int upload_archive(const struct argv_array *env)
+static int upload_archive(const struct strvec *env)
{
struct child_process cld = CHILD_PROCESS_INIT;
- argv_array_push(&cld.args, "upload-archive");
+ strvec_push(&cld.args, "upload-archive");
- argv_array_pushv(&cld.env_array, env->argv);
+ strvec_pushv(&cld.env_array, env->v);
return run_service_command(&cld);
}
-static int receive_pack(const struct argv_array *env)
+static int receive_pack(const struct strvec *env)
{
struct child_process cld = CHILD_PROCESS_INIT;
- argv_array_push(&cld.args, "receive-pack");
+ strvec_push(&cld.args, "receive-pack");
- argv_array_pushv(&cld.env_array, env->argv);
+ strvec_pushv(&cld.env_array, env->v);
return run_service_command(&cld);
}
@@ -633,7 +633,7 @@ static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
return extra_args;
}
-static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
+static void parse_extra_args(struct hostinfo *hi, struct strvec *env,
char *extra_args, int buflen)
{
const char *end = extra_args + buflen;
@@ -664,8 +664,8 @@ static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
if (git_protocol.len > 0) {
loginfo("Extended attribute \"protocol\": %s", git_protocol.buf);
- argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
- git_protocol.buf);
+ strvec_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
+ git_protocol.buf);
}
strbuf_release(&git_protocol);
}
@@ -761,7 +761,7 @@ static int execute(void)
int pktlen, len, i;
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
struct hostinfo hi;
- struct argv_array env = ARGV_ARRAY_INIT;
+ struct strvec env = STRVEC_INIT;
hostinfo_init(&hi);
@@ -794,13 +794,13 @@ static int execute(void)
*/
int rc = run_service(arg, s, &hi, &env);
hostinfo_clear(&hi);
- argv_array_clear(&env);
+ strvec_clear(&env);
return rc;
}
}
hostinfo_clear(&hi);
- argv_array_clear(&env);
+ strvec_clear(&env);
logerror("Protocol error: '%s'", line);
return -1;
}
@@ -893,7 +893,7 @@ static void check_dead_children(void)
cradle = &blanket->next;
}
-static struct argv_array cld_argv = ARGV_ARRAY_INIT;
+static struct strvec cld_argv = STRVEC_INIT;
static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
{
struct child_process cld = CHILD_PROCESS_INIT;
@@ -913,21 +913,21 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
char buf[128] = "";
struct sockaddr_in *sin_addr = (void *) addr;
inet_ntop(addr->sa_family, &sin_addr->sin_addr, buf, sizeof(buf));
- argv_array_pushf(&cld.env_array, "REMOTE_ADDR=%s", buf);
- argv_array_pushf(&cld.env_array, "REMOTE_PORT=%d",
- ntohs(sin_addr->sin_port));
+ strvec_pushf(&cld.env_array, "REMOTE_ADDR=%s", buf);
+ strvec_pushf(&cld.env_array, "REMOTE_PORT=%d",
+ ntohs(sin_addr->sin_port));
#ifndef NO_IPV6
} else if (addr->sa_family == AF_INET6) {
char buf[128] = "";
struct sockaddr_in6 *sin6_addr = (void *) addr;
inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(buf));
- argv_array_pushf(&cld.env_array, "REMOTE_ADDR=[%s]", buf);
- argv_array_pushf(&cld.env_array, "REMOTE_PORT=%d",
- ntohs(sin6_addr->sin6_port));
+ strvec_pushf(&cld.env_array, "REMOTE_ADDR=[%s]", buf);
+ strvec_pushf(&cld.env_array, "REMOTE_PORT=%d",
+ ntohs(sin6_addr->sin6_port));
#endif
}
- cld.argv = cld_argv.argv;
+ cld.argv = cld_argv.v;
cld.in = incoming;
cld.out = dup(incoming);
@@ -1476,10 +1476,10 @@ int cmd_main(int argc, const char **argv)
write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
/* prepare argv for serving-processes */
- argv_array_push(&cld_argv, argv[0]); /* git-daemon */
- argv_array_push(&cld_argv, "--serve");
+ strvec_push(&cld_argv, argv[0]); /* git-daemon */
+ strvec_push(&cld_argv, "--serve");
for (i = 1; i < argc; ++i)
- argv_array_push(&cld_argv, argv[i]);
+ strvec_push(&cld_argv, argv[i]);
return serve(&listen_addr, listen_port, cred);
}
diff --git a/diff-lib.c b/diff-lib.c
index 25fd2dee19..346fdcf0b0 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -219,7 +219,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
continue;
} else if (revs->diffopt.ita_invisible_in_index &&
ce_intent_to_add(ce)) {
- diff_addremove(&revs->diffopt, '+', ce->ce_mode,
+ newmode = ce_mode_from_stat(ce, st.st_mode);
+ diff_addremove(&revs->diffopt, '+', newmode,
&null_oid, 0, ce->name, 0);
continue;
}
@@ -404,14 +405,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
/* if the entry is not checked out, don't examine work tree */
cached = o->index_only ||
(idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx)));
- /*
- * Backward compatibility wart - "diff-index -m" does
- * not mean "do not ignore merges", but "match_missing".
- *
- * But with the revision flag parsing, that's found in
- * "!revs->ignore_merges".
- */
- match_missing = !revs->ignore_merges;
+
+ match_missing = revs->match_missing;
if (cached && idx && ce_stage(idx)) {
struct diff_filepair *pair;
@@ -570,3 +565,28 @@ int index_differs_from(struct repository *r,
object_array_clear(&rev.pending);
return (rev.diffopt.flags.has_changes != 0);
}
+
+static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
+{
+ return data;
+}
+
+void show_interdiff(const struct object_id *oid1, const struct object_id *oid2,
+ int indent, struct diff_options *diffopt)
+{
+ struct diff_options opts;
+ struct strbuf prefix = STRBUF_INIT;
+
+ memcpy(&opts, diffopt, sizeof(opts));
+ opts.output_format = DIFF_FORMAT_PATCH;
+ opts.output_prefix = idiff_prefix_cb;
+ strbuf_addchars(&prefix, ' ', indent);
+ opts.output_prefix_data = &prefix;
+ diff_setup_done(&opts);
+
+ diff_tree_oid(oid1, oid2, "", &opts);
+ diffcore_std(&opts);
+ diff_flush(&opts);
+
+ strbuf_release(&prefix);
+}
diff --git a/diff.c b/diff.c
index d24aaa3047..2bb2f8f57e 100644
--- a/diff.c
+++ b/diff.c
@@ -20,7 +20,7 @@
#include "hashmap.h"
#include "ll-merge.h"
#include "string-list.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "graph.h"
#include "packfile.h"
#include "parse-options.h"
@@ -482,14 +482,14 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
static char *quote_two(const char *one, const char *two)
{
- int need_one = quote_c_style(one, NULL, NULL, 1);
- int need_two = quote_c_style(two, NULL, NULL, 1);
+ int need_one = quote_c_style(one, NULL, NULL, CQUOTE_NODQ);
+ int need_two = quote_c_style(two, NULL, NULL, CQUOTE_NODQ);
struct strbuf res = STRBUF_INIT;
if (need_one + need_two) {
strbuf_addch(&res, '"');
- quote_c_style(one, &res, NULL, 1);
- quote_c_style(two, &res, NULL, 1);
+ quote_c_style(one, &res, NULL, CQUOTE_NODQ);
+ quote_c_style(two, &res, NULL, CQUOTE_NODQ);
strbuf_addch(&res, '"');
} else {
strbuf_addstr(&res, one);
@@ -3153,16 +3153,19 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
gather_dirstat(options, &dir, changed, "", 0);
}
+static void free_diffstat_file(struct diffstat_file *f)
+{
+ free(f->print_name);
+ free(f->name);
+ free(f->from_name);
+ free(f);
+}
+
void free_diffstat_info(struct diffstat_t *diffstat)
{
int i;
- for (i = 0; i < diffstat->nr; i++) {
- struct diffstat_file *f = diffstat->files[i];
- free(f->print_name);
- free(f->name);
- free(f->from_name);
- free(f);
- }
+ for (i = 0; i < diffstat->nr; i++)
+ free_diffstat_file(diffstat->files[i]);
free(diffstat->files);
}
@@ -3429,7 +3432,7 @@ static void builtin_diff(const char *name_a,
if (o->submodule_format == DIFF_SUBMODULE_LOG &&
(!one->mode || S_ISGITLINK(one->mode)) &&
(!two->mode || S_ISGITLINK(two->mode))) {
- show_submodule_summary(o, one->path ? one->path : two->path,
+ show_submodule_diff_summary(o, one->path ? one->path : two->path,
&one->oid, &two->oid,
two->dirty_submodule);
return;
@@ -3660,7 +3663,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
{
mmfile_t mf1, mf2;
struct diffstat_file *data;
- int same_contents;
+ int may_differ;
int complete_rewrite = 0;
if (!DIFF_PAIR_UNMERGED(p)) {
@@ -3678,12 +3681,14 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
return;
}
- same_contents = oideq(&one->oid, &two->oid);
+ /* saves some reads if true, not a guarantee of diff outcome */
+ may_differ = !(one->oid_valid && two->oid_valid &&
+ oideq(&one->oid, &two->oid));
if (diff_filespec_is_binary(o->repo, one) ||
diff_filespec_is_binary(o->repo, two)) {
data->is_binary = 1;
- if (same_contents) {
+ if (!may_differ) {
data->added = 0;
data->deleted = 0;
} else {
@@ -3699,7 +3704,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
data->added = count_lines(two->data, two->size);
}
- else if (!same_contents) {
+ else if (may_differ) {
/* Crazy xdl interfaces.. */
xpparam_t xpp;
xdemitconf_t xecfg;
@@ -3718,6 +3723,27 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
if (xdi_diff_outf(&mf1, &mf2, discard_hunk_line,
diffstat_consume, diffstat, &xpp, &xecfg))
die("unable to generate diffstat for %s", one->path);
+
+ if (DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two)) {
+ struct diffstat_file *file =
+ diffstat->files[diffstat->nr - 1];
+ /*
+ * Omit diffstats of modified files where nothing changed.
+ * Even if may_differ, this might be the case due to
+ * ignoring whitespace changes, etc.
+ *
+ * But note that we special-case additions, deletions,
+ * renames, and mode changes as adding an empty file,
+ * for example is still of interest.
+ */
+ if ((p->status == DIFF_STATUS_MODIFIED)
+ && !file->added
+ && !file->deleted
+ && one->mode == two->mode) {
+ free_diffstat_file(file);
+ diffstat->nr--;
+ }
+ }
}
diff_free_filespec_data(one);
@@ -4192,14 +4218,14 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
}
static void add_external_diff_name(struct repository *r,
- struct argv_array *argv,
+ struct strvec *argv,
const char *name,
struct diff_filespec *df)
{
struct diff_tempfile *temp = prepare_temp_file(r, name, df);
- argv_array_push(argv, temp->name);
- argv_array_push(argv, temp->hex);
- argv_array_push(argv, temp->mode);
+ strvec_push(argv, temp->name);
+ strvec_push(argv, temp->hex);
+ strvec_push(argv, temp->mode);
}
/* An external diff command takes:
@@ -4216,12 +4242,12 @@ static void run_external_diff(const char *pgm,
const char *xfrm_msg,
struct diff_options *o)
{
- struct argv_array argv = ARGV_ARRAY_INIT;
- struct argv_array env = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
+ struct strvec env = STRVEC_INIT;
struct diff_queue_struct *q = &diff_queued_diff;
- argv_array_push(&argv, pgm);
- argv_array_push(&argv, name);
+ strvec_push(&argv, pgm);
+ strvec_push(&argv, name);
if (one && two) {
add_external_diff_name(o->repo, &argv, name, one);
@@ -4229,22 +4255,22 @@ static void run_external_diff(const char *pgm,
add_external_diff_name(o->repo, &argv, name, two);
else {
add_external_diff_name(o->repo, &argv, other, two);
- argv_array_push(&argv, other);
- argv_array_push(&argv, xfrm_msg);
+ strvec_push(&argv, other);
+ strvec_push(&argv, xfrm_msg);
}
}
- argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
- argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+ strvec_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
+ strvec_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
diff_free_filespec_data(one);
diff_free_filespec_data(two);
- if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
+ if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v))
die(_("external diff died, stopping at %s"), name);
remove_tempfile();
- argv_array_clear(&argv);
- argv_array_clear(&env);
+ strvec_clear(&argv);
+ strvec_clear(&env);
}
static int similarity_index(struct diff_filepair *p)
@@ -4319,7 +4345,10 @@ static void fill_metainfo(struct strbuf *msg,
}
if (one && two && !oideq(&one->oid, &two->oid)) {
const unsigned hexsz = the_hash_algo->hexsz;
- int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV;
+ int abbrev = o->abbrev ? o->abbrev : DEFAULT_ABBREV;
+
+ if (o->flags.full_index)
+ abbrev = hexsz;
if (o->flags.binary) {
mmfile_t mf;
@@ -6044,6 +6073,8 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
struct patch_id_t *data = priv;
int new_len;
+ if (len > 12 && starts_with(line, "\\ "))
+ return;
new_len = remove_space(line, len);
the_hash_algo->update_fn(data->ctx, line, new_len);
diff --git a/diff.h b/diff.h
index 9443dc1b00..3de343270f 100644
--- a/diff.h
+++ b/diff.h
@@ -287,8 +287,6 @@ struct diff_options {
/* If non-zero, then stop computing after this many changes. */
int max_changes;
- /* For internal use only. */
- int num_changes;
int ita_invisible_in_index;
/* white-space error highlighting */
@@ -431,11 +429,11 @@ struct combine_diff_path *diff_tree_paths(
struct combine_diff_path *p, const struct object_id *oid,
const struct object_id **parents_oid, int nparent,
struct strbuf *base, struct diff_options *opt);
-int diff_tree_oid(const struct object_id *old_oid,
- const struct object_id *new_oid,
- const char *base, struct diff_options *opt);
-int diff_root_tree_oid(const struct object_id *new_oid, const char *base,
- struct diff_options *opt);
+void diff_tree_oid(const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ const char *base, struct diff_options *opt);
+void diff_root_tree_oid(const struct object_id *new_oid, const char *base,
+ struct diff_options *opt);
struct combine_diff_path {
struct combine_diff_path *next;
@@ -601,6 +599,13 @@ int index_differs_from(struct repository *r, const char *def,
int ita_invisible_in_index);
/*
+ * Emit an interdiff of two object ID's to 'diff_options.file' optionally
+ * indented by 'indent' spaces.
+ */
+void show_interdiff(const struct object_id *, const struct object_id *,
+ int indent, struct diff_options *);
+
+/*
* Fill the contents of the filespec "df", respecting any textconv defined by
* its userdiff driver. The "driver" parameter must come from a
* previous call to get_textconv(), and therefore should either be NULL or have
diff --git a/dir.c b/dir.c
index 1045cc9c6f..3018a657b0 100644
--- a/dir.c
+++ b/dir.c
@@ -54,6 +54,11 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
static int resolve_dtype(int dtype, struct index_state *istate,
const char *path, int len);
+void dir_init(struct dir_struct *dir)
+{
+ memset(dir, 0, sizeof(*dir));
+}
+
int count_slashes(const char *s)
{
int cnt = 0;
@@ -916,6 +921,8 @@ void clear_pattern_list(struct pattern_list *pl)
free(pl->patterns[i]);
free(pl->patterns);
free(pl->filebuf);
+ hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent);
+ hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent);
memset(pl, 0, sizeof(*pl));
}
@@ -1792,9 +1799,12 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
nested_repo = is_nonbare_repository_dir(&sb);
strbuf_release(&sb);
}
- if (nested_repo)
- return ((dir->flags & DIR_SKIP_NESTED_GIT) ? path_none :
- (excluded ? path_excluded : path_untracked));
+ if (nested_repo) {
+ if ((dir->flags & DIR_SKIP_NESTED_GIT) ||
+ (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC))
+ return path_none;
+ return excluded ? path_excluded : path_untracked;
+ }
if (!(dir->flags & DIR_SHOW_OTHER_DIRECTORIES)) {
if (excluded &&
@@ -2209,13 +2219,13 @@ static enum path_treatment treat_path(struct dir_struct *dir,
baselen, excluded, pathspec);
case DT_REG:
case DT_LNK:
- if (excluded)
- return path_excluded;
if (pathspec &&
!match_pathspec(istate, pathspec, path->buf, path->len,
0 /* prefix */, NULL /* seen */,
0 /* is_dir */))
return path_none;
+ if (excluded)
+ return path_excluded;
return path_untracked;
}
}
@@ -3009,10 +3019,10 @@ int remove_path(const char *name)
}
/*
- * Frees memory within dir which was allocated for exclude lists and
- * the exclude_stack. Does not free dir itself.
+ * Frees memory within dir which was allocated, and resets fields for further
+ * use. Does not free dir itself.
*/
-void clear_directory(struct dir_struct *dir)
+void dir_clear(struct dir_struct *dir)
{
int i, j;
struct exclude_list_group *group;
@@ -3030,6 +3040,13 @@ void clear_directory(struct dir_struct *dir)
free(group->pl);
}
+ for (i = 0; i < dir->ignored_nr; i++)
+ free(dir->ignored[i]);
+ for (i = 0; i < dir->nr; i++)
+ free(dir->entries[i]);
+ free(dir->ignored);
+ free(dir->entries);
+
stk = dir->exclude_stack;
while (stk) {
struct exclude_stack *prev = stk->prev;
@@ -3037,6 +3054,8 @@ void clear_directory(struct dir_struct *dir)
stk = prev;
}
strbuf_release(&dir->basebuf);
+
+ dir_init(dir);
}
struct ondisk_untracked_cache {
diff --git a/dir.h b/dir.h
index 5855c065a6..a3c40dec51 100644
--- a/dir.h
+++ b/dir.h
@@ -19,24 +19,23 @@
* CE_SKIP_WORKTREE marked. If you want to exclude files, make sure you have
* loaded the index first.
*
- * - Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
- * sizeof(dir))`.
+ * - Prepare `struct dir_struct dir` using `dir_init()` function.
*
* - 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_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.
+ * `add_patterns_from_file()` , and/or set `dir.exclude_per_dir`.
*
- * - Set options described in the Data Structure section above.
+ * - A short-hand function `setup_standard_excludes()` can be used to set
+ * up the standard set of exclude settings, instead of manually calling
+ * the add_pattern*() family of functions.
*
- * - Call `read_directory()`.
+ * - Call `fill_directory()`.
*
- * - Use `dir.entries[]`.
+ * - Use `dir.entries[]` and `dir.ignored[]`.
*
- * - Call `clear_directory()` when none of the contained elements are no longer in use.
+ * - Call `dir_clear()` when the contained elements are no longer in use.
*
*/
@@ -362,6 +361,8 @@ int match_pathspec(const struct index_state *istate,
int report_path_error(const char *ps_matched, const struct pathspec *pathspec);
int within_depth(const char *name, int namelen, int depth, int max_depth);
+void dir_init(struct dir_struct *dir);
+
int fill_directory(struct dir_struct *dir,
struct index_state *istate,
const struct pathspec *pathspec);
@@ -428,7 +429,7 @@ void parse_path_pattern(const char **string, int *patternlen, unsigned *flags, i
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);
+void dir_clear(struct dir_struct *dir);
int repo_file_exists(struct repository *repo, const char *path);
int file_exists(const char *);
diff --git a/editor.c b/editor.c
index 91989ee8a1..6303ae0ab0 100644
--- a/editor.c
+++ b/editor.c
@@ -40,7 +40,7 @@ const char *git_sequence_editor(void)
const char *editor = getenv("GIT_SEQUENCE_EDITOR");
if (!editor)
- git_config_get_string_const("sequence.editor", &editor);
+ git_config_get_string_tmp("sequence.editor", &editor);
if (!editor)
editor = git_editor();
diff --git a/entry.c b/entry.c
index 449bd32dee..a0532f1f00 100644
--- a/entry.c
+++ b/entry.c
@@ -510,8 +510,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state,
/* If it is a gitlink, leave it alone! */
if (S_ISGITLINK(ce->ce_mode))
return 0;
- if (!state->force)
- return error("%s is a directory", path.buf);
remove_subtree(&path);
} else if (unlink(path.buf))
return error_errno("unable to unlink old '%s'", path.buf);
diff --git a/environment.c b/environment.c
index aaca0e91ac..bb518c61cd 100644
--- a/environment.c
+++ b/environment.c
@@ -14,7 +14,7 @@
#include "refs.h"
#include "fmt-merge-msg.h"
#include "commit.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "object-store.h"
#include "chdir-notify.h"
#include "shallow.h"
@@ -35,8 +35,8 @@ int repository_format_precious_objects;
int repository_format_worktree_config;
const char *git_commit_encoding;
const char *git_log_output_encoding;
-const char *apply_default_whitespace;
-const char *apply_default_ignorewhitespace;
+char *apply_default_whitespace;
+char *apply_default_ignorewhitespace;
const char *git_attributes_file;
const char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
@@ -156,15 +156,15 @@ static char *expand_namespace(const char *raw_namespace)
* Wrapper of getenv() that returns a strdup value. This value is kept
* in argv to be freed later.
*/
-static const char *getenv_safe(struct argv_array *argv, const char *name)
+static const char *getenv_safe(struct strvec *argv, const char *name)
{
const char *value = getenv(name);
if (!value)
return NULL;
- argv_array_push(argv, value);
- return argv->argv[argv->argc - 1];
+ strvec_push(argv, value);
+ return argv->v[argv->nr - 1];
}
void setup_git_env(const char *git_dir)
@@ -172,7 +172,7 @@ void setup_git_env(const char *git_dir)
const char *shallow_file;
const char *replace_ref_base;
struct set_gitdir_args args = { NULL };
- struct argv_array to_free = ARGV_ARRAY_INIT;
+ struct strvec to_free = STRVEC_INIT;
args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
@@ -180,7 +180,7 @@ void setup_git_env(const char *git_dir)
args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
repo_set_gitdir(the_repository, git_dir, &args);
- argv_array_clear(&to_free);
+ strvec_clear(&to_free);
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
read_replace_refs = 0;
diff --git a/exec-cmd.c b/exec-cmd.c
index 7deeab3039..eeb2ee52b8 100644
--- a/exec-cmd.c
+++ b/exec-cmd.c
@@ -1,7 +1,7 @@
#include "cache.h"
#include "exec-cmd.h"
#include "quote.h"
-#include "argv-array.h"
+#include "strvec.h"
#if defined(RUNTIME_PREFIX)
@@ -320,26 +320,26 @@ void setup_path(void)
strbuf_release(&new_path);
}
-const char **prepare_git_cmd(struct argv_array *out, const char **argv)
+const char **prepare_git_cmd(struct strvec *out, const char **argv)
{
- argv_array_push(out, "git");
- argv_array_pushv(out, argv);
- return out->argv;
+ strvec_push(out, "git");
+ strvec_pushv(out, argv);
+ return out->v;
}
int execv_git_cmd(const char **argv)
{
- struct argv_array nargv = ARGV_ARRAY_INIT;
+ struct strvec nargv = STRVEC_INIT;
prepare_git_cmd(&nargv, argv);
- trace_argv_printf(nargv.argv, "trace: exec:");
+ trace_argv_printf(nargv.v, "trace: exec:");
/* execvp() can only ever return if it fails */
- sane_execvp("git", (char **)nargv.argv);
+ sane_execvp("git", (char **)nargv.v);
trace_printf("trace: exec failed: %s\n", strerror(errno));
- argv_array_clear(&nargv);
+ strvec_clear(&nargv);
return -1;
}
diff --git a/exec-cmd.h b/exec-cmd.h
index 8cd1df28d3..330b41d54d 100644
--- a/exec-cmd.h
+++ b/exec-cmd.h
@@ -1,13 +1,13 @@
#ifndef GIT_EXEC_CMD_H
#define GIT_EXEC_CMD_H
-struct argv_array;
+struct strvec;
void git_set_exec_path(const char *exec_path);
void git_resolve_executable_dir(const char *path);
const char *git_exec_path(void);
void setup_path(void);
-const char **prepare_git_cmd(struct argv_array *out, const char **argv);
+const char **prepare_git_cmd(struct strvec *out, const char **argv);
int execv_git_cmd(const char **argv); /* NULL terminated */
LAST_ARG_MUST_BE_NULL
int execl_git_cmd(const char *cmd, ...);
diff --git a/fetch-negotiator.c b/fetch-negotiator.c
index 0a1357dc9d..57ed5784e1 100644
--- a/fetch-negotiator.c
+++ b/fetch-negotiator.c
@@ -2,6 +2,7 @@
#include "fetch-negotiator.h"
#include "negotiator/default.h"
#include "negotiator/skipping.h"
+#include "negotiator/noop.h"
#include "repository.h"
void fetch_negotiator_init(struct repository *r,
@@ -13,6 +14,10 @@ void fetch_negotiator_init(struct repository *r,
skipping_negotiator_init(negotiator);
return;
+ case FETCH_NEGOTIATION_NOOP:
+ noop_negotiator_init(negotiator);
+ return;
+
case FETCH_NEGOTIATION_DEFAULT:
default:
default_negotiator_init(negotiator);
diff --git a/fetch-pack.c b/fetch-pack.c
index 80fb3bd899..b10c432315 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -108,24 +108,48 @@ static void for_each_cached_alternate(struct fetch_negotiator *negotiator,
cb(negotiator, cache.items[i]);
}
+static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
+ int mark_tags_complete)
+{
+ enum object_type type;
+ struct object_info info = { .typep = &type };
+
+ while (1) {
+ if (oid_object_info_extended(the_repository, oid, &info,
+ OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK))
+ return NULL;
+ if (type == OBJ_TAG) {
+ struct tag *tag = (struct tag *)
+ parse_object(the_repository, oid);
+
+ if (!tag->tagged)
+ return NULL;
+ if (mark_tags_complete)
+ tag->object.flags |= COMPLETE;
+ oid = &tag->tagged->oid;
+ } else {
+ break;
+ }
+ }
+ if (type == OBJ_COMMIT)
+ return (struct commit *) parse_object(the_repository, oid);
+ return NULL;
+}
+
static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
- const char *refname,
const struct object_id *oid)
{
- struct object *o = deref_tag(the_repository,
- parse_object(the_repository, oid),
- refname, 0);
-
- if (o && o->type == OBJ_COMMIT)
- negotiator->add_tip(negotiator, (struct commit *)o);
+ struct commit *c = deref_without_lazy_fetch(oid, 0);
+ if (c)
+ negotiator->add_tip(negotiator, c);
return 0;
}
static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
{
- return rev_list_insert_ref(cb_data, refname, oid);
+ return rev_list_insert_ref(cb_data, oid);
}
enum ack_type {
@@ -201,7 +225,7 @@ static void send_request(struct fetch_pack_args *args,
static void insert_one_alternate_object(struct fetch_negotiator *negotiator,
struct object *obj)
{
- rev_list_insert_ref(negotiator, NULL, &obj->oid);
+ rev_list_insert_ref(negotiator, &obj->oid);
}
#define INITIAL_FLUSH 16
@@ -230,13 +254,12 @@ static void mark_tips(struct fetch_negotiator *negotiator,
int i;
if (!negotiation_tips) {
- for_each_ref(rev_list_insert_ref_oid, negotiator);
+ for_each_rawref(rev_list_insert_ref_oid, negotiator);
return;
}
for (i = 0; i < negotiation_tips->nr; i++)
- rev_list_insert_ref(negotiator, NULL,
- &negotiation_tips->oid[i]);
+ rev_list_insert_ref(negotiator, &negotiation_tips->oid[i]);
return;
}
@@ -262,10 +285,8 @@ static int find_common(struct fetch_negotiator *negotiator,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
- if (!args->no_dependents) {
- mark_tips(negotiator, args->negotiation_tips);
- for_each_cached_alternate(negotiator, insert_one_alternate_object);
- }
+ mark_tips(negotiator, args->negotiation_tips);
+ for_each_cached_alternate(negotiator, insert_one_alternate_object);
fetching = 0;
for ( ; refs ; refs = refs->next) {
@@ -282,12 +303,8 @@ static int find_common(struct fetch_negotiator *negotiator,
* We use lookup_object here because we are only
* interested in the case we *know* the object is
* reachable and we have already scanned it.
- *
- * Do this only if args->no_dependents is false (if it is true,
- * we cannot trust the object flags).
*/
- if (!args->no_dependents &&
- ((o = lookup_object(the_repository, remote)) != NULL) &&
+ if (((o = lookup_object(the_repository, remote)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
@@ -387,8 +404,6 @@ static int find_common(struct fetch_negotiator *negotiator,
trace2_region_enter("fetch-pack", "negotiation_v0_v1", the_repository);
flushes = 0;
retval = -1;
- if (args->no_dependents)
- goto done;
while ((oid = negotiator->next(negotiator))) {
packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
print_verbose(args, "have %s", oid_to_hex(oid));
@@ -503,21 +518,11 @@ static struct commit_list *complete;
static int mark_complete(const struct object_id *oid)
{
- struct object *o = parse_object(the_repository, oid);
-
- while (o && o->type == OBJ_TAG) {
- struct tag *t = (struct tag *) o;
- if (!t->tagged)
- break; /* broken repository */
- o->flags |= COMPLETE;
- o = parse_object(the_repository, &t->tagged->oid);
- }
- if (o && o->type == OBJ_COMMIT) {
- struct commit *commit = (struct commit *)o;
- if (!(commit->object.flags & COMPLETE)) {
- commit->object.flags |= COMPLETE;
- commit_list_insert(commit, &complete);
- }
+ struct commit *commit = deref_without_lazy_fetch(oid, 1);
+
+ if (commit && !(commit->object.flags & COMPLETE)) {
+ commit->object.flags |= COMPLETE;
+ commit_list_insert(commit, &complete);
}
return 0;
}
@@ -653,9 +658,7 @@ struct loose_object_iter {
/*
* Mark recent commits available locally and reachable from a local ref as
- * COMPLETE. If args->no_dependents is false, also mark COMPLETE remote refs as
- * COMMON_REF (otherwise, we are not planning to participate in negotiation, and
- * thus do not need COMMON_REF marks).
+ * COMPLETE.
*
* The cutoff time for recency is determined by this heuristic: it is the
* earliest commit time of the objects in refs that are commits and that we know
@@ -702,7 +705,7 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
*/
trace2_region_enter("fetch-pack", "mark_complete_local_refs", NULL);
if (!args->deepen) {
- for_each_ref(mark_complete_oid, NULL);
+ for_each_rawref(mark_complete_oid, NULL);
for_each_cached_alternate(NULL, mark_alternate_complete);
commit_list_sort_by_date(&complete);
if (cutoff)
@@ -716,16 +719,12 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
*/
trace2_region_enter("fetch-pack", "mark_common_remote_refs", NULL);
for (ref = *refs; ref; ref = ref->next) {
- struct object *o = deref_tag(the_repository,
- lookup_object(the_repository,
- &ref->old_oid),
- NULL, 0);
+ struct commit *c = deref_without_lazy_fetch(&ref->old_oid, 0);
- if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
+ if (!c || !(c->object.flags & COMPLETE))
continue;
- negotiator->known_common(negotiator,
- (struct commit *)o);
+ negotiator->known_common(negotiator, c);
}
trace2_region_leave("fetch-pack", "mark_common_remote_refs", NULL);
@@ -794,6 +793,10 @@ static void write_promisor_file(const char *keep_name,
strbuf_release(&promisor_name);
}
+/*
+ * Pass 1 as "only_packfile" if the pack received is the only pack in this
+ * fetch request (that is, if there were no packfile URIs provided).
+ */
static int get_pack(struct fetch_pack_args *args,
int xd[2], struct string_list *pack_lockfiles,
int only_packfile,
@@ -835,30 +838,30 @@ static int get_pack(struct fetch_pack_args *args,
}
if (alternate_shallow_file) {
- argv_array_push(&cmd.args, "--shallow-file");
- argv_array_push(&cmd.args, alternate_shallow_file);
+ strvec_push(&cmd.args, "--shallow-file");
+ strvec_push(&cmd.args, alternate_shallow_file);
}
if (do_keep || args->from_promisor) {
if (pack_lockfiles)
cmd.out = -1;
cmd_name = "index-pack";
- argv_array_push(&cmd.args, cmd_name);
- argv_array_push(&cmd.args, "--stdin");
+ strvec_push(&cmd.args, cmd_name);
+ strvec_push(&cmd.args, "--stdin");
if (!args->quiet && !args->no_progress)
- argv_array_push(&cmd.args, "-v");
+ strvec_push(&cmd.args, "-v");
if (args->use_thin_pack)
- argv_array_push(&cmd.args, "--fix-thin");
+ strvec_push(&cmd.args, "--fix-thin");
if (do_keep && (args->lock_pack || unpack_limit)) {
char hostname[HOST_NAME_MAX + 1];
if (xgethostname(hostname, sizeof(hostname)))
xsnprintf(hostname, sizeof(hostname), "localhost");
- argv_array_pushf(&cmd.args,
- "--keep=fetch-pack %"PRIuMAX " on %s",
- (uintmax_t)getpid(), hostname);
+ strvec_pushf(&cmd.args,
+ "--keep=fetch-pack %"PRIuMAX " on %s",
+ (uintmax_t)getpid(), hostname);
}
if (only_packfile && args->check_self_contained_and_connected)
- argv_array_push(&cmd.args, "--check-self-contained-and-connected");
+ strvec_push(&cmd.args, "--check-self-contained-and-connected");
else
/*
* We cannot perform any connectivity checks because
@@ -866,42 +869,45 @@ static int get_pack(struct fetch_pack_args *args,
* have this responsibility.
*/
args->check_self_contained_and_connected = 0;
- /*
- * If we're obtaining the filename of a lockfile, we'll use
- * that filename to write a .promisor file with more
- * information below. If not, we need index-pack to do it for
- * us.
- */
- if (!(do_keep && pack_lockfiles) && args->from_promisor)
- argv_array_push(&cmd.args, "--promisor");
+
+ if (args->from_promisor)
+ /*
+ * write_promisor_file() may be called afterwards but
+ * we still need index-pack to know that this is a
+ * promisor pack. For example, if transfer.fsckobjects
+ * is true, index-pack needs to know that .gitmodules
+ * is a promisor object (so that it won't complain if
+ * it is missing).
+ */
+ strvec_push(&cmd.args, "--promisor");
}
else {
cmd_name = "unpack-objects";
- argv_array_push(&cmd.args, cmd_name);
+ strvec_push(&cmd.args, cmd_name);
if (args->quiet || args->no_progress)
- argv_array_push(&cmd.args, "-q");
+ strvec_push(&cmd.args, "-q");
args->check_self_contained_and_connected = 0;
}
if (pass_header)
- argv_array_pushf(&cmd.args, "--pack_header=%"PRIu32",%"PRIu32,
- ntohl(header.hdr_version),
+ strvec_pushf(&cmd.args, "--pack_header=%"PRIu32",%"PRIu32,
+ ntohl(header.hdr_version),
ntohl(header.hdr_entries));
if (fetch_fsck_objects >= 0
? fetch_fsck_objects
: transfer_fsck_objects >= 0
? transfer_fsck_objects
: 0) {
- if (args->from_promisor)
+ if (args->from_promisor || !only_packfile)
/*
* We cannot use --strict in index-pack because it
* checks both broken objects and links, but we only
* want to check for broken objects.
*/
- argv_array_push(&cmd.args, "--fsck-objects");
+ strvec_push(&cmd.args, "--fsck-objects");
else
- argv_array_pushf(&cmd.args, "--strict%s",
- fsck_msg_types.buf);
+ strvec_pushf(&cmd.args, "--strict%s",
+ fsck_msg_types.buf);
}
cmd.in = demux.out;
@@ -960,12 +966,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
struct fetch_negotiator negotiator_alloc;
struct fetch_negotiator *negotiator;
- if (args->no_dependents) {
- negotiator = NULL;
- } else {
- negotiator = &negotiator_alloc;
- fetch_negotiator_init(r, negotiator);
- }
+ negotiator = &negotiator_alloc;
+ fetch_negotiator_init(r, negotiator);
sort_ref_list(&ref, ref_compare_name);
QSORT(sought, nr_sought, cmp_ref_by_name);
@@ -1053,15 +1055,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
if (!server_supports_hash(the_hash_algo->name, NULL))
die(_("Server does not support this repository's object format"));
- if (!args->no_dependents) {
- mark_complete_and_common_ref(negotiator, args, &ref);
- filter_refs(args, &ref, sought, nr_sought);
- if (everything_local(args, &ref)) {
- packet_flush(fd[1]);
- goto all_done;
- }
- } else {
- filter_refs(args, &ref, sought, nr_sought);
+ mark_complete_and_common_ref(negotiator, args, &ref);
+ filter_refs(args, &ref, sought, nr_sought);
+ if (everything_local(args, &ref)) {
+ packet_flush(fd[1]);
+ goto all_done;
}
if (find_common(negotiator, args, fd, &oid, ref) < 0)
if (!args->keep_pack)
@@ -1110,7 +1108,7 @@ static void add_shallow_requests(struct strbuf *req_buf,
packet_buf_write(req_buf, "deepen-relative\n");
}
-static void add_wants(int no_dependents, const struct ref *wants, struct strbuf *req_buf)
+static void add_wants(const struct ref *wants, struct strbuf *req_buf)
{
int use_ref_in_want = server_supports_feature("fetch", "ref-in-want", 0);
@@ -1127,12 +1125,8 @@ static void add_wants(int no_dependents, const struct ref *wants, struct strbuf
* We use lookup_object here because we are only
* interested in the case we *know* the object is
* reachable and we have already scanned it.
- *
- * Do this only if args->no_dependents is false (if it is true,
- * we cannot trust the object flags).
*/
- if (!no_dependents &&
- ((o = lookup_object(the_repository, remote)) != NULL) &&
+ if (((o = lookup_object(the_repository, remote)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
@@ -1266,19 +1260,14 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
}
/* add wants */
- add_wants(args->no_dependents, wants, &req_buf);
+ add_wants(wants, &req_buf);
- if (args->no_dependents) {
- packet_buf_write(&req_buf, "done");
- ret = 1;
- } else {
- /* Add all of the common commits we've found in previous rounds */
- add_common(&req_buf, common);
+ /* Add all of the common commits we've found in previous rounds */
+ add_common(&req_buf, common);
- /* Add initial haves */
- ret = add_haves(negotiator, seen_ack, &req_buf,
- haves_to_send, in_vain);
- }
+ /* Add initial haves */
+ ret = add_haves(negotiator, seen_ack, &req_buf,
+ haves_to_send, in_vain);
/* Send request */
packet_buf_flush(&req_buf);
@@ -1538,12 +1527,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
struct string_list packfile_uris = STRING_LIST_INIT_DUP;
int i;
- if (args->no_dependents) {
- negotiator = NULL;
- } else {
- negotiator = &negotiator_alloc;
- fetch_negotiator_init(r, negotiator);
- }
+ negotiator = &negotiator_alloc;
+ fetch_negotiator_init(r, negotiator);
packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
@@ -1567,21 +1552,16 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
args->deepen = 1;
/* Filter 'ref' by 'sought' and those that aren't local */
- if (!args->no_dependents) {
- mark_complete_and_common_ref(negotiator, args, &ref);
- filter_refs(args, &ref, sought, nr_sought);
- if (everything_local(args, &ref))
- state = FETCH_DONE;
- else
- state = FETCH_SEND_REQUEST;
-
- mark_tips(negotiator, args->negotiation_tips);
- for_each_cached_alternate(negotiator,
- insert_one_alternate_object);
- } else {
- filter_refs(args, &ref, sought, nr_sought);
+ mark_complete_and_common_ref(negotiator, args, &ref);
+ filter_refs(args, &ref, sought, nr_sought);
+ if (everything_local(args, &ref))
+ state = FETCH_DONE;
+ else
state = FETCH_SEND_REQUEST;
- }
+
+ mark_tips(negotiator, args->negotiation_tips);
+ for_each_cached_alternate(negotiator,
+ insert_one_alternate_object);
break;
case FETCH_SEND_REQUEST:
if (!negotiation_started) {
@@ -1652,11 +1632,11 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
const char *uri = packfile_uris.items[i].string +
the_hash_algo->hexsz + 1;
- argv_array_push(&cmd.args, "http-fetch");
- argv_array_pushf(&cmd.args, "--packfile=%.*s",
- (int) the_hash_algo->hexsz,
- packfile_uris.items[i].string);
- argv_array_push(&cmd.args, uri);
+ strvec_push(&cmd.args, "http-fetch");
+ strvec_pushf(&cmd.args, "--packfile=%.*s",
+ (int) the_hash_algo->hexsz,
+ packfile_uris.items[i].string);
+ strvec_push(&cmd.args, uri);
cmd.git_cmd = 1;
cmd.no_stdin = 1;
cmd.out = -1;
@@ -1902,20 +1882,6 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
if (nr_sought)
nr_sought = remove_duplicates_in_refs(sought, nr_sought);
- if (args->no_dependents && !args->filter_options.choice) {
- /*
- * The protocol does not support requesting that only the
- * wanted objects be sent, so approximate this by setting a
- * "blob:none" filter if no filter is already set. This works
- * for all object types: note that wanted blobs will still be
- * sent because they are directly specified as a "want".
- *
- * NEEDSWORK: Add an option in the protocol to request that
- * only the wanted objects be sent, and implement it.
- */
- parse_list_objects_filter(&args->filter_options, "blob:none");
- }
-
if (version != protocol_v2 && !ref) {
packet_flush(fd[1]);
die(_("no matching remote head"));
diff --git a/fetch-pack.h b/fetch-pack.h
index 85d1e39fe7..736a3dae46 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -40,21 +40,15 @@ struct fetch_pack_args {
unsigned cloning:1;
unsigned update_shallow:1;
unsigned deepen:1;
- unsigned from_promisor:1;
/*
- * Attempt to fetch only the wanted objects, and not any objects
- * referred to by them. Due to protocol limitations, extraneous
- * objects may still be included. (When fetching non-blob
- * objects, only blobs are excluded; when fetching a blob, the
- * blob itself will still be sent. The client does not need to
- * know whether a wanted object is a blob or not.)
- *
- * If 1, fetch_pack() will also not modify any object flags.
- * This allows fetch_pack() to safely be called by any function,
- * regardless of which object flags it uses (if any).
+ * Indicate that the remote of this request is a promisor remote. The
+ * pack received does not need all referred-to objects to be present in
+ * the local object store, and fetch-pack will store the pack received
+ * together with a ".promisor" file indicating that the aforementioned
+ * pack is a promisor pack.
*/
- unsigned no_dependents:1;
+ unsigned from_promisor:1;
/*
* Because fetch_pack() overwrites the shallow file upon a
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index cfb8ff2f33..bd22e1ea88 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -10,6 +10,8 @@
#include "commit-reach.h"
static int use_branch_desc;
+static int suppress_dest_pattern_seen;
+static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
int fmt_merge_msg_config(const char *key, const char *value, void *cb)
{
@@ -22,6 +24,14 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
merge_log_config = DEFAULT_MERGE_LOG_LEN;
} else if (!strcmp(key, "merge.branchdesc")) {
use_branch_desc = git_config_bool(key, value);
+ } else if (!strcmp(key, "merge.suppressdest")) {
+ if (!value)
+ return config_error_nonbool(key);
+ if (!*value)
+ string_list_clear(&suppress_dest_patterns, 0);
+ else
+ string_list_append(&suppress_dest_patterns, value);
+ suppress_dest_pattern_seen = 1;
} else {
return git_default_config(key, value, cb);
}
@@ -403,6 +413,24 @@ static void shortlog(const char *name,
string_list_clear(&subjects, 0);
}
+/*
+ * See if dest_branch matches with any glob pattern on the
+ * suppress_dest_patterns list.
+ *
+ * We may want to also allow negative matches e.g. ":!glob" like we do
+ * for pathspec, but for now, let's keep it simple and stupid.
+ */
+static int dest_suppressed(const char *dest_branch)
+{
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, &suppress_dest_patterns) {
+ if (!wildmatch(item->string, dest_branch, WM_PATHNAME))
+ return 1;
+ }
+ return 0;
+}
+
static void fmt_merge_msg_title(struct strbuf *out,
const char *current_branch)
{
@@ -451,7 +479,9 @@ static void fmt_merge_msg_title(struct strbuf *out,
strbuf_addf(out, " of %s", srcs.items[i].string);
}
- strbuf_addf(out, " into %s\n", current_branch);
+ if (!dest_suppressed(current_branch))
+ strbuf_addf(out, " into %s", current_branch);
+ strbuf_addch(out, '\n');
}
static void fmt_tag_signature(struct strbuf *tagbuf,
@@ -596,6 +626,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
void *current_branch_to_free;
struct merge_parents merge_parents;
+ if (!suppress_dest_pattern_seen)
+ string_list_append(&suppress_dest_patterns, "master");
+
memset(&merge_parents, 0, sizeof(merge_parents));
/* get current branch */
diff --git a/fsmonitor.c b/fsmonitor.c
index 932bd9012d..ca031c3abb 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -146,9 +146,9 @@ static int query_fsmonitor(int version, const char *last_update, struct strbuf *
if (!core_fsmonitor)
return -1;
- argv_array_push(&cp.args, core_fsmonitor);
- argv_array_pushf(&cp.args, "%d", version);
- argv_array_pushf(&cp.args, "%s", last_update);
+ strvec_push(&cp.args, core_fsmonitor);
+ strvec_pushf(&cp.args, "%d", version);
+ strvec_pushf(&cp.args, "%s", last_update);
cp.use_shell = 1;
cp.dir = get_git_work_tree();
@@ -217,7 +217,7 @@ void refresh_fsmonitor(struct index_state *istate)
* Need to use a char * variable because static
* analysis was suggesting to use strbuf_addbuf
* but we don't want to copy the entire strbuf
- * only the the chars up to the first NUL
+ * only the chars up to the first NUL
*/
buf = query_result.buf;
strbuf_addstr(&last_update_token, buf);
diff --git a/fuzz-commit-graph.c b/fuzz-commit-graph.c
index 430817214d..e7cf6d5b0f 100644
--- a/fuzz-commit-graph.c
+++ b/fuzz-commit-graph.c
@@ -1,7 +1,8 @@
#include "commit-graph.h"
#include "repository.h"
-struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size);
+struct commit_graph *parse_commit_graph(struct repository *r,
+ void *graph_map, size_t graph_size);
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
@@ -10,7 +11,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
struct commit_graph *g;
initialize_the_repository();
- g = parse_commit_graph((void *)data, size);
+ g = parse_commit_graph(the_repository, (void *)data, size);
repo_clear(the_repository);
free_commit_graph(g);
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 45fecf8bdf..9dbbb08e70 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -10,7 +10,7 @@ command_list () {
}
get_categories () {
- tr ' ' '\n'|
+ tr ' ' '\012'|
grep -v '^$' |
sort |
uniq
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index f36c0078ac..8a72018712 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -714,7 +714,7 @@ sub parse_diff {
if (defined $patch_mode_revision) {
push @diff_cmd, get_diff_reference($patch_mode_revision);
}
- my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path);
+ my @diff = run_cmd_pipe("git", @diff_cmd, qw(--no-color --), $path);
my @colored = ();
if ($diff_use_color) {
my @display_cmd = ("git", @diff_cmd, qw(--color --), $path);
@@ -754,13 +754,16 @@ sub parse_diff_header {
my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
- my $addition = { TEXT => [], DISPLAY => [], TYPE => 'addition' };
+ my $addition;
for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
+ if ($src->{TEXT}->[$i] =~ /^new file/) {
+ $addition = 1;
+ $head->{TYPE} = 'addition';
+ }
my $dest =
$src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
$src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
- $src->{TEXT}->[$i] =~ /^new file/ ? $addition :
$head;
push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
@@ -1501,12 +1504,6 @@ sub patch_update_file {
push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
}
@hunk = ($deletion);
- } elsif (@{$addition->{TEXT}}) {
- foreach my $hunk (@hunk) {
- push @{$addition->{TEXT}}, @{$hunk->{TEXT}};
- push @{$addition->{DISPLAY}}, @{$hunk->{DISPLAY}};
- }
- @hunk = ($addition);
}
$num = scalar @hunk;
@@ -1516,6 +1513,7 @@ sub patch_update_file {
my ($prev, $next, $other, $undecided, $i);
$other = '';
+ last if ($ix and !$num);
if ($num <= $ix) {
$ix = 0;
}
@@ -1548,35 +1546,51 @@ sub patch_update_file {
last;
}
}
- last if (!$undecided);
+ last if (!$undecided && ($num || !$addition));
- if ($hunk[$ix]{TYPE} eq 'hunk' &&
- hunk_splittable($hunk[$ix]{TEXT})) {
- $other .= ',s';
- }
- if ($hunk[$ix]{TYPE} eq 'hunk') {
- $other .= ',e';
- }
- for (@{$hunk[$ix]{DISPLAY}}) {
- print;
+ if ($num) {
+ if ($hunk[$ix]{TYPE} eq 'hunk' &&
+ hunk_splittable($hunk[$ix]{TEXT})) {
+ $other .= ',s';
+ }
+ if ($hunk[$ix]{TYPE} eq 'hunk') {
+ $other .= ',e';
+ }
+ for (@{$hunk[$ix]{DISPLAY}}) {
+ print;
+ }
}
- print colored $prompt_color, "(", ($ix+1), "/$num) ",
- sprintf(__($patch_update_prompt_modes{$patch_mode}{$hunk[$ix]{TYPE}}), $other);
+ my $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};
+ print colored $prompt_color, "(", ($ix+1), "/", ($num ? $num : 1), ") ",
+ sprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);
my $line = prompt_single_character;
last unless defined $line;
if ($line) {
if ($line =~ /^y/i) {
- $hunk[$ix]{USE} = 1;
+ if ($num) {
+ $hunk[$ix]{USE} = 1;
+ } else {
+ $head->{USE} = 1;
+ }
}
elsif ($line =~ /^n/i) {
- $hunk[$ix]{USE} = 0;
+ if ($num) {
+ $hunk[$ix]{USE} = 0;
+ } else {
+ $head->{USE} = 0;
+ }
}
elsif ($line =~ /^a/i) {
- while ($ix < $num) {
- if (!defined $hunk[$ix]{USE}) {
- $hunk[$ix]{USE} = 1;
+ if ($num) {
+ while ($ix < $num) {
+ if (!defined $hunk[$ix]{USE}) {
+ $hunk[$ix]{USE} = 1;
+ }
+ $ix++;
}
+ } else {
+ $head->{USE} = 1;
$ix++;
}
next;
@@ -1613,19 +1627,28 @@ sub patch_update_file {
next;
}
elsif ($line =~ /^d/i) {
- while ($ix < $num) {
- if (!defined $hunk[$ix]{USE}) {
- $hunk[$ix]{USE} = 0;
+ if ($num) {
+ while ($ix < $num) {
+ if (!defined $hunk[$ix]{USE}) {
+ $hunk[$ix]{USE} = 0;
+ }
+ $ix++;
}
+ } else {
+ $head->{USE} = 0;
$ix++;
}
next;
}
elsif ($line =~ /^q/i) {
- for ($i = 0; $i < $num; $i++) {
- if (!defined $hunk[$i]{USE}) {
- $hunk[$i]{USE} = 0;
+ if ($num) {
+ for ($i = 0; $i < $num; $i++) {
+ if (!defined $hunk[$i]{USE}) {
+ $hunk[$i]{USE} = 0;
+ }
}
+ } elsif (!defined $head->{USE}) {
+ $head->{USE} = 0;
}
$quit = 1;
last;
@@ -1743,7 +1766,7 @@ sub patch_update_file {
}
}
- @hunk = coalesce_overlapping_hunks(@hunk);
+ @hunk = coalesce_overlapping_hunks(@hunk) if ($num);
my $n_lofs = 0;
my @result = ();
@@ -1753,7 +1776,7 @@ sub patch_update_file {
}
}
- if (@result) {
+ if (@result or $head->{USE}) {
my @patch = reassemble_patch($head->{TEXT}, @result);
my $apply_routine = $patch_mode_flavour{APPLY};
&$apply_routine(@patch);
diff --git a/git-bisect.sh b/git-bisect.sh
index 08a6ed57dd..ea7e684ebb 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -3,8 +3,8 @@
USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]'
LONG_USAGE='git bisect help
print this long help message.
-git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
- [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
+git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]
+ [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
reset bisect state and start bisection.
git bisect (bad|new) [<rev>]
mark <rev> a known-bad revision/
@@ -41,7 +41,7 @@ TERM_GOOD=good
bisect_head()
{
- if test -f "$GIT_DIR/BISECT_HEAD"
+ if git rev-parse --verify -q BISECT_HEAD > /dev/null
then
echo BISECT_HEAD
else
@@ -49,27 +49,6 @@ bisect_head()
fi
}
-bisect_autostart() {
- test -s "$GIT_DIR/BISECT_START" || {
- gettextln "You need to start by \"git bisect start\"" >&2
- if test -t 0
- then
- # TRANSLATORS: Make sure to include [Y] and [n] in your
- # translation. The program will only accept English input
- # at this point.
- gettext "Do you want me to do it for you [Y/n]? " >&2
- read yesno
- case "$yesno" in
- [Nn]*)
- exit ;;
- esac
- bisect_start
- else
- exit 1
- fi
- }
-}
-
bisect_start() {
git bisect--helper --bisect-start $@ || exit
@@ -86,8 +65,7 @@ bisect_start() {
#
# Check if we can proceed to the next bisect state.
#
- get_terms
- bisect_auto_next
+ git bisect--helper --bisect-auto-next || exit
trap '-' 0
}
@@ -108,7 +86,7 @@ bisect_skip() {
}
bisect_state() {
- bisect_autostart
+ git bisect--helper --bisect-autostart || exit
state=$1
git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
get_terms
@@ -140,45 +118,7 @@ bisect_state() {
*)
usage ;;
esac
- bisect_auto_next
-}
-
-bisect_auto_next() {
- git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD && bisect_next || :
-}
-
-bisect_next() {
- case "$#" in 0) ;; *) usage ;; esac
- bisect_autostart
- git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit
-
- # Perform all bisection computation, display and checkout
- git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
- res=$?
-
- # Check if we should exit because bisection is finished
- if test $res -eq 10
- then
- bad_rev=$(git show-ref --hash --verify refs/bisect/$TERM_BAD)
- bad_commit=$(git show-branch $bad_rev)
- echo "# first $TERM_BAD commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
- exit 0
- elif test $res -eq 2
- then
- echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
- good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/$TERM_GOOD-*")
- for skipped in $(git rev-list refs/bisect/$TERM_BAD --not $good_revs)
- do
- skipped_commit=$(git show-branch $skipped)
- echo "# possible first $TERM_BAD commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
- done
- exit $res
- fi
-
- # Check for an error in the bisection process
- test $res -ne 0 && exit $res
-
- return 0
+ git bisect--helper --bisect-auto-next
}
bisect_visualize() {
@@ -234,7 +174,7 @@ bisect_replay () {
esac
done <"$file"
IFS="$oIFS"
- bisect_auto_next
+ git bisect--helper --bisect-auto-next || exit
}
bisect_run () {
@@ -331,7 +271,7 @@ case "$#" in
bisect_skip "$@" ;;
next)
# Not sure we want "next" at the UI level anymore.
- bisect_next "$@" ;;
+ git bisect--helper --bisect-next "$@" || exit ;;
visualize|view)
bisect_visualize "$@" ;;
reset)
diff --git a/git-compat-util.h b/git-compat-util.h
index 5637114b8d..7a0fb7a045 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -252,8 +252,10 @@ typedef unsigned long uintptr_t;
#ifdef PRECOMPOSE_UNICODE
#include "compat/precompose_utf8.h"
#else
-#define precompose_str(in,i_nfd2nfc)
-#define precompose_argv(c,v)
+static inline void precompose_argv(int argc, const char **argv)
+{
+ ; /* nothing */
+}
#define probe_utf8_pathname_composition()
#endif
@@ -270,7 +272,9 @@ struct itimerval {
#endif
#ifdef NO_SETITIMER
-#define setitimer(which,value,ovalue)
+static inline int setitimer(int which, const struct itimerval *value, struct itimerval *newvalue) {
+ ; /* nothing */
+}
#endif
#ifndef NO_LIBGEN_H
@@ -1231,8 +1235,14 @@ int warn_on_fopen_errors(const char *path);
#endif
#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
-#define flockfile(fh)
-#define funlockfile(fh)
+static inline void flockfile(FILE *fh)
+{
+ ; /* nothing */
+}
+static inline void funlockfile(FILE *fh)
+{
+ ; /* nothing */
+}
#define getc_unlocked(fh) getc(fh)
#endif
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 6483d792d3..289d4bc684 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -22,7 +22,7 @@ die "Need at least one commit identifier!" unless @ARGV;
my $repo = Git->repository();
$opt_w = $repo->config('cvsexportcommit.cvsdir') unless defined $opt_w;
-my $tmpdir = File::Temp->newdir;
+my $tmpdir = File::Temp::tempdir(CLEANUP => 1);
my $hash_algo = $repo->config('extensions.objectformat') || 'sha1';
my $hexsz = $hash_algo eq 'sha256' ? 64 : 40;
@@ -30,7 +30,7 @@ if ($opt_w || $opt_W) {
# Remember where GIT_DIR is before changing to CVS checkout
unless ($ENV{GIT_DIR}) {
# No GIT_DIR set. Figure it out for ourselves
- my $gd =`git-rev-parse --git-dir`;
+ my $gd =`git rev-parse --git-dir`;
chomp($gd);
$ENV{GIT_DIR} = $gd;
}
@@ -66,7 +66,7 @@ if ($opt_d) {
# resolve target commit
my $commit;
$commit = pop @ARGV;
-$commit = safe_pipe_capture('git-rev-parse', '--verify', "$commit^0");
+$commit = safe_pipe_capture('git', 'rev-parse', '--verify', "$commit^0");
chomp $commit;
if ($?) {
die "The commit reference $commit did not resolve!";
@@ -76,7 +76,7 @@ if ($?) {
my $parent;
if (@ARGV) {
$parent = pop @ARGV;
- $parent = safe_pipe_capture('git-rev-parse', '--verify', "$parent^0");
+ $parent = safe_pipe_capture('git', 'rev-parse', '--verify', "$parent^0");
chomp $parent;
if ($?) {
die "The parent reference did not resolve!";
@@ -84,7 +84,7 @@ if (@ARGV) {
}
# find parents from the commit itself
-my @commit = safe_pipe_capture('git-cat-file', 'commit', $commit);
+my @commit = safe_pipe_capture('git', 'cat-file', 'commit', $commit);
my @parents;
my $committer;
my $author;
@@ -162,9 +162,9 @@ if ($opt_a) {
close MSG;
if ($parent eq $noparent) {
- `git-diff-tree --binary -p --root $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+ `git diff-tree --binary -p --root $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
} else {
- `git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+ `git diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
}
## apply non-binary changes
@@ -178,7 +178,7 @@ my $context = $opt_p ? '' : '-C1';
print "Checking if patch will apply\n";
my @stat;
-open APPLY, "GIT_INDEX_FILE=$tmpdir/index git-apply $context --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+open APPLY, "GIT_INDEX_FILE=$tmpdir/index git apply $context --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
@stat=<APPLY>;
close APPLY || die "Cannot patch";
my (@bfiles,@files,@afiles,@dfiles);
@@ -333,7 +333,7 @@ print "Applying\n";
if ($opt_W) {
system("git checkout -q $commit^0") && die "cannot patch";
} else {
- `GIT_INDEX_FILE=$tmpdir/index git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+ `GIT_INDEX_FILE=$tmpdir/index git apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
}
print "Patch applied successfully. Adding new files and directories to CVS\n";
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index 204a5acd66..2defef28cd 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -43,7 +43,14 @@ show_tool_names () {
shown_any=
( cd "$MERGE_TOOLS_DIR" && ls ) | {
- while read toolname
+ while read scriptname
+ do
+ setup_tool "$scriptname" 2>/dev/null
+ variants="$variants$(list_tool_variants)\n"
+ done
+ variants="$(echo "$variants" | sort | uniq)"
+
+ for toolname in $variants
do
if setup_tool "$toolname" 2>/dev/null &&
(eval "$condition" "$toolname")
@@ -157,6 +164,10 @@ setup_tool () {
echo "$1"
}
+ list_tool_variants () {
+ echo "$tool"
+ }
+
# Most tools' exit codes cannot be trusted, so By default we ignore
# their exit code and check the merged file's modification time in
# check_unchanged() to determine whether or not the merge was
@@ -178,19 +189,26 @@ setup_tool () {
false
}
-
- if ! test -f "$MERGE_TOOLS_DIR/$tool"
+ if test -f "$MERGE_TOOLS_DIR/$tool"
+ then
+ . "$MERGE_TOOLS_DIR/$tool"
+ elif test -f "$MERGE_TOOLS_DIR/${tool%[0-9]}"
then
+ . "$MERGE_TOOLS_DIR/${tool%[0-9]}"
+ else
setup_user_tool
return $?
fi
- # Load the redefined functions
- . "$MERGE_TOOLS_DIR/$tool"
# Now let the user override the default command for the tool. If
# they have not done so then this will return 1 which we ignore.
setup_user_tool
+ if ! list_tool_variants | grep -q "^$tool$"
+ then
+ return 1
+ fi
+
if merge_mode && ! can_merge
then
echo "error: '$tool' can not be used to resolve merges" >&2
@@ -286,11 +304,14 @@ list_merge_tool_candidates () {
tools="$tools smerge"
fi
case "${VISUAL:-$EDITOR}" in
+ *nvim*)
+ tools="$tools nvimdiff vimdiff emerge"
+ ;;
*vim*)
- tools="$tools vimdiff emerge"
+ tools="$tools vimdiff nvimdiff emerge"
;;
*)
- tools="$tools emerge vimdiff"
+ tools="$tools emerge vimdiff nvimdiff"
;;
esac
}
diff --git a/git-p4.py b/git-p4.py
index ca79dc0900..4433ca53de 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -4237,7 +4237,7 @@ class P4Unshelve(Command):
"""
for parent in (range(65535)):
- log = extractLogMessageFromGitCommit("{0}^{1}".format(starting_point, parent))
+ log = extractLogMessageFromGitCommit("{0}~{1}".format(starting_point, parent))
settings = extractSettingsGitLog(log)
if 'change' in settings:
return settings
diff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh
index dec90e9af6..b9c71d2a71 100644
--- a/git-rebase--preserve-merges.sh
+++ b/git-rebase--preserve-merges.sh
@@ -193,16 +193,6 @@ mark_action_done () {
fi
}
-# Put the last action marked done at the beginning of the todo list
-# again. If there has not been an action marked done yet, leave the list of
-# items on the todo list unchanged.
-reschedule_last_action () {
- tail -n 1 "$done" | cat - "$todo" >"$todo".new
- sed -e \$d <"$done" >"$done".new
- mv -f "$todo".new "$todo"
- mv -f "$done".new "$done"
-}
-
append_todo_help () {
gettext "
Commands:
diff --git a/git-send-email.perl b/git-send-email.perl
index 36c47bae1d..1f425c0809 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -250,6 +250,7 @@ my $chain_reply_to = 0;
my $use_xmailer = 1;
my $validate = 1;
my $target_xfer_encoding = 'auto';
+my $forbid_sendmail_variables = 1;
my %config_bool_settings = (
"thread" => \$thread,
@@ -263,6 +264,7 @@ my %config_bool_settings = (
"multiedit" => \$multiedit,
"annotate" => \$annotate,
"xmailer" => \$use_xmailer,
+ "forbidsendmailvariables" => \$forbid_sendmail_variables,
);
my %config_settings = (
@@ -478,6 +480,12 @@ unless ($rc) {
usage();
}
+if ($forbid_sendmail_variables && (scalar Git::config_regexp("^sendmail[.]")) != 0) {
+ die __("fatal: found configuration options for 'sendmail'\n" .
+ "git-send-email is configured with the sendemail.* options - note the 'e'.\n" .
+ "Set sendemail.forbidSendmailVariables to false to disable this check.\n");
+}
+
die __("Cannot run git format-patch from outside a repository\n")
if $format_patch and not $repo;
diff --git a/git-submodule.sh b/git-submodule.sh
index 43eb6051d2..6fb12585cb 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -59,31 +59,6 @@ die_if_unmatched ()
fi
}
-#
-# Print a submodule configuration setting
-#
-# $1 = submodule name
-# $2 = option name
-# $3 = default value
-#
-# Checks in the usual git-config places first (for overrides),
-# otherwise it falls back on .gitmodules. This allows you to
-# distribute project-wide defaults in .gitmodules, while still
-# customizing individual repositories if necessary. If the option is
-# not in .gitmodules either, print a default value.
-#
-get_submodule_config () {
- name="$1"
- option="$2"
- default="$3"
- value=$(git config submodule."$name"."$option")
- if test -z "$value"
- then
- value=$(git submodule--helper config submodule."$name"."$option")
- fi
- printf '%s' "${value:-$default}"
-}
-
isnumber()
{
n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -831,166 +806,7 @@ cmd_summary() {
shift
done
- test $summary_limit = 0 && return
-
- if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
- then
- head=$rev
- test $# = 0 || shift
- elif test -z "$1" || test "$1" = "HEAD"
- then
- # before the first commit: compare with an empty tree
- head=$(git hash-object -w -t tree --stdin </dev/null)
- test -z "$1" || shift
- else
- head="HEAD"
- fi
-
- if [ -n "$files" ]
- then
- test -n "$cached" &&
- die "$(gettext "The --cached option cannot be used with the --files option")"
- diff_cmd=diff-files
- head=
- fi
-
- cd_to_toplevel
- eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
- # Get modified modules cared by user
- modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
- sane_egrep '^:([0-7]* )?160000' |
- while read -r mod_src mod_dst sha1_src sha1_dst status sm_path
- do
- # Always show modules deleted or type-changed (blob<->module)
- if test "$status" = D || test "$status" = T
- then
- printf '%s\n' "$sm_path"
- continue
- fi
- # Respect the ignore setting for --for-status.
- if test -n "$for_status"
- then
- name=$(git submodule--helper name "$sm_path")
- ignore_config=$(get_submodule_config "$name" ignore none)
- test $status != A && test $ignore_config = all && continue
- fi
- # Also show added or modified modules which are checked out
- GIT_DIR="$sm_path/.git" git rev-parse --git-dir >/dev/null 2>&1 &&
- printf '%s\n' "$sm_path"
- done
- )
-
- test -z "$modules" && return
-
- git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
- sane_egrep '^:([0-7]* )?160000' |
- cut -c2- |
- while read -r mod_src mod_dst sha1_src sha1_dst status name
- do
- if test -z "$cached" &&
- is_zero_oid $sha1_dst
- then
- case "$mod_dst" in
- 160000)
- sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
- ;;
- 100644 | 100755 | 120000)
- sha1_dst=$(git hash-object $name)
- ;;
- 000000)
- ;; # removed
- *)
- # unexpected type
- eval_gettextln "unexpected mode \$mod_dst" >&2
- continue ;;
- esac
- fi
- missing_src=
- missing_dst=
-
- test $mod_src = 160000 &&
- ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_src^0 >/dev/null &&
- missing_src=t
-
- test $mod_dst = 160000 &&
- ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_dst^0 >/dev/null &&
- missing_dst=t
-
- display_name=$(git submodule--helper relative-path "$name" "$wt_prefix")
-
- total_commits=
- case "$missing_src,$missing_dst" in
- t,)
- errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_src")"
- ;;
- ,t)
- errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_dst")"
- ;;
- t,t)
- errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
- ;;
- *)
- errmsg=
- total_commits=$(
- if test $mod_src = 160000 && test $mod_dst = 160000
- then
- range="$sha1_src...$sha1_dst"
- elif test $mod_src = 160000
- then
- range=$sha1_src
- else
- range=$sha1_dst
- fi
- GIT_DIR="$name/.git" \
- git rev-list --first-parent $range -- | wc -l
- )
- total_commits=" ($(($total_commits + 0)))"
- ;;
- esac
-
- sha1_abbr_src=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_src 2>/dev/null ||
- echo $sha1_src | cut -c1-7)
- sha1_abbr_dst=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_dst 2>/dev/null ||
- echo $sha1_dst | cut -c1-7)
-
- if test $status = T
- then
- blob="$(gettext "blob")"
- submodule="$(gettext "submodule")"
- if test $mod_dst = 160000
- then
- echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
- else
- echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
- fi
- else
- echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
- fi
- if test -n "$errmsg"
- then
- # Don't give error msg for modification whose dst is not submodule
- # i.e. deleted or changed to blob
- test $mod_dst = 160000 && echo "$errmsg"
- else
- if test $mod_src = 160000 && test $mod_dst = 160000
- then
- limit=
- test $summary_limit -gt 0 && limit="-$summary_limit"
- GIT_DIR="$name/.git" \
- git log $limit --pretty='format: %m %s' \
- --first-parent $sha1_src...$sha1_dst
- elif test $mod_dst = 160000
- then
- GIT_DIR="$name/.git" \
- git log --pretty='format: > %s' -1 $sha1_dst
- else
- GIT_DIR="$name/.git" \
- git log --pretty='format: < %s' -1 $sha1_src
- fi
- echo
- fi
- echo
- done
+ git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary ${prefix:+--prefix "$prefix"} ${files:+--files} ${cached:+--cached} ${for_status:+--for-status} ${summary_limit:+-n $summary_limit} -- "$@"
}
#
# List all submodules, prefixed with:
diff --git a/git.c b/git.c
index 6cd887bb0c..f1e8b56d99 100644
--- a/git.c
+++ b/git.c
@@ -349,10 +349,10 @@ static int handle_alias(int *argcp, const char ***argv)
child.clean_on_exit = 1;
child.wait_after_clean = 1;
child.trace2_child_class = "shell_alias";
- argv_array_push(&child.args, alias_string + 1);
- argv_array_pushv(&child.args, (*argv) + 1);
+ strvec_push(&child.args, alias_string + 1);
+ strvec_pushv(&child.args, (*argv) + 1);
- trace2_cmd_alias(alias_command, child.args.argv);
+ trace2_cmd_alias(alias_command, child.args.v);
trace2_cmd_list_config();
trace2_cmd_list_env_vars();
trace2_cmd_name("_run_shell_alias_");
@@ -479,6 +479,7 @@ static struct cmd_struct commands[] = {
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
+ { "bugreport", cmd_bugreport, RUN_SETUP_GENTLY },
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY | NO_PARSEOPT },
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "check-attr", cmd_check_attr, RUN_SETUP },
@@ -499,6 +500,9 @@ static struct cmd_struct commands[] = {
{ "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
{ "count-objects", cmd_count_objects, RUN_SETUP },
{ "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT },
+ { "credential-cache", cmd_credential_cache },
+ { "credential-cache--daemon", cmd_credential_cache_daemon },
+ { "credential-store", cmd_credential_store },
{ "describe", cmd_describe, RUN_SETUP },
{ "diff", cmd_diff, NO_PARSEOPT },
{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
@@ -507,6 +511,7 @@ static struct cmd_struct commands[] = {
{ "difftool", cmd_difftool, RUN_SETUP_GENTLY },
{ "env--helper", cmd_env__helper },
{ "fast-export", cmd_fast_export, RUN_SETUP },
+ { "fast-import", cmd_fast_import, RUN_SETUP | NO_PARSEOPT },
{ "fetch", cmd_fetch, RUN_SETUP },
{ "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
@@ -529,6 +534,7 @@ static struct cmd_struct commands[] = {
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY | NO_PARSEOPT },
{ "mailsplit", cmd_mailsplit, NO_PARSEOPT },
+ { "maintenance", cmd_maintenance, RUN_SETUP_GENTLY | NO_PARSEOPT },
{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
{ "merge-base", cmd_merge_base, RUN_SETUP },
{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
@@ -646,7 +652,7 @@ static void strip_extension(const char **argv)
static void handle_builtin(int argc, const char **argv)
{
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
const char *cmd;
struct cmd_struct *builtin;
@@ -661,19 +667,19 @@ static void handle_builtin(int argc, const char **argv)
argv[0] = cmd = "help";
for (i = 0; i < argc; i++) {
- argv_array_push(&args, argv[i]);
+ strvec_push(&args, argv[i]);
if (!i)
- argv_array_push(&args, "--exclude-guides");
+ strvec_push(&args, "--exclude-guides");
}
argc++;
- argv = args.argv;
+ argv = args.v;
}
builtin = get_builtin(cmd);
if (builtin)
exit(run_builtin(builtin, argc, argv));
- argv_array_clear(&args);
+ strvec_clear(&args);
}
static void execv_dashed_external(const char **argv)
@@ -688,8 +694,8 @@ static void execv_dashed_external(const char **argv)
use_pager = check_pager_config(argv[0]);
commit_pager_choice();
- argv_array_pushf(&cmd.args, "git-%s", argv[0]);
- argv_array_pushv(&cmd.args, argv + 1);
+ strvec_pushf(&cmd.args, "git-%s", argv[0]);
+ strvec_pushv(&cmd.args, argv + 1);
cmd.clean_on_exit = 1;
cmd.wait_after_clean = 1;
cmd.silent_exec_failure = 1;
@@ -701,7 +707,7 @@ static void execv_dashed_external(const char **argv)
* The code in run_command() logs trace2 child_start/child_exit
* events, so we do not need to report exec/exec_result events here.
*/
- trace_argv_printf(cmd.args.argv, "trace: exec:");
+ trace_argv_printf(cmd.args.v, "trace: exec:");
/*
* If we fail because the command is not found, it is
@@ -741,7 +747,7 @@ static int run_argv(int *argcp, const char ***argv)
if (!done_alias)
handle_builtin(*argcp, *argv);
else if (get_builtin(**argv)) {
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
int i;
/*
@@ -758,17 +764,17 @@ static int run_argv(int *argcp, const char ***argv)
commit_pager_choice();
- argv_array_push(&args, "git");
+ strvec_push(&args, "git");
for (i = 0; i < *argcp; i++)
- argv_array_push(&args, (*argv)[i]);
+ strvec_push(&args, (*argv)[i]);
- trace_argv_printf(args.argv, "trace: exec:");
+ trace_argv_printf(args.v, "trace: exec:");
/*
* if we fail because the command is not found, it is
* OK to return. Otherwise, we just pass along the status code.
*/
- i = run_command_v_opt_tr2(args.argv, RUN_SILENT_EXEC_FAILURE |
+ i = run_command_v_opt_tr2(args.v, RUN_SILENT_EXEC_FAILURE |
RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, "git_alias");
if (i >= 0 || errno != ENOENT)
exit(i);
diff --git a/gpg-interface.c b/gpg-interface.c
index 2d538bcd6e..b499270836 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -282,12 +282,12 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
if (!fmt)
BUG("bad signature '%s'", signature);
- argv_array_push(&gpg.args, fmt->program);
- argv_array_pushv(&gpg.args, fmt->verify_args);
- argv_array_pushl(&gpg.args,
- "--status-fd=1",
- "--verify", temp->filename.buf, "-",
- NULL);
+ strvec_push(&gpg.args, fmt->program);
+ strvec_pushv(&gpg.args, fmt->verify_args);
+ strvec_pushl(&gpg.args,
+ "--status-fd=1",
+ "--verify", temp->filename.buf, "-",
+ NULL);
if (!gpg_status)
gpg_status = &buf;
@@ -434,11 +434,11 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
size_t i, j, bottom;
struct strbuf gpg_status = STRBUF_INIT;
- argv_array_pushl(&gpg.args,
- use_format->program,
- "--status-fd=2",
- "-bsau", signing_key,
- NULL);
+ strvec_pushl(&gpg.args,
+ use_format->program,
+ "--status-fd=2",
+ "-bsau", signing_key,
+ NULL);
bottom = signature->len;
diff --git a/graph.c b/graph.c
index 4cd9915075..c128ad0cce 100644
--- a/graph.c
+++ b/graph.c
@@ -4,7 +4,7 @@
#include "color.h"
#include "graph.h"
#include "revision.h"
-#include "argv-array.h"
+#include "strvec.h"
/* Internal API */
@@ -82,7 +82,7 @@ static void graph_show_line_prefix(const struct diff_options *diffopt)
static const char **column_colors;
static unsigned short column_colors_max;
-static void parse_graph_colors_config(struct argv_array *colors, const char *string)
+static void parse_graph_colors_config(struct strvec *colors, const char *string)
{
const char *end, *start;
@@ -93,13 +93,13 @@ static void parse_graph_colors_config(struct argv_array *colors, const char *str
char color[COLOR_MAXLEN];
if (!color_parse_mem(start, comma - start, color))
- argv_array_push(colors, color);
+ strvec_push(colors, color);
else
warning(_("ignore invalid color '%.*s' in log.graphColors"),
(int)(comma - start), start);
start = comma + 1;
}
- argv_array_push(colors, GIT_COLOR_RESET);
+ strvec_push(colors, GIT_COLOR_RESET);
}
void graph_set_column_colors(const char **colors, unsigned short colors_max)
@@ -350,13 +350,13 @@ struct git_graph *graph_init(struct rev_info *opt)
graph_set_column_colors(column_colors_ansi,
column_colors_ansi_max);
} else {
- static struct argv_array custom_colors = ARGV_ARRAY_INIT;
- argv_array_clear(&custom_colors);
+ static struct strvec custom_colors = STRVEC_INIT;
+ strvec_clear(&custom_colors);
parse_graph_colors_config(&custom_colors, string);
free(string);
/* graph_set_column_colors takes a max-index, not a count */
- graph_set_column_colors(custom_colors.argv,
- custom_colors.argc - 1);
+ graph_set_column_colors(custom_colors.v,
+ custom_colors.nr - 1);
}
}
diff --git a/grep.c b/grep.c
index 13232a904a..54af9f813e 100644
--- a/grep.c
+++ b/grep.c
@@ -1817,7 +1817,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
* We might set up the shared textconv cache data here, which
* is not thread-safe. Also, get_oid_with_context() and
* parse_object() might be internally called. As they are not
- * currenty thread-safe and might be racy with object reading,
+ * currently thread-safe and might be racy with object reading,
* obj_read_lock() must be called.
*/
grep_attr_lock();
diff --git a/hashmap.h b/hashmap.h
index 79ae9f80de..ef220de4c6 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -168,7 +168,7 @@ struct hashmap_entry {
* argument `keydata`, respectively. Otherwise, `keydata` is NULL.
*
* When it is too expensive to allocate a user entry (either because it is
- * large or varialbe sized, such that it is not on the stack), then the
+ * large or variable sized, such that it is not on the stack), then the
* relevant data to check for equality should be passed via `keydata`.
* In this case `key` can be a stripped down version of the user key data
* or even just a hashmap_entry having the correct hash.
diff --git a/help.c b/help.c
index 44cee69c11..4e2468a44d 100644
--- a/help.c
+++ b/help.c
@@ -375,7 +375,7 @@ void list_cmds_by_config(struct string_list *list)
{
const char *cmd_list;
- if (git_config_get_string_const("completion.commands", &cmd_list))
+ if (git_config_get_string_tmp("completion.commands", &cmd_list))
return;
string_list_sort(list);
@@ -397,10 +397,10 @@ void list_cmds_by_config(struct string_list *list)
}
}
-void list_common_guides_help(void)
+void list_guides_help(void)
{
struct category_description catdesc[] = {
- { CAT_guide, N_("The common Git guides are:") },
+ { CAT_guide, N_("The Git concept guides are:") },
{ 0, NULL }
};
print_cmd_by_category(catdesc, NULL);
diff --git a/help.h b/help.h
index 500521b908..dc02458855 100644
--- a/help.h
+++ b/help.h
@@ -21,7 +21,7 @@ static inline void mput_char(char c, unsigned int num)
void list_common_cmds_help(void);
void list_all_cmds_help(void);
-void list_common_guides_help(void);
+void list_guides_help(void);
void list_all_main_cmds(struct string_list *list);
void list_all_other_cmds(struct string_list *list);
diff --git a/http-backend.c b/http-backend.c
index ec3144b444..a03b4bae22 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -9,7 +9,7 @@
#include "run-command.h"
#include "string-list.h"
#include "url.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "packfile.h"
#include "object-store.h"
#include "protocol.h"
@@ -477,10 +477,10 @@ static void run_service(const char **argv, int buffer_input)
host = "(none)";
if (!getenv("GIT_COMMITTER_NAME"))
- argv_array_pushf(&cld.env_array, "GIT_COMMITTER_NAME=%s", user);
+ strvec_pushf(&cld.env_array, "GIT_COMMITTER_NAME=%s", user);
if (!getenv("GIT_COMMITTER_EMAIL"))
- argv_array_pushf(&cld.env_array,
- "GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
+ strvec_pushf(&cld.env_array,
+ "GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
cld.argv = argv;
if (buffer_input || gzipped_request || req_len >= 0)
diff --git a/http-fetch.c b/http-fetch.c
index 1df376e745..c4ccc5fea9 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -84,8 +84,11 @@ int cmd_main(int argc, const char **argv)
int get_verbosely = 0;
int get_recover = 0;
int packfile = 0;
+ int nongit;
struct object_id packfile_hash;
+ setup_git_directory_gently(&nongit);
+
while (arg < argc && argv[arg][0] == '-') {
const char *p;
@@ -115,7 +118,8 @@ int cmd_main(int argc, const char **argv)
if (argc != arg + 2 - (commits_on_stdin || packfile))
usage(http_fetch_usage);
- setup_git_directory();
+ if (nongit)
+ die(_("not a git repository"));
git_config(git_default_config, NULL);
diff --git a/http-push.c b/http-push.c
index 1ff1883cdd..6a4a43e07f 100644
--- a/http-push.c
+++ b/http-push.c
@@ -11,7 +11,7 @@
#include "remote.h"
#include "list-objects.h"
#include "sigchain.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "packfile.h"
#include "object-store.h"
#include "commit-reach.h"
@@ -1846,7 +1846,7 @@ int cmd_main(int argc, const char **argv)
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
- struct argv_array commit_argv = ARGV_ARRAY_INIT;
+ struct strvec commit_argv = STRVEC_INIT;
if (!ref->peer_ref)
continue;
@@ -1924,14 +1924,14 @@ int cmd_main(int argc, const char **argv)
}
/* Set up revision info for this refspec */
- argv_array_push(&commit_argv, ""); /* ignored */
- argv_array_push(&commit_argv, "--objects");
- argv_array_push(&commit_argv, oid_to_hex(&ref->new_oid));
+ strvec_push(&commit_argv, ""); /* ignored */
+ strvec_push(&commit_argv, "--objects");
+ strvec_push(&commit_argv, oid_to_hex(&ref->new_oid));
if (!push_all && !is_null_oid(&ref->old_oid))
- argv_array_pushf(&commit_argv, "^%s",
- oid_to_hex(&ref->old_oid));
+ strvec_pushf(&commit_argv, "^%s",
+ oid_to_hex(&ref->old_oid));
repo_init_revisions(the_repository, &revs, setup_git_directory());
- setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL);
+ setup_revisions(commit_argv.nr, commit_argv.v, &revs, NULL);
revs.edge_hint = 0; /* just in case */
/* Generate a list of objects that need to be pushed */
@@ -1961,7 +1961,7 @@ int cmd_main(int argc, const char **argv)
printf("%s %s\n", !rc ? "ok" : "error", ref->name);
unlock_remote(ref_lock);
check_locks();
- argv_array_clear(&commit_argv);
+ strvec_clear(&commit_argv);
}
/* Update remote server info if appropriate */
diff --git a/http.c b/http.c
index 3b12843a5b..8b23a546af 100644
--- a/http.c
+++ b/http.c
@@ -2270,13 +2270,13 @@ int finish_http_pack_request(struct http_pack_request *preq)
tmpfile_fd = xopen(preq->tmpfile.buf, O_RDONLY);
- argv_array_push(&ip.args, "index-pack");
- argv_array_push(&ip.args, "--stdin");
+ strvec_push(&ip.args, "index-pack");
+ strvec_push(&ip.args, "--stdin");
ip.git_cmd = 1;
ip.in = tmpfile_fd;
if (preq->generate_keep) {
- argv_array_pushf(&ip.args, "--keep=git %"PRIuMAX,
- (uintmax_t)getpid());
+ strvec_pushf(&ip.args, "--keep=git %"PRIuMAX,
+ (uintmax_t)getpid());
ip.out = 0;
} else {
ip.no_stdout = 1;
diff --git a/ident.c b/ident.c
index e666ee4e59..6aba4b5cb6 100644
--- a/ident.c
+++ b/ident.c
@@ -345,27 +345,45 @@ person_only:
return 0;
}
-static const char *env_hint =
-N_("\n"
- "*** Please tell me who you are.\n"
- "\n"
- "Run\n"
- "\n"
- " git config --global user.email \"you@example.com\"\n"
- " git config --global user.name \"Your Name\"\n"
- "\n"
- "to set your account\'s default identity.\n"
- "Omit --global to set the identity only in this repository.\n"
- "\n");
+
+static void ident_env_hint(enum want_ident whose_ident)
+{
+ switch (whose_ident) {
+ case WANT_AUTHOR_IDENT:
+ fputs(_("Author identity unknown\n"), stderr);
+ break;
+ case WANT_COMMITTER_IDENT:
+ fputs(_("Committer identity unknown\n"), stderr);
+ break;
+ default:
+ break;
+ }
+
+ fputs(_("\n"
+ "*** Please tell me who you are.\n"
+ "\n"
+ "Run\n"
+ "\n"
+ " git config --global user.email \"you@example.com\"\n"
+ " git config --global user.name \"Your Name\"\n"
+ "\n"
+ "to set your account\'s default identity.\n"
+ "Omit --global to set the identity only in this repository.\n"
+ "\n"), stderr);
+}
const char *fmt_ident(const char *name, const char *email,
enum want_ident whose_ident, const char *date_str, int flag)
{
- static struct strbuf ident = STRBUF_INIT;
+ static int index;
+ static struct strbuf ident_pool[2] = { STRBUF_INIT, STRBUF_INIT };
int strict = (flag & IDENT_STRICT);
int want_date = !(flag & IDENT_NO_DATE);
int want_name = !(flag & IDENT_NO_NAME);
+ struct strbuf *ident = &ident_pool[index];
+ index = (index + 1) % ARRAY_SIZE(ident_pool);
+
if (!email) {
if (whose_ident == WANT_AUTHOR_IDENT && git_author_email.len)
email = git_author_email.buf;
@@ -375,12 +393,12 @@ const char *fmt_ident(const char *name, const char *email,
if (!email) {
if (strict && ident_use_config_only
&& !(ident_config_given & IDENT_MAIL_GIVEN)) {
- fputs(_(env_hint), stderr);
+ ident_env_hint(whose_ident);
die(_("no email was given and auto-detection is disabled"));
}
email = ident_default_email();
if (strict && default_email_is_bogus) {
- fputs(_(env_hint), stderr);
+ ident_env_hint(whose_ident);
die(_("unable to auto-detect email address (got '%s')"), email);
}
}
@@ -397,13 +415,13 @@ const char *fmt_ident(const char *name, const char *email,
if (!name) {
if (strict && ident_use_config_only
&& !(ident_config_given & IDENT_NAME_GIVEN)) {
- fputs(_(env_hint), stderr);
+ ident_env_hint(whose_ident);
die(_("no name was given and auto-detection is disabled"));
}
name = ident_default_name();
using_default = 1;
if (strict && default_name_is_bogus) {
- fputs(_(env_hint), stderr);
+ ident_env_hint(whose_ident);
die(_("unable to auto-detect name (got '%s')"), name);
}
}
@@ -411,7 +429,7 @@ const char *fmt_ident(const char *name, const char *email,
struct passwd *pw;
if (strict) {
if (using_default)
- fputs(_(env_hint), stderr);
+ ident_env_hint(whose_ident);
die(_("empty ident name (for <%s>) not allowed"), email);
}
pw = xgetpwuid_self(NULL);
@@ -421,25 +439,25 @@ const char *fmt_ident(const char *name, const char *email,
die(_("name consists only of disallowed characters: %s"), name);
}
- strbuf_reset(&ident);
+ strbuf_reset(ident);
if (want_name) {
- strbuf_addstr_without_crud(&ident, name);
- strbuf_addstr(&ident, " <");
+ strbuf_addstr_without_crud(ident, name);
+ strbuf_addstr(ident, " <");
}
- strbuf_addstr_without_crud(&ident, email);
+ strbuf_addstr_without_crud(ident, email);
if (want_name)
- strbuf_addch(&ident, '>');
+ strbuf_addch(ident, '>');
if (want_date) {
- strbuf_addch(&ident, ' ');
+ strbuf_addch(ident, ' ');
if (date_str && date_str[0]) {
- if (parse_date(date_str, &ident) < 0)
+ if (parse_date(date_str, ident) < 0)
die(_("invalid date format: %s"), date_str);
}
else
- strbuf_addstr(&ident, ident_default_date());
+ strbuf_addstr(ident, ident_default_date());
}
- return ident.buf;
+ return ident->buf;
}
const char *fmt_name(enum want_ident whose_ident)
diff --git a/imap-send.c b/imap-send.c
index 52737546f3..5764dd812c 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -976,7 +976,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
imap_info("Starting tunnel '%s'... ", srvc->tunnel);
- argv_array_push(&tunnel.args, srvc->tunnel);
+ strvec_push(&tunnel.args, srvc->tunnel);
tunnel.use_shell = 1;
tunnel.in = -1;
tunnel.out = -1;
diff --git a/interdiff.c b/interdiff.c
deleted file mode 100644
index c81d680a6c..0000000000
--- a/interdiff.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "revision.h"
-#include "interdiff.h"
-
-static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
-{
- return data;
-}
-
-void show_interdiff(struct rev_info *rev, int indent)
-{
- struct diff_options opts;
- struct strbuf prefix = STRBUF_INIT;
-
- memcpy(&opts, &rev->diffopt, sizeof(opts));
- opts.output_format = DIFF_FORMAT_PATCH;
- opts.output_prefix = idiff_prefix_cb;
- strbuf_addchars(&prefix, ' ', indent);
- opts.output_prefix_data = &prefix;
- diff_setup_done(&opts);
-
- diff_tree_oid(rev->idiff_oid1, rev->idiff_oid2, "", &opts);
- diffcore_std(&opts);
- diff_flush(&opts);
-
- strbuf_release(&prefix);
-}
diff --git a/interdiff.h b/interdiff.h
deleted file mode 100644
index 01c730a5c9..0000000000
--- a/interdiff.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef INTERDIFF_H
-#define INTERDIFF_H
-
-struct rev_info;
-
-void show_interdiff(struct rev_info *, int indent);
-
-#endif
diff --git a/line-log.c b/line-log.c
index c53692834d..68eeb425f8 100644
--- a/line-log.c
+++ b/line-log.c
@@ -14,7 +14,7 @@
#include "graph.h"
#include "userdiff.h"
#include "line-log.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "bloom.h"
static void range_set_grow(struct range_set *rs, size_t extra)
@@ -758,12 +758,12 @@ static void parse_pathspec_from_ranges(struct pathspec *pathspec,
struct line_log_data *range)
{
struct line_log_data *r;
- struct argv_array array = ARGV_ARRAY_INIT;
+ struct strvec array = STRVEC_INIT;
const char **paths;
for (r = range; r; r = r->next)
- argv_array_push(&array, r->path);
- paths = argv_array_detach(&array);
+ strvec_push(&array, r->path);
+ paths = strvec_detach(&array);
parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", paths);
/* strings are now owned by pathspec */
@@ -1159,7 +1159,7 @@ static int bloom_filter_check(struct rev_info *rev,
return 1;
if (!rev->bloom_filter_settings ||
- !(filter = get_bloom_filter(rev->repo, commit, 0)))
+ !(filter = get_bloom_filter(rev->repo, commit)))
return 1;
if (!range)
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index 3553ad7b0a..b66314560a 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -2,7 +2,7 @@
#include "commit.h"
#include "config.h"
#include "revision.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "list-objects.h"
#include "list-objects-filter.h"
#include "list-objects-filter-options.h"
@@ -15,6 +15,29 @@ static int parse_combine_filter(
const char *arg,
struct strbuf *errbuf);
+const char *list_object_filter_config_name(enum list_objects_filter_choice c)
+{
+ switch (c) {
+ case LOFC_DISABLED:
+ /* we have no name for "no filter at all" */
+ break;
+ case LOFC_BLOB_NONE:
+ return "blob:none";
+ case LOFC_BLOB_LIMIT:
+ return "blob:limit";
+ case LOFC_TREE_DEPTH:
+ return "tree";
+ case LOFC_SPARSE_OID:
+ return "sparse:oid";
+ case LOFC_COMBINE:
+ return "combine";
+ case LOFC__COUNT:
+ /* not a real filter type; just the count of all filters */
+ break;
+ }
+ BUG("list_object_filter_choice_name: invalid argument '%d'", c);
+}
+
/*
* Parse value of the argument to the "filter" keyword.
* On the command line this looks like:
diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h
index 73fffa4ad7..01767c3c96 100644
--- a/list-objects-filter-options.h
+++ b/list-objects-filter-options.h
@@ -17,6 +17,12 @@ enum list_objects_filter_choice {
LOFC__COUNT /* must be last */
};
+/*
+ * Returns a configuration key suitable for describing the given object filter,
+ * e.g.: "blob:none", "combine", etc.
+ */
+const char *list_object_filter_config_name(enum list_objects_filter_choice c);
+
struct list_objects_filter_options {
/*
* 'filter_spec' is the raw argument value given on the command line
diff --git a/log-tree.c b/log-tree.c
index 55a68d0c61..cb8942fec1 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -15,7 +15,6 @@
#include "sequencer.h"
#include "line-log.h"
#include "help.h"
-#include "interdiff.h"
#include "range-diff.h"
static struct decoration name_decoration = { "object names" };
@@ -800,7 +799,8 @@ void show_log(struct rev_info *opt)
next_commentary_block(opt, NULL);
fprintf_ln(opt->diffopt.file, "%s", opt->idiff_title);
- show_interdiff(opt, 2);
+ show_interdiff(opt->idiff_oid1, opt->idiff_oid2, 2,
+ &opt->diffopt);
memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
}
@@ -917,26 +917,15 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
}
/* More than one parent? */
- if (parents && parents->next) {
+ if (parents->next) {
if (opt->ignore_merges)
return 0;
else if (opt->combine_merges)
return do_diff_combined(opt, commit);
- else if (opt->first_parent_only) {
- /*
- * Generate merge log entry only for the first
- * parent, showing summary diff of the others
- * we merged _in_.
- */
- parse_commit_or_die(parents->item);
- diff_tree_oid(get_commit_tree_oid(parents->item),
- oid, "", &opt->diffopt);
- log_tree_diff_flush(opt);
- return !opt->loginfo;
+ else if (!opt->first_parent_only) {
+ /* If we show multiple diffs, show the parent info */
+ log->parent = parents->item;
}
-
- /* If we show individual diffs, show the parent info */
- log->parent = parents->item;
}
showed_log = 0;
@@ -952,7 +941,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
/* Set up the log info for the next parent, if any.. */
parents = parents->next;
- if (!parents)
+ if (!parents || opt->first_parent_only)
break;
log->parent = parents->item;
opt->loginfo = log;
diff --git a/ls-refs.c b/ls-refs.c
index 50d86866c6..a1e0b473e4 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -2,7 +2,7 @@
#include "repository.h"
#include "refs.h"
#include "remote.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "ls-refs.h"
#include "pkt-line.h"
#include "config.h"
@@ -11,15 +11,15 @@
* Check if one of the prefixes is a prefix of the ref.
* If no prefixes were provided, all refs match.
*/
-static int ref_match(const struct argv_array *prefixes, const char *refname)
+static int ref_match(const struct strvec *prefixes, const char *refname)
{
int i;
- if (!prefixes->argc)
+ if (!prefixes->nr)
return 1; /* no restriction */
- for (i = 0; i < prefixes->argc; i++) {
- const char *prefix = prefixes->argv[i];
+ for (i = 0; i < prefixes->nr; i++) {
+ const char *prefix = prefixes->v[i];
if (starts_with(refname, prefix))
return 1;
@@ -31,7 +31,7 @@ static int ref_match(const struct argv_array *prefixes, const char *refname)
struct ls_refs_data {
unsigned peel;
unsigned symrefs;
- struct argv_array prefixes;
+ struct strvec prefixes;
};
static int send_ref(const char *refname, const struct object_id *oid,
@@ -84,7 +84,7 @@ static int ls_refs_config(const char *var, const char *value, void *data)
return parse_hide_refs_config(var, value, "uploadpack");
}
-int ls_refs(struct repository *r, struct argv_array *keys,
+int ls_refs(struct repository *r, struct strvec *keys,
struct packet_reader *request)
{
struct ls_refs_data data;
@@ -102,7 +102,7 @@ int ls_refs(struct repository *r, struct argv_array *keys,
else if (!strcmp("symrefs", arg))
data.symrefs = 1;
else if (skip_prefix(arg, "ref-prefix ", &out))
- argv_array_push(&data.prefixes, out);
+ strvec_push(&data.prefixes, out);
}
if (request->status != PACKET_READ_FLUSH)
@@ -111,6 +111,6 @@ int ls_refs(struct repository *r, struct argv_array *keys,
head_ref_namespaced(send_ref, &data);
for_each_namespaced_ref(send_ref, &data);
packet_flush(1);
- argv_array_clear(&data.prefixes);
+ strvec_clear(&data.prefixes);
return 0;
}
diff --git a/ls-refs.h b/ls-refs.h
index 7e5646f5f6..7b33a7c6b8 100644
--- a/ls-refs.h
+++ b/ls-refs.h
@@ -2,9 +2,9 @@
#define LS_REFS_H
struct repository;
-struct argv_array;
+struct strvec;
struct packet_reader;
-int ls_refs(struct repository *r, struct argv_array *keys,
+int ls_refs(struct repository *r, struct strvec *keys,
struct packet_reader *request);
#endif /* LS_REFS_H */
diff --git a/mem-pool.c b/mem-pool.c
index a2841a4a9a..8401761dda 100644
--- a/mem-pool.c
+++ b/mem-pool.c
@@ -12,11 +12,13 @@
* `insert_after`. If `insert_after` is NULL, then insert block at the
* head of the linked list.
*/
-static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t block_alloc, struct mp_block *insert_after)
+static struct mp_block *mem_pool_alloc_block(struct mem_pool *pool,
+ size_t block_alloc,
+ struct mp_block *insert_after)
{
struct mp_block *p;
- mem_pool->pool_alloc += sizeof(struct mp_block) + block_alloc;
+ pool->pool_alloc += sizeof(struct mp_block) + block_alloc;
p = xmalloc(st_add(sizeof(struct mp_block), block_alloc));
p->next_free = (char *)p->space;
@@ -26,35 +28,27 @@ static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t b
p->next_block = insert_after->next_block;
insert_after->next_block = p;
} else {
- p->next_block = mem_pool->mp_block;
- mem_pool->mp_block = p;
+ p->next_block = pool->mp_block;
+ pool->mp_block = p;
}
return p;
}
-void mem_pool_init(struct mem_pool **mem_pool, size_t initial_size)
+void mem_pool_init(struct mem_pool *pool, size_t initial_size)
{
- struct mem_pool *pool;
-
- if (*mem_pool)
- return;
-
- pool = xcalloc(1, sizeof(*pool));
-
+ memset(pool, 0, sizeof(*pool));
pool->block_alloc = BLOCK_GROWTH_SIZE;
if (initial_size > 0)
mem_pool_alloc_block(pool, initial_size, NULL);
-
- *mem_pool = pool;
}
-void mem_pool_discard(struct mem_pool *mem_pool, int invalidate_memory)
+void mem_pool_discard(struct mem_pool *pool, int invalidate_memory)
{
struct mp_block *block, *block_to_free;
- block = mem_pool->mp_block;
+ block = pool->mp_block;
while (block)
{
block_to_free = block;
@@ -66,10 +60,11 @@ void mem_pool_discard(struct mem_pool *mem_pool, int invalidate_memory)
free(block_to_free);
}
- free(mem_pool);
+ pool->mp_block = NULL;
+ pool->pool_alloc = 0;
}
-void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len)
+void *mem_pool_alloc(struct mem_pool *pool, size_t len)
{
struct mp_block *p = NULL;
void *r;
@@ -78,15 +73,15 @@ void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len)
if (len & (sizeof(uintmax_t) - 1))
len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
- if (mem_pool->mp_block &&
- mem_pool->mp_block->end - mem_pool->mp_block->next_free >= len)
- p = mem_pool->mp_block;
+ if (pool->mp_block &&
+ pool->mp_block->end - pool->mp_block->next_free >= len)
+ p = pool->mp_block;
if (!p) {
- if (len >= (mem_pool->block_alloc / 2))
- return mem_pool_alloc_block(mem_pool, len, mem_pool->mp_block);
+ if (len >= (pool->block_alloc / 2))
+ return mem_pool_alloc_block(pool, len, pool->mp_block);
- p = mem_pool_alloc_block(mem_pool, mem_pool->block_alloc, NULL);
+ p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
}
r = p->next_free;
@@ -94,20 +89,38 @@ void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len)
return r;
}
-void *mem_pool_calloc(struct mem_pool *mem_pool, size_t count, size_t size)
+void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size)
{
size_t len = st_mult(count, size);
- void *r = mem_pool_alloc(mem_pool, len);
+ void *r = mem_pool_alloc(pool, len);
memset(r, 0, len);
return r;
}
-int mem_pool_contains(struct mem_pool *mem_pool, void *mem)
+char *mem_pool_strdup(struct mem_pool *pool, const char *str)
+{
+ size_t len = strlen(str) + 1;
+ char *ret = mem_pool_alloc(pool, len);
+
+ return memcpy(ret, str, len);
+}
+
+char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len)
+{
+ char *p = memchr(str, '\0', len);
+ size_t actual_len = (p ? p - str : len);
+ char *ret = mem_pool_alloc(pool, actual_len+1);
+
+ ret[actual_len] = '\0';
+ return memcpy(ret, str, actual_len);
+}
+
+int mem_pool_contains(struct mem_pool *pool, void *mem)
{
struct mp_block *p;
/* Check if memory is allocated in a block */
- for (p = mem_pool->mp_block; p; p = p->next_block)
+ for (p = pool->mp_block; p; p = p->next_block)
if ((mem >= ((void *)p->space)) &&
(mem < ((void *)p->end)))
return 1;
diff --git a/mem-pool.h b/mem-pool.h
index 999d3c3a52..fe7507f022 100644
--- a/mem-pool.h
+++ b/mem-pool.h
@@ -24,12 +24,12 @@ struct mem_pool {
/*
* Initialize mem_pool with specified initial size.
*/
-void mem_pool_init(struct mem_pool **mem_pool, size_t initial_size);
+void mem_pool_init(struct mem_pool *pool, size_t initial_size);
/*
- * Discard a memory pool and free all the memory it is responsible for.
+ * Discard all the memory the memory pool is responsible for.
*/
-void mem_pool_discard(struct mem_pool *mem_pool, int invalidate_memory);
+void mem_pool_discard(struct mem_pool *pool, int invalidate_memory);
/*
* Alloc memory from the mem_pool.
@@ -42,6 +42,12 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len);
void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size);
/*
+ * Allocate memory from the memory pool and copy str into it.
+ */
+char *mem_pool_strdup(struct mem_pool *pool, const char *str);
+char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len);
+
+/*
* Move the memory associated with the 'src' pool to the 'dst' pool. The 'src'
* pool will be empty and not contain any memory. It still needs to be free'd
* with a call to `mem_pool_discard`.
@@ -52,6 +58,6 @@ void mem_pool_combine(struct mem_pool *dst, struct mem_pool *src);
* Check if a memory pointed at by 'mem' is part of the range of
* memory managed by the specified mem_pool.
*/
-int mem_pool_contains(struct mem_pool *mem_pool, void *mem);
+int mem_pool_contains(struct mem_pool *pool, void *mem);
#endif
diff --git a/merge-recursive.c b/merge-recursive.c
index 36948eafb7..d0214335a7 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3529,8 +3529,9 @@ static struct commit_list *reverse_commit_list(struct commit_list *list)
}
/*
- * Merge the commits h1 and h2, return the resulting virtual
- * commit object and a flag indicating the cleanness of the merge.
+ * Merge the commits h1 and h2, returning a flag (int) indicating the
+ * cleanness of the merge. Also, if opt->priv->call_depth, create a
+ * virtual commit and write its location to *result.
*/
static int merge_recursive_internal(struct merge_options *opt,
struct commit *h1,
@@ -3791,9 +3792,12 @@ int merge_recursive_generic(struct merge_options *opt,
static void merge_recursive_config(struct merge_options *opt)
{
char *value = NULL;
+ int renormalize = 0;
git_config_get_int("merge.verbosity", &opt->verbosity);
git_config_get_int("diff.renamelimit", &opt->rename_limit);
git_config_get_int("merge.renamelimit", &opt->rename_limit);
+ git_config_get_bool("merge.renormalize", &renormalize);
+ opt->renormalize = renormalize;
if (!git_config_get_string("diff.renames", &value)) {
opt->detect_renames = git_config_rename("diff.renames", value);
free(value);
diff --git a/merge-recursive.h b/merge-recursive.h
index 978847e672..0795a1d3ec 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -69,9 +69,8 @@ int parse_merge_opt(struct merge_options *opt, const char *s);
*
* Outputs:
* - See RETURN VALUES above
- * - No commit is created
* - opt->repo->index has the new index
- * - $GIT_INDEX_FILE is not updated
+ * - new index NOT written to disk
* - The working tree is updated with results of the merge
*/
int merge_trees(struct merge_options *opt,
@@ -81,7 +80,7 @@ int merge_trees(struct merge_options *opt,
/*
* merge_recursive is like merge_trees() but with recursive ancestor
- * consolidation and, if the commit is clean, creation of a commit.
+ * consolidation.
*
* NOTE: empirically, about a decade ago it was determined that with more
* than two merge bases, optimal behavior was found when the
@@ -91,9 +90,9 @@ int merge_trees(struct merge_options *opt,
*
* Outputs:
* - See RETURN VALUES above
- * - If merge is clean, a commit is created and its address written to *result
+ * - *result is treated as scratch space for temporary recursive merges
* - opt->repo->index has the new index
- * - $GIT_INDEX_FILE is not updated
+ * - new index NOT written to disk
* - The working tree is updated with results of the merge
*/
int merge_recursive(struct merge_options *opt,
@@ -109,9 +108,9 @@ int merge_recursive(struct merge_options *opt,
*
* Outputs:
* - See RETURN VALUES above
- * - If merge is clean, a commit is created and its address written to *result
+ * - *result is treated as scratch space for temporary recursive merges
* - opt->repo->index has the new index
- * - $GIT_INDEX_FILE is updated
+ * - new index also written to $GIT_INDEX_FILE on disk
* - The working tree is updated with results of the merge
*/
int merge_recursive_generic(struct merge_options *opt,
diff --git a/merge.c b/merge.c
index aa36de2f64..5fb88af102 100644
--- a/merge.c
+++ b/merge.c
@@ -19,22 +19,22 @@ int try_merge_command(struct repository *r,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes)
{
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
int i, ret;
struct commit_list *j;
- argv_array_pushf(&args, "merge-%s", strategy);
+ strvec_pushf(&args, "merge-%s", strategy);
for (i = 0; i < xopts_nr; i++)
- argv_array_pushf(&args, "--%s", xopts[i]);
+ strvec_pushf(&args, "--%s", xopts[i]);
for (j = common; j; j = j->next)
- argv_array_push(&args, merge_argument(j->item));
- argv_array_push(&args, "--");
- argv_array_push(&args, head_arg);
+ strvec_push(&args, merge_argument(j->item));
+ strvec_push(&args, "--");
+ strvec_push(&args, head_arg);
for (j = remotes; j; j = j->next)
- argv_array_push(&args, merge_argument(j->item));
+ strvec_push(&args, merge_argument(j->item));
- ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
- argv_array_clear(&args);
+ ret = run_command_v_opt(args.v, RUN_GIT_CMD);
+ strvec_clear(&args);
discard_index(r->index);
if (repo_read_index(r) < 0)
@@ -80,8 +80,8 @@ int checkout_fast_forward(struct repository *r,
}
memset(&opts, 0, sizeof(opts));
+ dir_init(&dir);
if (overwrite_ignore) {
- memset(&dir, 0, sizeof(dir));
dir.flags |= DIR_SHOW_IGNORED;
setup_standard_excludes(&dir);
opts.dir = &dir;
@@ -102,6 +102,7 @@ int checkout_fast_forward(struct repository *r,
clear_unpack_trees_porcelain(&opts);
return -1;
}
+ dir_clear(&dir);
clear_unpack_trees_porcelain(&opts);
if (write_locked_index(r->index, &lock_file, COMMIT_LOCK))
diff --git a/mergetools/bc b/mergetools/bc
index 3a69e60faa..a89086ee72 100644
--- a/mergetools/bc
+++ b/mergetools/bc
@@ -21,3 +21,8 @@ translate_merge_tool_path() {
echo bcompare
fi
}
+
+list_tool_variants () {
+ echo bc
+ echo bc3
+}
diff --git a/mergetools/bc3 b/mergetools/bc3
deleted file mode 100644
index 5d8dd48184..0000000000
--- a/mergetools/bc3
+++ /dev/null
@@ -1 +0,0 @@
-. "$MERGE_TOOLS_DIR/bc"
diff --git a/mergetools/gvimdiff3 b/mergetools/gvimdiff3
deleted file mode 100644
index 04a5bb0ea8..0000000000
--- a/mergetools/gvimdiff3
+++ /dev/null
@@ -1 +0,0 @@
-. "$MERGE_TOOLS_DIR/vimdiff"
diff --git a/mergetools/meld b/mergetools/meld
index 7a08470f88..aab4ebb935 100644
--- a/mergetools/meld
+++ b/mergetools/meld
@@ -3,34 +3,87 @@ diff_cmd () {
}
merge_cmd () {
- if test -z "${meld_has_output_option:+set}"
+ check_meld_for_features
+
+ option_auto_merge=
+ if test "$meld_use_auto_merge_option" = true
then
- check_meld_for_output_version
+ option_auto_merge="--auto-merge"
fi
if test "$meld_has_output_option" = true
then
- "$merge_tool_path" --output="$MERGED" \
+ "$merge_tool_path" $option_auto_merge --output="$MERGED" \
"$LOCAL" "$BASE" "$REMOTE"
else
- "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
+ "$merge_tool_path" $option_auto_merge "$LOCAL" "$MERGED" "$REMOTE"
fi
}
-# Check whether we should use 'meld --output <file>'
-check_meld_for_output_version () {
- meld_path="$(git config mergetool.meld.path)"
- meld_path="${meld_path:-meld}"
+# Get meld help message
+init_meld_help_msg () {
+ if test -z "$meld_help_msg"
+ then
+ meld_path="$(git config mergetool.meld.path || echo meld)"
+ meld_help_msg=$("$meld_path" --help 2>&1)
+ fi
+}
- if meld_has_output_option=$(git config --bool mergetool.meld.hasOutput)
+# Check the features and set flags
+check_meld_for_features () {
+ # Check whether we should use 'meld --output <file>'
+ if test -z "$meld_has_output_option"
then
- : use configured value
- elif "$meld_path" --help 2>&1 |
- grep -e '--output=' -e '\[OPTION\.\.\.\]' >/dev/null
+ meld_has_output_option=$(git config --bool mergetool.meld.hasOutput)
+ case "$meld_has_output_option" in
+ true | false)
+ : use configured value
+ ;;
+ *)
+ : empty or invalid configured value, detecting "--output" automatically
+ init_meld_help_msg
+
+ case "$meld_help_msg" in
+ *"--output="* | *'[OPTION...]'*)
+ # All version that has [OPTION...] supports --output
+ meld_has_output_option=true
+ ;;
+ *)
+ meld_has_output_option=false
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ # Check whether we should use 'meld --auto-merge ...'
+ if test -z "$meld_use_auto_merge_option"
then
- : old ones mention --output and new ones just say OPTION...
- meld_has_output_option=true
- else
- meld_has_output_option=false
+ meld_use_auto_merge_option=$(
+ git config --bool-or-str mergetool.meld.useAutoMerge
+ )
+ case "$meld_use_auto_merge_option" in
+ true | false)
+ : use well formatted boolean value
+ ;;
+ auto)
+ # testing the "--auto-merge" option only if config is "auto"
+ init_meld_help_msg
+
+ case "$meld_help_msg" in
+ *"--auto-merge"* | *'[OPTION...]'*)
+ meld_use_auto_merge_option=true
+ ;;
+ *)
+ meld_use_auto_merge_option=false
+ ;;
+ esac
+ ;;
+ "")
+ meld_use_auto_merge_option=false
+ ;;
+ *)
+ die "unknown mergetool.meld.useAutoMerge: $meld_use_auto_merge_option"
+ ;;
+ esac
fi
}
diff --git a/mergetools/gvimdiff2 b/mergetools/nvimdiff
index 04a5bb0ea8..04a5bb0ea8 100644
--- a/mergetools/gvimdiff2
+++ b/mergetools/nvimdiff
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index 10d86f3e19..abc8ce4ec4 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -5,7 +5,7 @@ diff_cmd () {
merge_cmd () {
case "$1" in
- gvimdiff|vimdiff)
+ *vimdiff)
if $base_present
then
"$merge_tool_path" -f -d -c '4wincmd w | wincmd J' \
@@ -15,11 +15,11 @@ merge_cmd () {
"$LOCAL" "$MERGED" "$REMOTE"
fi
;;
- gvimdiff2|vimdiff2)
+ *vimdiff2)
"$merge_tool_path" -f -d -c 'wincmd l' \
"$LOCAL" "$MERGED" "$REMOTE"
;;
- gvimdiff3|vimdiff3)
+ *vimdiff3)
if $base_present
then
"$merge_tool_path" -f -d -c 'hid | hid | hid' \
@@ -34,10 +34,13 @@ merge_cmd () {
translate_merge_tool_path() {
case "$1" in
- gvimdiff|gvimdiff2|gvimdiff3)
+ nvimdiff*)
+ echo nvim
+ ;;
+ gvimdiff*)
echo gvim
;;
- vimdiff|vimdiff2|vimdiff3)
+ vimdiff*)
echo vim
;;
esac
@@ -46,3 +49,11 @@ translate_merge_tool_path() {
exit_code_trustable () {
true
}
+
+list_tool_variants () {
+ for prefix in '' g n; do
+ for suffix in '' 2 3; do
+ echo "${prefix}vimdiff${suffix}"
+ done
+ done
+}
diff --git a/mergetools/vimdiff2 b/mergetools/vimdiff2
deleted file mode 100644
index 04a5bb0ea8..0000000000
--- a/mergetools/vimdiff2
+++ /dev/null
@@ -1 +0,0 @@
-. "$MERGE_TOOLS_DIR/vimdiff"
diff --git a/mergetools/vimdiff3 b/mergetools/vimdiff3
deleted file mode 100644
index 04a5bb0ea8..0000000000
--- a/mergetools/vimdiff3
+++ /dev/null
@@ -1 +0,0 @@
-. "$MERGE_TOOLS_DIR/vimdiff"
diff --git a/midx.c b/midx.c
index 6d1584ca51..0de42ffdfb 100644
--- a/midx.c
+++ b/midx.c
@@ -17,7 +17,6 @@
#define MIDX_BYTE_HASH_VERSION 5
#define MIDX_BYTE_NUM_CHUNKS 6
#define MIDX_BYTE_NUM_PACKS 8
-#define MIDX_HASH_VERSION 1
#define MIDX_HEADER_SIZE 12
#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
@@ -36,6 +35,18 @@
#define PACK_EXPIRED UINT_MAX
+static uint8_t oid_version(void)
+{
+ switch (hash_algo_by_ptr(the_hash_algo)) {
+ case GIT_HASH_SHA1:
+ return 1;
+ case GIT_HASH_SHA256:
+ return 2;
+ default:
+ die(_("invalid hash version"));
+ }
+}
+
static char *get_midx_filename(const char *object_dir)
{
return xstrfmt("%s/pack/multi-pack-index", object_dir);
@@ -90,8 +101,11 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
m->version);
hash_version = m->data[MIDX_BYTE_HASH_VERSION];
- if (hash_version != MIDX_HASH_VERSION)
- die(_("hash version %u does not match"), hash_version);
+ if (hash_version != oid_version()) {
+ error(_("multi-pack-index hash version %u does not match version %u"),
+ hash_version, oid_version());
+ goto cleanup_fail;
+ }
m->hash_len = the_hash_algo->rawsz;
m->num_chunks = m->data[MIDX_BYTE_NUM_CHUNKS];
@@ -402,8 +416,12 @@ int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, i
m = load_multi_pack_index(object_dir, local);
if (m) {
- m->next = r->objects->multi_pack_index;
- r->objects->multi_pack_index = m;
+ struct multi_pack_index *mp = r->objects->multi_pack_index;
+ if (mp) {
+ m->next = mp->next;
+ mp->next = m;
+ } else
+ r->objects->multi_pack_index = m;
return 1;
}
@@ -414,14 +432,11 @@ static size_t write_midx_header(struct hashfile *f,
unsigned char num_chunks,
uint32_t num_packs)
{
- unsigned char byte_values[4];
-
hashwrite_be32(f, MIDX_SIGNATURE);
- byte_values[0] = MIDX_VERSION;
- byte_values[1] = MIDX_HASH_VERSION;
- byte_values[2] = num_chunks;
- byte_values[3] = 0; /* unused */
- hashwrite(f, byte_values, sizeof(byte_values));
+ hashwrite_u8(f, MIDX_VERSION);
+ hashwrite_u8(f, oid_version());
+ hashwrite_u8(f, num_chunks);
+ hashwrite_u8(f, 0); /* unused */
hashwrite_be32(f, num_packs);
return MIDX_HEADER_SIZE;
@@ -807,11 +822,9 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
int result = 0;
midx_name = get_midx_filename(object_dir);
- if (safe_create_leading_directories(midx_name)) {
- UNLEAK(midx_name);
+ if (safe_create_leading_directories(midx_name))
die_errno(_("unable to create leading directories of %s"),
midx_name);
- }
if (m)
packs.m = m;
@@ -1051,10 +1064,8 @@ void clear_midx_file(struct repository *r)
r->objects->multi_pack_index = NULL;
}
- if (remove_path(midx)) {
- UNLEAK(midx);
+ if (remove_path(midx))
die(_("failed to clear multi-pack-index at %s"), midx);
- }
free(midx);
}
@@ -1105,8 +1116,17 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag
struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
verify_midx_error = 0;
- if (!m)
- return 0;
+ if (!m) {
+ int result = 0;
+ struct stat sb;
+ char *filename = get_midx_filename(object_dir);
+ if (!stat(filename, &sb)) {
+ error(_("multi-pack-index file exists, but failed to parse"));
+ result = 1;
+ }
+ free(filename);
+ return result;
+ }
if (flags & MIDX_PROGRESS)
progress = start_progress(_("Looking for referenced packfiles"),
@@ -1371,7 +1391,7 @@ static int fill_included_packs_batch(struct repository *r,
free(pack_info);
- if (total_size < batch_size || packs_to_repack < 2)
+ if (packs_to_repack < 2)
return 1;
return 0;
@@ -1383,6 +1403,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
uint32_t i;
unsigned char *include_pack;
struct child_process cmd = CHILD_PROCESS_INIT;
+ FILE *cmd_in;
struct strbuf base_name = STRBUF_INIT;
struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
@@ -1408,21 +1429,21 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
- argv_array_push(&cmd.args, "pack-objects");
+ strvec_push(&cmd.args, "pack-objects");
strbuf_addstr(&base_name, object_dir);
strbuf_addstr(&base_name, "/pack/pack");
- argv_array_push(&cmd.args, base_name.buf);
+ strvec_push(&cmd.args, base_name.buf);
if (delta_base_offset)
- argv_array_push(&cmd.args, "--delta-base-offset");
+ strvec_push(&cmd.args, "--delta-base-offset");
if (use_delta_islands)
- argv_array_push(&cmd.args, "--delta-islands");
+ strvec_push(&cmd.args, "--delta-islands");
if (flags & MIDX_PROGRESS)
- argv_array_push(&cmd.args, "--progress");
+ strvec_push(&cmd.args, "--progress");
else
- argv_array_push(&cmd.args, "-q");
+ strvec_push(&cmd.args, "-q");
strbuf_release(&base_name);
@@ -1435,6 +1456,8 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
goto cleanup;
}
+ cmd_in = xfdopen(cmd.in, "w");
+
for (i = 0; i < m->num_objects; i++) {
struct object_id oid;
uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
@@ -1443,10 +1466,9 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
continue;
nth_midxed_object_oid(&oid, m, i);
- xwrite(cmd.in, oid_to_hex(&oid), the_hash_algo->hexsz);
- xwrite(cmd.in, "\n", 1);
+ fprintf(cmd_in, "%s\n", oid_to_hex(&oid));
}
- close(cmd.in);
+ fclose(cmd_in);
if (finish_command(&cmd)) {
error(_("could not finish pack-objects"));
diff --git a/negotiator/noop.c b/negotiator/noop.c
new file mode 100644
index 0000000000..60569b8350
--- /dev/null
+++ b/negotiator/noop.c
@@ -0,0 +1,44 @@
+#include "cache.h"
+#include "noop.h"
+#include "../commit.h"
+#include "../fetch-negotiator.h"
+
+static void known_common(struct fetch_negotiator *n, struct commit *c)
+{
+ /* do nothing */
+}
+
+static void add_tip(struct fetch_negotiator *n, struct commit *c)
+{
+ /* do nothing */
+}
+
+static const struct object_id *next(struct fetch_negotiator *n)
+{
+ return NULL;
+}
+
+static int ack(struct fetch_negotiator *n, struct commit *c)
+{
+ /*
+ * This negotiator does not emit any commits, so there is no commit to
+ * be acknowledged. If there is any ack, there is a bug.
+ */
+ BUG("ack with noop negotiator, which does not emit any commits");
+ return 0;
+}
+
+static void release(struct fetch_negotiator *n)
+{
+ /* nothing to release */
+}
+
+void noop_negotiator_init(struct fetch_negotiator *negotiator)
+{
+ negotiator->known_common = known_common;
+ negotiator->add_tip = add_tip;
+ negotiator->next = next;
+ negotiator->ack = ack;
+ negotiator->release = release;
+ negotiator->data = NULL;
+}
diff --git a/negotiator/noop.h b/negotiator/noop.h
new file mode 100644
index 0000000000..2b4ec5d07a
--- /dev/null
+++ b/negotiator/noop.h
@@ -0,0 +1,8 @@
+#ifndef NEGOTIATOR_NOOP_H
+#define NEGOTIATOR_NOOP_H
+
+struct fetch_negotiator;
+
+void noop_negotiator_init(struct fetch_negotiator *negotiator);
+
+#endif
diff --git a/object-store.h b/object-store.h
index f439d47af8..c4fc9dd74e 100644
--- a/object-store.h
+++ b/object-store.h
@@ -239,12 +239,33 @@ int read_loose_object(const char *path,
unsigned long *size,
void **contents);
+/* Retry packed storage after checking packed and loose storage */
+#define HAS_OBJECT_RECHECK_PACKED 1
+
+/*
+ * Returns 1 if the object exists. This function will not lazily fetch objects
+ * in a partial clone.
+ */
+int has_object(struct repository *r, const struct object_id *oid,
+ unsigned flags);
+
+/*
+ * These macros and functions are deprecated. If checking existence for an
+ * object that is likely to be missing and/or whose absence is relatively
+ * inconsequential (or is consequential but the caller is prepared to handle
+ * it), use has_object(), which has better defaults (no lazy fetch in a partial
+ * clone and no rechecking of packed storage). In the unlikely event that a
+ * caller needs to assert existence of an object that it fully expects to
+ * exist, and wants to trigger a lazy fetch in a partial clone, use
+ * oid_object_info_extended() with a NULL struct object_info.
+ *
+ * These functions can be removed once all callers have migrated to
+ * has_object() and/or oid_object_info_extended().
+ */
#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
#define has_sha1_file_with_flags(sha1, flags) repo_has_sha1_file_with_flags(the_repository, sha1, flags)
#define has_sha1_file(sha1) repo_has_sha1_file(the_repository, sha1)
#endif
-
-/* Same as the above, except for struct object_id. */
int repo_has_object_file(struct repository *r, const struct object_id *oid);
int repo_has_object_file_with_flags(struct repository *r,
const struct object_id *oid, int flags);
diff --git a/object.h b/object.h
index 96a2105859..20b18805f0 100644
--- a/object.h
+++ b/object.h
@@ -73,6 +73,7 @@ struct object_array {
* sha1-name.c: 20
* list-objects-filter.c: 21
* builtin/fsck.c: 0--3
+ * builtin/gc.c: 0
* builtin/index-pack.c: 2021
* builtin/pack-objects.c: 20
* builtin/reflog.c: 10--12
diff --git a/oidset.c b/oidset.c
index 15d4e18c37..2d0ab76fb5 100644
--- a/oidset.c
+++ b/oidset.c
@@ -43,6 +43,12 @@ int oidset_size(struct oidset *set)
void oidset_parse_file(struct oidset *set, const char *path)
{
+ oidset_parse_file_carefully(set, path, NULL, NULL);
+}
+
+void oidset_parse_file_carefully(struct oidset *set, const char *path,
+ oidset_parse_tweak_fn fn, void *cbdata)
+{
FILE *fp;
struct strbuf sb = STRBUF_INIT;
struct object_id oid;
@@ -66,7 +72,8 @@ void oidset_parse_file(struct oidset *set, const char *path)
if (!sb.len)
continue;
- if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0')
+ if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0' ||
+ (fn && fn(&oid, cbdata)))
die("invalid object name: %s", sb.buf);
oidset_insert(set, &oid);
}
diff --git a/oidset.h b/oidset.h
index 209ae7a173..01f6560283 100644
--- a/oidset.h
+++ b/oidset.h
@@ -73,6 +73,15 @@ void oidset_clear(struct oidset *set);
*/
void oidset_parse_file(struct oidset *set, const char *path);
+/*
+ * Similar to the above, but with a callback which can (1) return non-zero to
+ * signal displeasure with the object and (2) replace object ID with something
+ * else (meant to be used to "peel").
+ */
+typedef int (*oidset_parse_tweak_fn)(struct object_id *, void *);
+void oidset_parse_file_carefully(struct oidset *set, const char *path,
+ oidset_parse_tweak_fn fn, void *cbdata);
+
struct oidset_iter {
kh_oid_set_t *set;
khiter_t iter;
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index a7a4964b50..5e998bdaa7 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -503,8 +503,7 @@ static void write_hash_cache(struct hashfile *f,
for (i = 0; i < index_nr; ++i) {
struct object_entry *entry = (struct object_entry *)index[i];
- uint32_t hash_value = htonl(entry->hash);
- hashwrite(f, &hash_value, sizeof(hash_value));
+ hashwrite_be32(f, entry->hash);
}
}
diff --git a/pack-write.c b/pack-write.c
index f0017beb9d..a6cdb3c67c 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -38,9 +38,8 @@ static int need_large_offset(off_t offset, const struct pack_idx_option *opts)
}
/*
- * On entry *sha1 contains the pack content SHA1 hash, on exit it is
- * the SHA1 hash of sorted object names. The objects array passed in
- * will be sorted by SHA1 on exit.
+ * The *sha1 contains the pack content SHA1 hash.
+ * The objects array passed in will be sorted by SHA1 on exit.
*/
const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects,
int nr_objects, const struct pack_idx_option *opts,
@@ -118,10 +117,8 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
list = sorted_by_sha;
for (i = 0; i < nr_objects; i++) {
struct pack_idx_entry *obj = *list++;
- if (index_version < 2) {
- uint32_t offset = htonl(obj->offset);
- hashwrite(f, &offset, 4);
- }
+ if (index_version < 2)
+ hashwrite_be32(f, obj->offset);
hashwrite(f, obj->oid.hash, the_hash_algo->rawsz);
if ((opts->flags & WRITE_IDX_STRICT) &&
(i && oideq(&list[-2]->oid, &obj->oid)))
@@ -136,8 +133,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
list = sorted_by_sha;
for (i = 0; i < nr_objects; i++) {
struct pack_idx_entry *obj = *list++;
- uint32_t crc32_val = htonl(obj->crc32);
- hashwrite(f, &crc32_val, 4);
+ hashwrite_be32(f, obj->crc32);
}
/* write the 32-bit offset table */
@@ -149,8 +145,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
offset = (need_large_offset(obj->offset, opts)
? (0x80000000 | nr_large_offset++)
: obj->offset);
- offset = htonl(offset);
- hashwrite(f, &offset, 4);
+ hashwrite_be32(f, offset);
}
/* write the large offset table */
diff --git a/packfile.c b/packfile.c
index 6ab5233613..e69012e7f2 100644
--- a/packfile.c
+++ b/packfile.c
@@ -923,6 +923,7 @@ unsigned long repo_approximate_object_count(struct repository *r)
count += p->num_objects;
}
r->objects->approximate_object_count = count;
+ r->objects->approximate_object_count_valid = 1;
}
return r->objects->approximate_object_count;
}
@@ -1027,6 +1028,17 @@ struct multi_pack_index *get_multi_pack_index(struct repository *r)
return r->objects->multi_pack_index;
}
+struct multi_pack_index *get_local_multi_pack_index(struct repository *r)
+{
+ struct multi_pack_index *m = get_multi_pack_index(r);
+
+ /* no need to iterate; we always put the local one first (if any) */
+ if (m && m->local)
+ return m;
+
+ return NULL;
+}
+
struct packed_git *get_all_packs(struct repository *r)
{
struct multi_pack_index *m;
diff --git a/packfile.h b/packfile.h
index 240aa73b95..a58fc738e0 100644
--- a/packfile.h
+++ b/packfile.h
@@ -57,6 +57,7 @@ void install_packed_git(struct repository *r, struct packed_git *pack);
struct packed_git *get_packed_git(struct repository *r);
struct list_head *get_packed_git_mru(struct repository *r);
struct multi_pack_index *get_multi_pack_index(struct repository *r);
+struct multi_pack_index *get_local_multi_pack_index(struct repository *r);
struct packed_git *get_all_packs(struct repository *r);
/*
diff --git a/pager.c b/pager.c
index 41446d4f05..ee435de675 100644
--- a/pager.c
+++ b/pager.c
@@ -68,7 +68,7 @@ const char *git_pager(int stdout_is_tty)
return pager;
}
-static void setup_pager_env(struct argv_array *env)
+static void setup_pager_env(struct strvec *env)
{
const char **argv;
int i;
@@ -88,7 +88,7 @@ static void setup_pager_env(struct argv_array *env)
*cp = '\0';
if (!getenv(argv[i])) {
*cp = '=';
- argv_array_push(env, argv[i]);
+ strvec_push(env, argv[i]);
}
}
free(pager_env);
@@ -97,7 +97,7 @@ static void setup_pager_env(struct argv_array *env)
void prepare_pager_args(struct child_process *pager_process, const char *pager)
{
- argv_array_push(&pager_process->args, pager);
+ strvec_push(&pager_process->args, pager);
pager_process->use_shell = 1;
setup_pager_env(&pager_process->env_array);
pager_process->trace2_child_class = "pager";
@@ -126,7 +126,7 @@ void setup_pager(void)
/* spawn the pager */
prepare_pager_args(&pager_process, pager);
pager_process.in = -1;
- argv_array_push(&pager_process.env_array, "GIT_PAGER_IN_USE");
+ strvec_push(&pager_process.env_array, "GIT_PAGER_IN_USE");
if (start_command(&pager_process))
return;
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 86cd393013..d9d3b0819f 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -4,7 +4,7 @@
#include "commit.h"
#include "color.h"
#include "string-list.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "oid-array.h"
/*----- some often used options -----*/
@@ -275,19 +275,19 @@ int parse_opt_passthru(const struct option *opt, const char *arg, int unset)
/**
* For an option opt, recreate the command-line option, appending it to
- * opt->value which must be a argv_array. This is useful when we need to pass
+ * opt->value which must be a strvec. This is useful when we need to pass
* the command-line option, which can be specified multiple times, to another
* command.
*/
int parse_opt_passthru_argv(const struct option *opt, const char *arg, int unset)
{
static struct strbuf sb = STRBUF_INIT;
- struct argv_array *opt_value = opt->value;
+ struct strvec *opt_value = opt->value;
if (recreate_opt(&sb, opt, arg, unset) < 0)
return -1;
- argv_array_push(opt_value, sb.buf);
+ strvec_push(opt_value, sb.buf);
return 0;
}
diff --git a/parse-options.c b/parse-options.c
index c57618d537..f0507432ee 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -525,7 +525,8 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
parse_options_start_1(ctx, argc, argv, prefix, options, flags);
}
-static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
+static void show_negated_gitcomp(const struct option *opts, int show_all,
+ int nr_noopts)
{
int printed_dashdash = 0;
@@ -535,7 +536,8 @@ static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
if (!opts->long_name)
continue;
- if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
+ if (!show_all &&
+ (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE)))
continue;
if (opts->flags & PARSE_OPT_NONEG)
continue;
@@ -572,7 +574,7 @@ static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
}
}
-static int show_gitcomp(const struct option *opts)
+static int show_gitcomp(const struct option *opts, int show_all)
{
const struct option *original_opts = opts;
int nr_noopts = 0;
@@ -582,7 +584,8 @@ static int show_gitcomp(const struct option *opts)
if (!opts->long_name)
continue;
- if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
+ if (!show_all &&
+ (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE)))
continue;
switch (opts->type) {
@@ -610,8 +613,8 @@ static int show_gitcomp(const struct option *opts)
nr_noopts++;
printf(" --%s%s", opts->long_name, suffix);
}
- show_negated_gitcomp(original_opts, -1);
- show_negated_gitcomp(original_opts, nr_noopts);
+ show_negated_gitcomp(original_opts, show_all, -1);
+ show_negated_gitcomp(original_opts, show_all, nr_noopts);
fputc('\n', stdout);
return PARSE_OPT_COMPLETE;
}
@@ -723,9 +726,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h"))
goto show_usage;
- /* lone --git-completion-helper is asked by git-completion.bash */
- if (ctx->total == 1 && !strcmp(arg + 1, "-git-completion-helper"))
- return show_gitcomp(options);
+ /*
+ * lone --git-completion-helper and --git-completion-helper-all
+ * are asked by git-completion.bash
+ */
+ if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper"))
+ return show_gitcomp(options, 0);
+ if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper-all"))
+ return show_gitcomp(options, 1);
if (arg[1] != '-') {
ctx->opt = arg + 1;
diff --git a/parse-options.h b/parse-options.h
index 46af942093..7030d8f3da 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -314,7 +314,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
#define OPT__FORCE(var, h, f) OPT_COUNTUP_F('f', "force", (var), (h), (f))
#define OPT__ABBREV(var) \
{ OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
- N_("use <n> digits to display SHA-1s"), \
+ N_("use <n> digits to display object names"), \
PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
#define OPT__COLOR(var, h) \
OPT_COLOR_FLAG(0, "color", (var), (h))
diff --git a/path.c b/path.c
index 8b2c753191..7b385e5eb2 100644
--- a/path.c
+++ b/path.c
@@ -1528,8 +1528,6 @@ char *xdg_cache_home(const char *filename)
return NULL;
}
-REPO_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD")
-REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD")
REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")
REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
diff --git a/path.h b/path.h
index 1f1bf8f87a..e7e77da6aa 100644
--- a/path.h
+++ b/path.h
@@ -170,8 +170,6 @@ void report_linked_checkout_garbage(void);
}
struct path_cache {
- const char *cherry_pick_head;
- const char *revert_head;
const char *squash_msg;
const char *merge_msg;
const char *merge_rr;
@@ -182,10 +180,11 @@ struct path_cache {
const char *shallow;
};
-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+#define PATH_CACHE_INIT \
+ { \
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \
+ }
-const char *git_path_cherry_pick_head(struct repository *r);
-const char *git_path_revert_head(struct repository *r);
const char *git_path_squash_msg(struct repository *r);
const char *git_path_merge_msg(struct repository *r);
const char *git_path_merge_rr(struct repository *r);
diff --git a/pathspec.c b/pathspec.c
index 8243e06eab..7a229d8d22 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -3,7 +3,7 @@
#include "dir.h"
#include "pathspec.h"
#include "attr.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "quote.h"
/*
@@ -624,7 +624,7 @@ void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
unsigned flags, const char *prefix,
const char *file, int nul_term_line)
{
- struct argv_array parsed_file = ARGV_ARRAY_INIT;
+ struct strvec parsed_file = STRVEC_INIT;
strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
strbuf_getline;
struct strbuf buf = STRBUF_INIT;
@@ -643,7 +643,7 @@ void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
die(_("line is badly quoted: %s"), buf.buf);
strbuf_swap(&buf, &unquoted);
}
- argv_array_push(&parsed_file, buf.buf);
+ strvec_push(&parsed_file, buf.buf);
strbuf_reset(&buf);
}
@@ -652,8 +652,8 @@ void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
if (in != stdin)
fclose(in);
- parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv);
- argv_array_clear(&parsed_file);
+ parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.v);
+ strvec_clear(&parsed_file);
}
void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
diff --git a/perl/Git.pm b/perl/Git.pm
index 54c9ed0dde..10df990959 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -723,6 +723,32 @@ sub config_int {
return scalar _config_common({'kind' => '--int'}, @_);
}
+=item config_regexp ( RE )
+
+Retrieve the list of configuration key names matching the regular
+expression C<RE>. The return value is a list of strings matching
+this regex.
+
+=cut
+
+sub config_regexp {
+ my ($self, $regex) = _maybe_self(@_);
+ try {
+ my @cmd = ('config', '--name-only', '--get-regexp', $regex);
+ unshift @cmd, $self if $self;
+ my @matches = command(@cmd);
+ return @matches;
+ } catch Git::Error::Command with {
+ my $E = shift;
+ if ($E->value() == 1) {
+ my @matches = ();
+ return @matches;
+ } else {
+ throw $E;
+ }
+ };
+}
+
# Common subroutine to implement bulk of what the config* family of methods
# do. This currently wraps command('config') so it is not so fast.
sub _config_common {
diff --git a/po/fr.po b/po/fr.po
index d20fc440ab..75b1e75f6a 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -6503,7 +6503,7 @@ msgstr "'%s' ne peut pas être résolue comme une branche"
#: remote.c:1088
#, c-format
msgid "unable to delete '%s': remote ref does not exist"
-msgstr "suppression d '%s' impossible : la référence distante n'existe pas"
+msgstr "suppression de '%s' impossible : la référence distante n'existe pas"
#: remote.c:1100
#, c-format
diff --git a/pretty.c b/pretty.c
index 2a3d46bf42..7a7708a0ea 100644
--- a/pretty.c
+++ b/pretty.c
@@ -839,21 +839,22 @@ static int istitlechar(char c)
(c >= '0' && c <= '9') || c == '.' || c == '_';
}
-static void format_sanitized_subject(struct strbuf *sb, const char *msg)
+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len)
{
size_t trimlen;
size_t start_len = sb->len;
int space = 2;
+ int i;
- for (; *msg && *msg != '\n'; msg++) {
- if (istitlechar(*msg)) {
+ for (i = 0; i < len; i++) {
+ if (istitlechar(msg[i])) {
if (space == 1)
strbuf_addch(sb, '-');
space = 0;
- strbuf_addch(sb, *msg);
- if (*msg == '.')
- while (*(msg+1) == '.')
- msg++;
+ strbuf_addch(sb, msg[i]);
+ if (msg[i] == '.')
+ while (msg[i+1] == '.')
+ i++;
} else
space |= 1;
}
@@ -1155,7 +1156,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
const struct commit *commit = c->commit;
const char *msg = c->message;
struct commit_list *p;
- const char *arg;
+ const char *arg, *eol;
size_t res;
char **slot;
@@ -1405,7 +1406,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
format_subject(sb, msg + c->subject_off, " ");
return 1;
case 'f': /* sanitized subject */
- format_sanitized_subject(sb, msg + c->subject_off);
+ eol = strchrnul(msg + c->subject_off, '\n');
+ format_sanitized_subject(sb, msg + c->subject_off, eol - (msg + c->subject_off));
return 1;
case 'b': /* body */
strbuf_addstr(sb, msg + c->body_off);
diff --git a/pretty.h b/pretty.h
index 071f2fb8e4..7ce6c0b437 100644
--- a/pretty.h
+++ b/pretty.h
@@ -139,4 +139,7 @@ const char *format_subject(struct strbuf *sb, const char *msg,
/* Check if "cmit_fmt" will produce an empty output. */
int commit_format_is_empty(enum cmit_fmt);
+/* Make subject of commit message suitable for filename */
+void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);
+
#endif /* PRETTY_H */
diff --git a/progress.c b/progress.c
index 3eda914518..31014e6fca 100644
--- a/progress.c
+++ b/progress.c
@@ -319,9 +319,12 @@ static void finish_if_sparse(struct progress *progress)
void stop_progress(struct progress **p_progress)
{
+ if (!p_progress)
+ BUG("don't provide NULL to stop_progress");
+
finish_if_sparse(*p_progress);
- if (p_progress && *p_progress) {
+ if (*p_progress) {
trace2_data_intmax("progress", the_repository, "total_objects",
(*p_progress)->total);
@@ -338,7 +341,12 @@ void stop_progress(struct progress **p_progress)
void stop_progress_msg(struct progress **p_progress, const char *msg)
{
- struct progress *progress = *p_progress;
+ struct progress *progress;
+
+ if (!p_progress)
+ BUG("don't provide NULL to stop_progress_msg");
+
+ progress = *p_progress;
if (!progress)
return;
*p_progress = NULL;
diff --git a/promisor-remote.c b/promisor-remote.c
index baaea12fd6..6530e26f98 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -3,6 +3,7 @@
#include "promisor-remote.h"
#include "config.h"
#include "transport.h"
+#include "strvec.h"
static char *repository_format_partial_clone;
static const char *core_partial_clone_filter_default;
@@ -12,39 +13,34 @@ void set_repository_format_partial_clone(char *partial_clone)
repository_format_partial_clone = xstrdup_or_null(partial_clone);
}
-static int fetch_refs(const char *remote_name, struct ref *ref)
-{
- struct remote *remote;
- struct transport *transport;
- int res;
-
- 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");
- res = transport_fetch_refs(transport, ref);
-
- return res;
-}
-
static int fetch_objects(const char *remote_name,
const struct object_id *oids,
int oid_nr)
{
- struct ref *ref = NULL;
+ struct child_process child = CHILD_PROCESS_INIT;
int i;
+ FILE *child_in;
+
+ child.git_cmd = 1;
+ child.in = -1;
+ strvec_pushl(&child.args, "-c", "fetch.negotiationAlgorithm=noop",
+ "fetch", remote_name, "--no-tags",
+ "--no-write-fetch-head", "--recurse-submodules=no",
+ "--filter=blob:none", "--stdin", NULL);
+ if (start_command(&child))
+ die(_("promisor-remote: unable to fork off fetch subprocess"));
+ child_in = xfdopen(child.in, "w");
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;
+ if (fputs(oid_to_hex(&oids[i]), child_in) < 0)
+ die_errno(_("promisor-remote: could not write to fetch subprocess"));
+ if (fputc('\n', child_in) < 0)
+ die_errno(_("promisor-remote: could not write to fetch subprocess"));
}
- return fetch_refs(remote_name, ref);
+
+ if (fclose(child_in) < 0)
+ die_errno(_("promisor-remote: could not close stdin to fetch subprocess"));
+ return finish_command(&child) ? -1 : 0;
}
static struct promisor_remote *promisors;
diff --git a/protocol.c b/protocol.c
index d1dd3424bb..052d7edbb9 100644
--- a/protocol.c
+++ b/protocol.c
@@ -17,11 +17,10 @@ static enum protocol_version parse_protocol_version(const char *value)
enum protocol_version get_protocol_version_config(void)
{
const char *value;
- int val;
const char *git_test_k = "GIT_TEST_PROTOCOL_VERSION";
const char *git_test_v;
- if (!git_config_get_string_const("protocol.version", &value)) {
+ if (!git_config_get_string_tmp("protocol.version", &value)) {
enum protocol_version version = parse_protocol_version(value);
if (version == protocol_unknown_version)
@@ -31,9 +30,6 @@ enum protocol_version get_protocol_version_config(void)
return version;
}
- if (!git_config_get_bool("feature.experimental", &val) && val)
- return protocol_v2;
-
git_test_v = getenv(git_test_k);
if (git_test_v && *git_test_v) {
enum protocol_version env = parse_protocol_version(git_test_v);
@@ -43,7 +39,7 @@ enum protocol_version get_protocol_version_config(void)
return env;
}
- return protocol_v0;
+ return protocol_v2;
}
enum protocol_version determine_protocol_version_server(void)
diff --git a/quote.c b/quote.c
index bcc0dbc50d..69f4ca45da 100644
--- a/quote.c
+++ b/quote.c
@@ -1,6 +1,6 @@
#include "cache.h"
#include "quote.h"
-#include "argv-array.h"
+#include "strvec.h"
int quote_path_fully = 1;
@@ -172,7 +172,7 @@ char *sq_dequote(char *arg)
static int sq_dequote_to_argv_internal(char *arg,
const char ***argv, int *nr, int *alloc,
- struct argv_array *array)
+ struct strvec *array)
{
char *next = arg;
@@ -187,7 +187,7 @@ static int sq_dequote_to_argv_internal(char *arg,
(*argv)[(*nr)++] = dequoted;
}
if (array)
- argv_array_push(array, dequoted);
+ strvec_push(array, dequoted);
} while (next);
return 0;
@@ -198,7 +198,7 @@ int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL);
}
-int sq_dequote_to_argv_array(char *arg, struct argv_array *array)
+int sq_dequote_to_strvec(char *arg, struct strvec *array)
{
return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array);
}
@@ -210,7 +210,7 @@ int sq_dequote_to_argv_array(char *arg, struct argv_array *array)
*/
#define X8(x) x, x, x, x, x, x, x, x
#define X16(x) X8(x), X8(x)
-static signed char const sq_lookup[256] = {
+static signed char const cq_lookup[256] = {
/* 0 1 2 3 4 5 6 7 */
/* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 'a',
/* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r', 1, 1,
@@ -223,9 +223,9 @@ static signed char const sq_lookup[256] = {
/* 0x80 */ /* set to 0 */
};
-static inline int sq_must_quote(char c)
+static inline int cq_must_quote(char c)
{
- return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
+ return cq_lookup[(unsigned char)c] + quote_path_fully > 0;
}
/* returns the longest prefix not needing a quote up to maxlen if positive.
@@ -235,9 +235,9 @@ static size_t next_quote_pos(const char *s, ssize_t maxlen)
{
size_t len;
if (maxlen < 0) {
- for (len = 0; !sq_must_quote(s[len]); len++);
+ for (len = 0; !cq_must_quote(s[len]); len++);
} else {
- for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
+ for (len = 0; len < maxlen && !cq_must_quote(s[len]); len++);
}
return len;
}
@@ -256,7 +256,7 @@ static size_t next_quote_pos(const char *s, ssize_t maxlen)
* Return value is the same as in (1).
*/
static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
- struct strbuf *sb, FILE *fp, int no_dq)
+ struct strbuf *sb, FILE *fp, unsigned flags)
{
#undef EMIT
#define EMIT(c) \
@@ -272,6 +272,7 @@ static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
count += (l); \
} while (0)
+ int no_dq = !!(flags & CQUOTE_NODQ);
size_t len, count = 0;
const char *p = name;
@@ -291,8 +292,8 @@ static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
ch = (unsigned char)*p++;
if (maxlen >= 0)
maxlen -= len + 1;
- if (sq_lookup[ch] >= ' ') {
- EMIT(sq_lookup[ch]);
+ if (cq_lookup[ch] >= ' ') {
+ EMIT(cq_lookup[ch]);
} else {
EMIT(((ch >> 6) & 03) + '0');
EMIT(((ch >> 3) & 07) + '0');
@@ -309,19 +310,21 @@ static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
return count;
}
-size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
+size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, unsigned flags)
{
- return quote_c_style_counted(name, -1, sb, fp, nodq);
+ return quote_c_style_counted(name, -1, sb, fp, flags);
}
-void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq)
+void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path,
+ unsigned flags)
{
+ int nodq = !!(flags & CQUOTE_NODQ);
if (quote_c_style(prefix, NULL, NULL, 0) ||
quote_c_style(path, NULL, NULL, 0)) {
if (!nodq)
strbuf_addch(sb, '"');
- quote_c_style(prefix, sb, NULL, 1);
- quote_c_style(path, sb, NULL, 1);
+ quote_c_style(prefix, sb, NULL, CQUOTE_NODQ);
+ quote_c_style(path, sb, NULL, CQUOTE_NODQ);
if (!nodq)
strbuf_addch(sb, '"');
} else {
@@ -352,13 +355,25 @@ void write_name_quoted_relative(const char *name, const char *prefix,
}
/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, const char *prefix,
- struct strbuf *out)
+char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigned flags)
{
struct strbuf sb = STRBUF_INIT;
const char *rel = relative_path(in, prefix, &sb);
+ int force_dq = ((flags & QUOTE_PATH_QUOTE_SP) && strchr(rel, ' '));
+
strbuf_reset(out);
- quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
+
+ /*
+ * If the caller wants us to enclose the output in a dq-pair
+ * whether quote_c_style_counted() needs to, we do it ourselves
+ * and tell quote_c_style_counted() not to.
+ */
+ if (force_dq)
+ strbuf_addch(out, '"');
+ quote_c_style_counted(rel, strlen(rel), out, NULL,
+ force_dq ? CQUOTE_NODQ : 0);
+ if (force_dq)
+ strbuf_addch(out, '"');
strbuf_release(&sb);
return out->buf;
diff --git a/quote.h b/quote.h
index ca8ee3144a..4b72a583cf 100644
--- a/quote.h
+++ b/quote.h
@@ -56,24 +56,27 @@ char *sq_dequote(char *);
int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
/*
- * Same as above, but store the unquoted strings in an argv_array. We will
- * still modify arg in place, but unlike sq_dequote_to_argv, the argv_array
+ * Same as above, but store the unquoted strings in a strvec. We will
+ * still modify arg in place, but unlike sq_dequote_to_argv, the strvec
* will duplicate and take ownership of the strings.
*/
-struct argv_array;
-int sq_dequote_to_argv_array(char *arg, struct argv_array *);
+struct strvec;
+int sq_dequote_to_strvec(char *arg, struct strvec *);
int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
-size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
-void quote_two_c_style(struct strbuf *, const char *, const char *, int);
+
+/* Bits in the flags parameter to quote_c_style() */
+#define CQUOTE_NODQ 01
+size_t quote_c_style(const char *name, struct strbuf *, FILE *, unsigned);
+void quote_two_c_style(struct strbuf *, const char *, const char *, unsigned);
void write_name_quoted(const char *name, FILE *, int terminator);
void write_name_quoted_relative(const char *name, const char *prefix,
FILE *fp, int terminator);
/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, const char *prefix,
- struct strbuf *out);
+char *quote_path(const char *in, const char *prefix, struct strbuf *out, unsigned flags);
+#define QUOTE_PATH_QUOTE_SP 01
/* quoting as a string literal for other languages */
void perl_quote_buf(struct strbuf *sb, const char *src);
diff --git a/range-diff.c b/range-diff.c
index 40af086281..24dc435e48 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -2,7 +2,7 @@
#include "range-diff.h"
#include "string-list.h"
#include "run-command.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "hashmap.h"
#include "xdiff-interface.h"
#include "linear-assignment.h"
@@ -41,7 +41,7 @@ static size_t find_end_of_line(char *buffer, unsigned long size)
* as struct object_id (will need to be free()d).
*/
static int read_patches(const char *range, struct string_list *list,
- const struct argv_array *other_arg)
+ const struct strvec *other_arg)
{
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;
@@ -51,24 +51,24 @@ static int read_patches(const char *range, struct string_list *list,
int offset, len;
size_t size;
- argv_array_pushl(&cp.args, "log", "--no-color", "-p", "--no-merges",
- "--reverse", "--date-order", "--decorate=no",
- "--no-prefix",
- /*
- * Choose indicators that are not used anywhere
- * else in diffs, but still look reasonable
- * (e.g. will not be confusing when debugging)
- */
- "--output-indicator-new=>",
- "--output-indicator-old=<",
- "--output-indicator-context=#",
- "--no-abbrev-commit",
- "--pretty=medium",
- "--notes",
- NULL);
+ strvec_pushl(&cp.args, "log", "--no-color", "-p", "--no-merges",
+ "--reverse", "--date-order", "--decorate=no",
+ "--no-prefix",
+ /*
+ * Choose indicators that are not used anywhere
+ * else in diffs, but still look reasonable
+ * (e.g. will not be confusing when debugging)
+ */
+ "--output-indicator-new=>",
+ "--output-indicator-old=<",
+ "--output-indicator-context=#",
+ "--no-abbrev-commit",
+ "--pretty=medium",
+ "--notes",
+ NULL);
if (other_arg)
- argv_array_pushv(&cp.args, other_arg->argv);
- argv_array_push(&cp.args, range);
+ strvec_pushv(&cp.args, other_arg->v);
+ strvec_push(&cp.args, range);
cp.out = -1;
cp.no_stdin = 1;
cp.git_cmd = 1;
@@ -523,7 +523,7 @@ static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
int show_range_diff(const char *range1, const char *range2,
int creation_factor, int dual_color,
const struct diff_options *diffopt,
- const struct argv_array *other_arg)
+ const struct strvec *other_arg)
{
int res = 0;
diff --git a/range-diff.h b/range-diff.h
index e11976dc81..583ced2e8e 100644
--- a/range-diff.h
+++ b/range-diff.h
@@ -2,7 +2,7 @@
#define RANGE_DIFF_H
#include "diff.h"
-#include "argv-array.h"
+#include "strvec.h"
#define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60
@@ -14,6 +14,6 @@
int show_range_diff(const char *range1, const char *range2,
int creation_factor, int dual_color,
const struct diff_options *diffopt,
- const struct argv_array *other_arg);
+ const struct strvec *other_arg);
#endif
diff --git a/read-cache.c b/read-cache.c
index aa427c5c17..ecf6f68994 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -89,8 +89,10 @@ static struct mem_pool *find_mem_pool(struct index_state *istate)
else
pool_ptr = &istate->ce_mem_pool;
- if (!*pool_ptr)
- mem_pool_init(pool_ptr, 0);
+ if (!*pool_ptr) {
+ *pool_ptr = xmalloc(sizeof(**pool_ptr));
+ mem_pool_init(*pool_ptr, 0);
+ }
return *pool_ptr;
}
@@ -1171,20 +1173,6 @@ static int has_dir_name(struct index_state *istate,
return retval;
}
- if (istate->cache_nr > 0 &&
- ce_namelen(istate->cache[istate->cache_nr - 1]) > len) {
- /*
- * The directory prefix lines up with part of
- * a longer file or directory name, but sorts
- * after it, so this sub-directory cannot
- * collide with a file.
- *
- * last: xxx/yy-file (because '-' sorts before '/')
- * this: xxx/yy/abc
- */
- return retval;
- }
-
/*
* This is a possible collision. Fall through and
* let the regular search code handle it.
@@ -2020,11 +2008,12 @@ static unsigned long load_all_cache_entries(struct index_state *istate,
{
unsigned long consumed;
+ istate->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool));
if (istate->version == 4) {
- mem_pool_init(&istate->ce_mem_pool,
+ mem_pool_init(istate->ce_mem_pool,
estimate_cache_size_from_compressed(istate->cache_nr));
} else {
- mem_pool_init(&istate->ce_mem_pool,
+ mem_pool_init(istate->ce_mem_pool,
estimate_cache_size(mmap_size, istate->cache_nr));
}
@@ -2084,7 +2073,8 @@ static unsigned long load_cache_entries_threaded(struct index_state *istate, con
if (istate->name_hash_initialized)
BUG("the name hash isn't thread safe");
- mem_pool_init(&istate->ce_mem_pool, 0);
+ istate->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool));
+ mem_pool_init(istate->ce_mem_pool, 0);
/* ensure we have no more threads than we have blocks to process */
if (nr_threads > ieot->nr)
@@ -2111,11 +2101,12 @@ static unsigned long load_cache_entries_threaded(struct index_state *istate, con
nr = 0;
for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++)
nr += p->ieot->entries[j].nr;
+ p->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool));
if (istate->version == 4) {
- mem_pool_init(&p->ce_mem_pool,
+ mem_pool_init(p->ce_mem_pool,
estimate_cache_size_from_compressed(nr));
} else {
- mem_pool_init(&p->ce_mem_pool,
+ mem_pool_init(p->ce_mem_pool,
estimate_cache_size(mmap_size, nr));
}
@@ -2372,7 +2363,7 @@ int discard_index(struct index_state *istate)
if (istate->ce_mem_pool) {
mem_pool_discard(istate->ce_mem_pool, should_validate_cache_entries());
- istate->ce_mem_pool = NULL;
+ FREE_AND_NULL(istate->ce_mem_pool);
}
return 0;
diff --git a/ref-filter.c b/ref-filter.c
index 8447cb09be..5550a0d34c 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -22,7 +22,7 @@
#include "commit-reach.h"
#include "worktree.h"
#include "hashmap.h"
-#include "argv-array.h"
+#include "strvec.h"
static struct ref_msg {
const char *gone;
@@ -127,7 +127,8 @@ static struct used_atom {
unsigned int nobracket : 1, push : 1, push_remote : 1;
} remote_ref;
struct {
- enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
+ enum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH, C_LINES,
+ C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;
struct process_trailer_options trailer_opts;
unsigned int nlines;
} contents;
@@ -138,7 +139,10 @@ static struct used_atom {
struct {
enum { O_FULL, O_LENGTH, O_SHORT } option;
unsigned int length;
- } objectname;
+ } oid;
+ struct email_option {
+ enum { EO_RAW, EO_TRIM, EO_LOCALPART } option;
+ } email_option;
struct refname_atom refname;
char *head;
} u;
@@ -297,9 +301,12 @@ static int body_atom_parser(const struct ref_format *format, struct used_atom *a
static int subject_atom_parser(const struct ref_format *format, struct used_atom *atom,
const char *arg, struct strbuf *err)
{
- if (arg)
- return strbuf_addf_ret(err, -1, _("%%(subject) does not take arguments"));
- atom->u.contents.option = C_SUB;
+ if (!arg)
+ atom->u.contents.option = C_SUB;
+ else if (!strcmp(arg, "sanitize"))
+ atom->u.contents.option = C_SUB_SANITIZE;
+ else
+ return strbuf_addf_ret(err, -1, _("unrecognized %%(subject) argument: %s"), arg);
return 0;
}
@@ -338,13 +345,17 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato
atom->u.contents.option = C_BARE;
else if (!strcmp(arg, "body"))
atom->u.contents.option = C_BODY;
+ else if (!strcmp(arg, "size"))
+ atom->u.contents.option = C_LENGTH;
else if (!strcmp(arg, "signature"))
atom->u.contents.option = C_SIG;
else if (!strcmp(arg, "subject"))
atom->u.contents.option = C_SUB;
- else if (skip_prefix(arg, "trailers", &arg)) {
- skip_prefix(arg, ":", &arg);
- if (trailers_atom_parser(format, atom, *arg ? arg : NULL, err))
+ else if (!strcmp(arg, "trailers")) {
+ if (trailers_atom_parser(format, atom, NULL, err))
+ return -1;
+ } else if (skip_prefix(arg, "trailers:", &arg)) {
+ if (trailers_atom_parser(format, atom, arg, err))
return -1;
} else if (skip_prefix(arg, "lines=", &arg)) {
atom->u.contents.option = C_LINES;
@@ -355,22 +366,36 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato
return 0;
}
-static int objectname_atom_parser(const struct ref_format *format, struct used_atom *atom,
- const char *arg, struct strbuf *err)
+static int oid_atom_parser(const struct ref_format *format, struct used_atom *atom,
+ const char *arg, struct strbuf *err)
{
if (!arg)
- atom->u.objectname.option = O_FULL;
+ atom->u.oid.option = O_FULL;
else if (!strcmp(arg, "short"))
- atom->u.objectname.option = O_SHORT;
+ atom->u.oid.option = O_SHORT;
else if (skip_prefix(arg, "short=", &arg)) {
- atom->u.objectname.option = O_LENGTH;
- if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||
- atom->u.objectname.length == 0)
- return strbuf_addf_ret(err, -1, _("positive value expected objectname:short=%s"), arg);
- if (atom->u.objectname.length < MINIMUM_ABBREV)
- atom->u.objectname.length = MINIMUM_ABBREV;
+ atom->u.oid.option = O_LENGTH;
+ if (strtoul_ui(arg, 10, &atom->u.oid.length) ||
+ atom->u.oid.length == 0)
+ return strbuf_addf_ret(err, -1, _("positive value expected '%s' in %%(%s)"), arg, atom->name);
+ if (atom->u.oid.length < MINIMUM_ABBREV)
+ atom->u.oid.length = MINIMUM_ABBREV;
} else
- return strbuf_addf_ret(err, -1, _("unrecognized %%(objectname) argument: %s"), arg);
+ return strbuf_addf_ret(err, -1, _("unrecognized argument '%s' in %%(%s)"), arg, atom->name);
+ return 0;
+}
+
+static int person_email_atom_parser(const struct ref_format *format, struct used_atom *atom,
+ const char *arg, struct strbuf *err)
+{
+ if (!arg)
+ atom->u.email_option.option = EO_RAW;
+ else if (!strcmp(arg, "trim"))
+ atom->u.email_option.option = EO_TRIM;
+ else if (!strcmp(arg, "localpart"))
+ atom->u.email_option.option = EO_LOCALPART;
+ else
+ return strbuf_addf_ret(err, -1, _("unrecognized email option: %s"), arg);
return 0;
}
@@ -475,25 +500,25 @@ static struct {
{ "refname", SOURCE_NONE, FIELD_STR, refname_atom_parser },
{ "objecttype", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser },
{ "objectsize", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },
- { "objectname", SOURCE_OTHER, FIELD_STR, objectname_atom_parser },
+ { "objectname", SOURCE_OTHER, FIELD_STR, oid_atom_parser },
{ "deltabase", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser },
- { "tree", SOURCE_OBJ },
- { "parent", SOURCE_OBJ },
+ { "tree", SOURCE_OBJ, FIELD_STR, oid_atom_parser },
+ { "parent", SOURCE_OBJ, FIELD_STR, oid_atom_parser },
{ "numparent", SOURCE_OBJ, FIELD_ULONG },
{ "object", SOURCE_OBJ },
{ "type", SOURCE_OBJ },
{ "tag", SOURCE_OBJ },
{ "author", SOURCE_OBJ },
{ "authorname", SOURCE_OBJ },
- { "authoremail", SOURCE_OBJ },
+ { "authoremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
{ "authordate", SOURCE_OBJ, FIELD_TIME },
{ "committer", SOURCE_OBJ },
{ "committername", SOURCE_OBJ },
- { "committeremail", SOURCE_OBJ },
+ { "committeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
{ "committerdate", SOURCE_OBJ, FIELD_TIME },
{ "tagger", SOURCE_OBJ },
{ "taggername", SOURCE_OBJ },
- { "taggeremail", SOURCE_OBJ },
+ { "taggeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser },
{ "taggerdate", SOURCE_OBJ, FIELD_TIME },
{ "creator", SOURCE_OBJ },
{ "creatordate", SOURCE_OBJ, FIELD_TIME },
@@ -898,21 +923,27 @@ int verify_ref_format(struct ref_format *format)
return 0;
}
-static int grab_objectname(const char *name, const struct object_id *oid,
- struct atom_value *v, struct used_atom *atom)
+static const char *do_grab_oid(const char *field, const struct object_id *oid,
+ struct used_atom *atom)
{
- if (starts_with(name, "objectname")) {
- if (atom->u.objectname.option == O_SHORT) {
- v->s = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV));
- return 1;
- } else if (atom->u.objectname.option == O_FULL) {
- v->s = xstrdup(oid_to_hex(oid));
- return 1;
- } else if (atom->u.objectname.option == O_LENGTH) {
- v->s = xstrdup(find_unique_abbrev(oid, atom->u.objectname.length));
- return 1;
- } else
- BUG("unknown %%(objectname) option");
+ switch (atom->u.oid.option) {
+ case O_FULL:
+ return oid_to_hex(oid);
+ case O_LENGTH:
+ return find_unique_abbrev(oid, atom->u.oid.length);
+ case O_SHORT:
+ return find_unique_abbrev(oid, DEFAULT_ABBREV);
+ default:
+ BUG("unknown %%(%s) option", field);
+ }
+}
+
+static int grab_oid(const char *name, const char *field, const struct object_id *oid,
+ struct atom_value *v, struct used_atom *atom)
+{
+ if (starts_with(name, field)) {
+ v->s = xstrdup(do_grab_oid(field, oid, atom));
+ return 1;
}
return 0;
}
@@ -940,7 +971,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
} else if (!strcmp(name, "deltabase"))
v->s = xstrdup(oid_to_hex(&oi->delta_base_oid));
else if (deref)
- grab_objectname(name, &oi->oid, v, &used_atom[i]);
+ grab_oid(name, "objectname", &oi->oid, v, &used_atom[i]);
}
}
@@ -979,21 +1010,20 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
continue;
if (deref)
name++;
- if (!strcmp(name, "tree")) {
- v->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));
- }
- else if (!strcmp(name, "numparent")) {
+ if (grab_oid(name, "tree", get_commit_tree_oid(commit), v, &used_atom[i]))
+ continue;
+ if (!strcmp(name, "numparent")) {
v->value = commit_list_count(commit->parents);
v->s = xstrfmt("%lu", (unsigned long)v->value);
}
- else if (!strcmp(name, "parent")) {
+ else if (starts_with(name, "parent")) {
struct commit_list *parents;
struct strbuf s = STRBUF_INIT;
for (parents = commit->parents; parents; parents = parents->next) {
- struct commit *parent = parents->item;
+ struct object_id *oid = &parents->item->object.oid;
if (parents != commit->parents)
strbuf_addch(&s, ' ');
- strbuf_addstr(&s, oid_to_hex(&parent->object.oid));
+ strbuf_addstr(&s, do_grab_oid("parent", oid, &used_atom[i]));
}
v->s = strbuf_detach(&s, NULL);
}
@@ -1034,16 +1064,35 @@ static const char *copy_name(const char *buf)
return xstrdup("");
}
-static const char *copy_email(const char *buf)
+static const char *copy_email(const char *buf, struct used_atom *atom)
{
const char *email = strchr(buf, '<');
const char *eoemail;
if (!email)
return xstrdup("");
- eoemail = strchr(email, '>');
+ switch (atom->u.email_option.option) {
+ case EO_RAW:
+ eoemail = strchr(email, '>');
+ if (eoemail)
+ eoemail++;
+ break;
+ case EO_TRIM:
+ email++;
+ eoemail = strchr(email, '>');
+ break;
+ case EO_LOCALPART:
+ email++;
+ eoemail = strchr(email, '@');
+ if (!eoemail)
+ eoemail = strchr(email, '>');
+ break;
+ default:
+ BUG("unknown email option");
+ }
+
if (!eoemail)
return xstrdup("");
- return xmemdupz(email, eoemail + 1 - email);
+ return xmemdupz(email, eoemail - email);
}
static char *copy_subject(const char *buf, unsigned long len)
@@ -1113,7 +1162,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
continue;
if (name[wholen] != 0 &&
strcmp(name + wholen, "name") &&
- strcmp(name + wholen, "email") &&
+ !starts_with(name + wholen, "email") &&
!starts_with(name + wholen, "date"))
continue;
if (!wholine)
@@ -1124,8 +1173,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
v->s = copy_line(wholine);
else if (!strcmp(name + wholen, "name"))
v->s = copy_name(wholine);
- else if (!strcmp(name + wholen, "email"))
- v->s = copy_email(wholine);
+ else if (starts_with(name + wholen, "email"))
+ v->s = copy_email(wholine, &used_atom[i]);
else if (starts_with(name + wholen, "date"))
grab_date(wholine, v, name);
}
@@ -1238,8 +1287,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
continue;
if (deref)
name++;
- if (strcmp(name, "subject") &&
- strcmp(name, "body") &&
+ if (strcmp(name, "body") &&
+ !starts_with(name, "subject") &&
!starts_with(name, "trailers") &&
!starts_with(name, "contents"))
continue;
@@ -1251,8 +1300,14 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
if (atom->u.contents.option == C_SUB)
v->s = copy_subject(subpos, sublen);
- else if (atom->u.contents.option == C_BODY_DEP)
+ else if (atom->u.contents.option == C_SUB_SANITIZE) {
+ struct strbuf sb = STRBUF_INIT;
+ format_sanitized_subject(&sb, subpos, sublen);
+ v->s = strbuf_detach(&sb, NULL);
+ } else if (atom->u.contents.option == C_BODY_DEP)
v->s = xmemdupz(bodypos, bodylen);
+ else if (atom->u.contents.option == C_LENGTH)
+ v->s = xstrfmt("%"PRIuMAX, (uintmax_t)strlen(subpos));
else if (atom->u.contents.option == C_BODY)
v->s = xmemdupz(bodypos, nonsiglen);
else if (atom->u.contents.option == C_SIG)
@@ -1699,7 +1754,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
v->s = xstrdup(buf + 1);
}
continue;
- } else if (!deref && grab_objectname(name, &ref->objectname, v, atom)) {
+ } else if (!deref && grab_oid(name, "objectname", &ref->objectname, v, atom)) {
continue;
} else if (!strcmp(name, "HEAD")) {
if (atom->u.head && !strcmp(ref->refname, atom->u.head))
@@ -1914,15 +1969,15 @@ static void find_longest_prefixes_1(struct string_list *out,
static void find_longest_prefixes(struct string_list *out,
const char **patterns)
{
- struct argv_array sorted = ARGV_ARRAY_INIT;
+ struct strvec sorted = STRVEC_INIT;
struct strbuf prefix = STRBUF_INIT;
- argv_array_pushv(&sorted, patterns);
- QSORT(sorted.argv, sorted.argc, qsort_strcmp);
+ strvec_pushv(&sorted, patterns);
+ QSORT(sorted.v, sorted.nr, qsort_strcmp);
- find_longest_prefixes_1(out, &prefix, sorted.argv, sorted.argc);
+ find_longest_prefixes_1(out, &prefix, sorted.v, sorted.nr);
- argv_array_clear(&sorted);
+ strvec_clear(&sorted);
strbuf_release(&prefix);
}
@@ -1980,7 +2035,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
* of oids. If the given ref is a tag, check if the given tag points
* at one of the oids in the given oid array.
* NEEDSWORK:
- * 1. Only a single level of inderection is obtained, we might want to
+ * 1. Only a single level of indirection is obtained, we might want to
* change this to account for multiple levels (e.g. annotated tags
* pointing to annotated tags pointing to a commit.)
* 2. As the refs are cached we might know what refname peels to without
@@ -2112,9 +2167,9 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
* obtain the commit using the 'oid' available and discard all
* non-commits early. The actual filtering is done later.
*/
- if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) {
- commit = lookup_commit_reference_gently(the_repository, oid,
- 1);
+ if (filter->reachable_from || filter->unreachable_from ||
+ filter->with_commit || filter->no_commit || filter->verbose) {
+ commit = lookup_commit_reference_gently(the_repository, oid, 1);
if (!commit)
return 0;
/* We perform the filtering for the '--contains' option... */
@@ -2176,13 +2231,19 @@ void ref_array_clear(struct ref_array *array)
}
}
-static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
+#define EXCLUDE_REACHED 0
+#define INCLUDE_REACHED 1
+static void reach_filter(struct ref_array *array,
+ struct commit_list *check_reachable,
+ int include_reached)
{
struct rev_info revs;
int i, old_nr;
- struct ref_filter *filter = ref_cbdata->filter;
- struct ref_array *array = ref_cbdata->array;
struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);
+ struct commit_list *cr;
+
+ if (!check_reachable)
+ return;
repo_init_revisions(the_repository, &revs, NULL);
@@ -2192,8 +2253,11 @@ static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
to_clear[i] = item->commit;
}
- filter->merge_commit->object.flags |= UNINTERESTING;
- add_pending_object(&revs, &filter->merge_commit->object, "");
+ for (cr = check_reachable; cr; cr = cr->next) {
+ struct commit *merge_commit = cr->item;
+ merge_commit->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &merge_commit->object, "");
+ }
revs.limited = 1;
if (prepare_revision_walk(&revs))
@@ -2208,14 +2272,19 @@ static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
int is_merged = !!(commit->object.flags & UNINTERESTING);
- if (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE))
+ if (is_merged == include_reached)
array->items[array->nr++] = array->items[i];
else
free_array_item(item);
}
clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
- clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
+
+ while (check_reachable) {
+ struct commit *merge_commit = pop_commit(&check_reachable);
+ clear_commit_marks(merge_commit, ALL_REV_FLAGS);
+ }
+
free(to_clear);
}
@@ -2267,8 +2336,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
clear_contains_cache(&ref_cbdata.no_contains_cache);
/* Filters that need revision walking */
- if (filter->merge_commit)
- do_merge_filter(&ref_cbdata);
+ reach_filter(array, filter->reachable_from, INCLUDE_REACHED);
+ reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED);
return ret;
}
@@ -2486,31 +2555,22 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
{
struct ref_filter *rf = opt->value;
struct object_id oid;
- int no_merged = starts_with(opt->long_name, "no");
+ struct commit *merge_commit;
BUG_ON_OPT_NEG(unset);
- if (rf->merge) {
- if (no_merged) {
- return error(_("option `%s' is incompatible with --merged"),
- opt->long_name);
- } else {
- return error(_("option `%s' is incompatible with --no-merged"),
- opt->long_name);
- }
- }
-
- rf->merge = no_merged
- ? REF_FILTER_MERGED_OMIT
- : REF_FILTER_MERGED_INCLUDE;
-
if (get_oid(arg, &oid))
die(_("malformed object name %s"), arg);
- rf->merge_commit = lookup_commit_reference_gently(the_repository,
- &oid, 0);
- if (!rf->merge_commit)
+ merge_commit = lookup_commit_reference_gently(the_repository, &oid, 0);
+
+ if (!merge_commit)
return error(_("option `%s' must point to a commit"), opt->long_name);
+ if (starts_with(opt->long_name, "no"))
+ commit_list_insert(merge_commit, &rf->unreachable_from);
+ else
+ commit_list_insert(merge_commit, &rf->reachable_from);
+
return 0;
}
diff --git a/ref-filter.h b/ref-filter.h
index 8ecc33cdfa..feaef4a8fd 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -54,13 +54,8 @@ struct ref_filter {
struct oid_array points_at;
struct commit_list *with_commit;
struct commit_list *no_commit;
-
- enum {
- REF_FILTER_MERGED_NONE = 0,
- REF_FILTER_MERGED_INCLUDE,
- REF_FILTER_MERGED_OMIT
- } merge;
- struct commit *merge_commit;
+ struct commit_list *reachable_from;
+ struct commit_list *unreachable_from;
unsigned int with_commit_tag_algo : 1,
match_as_path : 1,
diff --git a/refs.c b/refs.c
index 639cba93b4..fa01153151 100644
--- a/refs.c
+++ b/refs.c
@@ -15,7 +15,7 @@
#include "tag.h"
#include "submodule.h"
#include "worktree.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "repository.h"
#include "sigchain.h"
@@ -313,7 +313,7 @@ int read_ref(const char *refname, struct object_id *oid)
return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
}
-static int refs_ref_exists(struct ref_store *refs, const char *refname)
+int refs_ref_exists(struct ref_store *refs, const char *refname)
{
return !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING, NULL, NULL);
}
@@ -553,13 +553,13 @@ int refname_match(const char *abbrev_name, const char *full_name)
* Given a 'prefix' expand it by the rules in 'ref_rev_parse_rules' and add
* the results to 'prefixes'
*/
-void expand_ref_prefix(struct argv_array *prefixes, const char *prefix)
+void expand_ref_prefix(struct strvec *prefixes, const char *prefix)
{
const char **p;
int len = strlen(prefix);
for (p = ref_rev_parse_rules; *p; p++)
- argv_array_pushf(prefixes, *p, len, prefix);
+ strvec_pushf(prefixes, *p, len, prefix);
}
char *repo_default_branch_name(struct repository *r)
@@ -598,10 +598,14 @@ const char *git_default_branch_name(void)
* to name a branch.
*/
static char *substitute_branch_name(struct repository *r,
- const char **string, int *len)
+ const char **string, int *len,
+ int nonfatal_dangling_mark)
{
struct strbuf buf = STRBUF_INIT;
- int ret = repo_interpret_branch_name(r, *string, *len, &buf, 0);
+ struct interpret_branch_name_options options = {
+ .nonfatal_dangling_mark = nonfatal_dangling_mark
+ };
+ int ret = repo_interpret_branch_name(r, *string, *len, &buf, &options);
if (ret == *len) {
size_t size;
@@ -614,19 +618,15 @@ static char *substitute_branch_name(struct repository *r,
}
int repo_dwim_ref(struct repository *r, const char *str, int len,
- struct object_id *oid, char **ref)
+ struct object_id *oid, char **ref, int nonfatal_dangling_mark)
{
- char *last_branch = substitute_branch_name(r, &str, &len);
+ char *last_branch = substitute_branch_name(r, &str, &len,
+ nonfatal_dangling_mark);
int refs_found = expand_ref(r, str, len, oid, ref);
free(last_branch);
return refs_found;
}
-int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
-{
- return repo_dwim_ref(the_repository, str, len, oid, ref);
-}
-
int expand_ref(struct repository *repo, const char *str, int len,
struct object_id *oid, char **ref)
{
@@ -665,7 +665,7 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
struct object_id *oid, char **log)
{
struct ref_store *refs = get_main_ref_store(r);
- char *last_branch = substitute_branch_name(r, &str, &len);
+ char *last_branch = substitute_branch_name(r, &str, &len, 0);
const char **p;
int logs_found = 0;
struct strbuf path = STRBUF_INIT;
@@ -708,10 +708,9 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
static int is_per_worktree_ref(const char *refname)
{
- return !strcmp(refname, "HEAD") ||
- starts_with(refname, "refs/worktree/") ||
- starts_with(refname, "refs/bisect/") ||
- starts_with(refname, "refs/rewritten/");
+ return starts_with(refname, "refs/worktree/") ||
+ starts_with(refname, "refs/bisect/") ||
+ starts_with(refname, "refs/rewritten/");
}
static int is_pseudoref_syntax(const char *refname)
@@ -771,102 +770,6 @@ long get_files_ref_lock_timeout_ms(void)
return timeout_ms;
}
-static int write_pseudoref(const char *pseudoref, const struct object_id *oid,
- const struct object_id *old_oid, struct strbuf *err)
-{
- const char *filename;
- int fd;
- struct lock_file lock = LOCK_INIT;
- struct strbuf buf = STRBUF_INIT;
- int ret = -1;
-
- if (!oid)
- return 0;
-
- strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
-
- filename = git_path("%s", pseudoref);
- fd = hold_lock_file_for_update_timeout(&lock, filename, 0,
- get_files_ref_lock_timeout_ms());
- if (fd < 0) {
- strbuf_addf(err, _("could not open '%s' for writing: %s"),
- filename, strerror(errno));
- goto done;
- }
-
- if (old_oid) {
- struct object_id actual_old_oid;
-
- if (read_ref(pseudoref, &actual_old_oid)) {
- if (!is_null_oid(old_oid)) {
- strbuf_addf(err, _("could not read ref '%s'"),
- pseudoref);
- rollback_lock_file(&lock);
- goto done;
- }
- } else if (is_null_oid(old_oid)) {
- strbuf_addf(err, _("ref '%s' already exists"),
- pseudoref);
- rollback_lock_file(&lock);
- goto done;
- } else if (!oideq(&actual_old_oid, old_oid)) {
- strbuf_addf(err, _("unexpected object ID when writing '%s'"),
- pseudoref);
- rollback_lock_file(&lock);
- goto done;
- }
- }
-
- if (write_in_full(fd, buf.buf, buf.len) < 0) {
- strbuf_addf(err, _("could not write to '%s'"), filename);
- rollback_lock_file(&lock);
- goto done;
- }
-
- commit_lock_file(&lock);
- ret = 0;
-done:
- strbuf_release(&buf);
- return ret;
-}
-
-static int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid)
-{
- const char *filename;
-
- filename = git_path("%s", pseudoref);
-
- if (old_oid && !is_null_oid(old_oid)) {
- struct lock_file lock = LOCK_INIT;
- int fd;
- struct object_id actual_old_oid;
-
- fd = hold_lock_file_for_update_timeout(
- &lock, filename, 0,
- get_files_ref_lock_timeout_ms());
- if (fd < 0) {
- error_errno(_("could not open '%s' for writing"),
- filename);
- return -1;
- }
- if (read_ref(pseudoref, &actual_old_oid))
- die(_("could not read ref '%s'"), pseudoref);
- if (!oideq(&actual_old_oid, old_oid)) {
- error(_("unexpected object ID when deleting '%s'"),
- pseudoref);
- rollback_lock_file(&lock);
- return -1;
- }
-
- unlink(filename);
- rollback_lock_file(&lock);
- } else {
- unlink(filename);
- }
-
- return 0;
-}
-
int refs_delete_ref(struct ref_store *refs, const char *msg,
const char *refname,
const struct object_id *old_oid,
@@ -875,11 +778,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
- assert(refs == get_main_ref_store(the_repository));
- return delete_pseudoref(refname, old_oid);
- }
-
transaction = ref_store_transaction_begin(refs, &err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_oid,
@@ -902,12 +800,11 @@ int delete_ref(const char *msg, const char *refname,
old_oid, flags);
}
-void copy_reflog_msg(struct strbuf *sb, const char *msg)
+static void copy_reflog_msg(struct strbuf *sb, const char *msg)
{
char c;
int wasspace = 1;
- strbuf_addch(sb, '\t');
while ((c = *msg++)) {
if (wasspace && isspace(c))
continue;
@@ -919,6 +816,15 @@ void copy_reflog_msg(struct strbuf *sb, const char *msg)
strbuf_rtrim(sb);
}
+static char *normalize_reflog_message(const char *msg)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ if (msg && *msg)
+ copy_reflog_msg(&sb, msg);
+ return strbuf_detach(&sb, NULL);
+}
+
int should_autocreate_reflog(const char *refname)
{
switch (log_all_ref_updates) {
@@ -1124,7 +1030,7 @@ struct ref_update *ref_transaction_add_update(
oidcpy(&update->new_oid, new_oid);
if (flags & REF_HAVE_OLD)
oidcpy(&update->old_oid, old_oid);
- update->msg = xstrdup_or_null(msg);
+ update->msg = normalize_reflog_message(msg);
return update;
}
@@ -1202,18 +1108,13 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
struct strbuf err = STRBUF_INIT;
int ret = 0;
- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
- assert(refs == get_main_ref_store(the_repository));
- ret = write_pseudoref(refname, new_oid, old_oid, &err);
- } else {
- t = ref_store_transaction_begin(refs, &err);
- if (!t ||
- ref_transaction_update(t, refname, new_oid, old_oid,
- flags, msg, &err) ||
- ref_transaction_commit(t, &err)) {
- ret = 1;
- ref_transaction_free(t);
- }
+ t = ref_store_transaction_begin(refs, &err);
+ if (!t ||
+ ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
+ &err) ||
+ ref_transaction_commit(t, &err)) {
+ ret = 1;
+ ref_transaction_free(t);
}
if (ret) {
const char *str = _("update_ref failed for ref '%s': %s");
@@ -1626,11 +1527,37 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
}
+static int refs_read_special_head(struct ref_store *ref_store,
+ const char *refname, struct object_id *oid,
+ struct strbuf *referent, unsigned int *type)
+{
+ struct strbuf full_path = STRBUF_INIT;
+ struct strbuf content = STRBUF_INIT;
+ int result = -1;
+ strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
+
+ if (strbuf_read_file(&content, full_path.buf, 0) < 0)
+ goto done;
+
+ result = parse_loose_ref_contents(content.buf, oid, referent, type);
+
+done:
+ strbuf_release(&full_path);
+ strbuf_release(&content);
+ return result;
+}
+
int refs_read_raw_ref(struct ref_store *ref_store,
const char *refname, struct object_id *oid,
struct strbuf *referent, unsigned int *type)
{
- return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, type);
+ if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
+ return refs_read_special_head(ref_store, refname, oid, referent,
+ type);
+ }
+
+ return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
+ type);
}
/* This function needs to return a meaningful errno on failure */
@@ -1847,6 +1774,7 @@ struct ref_store *get_main_ref_store(struct repository *r)
BUG("attempting to get main_ref_store outside of repository");
r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
return r->refs_private;
}
@@ -1983,9 +1911,14 @@ int refs_create_symref(struct ref_store *refs,
const char *refs_heads_master,
const char *logmsg)
{
- return refs->be->create_symref(refs, ref_target,
- refs_heads_master,
- logmsg);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->create_symref(refs, ref_target, refs_heads_master,
+ msg);
+ free(msg);
+ return retval;
}
int create_symref(const char *ref_target, const char *refs_heads_master,
@@ -2018,26 +1951,19 @@ int ref_update_reject_duplicates(struct string_list *refnames,
return 0;
}
-static const char hook_not_found;
-static const char *hook;
-
static int run_transaction_hook(struct ref_transaction *transaction,
const char *state)
{
struct child_process proc = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
+ const char *hook;
int ret = 0, i;
- if (hook == &hook_not_found)
- return ret;
+ hook = find_hook("reference-transaction");
if (!hook)
- hook = find_hook("reference-transaction");
- if (!hook) {
- hook = &hook_not_found;
return ret;
- }
- argv_array_pushl(&proc.args, hook, state, NULL);
+ strvec_pushl(&proc.args, hook, state, NULL);
proc.in = -1;
proc.stdout_to_stderr = 1;
proc.trace2_hook_name = "reference-transaction";
@@ -2370,10 +2296,16 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
return refs->be->initial_transaction_commit(refs, transaction, err);
}
-int refs_delete_refs(struct ref_store *refs, const char *msg,
+int refs_delete_refs(struct ref_store *refs, const char *logmsg,
struct string_list *refnames, unsigned int flags)
{
- return refs->be->delete_refs(refs, msg, refnames, flags);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->delete_refs(refs, msg, refnames, flags);
+ free(msg);
+ return retval;
}
int delete_refs(const char *msg, struct string_list *refnames,
@@ -2385,7 +2317,13 @@ int delete_refs(const char *msg, struct string_list *refnames,
int refs_rename_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
- return refs->be->rename_ref(refs, oldref, newref, logmsg);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->rename_ref(refs, oldref, newref, msg);
+ free(msg);
+ return retval;
}
int rename_ref(const char *oldref, const char *newref, const char *logmsg)
@@ -2396,7 +2334,13 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
const char *newref, const char *logmsg)
{
- return refs->be->copy_ref(refs, oldref, newref, logmsg);
+ char *msg;
+ int retval;
+
+ msg = normalize_reflog_message(logmsg);
+ retval = refs->be->copy_ref(refs, oldref, newref, msg);
+ free(msg);
+ return retval;
}
int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
diff --git a/refs.h b/refs.h
index f212f8945e..6695518156 100644
--- a/refs.h
+++ b/refs.h
@@ -1,6 +1,8 @@
#ifndef REFS_H
#define REFS_H
+#include "cache.h"
+
struct object_id;
struct ref_store;
struct repository;
@@ -105,6 +107,8 @@ int refs_verify_refname_available(struct ref_store *refs,
const struct string_list *skip,
struct strbuf *err);
+int refs_ref_exists(struct ref_store *refs, const char *refname);
+
int ref_exists(const char *refname);
int should_autocreate_reflog(const char *refname);
@@ -145,13 +149,19 @@ int refname_match(const char *abbrev_name, const char *full_name);
* Given a 'prefix' expand it by the rules in 'ref_rev_parse_rules' and add
* the results to 'prefixes'
*/
-struct argv_array;
-void expand_ref_prefix(struct argv_array *prefixes, const char *prefix);
+struct strvec;
+void expand_ref_prefix(struct strvec *prefixes, const char *prefix);
int expand_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
-int repo_dwim_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
+int repo_dwim_ref(struct repository *r, const char *str, int len,
+ struct object_id *oid, char **ref, int nonfatal_dangling_mark);
int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
-int dwim_ref(const char *str, int len, struct object_id *oid, char **ref);
+static inline int dwim_ref(const char *str, int len, struct object_id *oid,
+ char **ref, int nonfatal_dangling_mark)
+{
+ return repo_dwim_ref(the_repository, str, len, oid, ref,
+ nonfatal_dangling_mark);
+}
int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
/*
diff --git a/refs/debug.c b/refs/debug.c
new file mode 100644
index 0000000000..922e64fa6a
--- /dev/null
+++ b/refs/debug.c
@@ -0,0 +1,398 @@
+
+#include "refs-internal.h"
+#include "trace.h"
+
+static struct trace_key trace_refs = TRACE_KEY_INIT(REFS);
+
+struct debug_ref_store {
+ struct ref_store base;
+ struct ref_store *refs;
+};
+
+extern struct ref_storage_be refs_be_debug;
+
+struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_store *store)
+{
+ struct debug_ref_store *res;
+ struct ref_storage_be *be_copy;
+
+ if (!trace_want(&trace_refs)) {
+ return store;
+ }
+ res = xmalloc(sizeof(struct debug_ref_store));
+ be_copy = xmalloc(sizeof(*be_copy));
+ *be_copy = refs_be_debug;
+ /* we never deallocate backends, so safe to copy the pointer. */
+ be_copy->name = store->be->name;
+ trace_printf_key(&trace_refs, "ref_store for %s\n", gitdir);
+ res->refs = store;
+ base_ref_store_init((struct ref_store *)res, be_copy);
+ return (struct ref_store *)res;
+}
+
+static int debug_init_db(struct ref_store *refs, struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res = drefs->refs->be->init_db(drefs->refs, err);
+ trace_printf_key(&trace_refs, "init_db: %d\n", res);
+ return res;
+}
+
+static int debug_transaction_prepare(struct ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res;
+ transaction->ref_store = drefs->refs;
+ res = drefs->refs->be->transaction_prepare(drefs->refs, transaction,
+ err);
+ trace_printf_key(&trace_refs, "transaction_prepare: %d\n", res);
+ return res;
+}
+
+static void print_update(int i, const char *refname,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid, unsigned int flags,
+ unsigned int type, const char *msg)
+{
+ char o[GIT_MAX_HEXSZ + 1] = "null";
+ char n[GIT_MAX_HEXSZ + 1] = "null";
+ if (old_oid)
+ oid_to_hex_r(o, old_oid);
+ if (new_oid)
+ oid_to_hex_r(n, new_oid);
+
+ type &= 0xf; /* see refs.h REF_* */
+ flags &= REF_HAVE_NEW | REF_HAVE_OLD | REF_NO_DEREF |
+ REF_FORCE_CREATE_REFLOG;
+ trace_printf_key(&trace_refs, "%d: %s %s -> %s (F=0x%x, T=0x%x) \"%s\"\n", i, refname,
+ o, n, flags, type, msg);
+}
+
+static void print_transaction(struct ref_transaction *transaction)
+{
+ int i;
+ trace_printf_key(&trace_refs, "transaction {\n");
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *u = transaction->updates[i];
+ print_update(i, u->refname, &u->old_oid, &u->new_oid, u->flags,
+ u->type, u->msg);
+ }
+ trace_printf_key(&trace_refs, "}\n");
+}
+
+static int debug_transaction_finish(struct ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res;
+ transaction->ref_store = drefs->refs;
+ res = drefs->refs->be->transaction_finish(drefs->refs, transaction,
+ err);
+ print_transaction(transaction);
+ trace_printf_key(&trace_refs, "finish: %d\n", res);
+ return res;
+}
+
+static int debug_transaction_abort(struct ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res;
+ transaction->ref_store = drefs->refs;
+ res = drefs->refs->be->transaction_abort(drefs->refs, transaction, err);
+ return res;
+}
+
+static int debug_initial_transaction_commit(struct ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res;
+ transaction->ref_store = drefs->refs;
+ res = drefs->refs->be->initial_transaction_commit(drefs->refs,
+ transaction, err);
+ return res;
+}
+
+static int debug_pack_refs(struct ref_store *ref_store, unsigned int flags)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->pack_refs(drefs->refs, flags);
+ trace_printf_key(&trace_refs, "pack_refs: %d\n", res);
+ return res;
+}
+
+static int debug_create_symref(struct ref_store *ref_store,
+ const char *ref_name, const char *target,
+ const char *logmsg)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->create_symref(drefs->refs, ref_name, target,
+ logmsg);
+ trace_printf_key(&trace_refs, "create_symref: %s -> %s \"%s\": %d\n", ref_name,
+ target, logmsg, res);
+ return res;
+}
+
+static int debug_delete_refs(struct ref_store *ref_store, const char *msg,
+ struct string_list *refnames, unsigned int flags)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res =
+ drefs->refs->be->delete_refs(drefs->refs, msg, refnames, flags);
+ int i;
+ trace_printf_key(&trace_refs, "delete_refs {\n");
+ for (i = 0; i < refnames->nr; i++)
+ trace_printf_key(&trace_refs, "%s\n", refnames->items[i].string);
+ trace_printf_key(&trace_refs, "}: %d\n", res);
+ return res;
+}
+
+static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
+ const char *newref, const char *logmsg)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->rename_ref(drefs->refs, oldref, newref,
+ logmsg);
+ trace_printf_key(&trace_refs, "rename_ref: %s -> %s \"%s\": %d\n", oldref, newref,
+ logmsg, res);
+ return res;
+}
+
+static int debug_copy_ref(struct ref_store *ref_store, const char *oldref,
+ const char *newref, const char *logmsg)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res =
+ drefs->refs->be->copy_ref(drefs->refs, oldref, newref, logmsg);
+ trace_printf_key(&trace_refs, "copy_ref: %s -> %s \"%s\": %d\n", oldref, newref,
+ logmsg, res);
+ return res;
+}
+
+struct debug_ref_iterator {
+ struct ref_iterator base;
+ struct ref_iterator *iter;
+};
+
+static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
+{
+ struct debug_ref_iterator *diter =
+ (struct debug_ref_iterator *)ref_iterator;
+ int res = diter->iter->vtable->advance(diter->iter);
+ if (res)
+ trace_printf_key(&trace_refs, "iterator_advance: (%d)\n", res);
+ else
+ trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
+ diter->iter->refname);
+
+ diter->base.ordered = diter->iter->ordered;
+ diter->base.refname = diter->iter->refname;
+ diter->base.oid = diter->iter->oid;
+ diter->base.flags = diter->iter->flags;
+ return res;
+}
+
+static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
+ struct object_id *peeled)
+{
+ struct debug_ref_iterator *diter =
+ (struct debug_ref_iterator *)ref_iterator;
+ int res = diter->iter->vtable->peel(diter->iter, peeled);
+ trace_printf_key(&trace_refs, "iterator_peel: %s: %d\n", diter->iter->refname, res);
+ return res;
+}
+
+static int debug_ref_iterator_abort(struct ref_iterator *ref_iterator)
+{
+ struct debug_ref_iterator *diter =
+ (struct debug_ref_iterator *)ref_iterator;
+ int res = diter->iter->vtable->abort(diter->iter);
+ trace_printf_key(&trace_refs, "iterator_abort: %d\n", res);
+ return res;
+}
+
+static struct ref_iterator_vtable debug_ref_iterator_vtable = {
+ debug_ref_iterator_advance, debug_ref_iterator_peel,
+ debug_ref_iterator_abort
+};
+
+static struct ref_iterator *
+debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
+ unsigned int flags)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ struct ref_iterator *res =
+ drefs->refs->be->iterator_begin(drefs->refs, prefix, flags);
+ struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
+ base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
+ diter->iter = res;
+ trace_printf_key(&trace_refs, "ref_iterator_begin: %s (0x%x)\n", prefix, flags);
+ return &diter->base;
+}
+
+static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
+ struct object_id *oid, struct strbuf *referent,
+ unsigned int *type)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = 0;
+
+ oidcpy(oid, &null_oid);
+ res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,
+ type);
+
+ if (res == 0) {
+ trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n",
+ refname, oid_to_hex(oid), referent->buf, *type, res);
+ } else {
+ trace_printf_key(&trace_refs, "read_raw_ref: %s: %d\n", refname, res);
+ }
+ return res;
+}
+
+static struct ref_iterator *
+debug_reflog_iterator_begin(struct ref_store *ref_store)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ struct ref_iterator *res =
+ drefs->refs->be->reflog_iterator_begin(drefs->refs);
+ trace_printf_key(&trace_refs, "for_each_reflog_iterator_begin\n");
+ return res;
+}
+
+struct debug_reflog {
+ const char *refname;
+ each_reflog_ent_fn *fn;
+ void *cb_data;
+};
+
+static int debug_print_reflog_ent(struct object_id *old_oid,
+ struct object_id *new_oid,
+ const char *committer, timestamp_t timestamp,
+ int tz, const char *msg, void *cb_data)
+{
+ struct debug_reflog *dbg = (struct debug_reflog *)cb_data;
+ int ret;
+ char o[GIT_MAX_HEXSZ + 1] = "null";
+ char n[GIT_MAX_HEXSZ + 1] = "null";
+ if (old_oid)
+ oid_to_hex_r(o, old_oid);
+ if (new_oid)
+ oid_to_hex_r(n, new_oid);
+
+ ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg,
+ dbg->cb_data);
+ trace_printf_key(&trace_refs, "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%s\"\n",
+ dbg->refname, ret, o, n, committer, (long int)timestamp, msg);
+ return ret;
+}
+
+static int debug_for_each_reflog_ent(struct ref_store *ref_store,
+ const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ struct debug_reflog dbg = {
+ .refname = refname,
+ .fn = fn,
+ .cb_data = cb_data,
+ };
+
+ int res = drefs->refs->be->for_each_reflog_ent(
+ drefs->refs, refname, &debug_print_reflog_ent, &dbg);
+ trace_printf_key(&trace_refs, "for_each_reflog: %s: %d\n", refname, res);
+ return res;
+}
+
+static int debug_for_each_reflog_ent_reverse(struct ref_store *ref_store,
+ const char *refname,
+ each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ struct debug_reflog dbg = {
+ .refname = refname,
+ .fn = fn,
+ .cb_data = cb_data,
+ };
+ int res = drefs->refs->be->for_each_reflog_ent_reverse(
+ drefs->refs, refname, &debug_print_reflog_ent, &dbg);
+ trace_printf_key(&trace_refs, "for_each_reflog_reverse: %s: %d\n", refname, res);
+ return res;
+}
+
+static int debug_reflog_exists(struct ref_store *ref_store, const char *refname)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->reflog_exists(drefs->refs, refname);
+ trace_printf_key(&trace_refs, "reflog_exists: %s: %d\n", refname, res);
+ return res;
+}
+
+static int debug_create_reflog(struct ref_store *ref_store, const char *refname,
+ int force_create, struct strbuf *err)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->create_reflog(drefs->refs, refname,
+ force_create, err);
+ trace_printf_key(&trace_refs, "create_reflog: %s: %d\n", refname, res);
+ return res;
+}
+
+static int debug_delete_reflog(struct ref_store *ref_store, const char *refname)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->delete_reflog(drefs->refs, refname);
+ trace_printf_key(&trace_refs, "delete_reflog: %s: %d\n", refname, res);
+ return res;
+}
+
+static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
+ const struct object_id *oid, unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->reflog_expire(drefs->refs, refname, oid,
+ flags, prepare_fn,
+ should_prune_fn, cleanup_fn,
+ policy_cb_data);
+ trace_printf_key(&trace_refs, "reflog_expire: %s: %d\n", refname, res);
+ return res;
+}
+
+struct ref_storage_be refs_be_debug = {
+ NULL,
+ "debug",
+ NULL,
+ debug_init_db,
+ debug_transaction_prepare,
+ debug_transaction_finish,
+ debug_transaction_abort,
+ debug_initial_transaction_commit,
+
+ debug_pack_refs,
+ debug_create_symref,
+ debug_delete_refs,
+ debug_rename_ref,
+ debug_copy_ref,
+
+ debug_ref_iterator_begin,
+ debug_read_raw_ref,
+
+ debug_reflog_iterator_begin,
+ debug_for_each_reflog_ent,
+ debug_for_each_reflog_ent_reverse,
+ debug_reflog_exists,
+ debug_create_reflog,
+ debug_delete_reflog,
+ debug_reflog_expire,
+};
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 6516c7bc8c..04e85e7002 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -39,13 +39,6 @@
#define REF_NEEDS_COMMIT (1 << 6)
/*
- * Used as a flag in ref_update::flags when we want to log a ref
- * update but not actually perform it. This is used when a symbolic
- * ref update is split up.
- */
-#define REF_LOG_ONLY (1 << 7)
-
-/*
* Used as a flag in ref_update::flags when the ref_update was via an
* update to HEAD.
*/
@@ -67,7 +60,6 @@ struct files_ref_store {
struct ref_store base;
unsigned int store_flags;
- char *gitdir;
char *gitcommondir;
struct ref_cache *loose;
@@ -94,18 +86,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
struct ref_store *ref_store = (struct ref_store *)refs;
struct strbuf sb = STRBUF_INIT;
+ ref_store->gitdir = xstrdup(gitdir);
base_ref_store_init(ref_store, &refs_be_files);
refs->store_flags = flags;
- refs->gitdir = xstrdup(gitdir);
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
strbuf_release(&sb);
- chdir_notify_reparent("files-backend $GIT_DIR",
- &refs->gitdir);
+ chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
chdir_notify_reparent("files-backend $GIT_COMMONDIR",
&refs->gitcommondir);
@@ -176,7 +167,7 @@ static void files_reflog_path(struct files_ref_store *refs,
switch (ref_type(refname)) {
case REF_TYPE_PER_WORKTREE:
case REF_TYPE_PSEUDOREF:
- strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
+ strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname);
break;
case REF_TYPE_OTHER_PSEUDOREF:
case REF_TYPE_MAIN_PSEUDOREF:
@@ -198,7 +189,7 @@ static void files_ref_path(struct files_ref_store *refs,
switch (ref_type(refname)) {
case REF_TYPE_PER_WORKTREE:
case REF_TYPE_PSEUDOREF:
- strbuf_addf(sb, "%s/%s", refs->gitdir, refname);
+ strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname);
break;
case REF_TYPE_MAIN_PSEUDOREF:
if (!skip_prefix(refname, "main-worktree/", &refname))
@@ -360,7 +351,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,
struct strbuf sb_path = STRBUF_INIT;
const char *path;
const char *buf;
- const char *p;
struct stat st;
int fd;
int ret = -1;
@@ -465,6 +455,21 @@ stat_ref:
close(fd);
strbuf_rtrim(&sb_contents);
buf = sb_contents.buf;
+
+ ret = parse_loose_ref_contents(buf, oid, referent, type);
+
+out:
+ save_errno = errno;
+ strbuf_release(&sb_path);
+ strbuf_release(&sb_contents);
+ errno = save_errno;
+ return ret;
+}
+
+int parse_loose_ref_contents(const char *buf, struct object_id *oid,
+ struct strbuf *referent, unsigned int *type)
+{
+ const char *p;
if (skip_prefix(buf, "ref:", &buf)) {
while (isspace(*buf))
buf++;
@@ -472,29 +477,19 @@ stat_ref:
strbuf_reset(referent);
strbuf_addstr(referent, buf);
*type |= REF_ISSYMREF;
- ret = 0;
- goto out;
+ return 0;
}
/*
- * Please note that FETCH_HEAD has additional
- * data after the sha.
+ * FETCH_HEAD has additional data after the sha.
*/
if (parse_oid_hex(buf, oid, &p) ||
(*p != '\0' && !isspace(*p))) {
*type |= REF_ISBROKEN;
errno = EINVAL;
- goto out;
+ return -1;
}
-
- ret = 0;
-
-out:
- save_errno = errno;
- strbuf_release(&sb_path);
- strbuf_release(&sb_contents);
- errno = save_errno;
- return ret;
+ return 0;
}
static void unlock_ref(struct ref_lock *lock)
@@ -1628,8 +1623,10 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,
int ret = 0;
strbuf_addf(&sb, "%s %s %s", oid_to_hex(old_oid), oid_to_hex(new_oid), committer);
- if (msg && *msg)
- copy_reflog_msg(&sb, msg);
+ if (msg && *msg) {
+ strbuf_addch(&sb, '\t');
+ strbuf_addstr(&sb, msg);
+ }
strbuf_addch(&sb, '\n');
if (write_in_full(fd, sb.buf, sb.len) < 0)
ret = -1;
@@ -2197,12 +2194,11 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
files_downcast(ref_store, REF_STORE_READ,
"reflog_iterator_begin");
- if (!strcmp(refs->gitdir, refs->gitcommondir)) {
+ if (!strcmp(refs->base.gitdir, refs->gitcommondir)) {
return reflog_iterator_begin(ref_store, refs->gitcommondir);
} else {
return merge_ref_iterator_begin(
- 0,
- reflog_iterator_begin(ref_store, refs->gitdir),
+ 0, reflog_iterator_begin(ref_store, refs->base.gitdir),
reflog_iterator_begin(ref_store, refs->gitcommondir),
reflog_iterator_select, refs);
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 4458a0f69c..b912f2505f 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -200,6 +200,7 @@ struct ref_store *packed_ref_store_create(const char *path,
struct ref_store *ref_store = (struct ref_store *)refs;
base_ref_store_init(ref_store, &refs_be_packed);
+ ref_store->gitdir = xstrdup(path);
refs->store_flags = store_flags;
refs->path = xstrdup(path);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 4271362d26..467f4b3c93 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -32,6 +32,13 @@ struct ref_transaction;
#define REF_HAVE_OLD (1 << 3)
/*
+ * Used as a flag in ref_update::flags when we want to log a ref
+ * update but not actually perform it. This is used when a symbolic
+ * ref update is split up.
+ */
+#define REF_LOG_ONLY (1 << 7)
+
+/*
* Return the length of time to retry acquiring a loose reference lock
* before giving up, in milliseconds:
*/
@@ -96,12 +103,6 @@ enum peel_status {
*/
enum peel_status peel_object(const struct object_id *name, struct object_id *oid);
-/*
- * Copy the reflog message msg to sb while cleaning up the whitespaces.
- * Especially, convert LF to space, because reflog file is one line per entry.
- */
-void copy_reflog_msg(struct strbuf *sb, const char *msg);
-
/**
* Information needed for a single ref update. Set new_oid to the new
* value or to null_oid to delete the ref. To check the old value
@@ -673,18 +674,32 @@ extern struct ref_storage_be refs_be_packed;
/*
* A representation of the reference store for the main repository or
* a submodule. The ref_store instances for submodules are kept in a
- * linked list.
+ * hash map; see get_submodule_ref_store() for more info.
*/
struct ref_store {
/* The backend describing this ref_store's storage scheme: */
const struct ref_storage_be *be;
+
+ /* The gitdir that this ref_store applies to: */
+ char *gitdir;
};
/*
+ * Parse contents of a loose ref file.
+ */
+int parse_loose_ref_contents(const char *buf, struct object_id *oid,
+ struct strbuf *referent, unsigned int *type);
+
+/*
* Fill in the generic part of refs and add it to our collection of
* reference stores.
*/
void base_ref_store_init(struct ref_store *refs,
const struct ref_storage_be *be);
+/*
+ * Support GIT_TRACE_REFS by optionally wrapping the given ref_store instance.
+ */
+struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_store *store);
+
#endif /* REFS_REFS_INTERNAL_H */
diff --git a/refspec.c b/refspec.c
index 9a9bf21934..8d0affc34a 100644
--- a/refspec.c
+++ b/refspec.c
@@ -1,5 +1,5 @@
#include "cache.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "refs.h"
#include "refspec.h"
@@ -153,7 +153,7 @@ void refspec_init(struct refspec *rs, int fetch)
rs->fetch = fetch;
}
-void refspec_append(struct refspec *rs, const char *refspec)
+static void refspec_append_nodup(struct refspec *rs, char *refspec)
{
struct refspec_item item;
@@ -163,7 +163,21 @@ void refspec_append(struct refspec *rs, const char *refspec)
rs->items[rs->nr++] = item;
ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);
- rs->raw[rs->raw_nr++] = xstrdup(refspec);
+ rs->raw[rs->raw_nr++] = refspec;
+}
+
+void refspec_append(struct refspec *rs, const char *refspec)
+{
+ refspec_append_nodup(rs, xstrdup(refspec));
+}
+
+void refspec_appendf(struct refspec *rs, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ refspec_append_nodup(rs, xstrvfmt(fmt, ap));
+ va_end(ap);
}
void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
@@ -202,7 +216,7 @@ int valid_fetch_refspec(const char *fetch_refspec_str)
}
void refspec_ref_prefixes(const struct refspec *rs,
- struct argv_array *ref_prefixes)
+ struct strvec *ref_prefixes)
{
int i;
for (i = 0; i < rs->nr; i++) {
@@ -221,9 +235,9 @@ void refspec_ref_prefixes(const struct refspec *rs,
if (prefix) {
if (item->pattern) {
const char *glob = strchr(prefix, '*');
- argv_array_pushf(ref_prefixes, "%.*s",
- (int)(glob - prefix),
- prefix);
+ strvec_pushf(ref_prefixes, "%.*s",
+ (int)(glob - prefix),
+ prefix);
} else {
expand_ref_prefix(ref_prefixes, prefix);
}
diff --git a/refspec.h b/refspec.h
index 3f2bd4aaa5..7569248d11 100644
--- a/refspec.h
+++ b/refspec.h
@@ -4,6 +4,19 @@
#define TAG_REFSPEC "refs/tags/*:refs/tags/*"
extern const struct refspec_item *tag_refspec;
+/**
+ * A struct refspec_item holds the parsed interpretation of a refspec. If it will
+ * force updates (starts with a '+'), force is true. If it is a pattern
+ * (sides end with '*') pattern is true. src and dest are the two sides
+ * (including '*' characters if present); if there is only one side, it is src,
+ * and dst is NULL; if sides exist but are empty (i.e., the refspec either
+ * starts or ends with ':'), the corresponding side is "".
+ *
+ * remote_find_tracking(), given a remote and a struct refspec_item with either src
+ * or dst filled out, will fill out the other such that the result is in the
+ * "fetch" specification for the remote (note that this evaluates patterns and
+ * returns a single result).
+ */
struct refspec_item {
unsigned force : 1;
unsigned pattern : 1;
@@ -21,20 +34,8 @@ struct refspec_item {
#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }
/**
- * A struct refspec holds the parsed interpretation of a refspec. If it will
- * force updates (starts with a '+'), force is true. If it is a pattern
- * (sides end with '*') pattern is true. src and dest are the two sides
- * (including '*' characters if present); if there is only one side, it is src,
- * and dst is NULL; if sides exist but are empty (i.e., the refspec either
- * starts or ends with ':'), the corresponding side is "".
- *
- * An array of strings can be parsed into an array of struct refspecs using
+ * An array of strings can be parsed into a struct refspec using
* parse_fetch_refspec() or parse_push_refspec().
- *
- * remote_find_tracking(), given a remote and a struct refspec with either src
- * or dst filled out, will fill out the other such that the result is in the
- * "fetch" specification for the remote (note that this evaluates patterns and
- * returns a single result).
*/
struct refspec {
struct refspec_item *items;
@@ -55,17 +56,19 @@ void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
void refspec_item_clear(struct refspec_item *item);
void refspec_init(struct refspec *rs, int fetch);
void refspec_append(struct refspec *rs, const char *refspec);
+__attribute__((format (printf,2,3)))
+void refspec_appendf(struct refspec *rs, const char *fmt, ...);
void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);
void refspec_clear(struct refspec *rs);
int valid_fetch_refspec(const char *refspec);
-struct argv_array;
+struct strvec;
/*
* Determine what <prefix> values to pass to the peer in ref-prefix lines
* (see Documentation/technical/protocol-v2.txt).
*/
void refspec_ref_prefixes(const struct refspec *rs,
- struct argv_array *ref_prefixes);
+ struct strvec *ref_prefixes);
#endif /* REFSPEC_H */
diff --git a/remote-curl.c b/remote-curl.c
index 5cbc6e5002..32cc4a0c55 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -10,7 +10,7 @@
#include "pkt-line.h"
#include "string-list.h"
#include "sideband.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "credential.h"
#include "oid-array.h"
#include "send-pack.h"
@@ -39,8 +39,10 @@ struct options {
/* One of the SEND_PACK_PUSH_CERT_* constants. */
push_cert : 2,
deepen_relative : 1,
+
+ /* see documentation of corresponding flag in fetch-pack.h */
from_promisor : 1,
- no_dependents : 1,
+
atomic : 1,
object_format : 1;
const struct git_hash_algo *hash_algo;
@@ -121,7 +123,11 @@ static int set_option(const char *name, const char *value)
}
else if (!strcmp(name, "cas")) {
struct strbuf val = STRBUF_INIT;
- strbuf_addf(&val, "--" CAS_OPT_NAME "=%s", value);
+ strbuf_addstr(&val, "--force-with-lease=");
+ if (*value != '"')
+ strbuf_addstr(&val, value);
+ else if (unquote_c_style(&val, value, NULL))
+ return -1;
string_list_append(&cas_options, val.buf);
strbuf_release(&val);
return 0;
@@ -186,9 +192,6 @@ static int set_option(const char *name, const char *value)
} else if (!strcmp(name, "from-promisor")) {
options.from_promisor = 1;
return 0;
- } else if (!strcmp(name, "no-dependents")) {
- options.no_dependents = 1;
- return 0;
} else if (!strcmp(name, "filter")) {
options.filter = xstrdup(value);
return 0;
@@ -1141,41 +1144,39 @@ static int fetch_git(struct discovery *heads,
struct rpc_state rpc;
struct strbuf preamble = STRBUF_INIT;
int i, err;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
struct strbuf rpc_result = STRBUF_INIT;
- argv_array_pushl(&args, "fetch-pack", "--stateless-rpc",
- "--stdin", "--lock-pack", NULL);
+ strvec_pushl(&args, "fetch-pack", "--stateless-rpc",
+ "--stdin", "--lock-pack", NULL);
if (options.followtags)
- argv_array_push(&args, "--include-tag");
+ strvec_push(&args, "--include-tag");
if (options.thin)
- argv_array_push(&args, "--thin");
+ strvec_push(&args, "--thin");
if (options.verbosity >= 3)
- argv_array_pushl(&args, "-v", "-v", NULL);
+ strvec_pushl(&args, "-v", "-v", NULL);
if (options.check_self_contained_and_connected)
- argv_array_push(&args, "--check-self-contained-and-connected");
+ strvec_push(&args, "--check-self-contained-and-connected");
if (options.cloning)
- argv_array_push(&args, "--cloning");
+ strvec_push(&args, "--cloning");
if (options.update_shallow)
- argv_array_push(&args, "--update-shallow");
+ strvec_push(&args, "--update-shallow");
if (!options.progress)
- argv_array_push(&args, "--no-progress");
+ strvec_push(&args, "--no-progress");
if (options.depth)
- argv_array_pushf(&args, "--depth=%lu", options.depth);
+ strvec_pushf(&args, "--depth=%lu", options.depth);
if (options.deepen_since)
- argv_array_pushf(&args, "--shallow-since=%s", options.deepen_since);
+ strvec_pushf(&args, "--shallow-since=%s", options.deepen_since);
for (i = 0; i < options.deepen_not.nr; i++)
- argv_array_pushf(&args, "--shallow-exclude=%s",
- options.deepen_not.items[i].string);
+ strvec_pushf(&args, "--shallow-exclude=%s",
+ options.deepen_not.items[i].string);
if (options.deepen_relative && options.depth)
- argv_array_push(&args, "--deepen-relative");
+ strvec_push(&args, "--deepen-relative");
if (options.from_promisor)
- argv_array_push(&args, "--from-promisor");
- if (options.no_dependents)
- argv_array_push(&args, "--no-dependents");
+ strvec_push(&args, "--from-promisor");
if (options.filter)
- argv_array_pushf(&args, "--filter=%s", options.filter);
- argv_array_push(&args, url.buf);
+ strvec_pushf(&args, "--filter=%s", options.filter);
+ strvec_push(&args, url.buf);
for (i = 0; i < nr_heads; i++) {
struct ref *ref = to_fetch[i];
@@ -1190,12 +1191,12 @@ static int fetch_git(struct discovery *heads,
rpc.service_name = "git-upload-pack",
rpc.gzip_request = 1;
- err = rpc_service(&rpc, heads, args.argv, &preamble, &rpc_result);
+ err = rpc_service(&rpc, heads, args.v, &preamble, &rpc_result);
if (rpc_result.len)
write_or_die(1, rpc_result.buf, rpc_result.len);
strbuf_release(&rpc_result);
strbuf_release(&preamble);
- argv_array_clear(&args);
+ strvec_clear(&args);
return err;
}
@@ -1267,15 +1268,15 @@ static int push_dav(int nr_spec, const char **specs)
size_t i;
child.git_cmd = 1;
- argv_array_push(&child.args, "http-push");
- argv_array_push(&child.args, "--helper-status");
+ strvec_push(&child.args, "http-push");
+ strvec_push(&child.args, "--helper-status");
if (options.dry_run)
- argv_array_push(&child.args, "--dry-run");
+ strvec_push(&child.args, "--dry-run");
if (options.verbosity > 1)
- argv_array_push(&child.args, "--verbose");
- argv_array_push(&child.args, url.buf);
+ strvec_push(&child.args, "--verbose");
+ strvec_push(&child.args, url.buf);
for (i = 0; i < nr_spec; i++)
- argv_array_push(&child.args, specs[i]);
+ strvec_push(&child.args, specs[i]);
if (run_command(&child))
die(_("git-http-push failed"));
@@ -1286,38 +1287,38 @@ static int push_git(struct discovery *heads, int nr_spec, const char **specs)
{
struct rpc_state rpc;
int i, err;
- struct argv_array args;
+ struct strvec args;
struct string_list_item *cas_option;
struct strbuf preamble = STRBUF_INIT;
struct strbuf rpc_result = STRBUF_INIT;
- argv_array_init(&args);
- argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
- NULL);
+ strvec_init(&args);
+ strvec_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
+ NULL);
if (options.thin)
- argv_array_push(&args, "--thin");
+ strvec_push(&args, "--thin");
if (options.dry_run)
- argv_array_push(&args, "--dry-run");
+ strvec_push(&args, "--dry-run");
if (options.push_cert == SEND_PACK_PUSH_CERT_ALWAYS)
- argv_array_push(&args, "--signed=yes");
+ strvec_push(&args, "--signed=yes");
else if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED)
- argv_array_push(&args, "--signed=if-asked");
+ strvec_push(&args, "--signed=if-asked");
if (options.atomic)
- argv_array_push(&args, "--atomic");
+ strvec_push(&args, "--atomic");
if (options.verbosity == 0)
- argv_array_push(&args, "--quiet");
+ strvec_push(&args, "--quiet");
else if (options.verbosity > 1)
- argv_array_push(&args, "--verbose");
+ strvec_push(&args, "--verbose");
for (i = 0; i < options.push_options.nr; i++)
- argv_array_pushf(&args, "--push-option=%s",
- options.push_options.items[i].string);
- argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
+ strvec_pushf(&args, "--push-option=%s",
+ options.push_options.items[i].string);
+ strvec_push(&args, options.progress ? "--progress" : "--no-progress");
for_each_string_list_item(cas_option, &cas_options)
- argv_array_push(&args, cas_option->string);
- argv_array_push(&args, url.buf);
+ strvec_push(&args, cas_option->string);
+ strvec_push(&args, url.buf);
- argv_array_push(&args, "--stdin");
+ strvec_push(&args, "--stdin");
for (i = 0; i < nr_spec; i++)
packet_buf_write(&preamble, "%s\n", specs[i]);
packet_buf_flush(&preamble);
@@ -1325,12 +1326,12 @@ static int push_git(struct discovery *heads, int nr_spec, const char **specs)
memset(&rpc, 0, sizeof(rpc));
rpc.service_name = "git-receive-pack",
- err = rpc_service(&rpc, heads, args.argv, &preamble, &rpc_result);
+ err = rpc_service(&rpc, heads, args.v, &preamble, &rpc_result);
if (rpc_result.len)
write_or_die(1, rpc_result.buf, rpc_result.len);
strbuf_release(&rpc_result);
strbuf_release(&preamble);
- argv_array_clear(&args);
+ strvec_clear(&args);
return err;
}
@@ -1349,13 +1350,13 @@ static int push(int nr_spec, const char **specs)
static void parse_push(struct strbuf *buf)
{
- struct argv_array specs = ARGV_ARRAY_INIT;
+ struct strvec specs = STRVEC_INIT;
int ret;
do {
const char *arg;
if (skip_prefix(buf->buf, "push ", &arg))
- argv_array_push(&specs, arg);
+ strvec_push(&specs, arg);
else
die(_("http transport does not support %s"), buf->buf);
@@ -1366,7 +1367,7 @@ static void parse_push(struct strbuf *buf)
break;
} while (1);
- ret = push(specs.argc, specs.argv);
+ ret = push(specs.nr, specs.v);
printf("\n");
fflush(stdout);
@@ -1374,7 +1375,7 @@ static void parse_push(struct strbuf *buf)
exit(128); /* error already reported */
free_specs:
- argv_array_clear(&specs);
+ strvec_clear(&specs);
}
static int stateless_connect(const char *service_name)
diff --git a/remote-testsvn.c b/remote-testsvn.c
deleted file mode 100644
index cde39b94fb..0000000000
--- a/remote-testsvn.c
+++ /dev/null
@@ -1,341 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-#include "remote.h"
-#include "object-store.h"
-#include "strbuf.h"
-#include "url.h"
-#include "exec-cmd.h"
-#include "run-command.h"
-#include "vcs-svn/svndump.h"
-#include "notes.h"
-#include "argv-array.h"
-
-static const char *url;
-static int dump_from_file;
-static const char *private_ref;
-static char *remote_ref;
-static const char *marksfilename, *notes_ref;
-struct rev_note { unsigned int rev_nr; };
-
-static int cmd_capabilities(const char *line);
-static int cmd_import(const char *line);
-static int cmd_list(const char *line);
-
-typedef int (*input_command_handler)(const char *);
-struct input_command_entry {
- const char *name;
- input_command_handler fn;
- unsigned char batchable; /* whether the command starts or is part of a batch */
-};
-
-static const struct input_command_entry input_command_list[] = {
- { "capabilities", cmd_capabilities, 0 },
- { "import", cmd_import, 1 },
- { "list", cmd_list, 0 },
- { NULL, NULL }
-};
-
-static int cmd_capabilities(const char *line)
-{
- printf("import\n");
- printf("bidi-import\n");
- printf("refspec %s:%s\n\n", remote_ref, private_ref);
- fflush(stdout);
- return 0;
-}
-
-static void terminate_batch(void)
-{
- /* terminate a current batch's fast-import stream */
- printf("done\n");
- fflush(stdout);
-}
-
-/* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */
-static char *read_ref_note(const struct object_id *oid)
-{
- const struct object_id *note_oid;
- char *msg = NULL;
- unsigned long msglen;
- enum object_type type;
-
- init_notes(NULL, notes_ref, NULL, 0);
- if (!(note_oid = get_note(NULL, oid)))
- return NULL; /* note tree not found */
- if (!(msg = read_object_file(note_oid, &type, &msglen)))
- error("Empty notes tree. %s", notes_ref);
- else if (!msglen || type != OBJ_BLOB) {
- error("Note contains unusable content. "
- "Is something else using this notes tree? %s", notes_ref);
- FREE_AND_NULL(msg);
- }
- free_notes(NULL);
- return msg;
-}
-
-static int parse_rev_note(const char *msg, struct rev_note *res)
-{
- const char *key, *value, *end;
- size_t len;
-
- while (*msg) {
- end = strchrnul(msg, '\n');
- len = end - msg;
-
- key = "Revision-number: ";
- if (starts_with(msg, key)) {
- long i;
- char *end;
- value = msg + strlen(key);
- i = strtol(value, &end, 0);
- if (end == value || i < 0 || i > UINT32_MAX)
- return -1;
- res->rev_nr = i;
- return 0;
- }
- msg += len + 1;
- }
- /* didn't find it */
- return -1;
-}
-
-static int note2mark_cb(const struct object_id *object_oid,
- const struct object_id *note_oid, char *note_path,
- void *cb_data)
-{
- FILE *file = (FILE *)cb_data;
- char *msg;
- unsigned long msglen;
- enum object_type type;
- struct rev_note note;
-
- if (!(msg = read_object_file(note_oid, &type, &msglen)) ||
- !msglen || type != OBJ_BLOB) {
- free(msg);
- return 1;
- }
- if (parse_rev_note(msg, &note))
- return 2;
- if (fprintf(file, ":%d %s\n", note.rev_nr, oid_to_hex(object_oid)) < 1)
- return 3;
- return 0;
-}
-
-static void regenerate_marks(void)
-{
- int ret;
- FILE *marksfile = xfopen(marksfilename, "w+");
-
- ret = for_each_note(NULL, 0, note2mark_cb, marksfile);
- if (ret)
- die("Regeneration of marks failed, returned %d.", ret);
- fclose(marksfile);
-}
-
-static void check_or_regenerate_marks(int latestrev)
-{
- FILE *marksfile;
- struct strbuf sb = STRBUF_INIT;
- struct strbuf line = STRBUF_INIT;
- int found = 0;
-
- if (latestrev < 1)
- return;
-
- init_notes(NULL, notes_ref, NULL, 0);
- marksfile = fopen(marksfilename, "r");
- if (!marksfile) {
- regenerate_marks();
- marksfile = xfopen(marksfilename, "r");
- fclose(marksfile);
- } else {
- strbuf_addf(&sb, ":%d ", latestrev);
- while (strbuf_getline_lf(&line, marksfile) != EOF) {
- if (starts_with(line.buf, sb.buf)) {
- found++;
- break;
- }
- }
- fclose(marksfile);
- if (!found)
- regenerate_marks();
- }
- free_notes(NULL);
- strbuf_release(&sb);
- strbuf_release(&line);
-}
-
-static int cmd_import(const char *line)
-{
- int code;
- int dumpin_fd;
- char *note_msg;
- struct object_id head_oid;
- unsigned int startrev;
- struct child_process svndump_proc = CHILD_PROCESS_INIT;
- const char *command = "svnrdump";
-
- if (read_ref(private_ref, &head_oid))
- startrev = 0;
- else {
- note_msg = read_ref_note(&head_oid);
- if(note_msg == NULL) {
- warning("No note found for %s.", private_ref);
- startrev = 0;
- } else {
- struct rev_note note = { 0 };
- if (parse_rev_note(note_msg, &note))
- die("Revision number couldn't be parsed from note.");
- startrev = note.rev_nr + 1;
- free(note_msg);
- }
- }
- check_or_regenerate_marks(startrev - 1);
-
- if (dump_from_file) {
- dumpin_fd = open(url, O_RDONLY);
- if(dumpin_fd < 0)
- die_errno("Couldn't open svn dump file %s.", url);
- } else {
- svndump_proc.out = -1;
- argv_array_push(&svndump_proc.args, command);
- argv_array_push(&svndump_proc.args, "dump");
- argv_array_push(&svndump_proc.args, url);
- argv_array_pushf(&svndump_proc.args, "-r%u:HEAD", startrev);
-
- code = start_command(&svndump_proc);
- if (code)
- die("Unable to start %s, code %d", command, code);
- dumpin_fd = svndump_proc.out;
- }
- /* setup marks file import/export */
- printf("feature import-marks-if-exists=%s\n"
- "feature export-marks=%s\n", marksfilename, marksfilename);
-
- svndump_init_fd(dumpin_fd, STDIN_FILENO);
- svndump_read(url, private_ref, notes_ref);
- svndump_deinit();
- svndump_reset();
-
- close(dumpin_fd);
- if (!dump_from_file) {
- code = finish_command(&svndump_proc);
- if (code)
- warning("%s, returned %d", command, code);
- }
-
- return 0;
-}
-
-static int cmd_list(const char *line)
-{
- printf("? %s\n\n", remote_ref);
- fflush(stdout);
- return 0;
-}
-
-static int do_command(struct strbuf *line)
-{
- const struct input_command_entry *p = input_command_list;
- static struct string_list batchlines = STRING_LIST_INIT_DUP;
- static const struct input_command_entry *batch_cmd;
- /*
- * commands can be grouped together in a batch.
- * Batches are ended by \n. If no batch is active the program ends.
- * During a batch all lines are buffered and passed to the handler function
- * when the batch is terminated.
- */
- if (line->len == 0) {
- if (batch_cmd) {
- struct string_list_item *item;
- for_each_string_list_item(item, &batchlines)
- batch_cmd->fn(item->string);
- terminate_batch();
- batch_cmd = NULL;
- string_list_clear(&batchlines, 0);
- return 0; /* end of the batch, continue reading other commands. */
- }
- return 1; /* end of command stream, quit */
- }
- if (batch_cmd) {
- if (!starts_with(batch_cmd->name, line->buf))
- die("Active %s batch interrupted by %s", batch_cmd->name, line->buf);
- /* buffer batch lines */
- string_list_append(&batchlines, line->buf);
- return 0;
- }
-
- for (p = input_command_list; p->name; p++) {
- if (starts_with(line->buf, p->name) && (strlen(p->name) == line->len ||
- line->buf[strlen(p->name)] == ' ')) {
- if (p->batchable) {
- batch_cmd = p;
- string_list_append(&batchlines, line->buf);
- return 0;
- }
- return p->fn(line->buf);
- }
- }
- die("Unknown command '%s'\n", line->buf);
- return 0;
-}
-
-int cmd_main(int argc, const char **argv)
-{
- struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
- private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT,
- notes_ref_sb = STRBUF_INIT;
- static struct remote *remote;
- const char *url_in, *remote_ref_short;
-
- setup_git_directory();
- if (argc < 2 || argc > 3) {
- usage("git-remote-svn <remote-name> [<url>]");
- return 1;
- }
-
- remote_ref_short = git_default_branch_name();
- remote_ref = xstrfmt("refs/heads/%s", remote_ref_short);
-
- remote = remote_get(argv[1]);
- url_in = (argc == 3) ? argv[2] : remote->url[0];
-
- if (starts_with(url_in, "file://")) {
- dump_from_file = 1;
- url = url_decode(url_in + sizeof("file://")-1);
- } else {
- dump_from_file = 0;
- end_url_with_slash(&url_sb, url_in);
- url = url_sb.buf;
- }
-
- strbuf_addf(&private_ref_sb, "refs/svn/%s/%s",
- remote->name, remote_ref_short);
- private_ref = private_ref_sb.buf;
-
- strbuf_addf(&notes_ref_sb, "refs/notes/%s/revs", remote->name);
- notes_ref = notes_ref_sb.buf;
-
- strbuf_addf(&marksfilename_sb, "%s/info/fast-import/remote-svn/%s.marks",
- get_git_dir(), remote->name);
- marksfilename = marksfilename_sb.buf;
-
- while (1) {
- if (strbuf_getline_lf(&buf, stdin) == EOF) {
- if (ferror(stdin))
- die("Error reading command stream");
- else
- die("Unexpected end of command stream");
- }
- if (do_command(&buf))
- break;
- strbuf_reset(&buf);
- }
-
- strbuf_release(&buf);
- strbuf_release(&url_sb);
- strbuf_release(&private_ref_sb);
- strbuf_release(&notes_ref_sb);
- strbuf_release(&marksfilename_sb);
- return 0;
-}
diff --git a/remote.c b/remote.c
index bc46413e6a..eafc14cbe7 100644
--- a/remote.c
+++ b/remote.c
@@ -11,7 +11,7 @@
#include "tag.h"
#include "string-list.h"
#include "mergesort.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "commit-reach.h"
#include "advice.h"
@@ -287,19 +287,15 @@ static void read_branches_file(struct remote *remote)
frag = (char *)git_default_branch_name();
add_url_alias(remote, strbuf_detach(&buf, NULL));
- strbuf_addf(&buf, "refs/heads/%s:refs/heads/%s",
- frag, remote->name);
- refspec_append(&remote->fetch, buf.buf);
+ refspec_appendf(&remote->fetch, "refs/heads/%s:refs/heads/%s",
+ frag, remote->name);
/*
* Cogito compatible push: push current HEAD to remote #branch
* (master if missing)
*/
- strbuf_reset(&buf);
- strbuf_addf(&buf, "HEAD:refs/heads/%s", frag);
- refspec_append(&remote->push, buf.buf);
+ refspec_appendf(&remote->push, "HEAD:refs/heads/%s", frag);
remote->fetch_tags = 1; /* always auto-follow */
- strbuf_release(&buf);
}
static int handle_config(const char *key, const char *value, void *cb)
@@ -1558,7 +1554,7 @@ static void set_merge(struct branch *ret)
strcmp(ret->remote_name, "."))
continue;
if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
- &oid, &ref) == 1)
+ &oid, &ref, 0) == 1)
ret->merge[i]->dst = ref;
else
ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
@@ -1885,7 +1881,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
struct object_id oid;
struct commit *ours, *theirs;
struct rev_info revs;
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
/* Cannot stat if what we used to build on no longer exists */
if (read_ref(base, &oid))
@@ -1911,15 +1907,15 @@ static int stat_branch_pair(const char *branch_name, const char *base,
BUG("stat_branch_pair: invalid abf '%d'", abf);
/* Run "rev-list --left-right ours...theirs" internally... */
- argv_array_push(&argv, ""); /* ignored */
- argv_array_push(&argv, "--left-right");
- argv_array_pushf(&argv, "%s...%s",
- oid_to_hex(&ours->object.oid),
- oid_to_hex(&theirs->object.oid));
- argv_array_push(&argv, "--");
+ strvec_push(&argv, ""); /* ignored */
+ strvec_push(&argv, "--left-right");
+ strvec_pushf(&argv, "%s...%s",
+ oid_to_hex(&ours->object.oid),
+ oid_to_hex(&theirs->object.oid));
+ strvec_push(&argv, "--");
repo_init_revisions(the_repository, &revs, NULL);
- setup_revisions(argv.argc, argv.argv, &revs, NULL);
+ setup_revisions(argv.nr, argv.v, &revs, NULL);
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
@@ -1938,7 +1934,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
clear_commit_marks(ours, ALL_REV_FLAGS);
clear_commit_marks(theirs, ALL_REV_FLAGS);
- argv_array_clear(&argv);
+ strvec_clear(&argv);
return 1;
}
diff --git a/remote.h b/remote.h
index 5cc26c1b3b..eb62a47044 100644
--- a/remote.h
+++ b/remote.h
@@ -93,6 +93,14 @@ int for_each_remote(each_remote_fn fn, void *priv);
int remote_has_url(struct remote *remote, const char *url);
+struct ref_push_report {
+ const char *ref_name;
+ struct object_id *old_oid;
+ struct object_id *new_oid;
+ unsigned int forced_update:1;
+ struct ref_push_report *next;
+};
+
struct ref {
struct ref *next;
struct object_id old_oid;
@@ -140,6 +148,7 @@ struct ref {
REF_STATUS_ATOMIC_PUSH_FAILED
} status;
char *remote_status;
+ struct ref_push_report *report;
struct ref *peer_ref; /* when renaming */
char name[FLEX_ARRAY]; /* more */
};
@@ -168,7 +177,7 @@ void free_refs(struct ref *ref);
struct oid_array;
struct packet_reader;
-struct argv_array;
+struct strvec;
struct string_list;
struct ref **get_remote_heads(struct packet_reader *reader,
struct ref **list, unsigned int flags,
@@ -178,7 +187,7 @@ struct ref **get_remote_heads(struct packet_reader *reader,
/* Used for protocol v2 in order to retrieve refs from a remote */
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
struct ref **list, int for_push,
- const struct argv_array *ref_prefixes,
+ const struct strvec *ref_prefixes,
const struct string_list *server_options,
int stateless_rpc);
diff --git a/repo-settings.c b/repo-settings.c
index 0918408b34..88ccce2036 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -17,9 +17,12 @@ void prepare_repo_settings(struct repository *r)
if (!repo_config_get_bool(r, "core.commitgraph", &value))
r->settings.core_commit_graph = value;
+ if (!repo_config_get_bool(r, "commitgraph.readchangedpaths", &value))
+ r->settings.commit_graph_read_changed_paths = value;
if (!repo_config_get_bool(r, "gc.writecommitgraph", &value))
r->settings.gc_write_commit_graph = value;
UPDATE_DEFAULT_BOOL(r->settings.core_commit_graph, 1);
+ UPDATE_DEFAULT_BOOL(r->settings.commit_graph_read_changed_paths, 1);
UPDATE_DEFAULT_BOOL(r->settings.gc_write_commit_graph, 1);
if (!repo_config_get_int(r, "index.version", &value))
@@ -39,6 +42,8 @@ void prepare_repo_settings(struct repository *r)
if (!repo_config_get_string(r, "fetch.negotiationalgorithm", &strval)) {
if (!strcasecmp(strval, "skipping"))
r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
+ else if (!strcasecmp(strval, "noop"))
+ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_NOOP;
else
r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
}
diff --git a/repository.c b/repository.c
index 6f7f6f002b..a4174ddb06 100644
--- a/repository.c
+++ b/repository.c
@@ -89,10 +89,6 @@ void repo_set_gitdir(struct repository *repo,
void repo_set_hash_algo(struct repository *repo, int hash_algo)
{
repo->hash_algo = &hash_algos[hash_algo];
-#ifndef ENABLE_SHA256
- if (hash_algo != GIT_HASH_SHA1)
- die(_("The hash algorithm %s is not supported in this build."), repo->hash_algo->name);
-#endif
}
/*
diff --git a/repository.h b/repository.h
index 3c1f7d54bd..bacf843d46 100644
--- a/repository.h
+++ b/repository.h
@@ -23,12 +23,14 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NONE = 0,
FETCH_NEGOTIATION_DEFAULT = 1,
FETCH_NEGOTIATION_SKIPPING = 2,
+ FETCH_NEGOTIATION_NOOP = 3,
};
struct repo_settings {
int initialized;
int core_commit_graph;
+ int commit_graph_read_changed_paths;
int gc_write_commit_graph;
int fetch_write_commit_graph;
diff --git a/revision.c b/revision.c
index 6aa7f4f567..d9dc5781ac 100644
--- a/revision.c
+++ b/revision.c
@@ -23,7 +23,7 @@
#include "bisect.h"
#include "packfile.h"
#include "worktree.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "commit-reach.h"
#include "commit-graph.h"
#include "prio-queue.h"
@@ -315,13 +315,14 @@ static void add_pending_object_with_path(struct rev_info *revs,
const char *name, unsigned mode,
const char *path)
{
+ struct interpret_branch_name_options options = { 0 };
if (!obj)
return;
if (revs->no_walk && (obj->flags & UNINTERESTING))
revs->no_walk = 0;
if (revs->reflog_info && obj->type == OBJ_COMMIT) {
struct strbuf buf = STRBUF_INIT;
- int len = interpret_branch_name(name, 0, &buf, 0);
+ int len = interpret_branch_name(name, 0, &buf, &options);
if (0 < len && name[len] && buf.len)
strbuf_addstr(&buf, name + len);
@@ -439,7 +440,7 @@ static struct commit *handle_commit(struct rev_info *revs,
if (object->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)object;
- if (parse_commit(commit) < 0)
+ if (repo_parse_commit(revs->repo, commit) < 0)
die("unable to parse commit %s", name);
if (flags & UNINTERESTING) {
mark_parents_uninteresting(commit);
@@ -633,7 +634,6 @@ static unsigned int count_bloom_filter_maybe;
static unsigned int count_bloom_filter_definitely_not;
static unsigned int count_bloom_filter_false_positive;
static unsigned int count_bloom_filter_not_present;
-static unsigned int count_bloom_filter_length_zero;
static void trace2_bloom_filter_statistics_atexit(void)
{
@@ -641,7 +641,6 @@ static void trace2_bloom_filter_statistics_atexit(void)
jw_object_begin(&jw, 0);
jw_object_intmax(&jw, "filter_not_present", count_bloom_filter_not_present);
- jw_object_intmax(&jw, "zero_length_filter", count_bloom_filter_length_zero);
jw_object_intmax(&jw, "maybe", count_bloom_filter_maybe);
jw_object_intmax(&jw, "definitely_not", count_bloom_filter_definitely_not);
jw_object_intmax(&jw, "false_positive", count_bloom_filter_false_positive);
@@ -670,9 +669,9 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs)
{
struct pathspec_item *pi;
char *path_alloc = NULL;
- const char *path;
- int last_index;
- int len;
+ const char *path, *p;
+ size_t len;
+ int path_component_nr = 1;
if (!revs->commits)
return;
@@ -682,10 +681,7 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs)
repo_parse_commit(revs->repo, revs->commits->item);
- if (!revs->repo->objects->commit_graph)
- return;
-
- revs->bloom_filter_settings = revs->repo->objects->commit_graph->bloom_filter_settings;
+ revs->bloom_filter_settings = get_bloom_filter_settings(revs->repo);
if (!revs->bloom_filter_settings)
return;
@@ -693,20 +689,48 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs)
return;
pi = &revs->pruning.pathspec.items[0];
- last_index = pi->len - 1;
/* remove single trailing slash from path, if needed */
- if (pi->match[last_index] == '/') {
- path_alloc = xstrdup(pi->match);
- path_alloc[last_index] = '\0';
- path = path_alloc;
+ if (pi->len > 0 && pi->match[pi->len - 1] == '/') {
+ path_alloc = xmemdupz(pi->match, pi->len - 1);
+ path = path_alloc;
} else
- path = pi->match;
+ path = pi->match;
len = strlen(path);
+ if (!len) {
+ revs->bloom_filter_settings = NULL;
+ free(path_alloc);
+ return;
+ }
+
+ p = path;
+ while (*p) {
+ /*
+ * At this point, the path is normalized to use Unix-style
+ * path separators. This is required due to how the
+ * changed-path Bloom filters store the paths.
+ */
+ if (*p == '/')
+ path_component_nr++;
+ p++;
+ }
- revs->bloom_key = xmalloc(sizeof(struct bloom_key));
- fill_bloom_key(path, len, revs->bloom_key, revs->bloom_filter_settings);
+ revs->bloom_keys_nr = path_component_nr;
+ ALLOC_ARRAY(revs->bloom_keys, revs->bloom_keys_nr);
+
+ fill_bloom_key(path, len, &revs->bloom_keys[0],
+ revs->bloom_filter_settings);
+ path_component_nr = 1;
+
+ p = path + len - 1;
+ while (p > path) {
+ if (*p == '/')
+ fill_bloom_key(path, p - path,
+ &revs->bloom_keys[path_component_nr++],
+ revs->bloom_filter_settings);
+ p--;
+ }
if (trace2_is_enabled() && !bloom_filter_atexit_registered) {
atexit(trace2_bloom_filter_statistics_atexit);
@@ -720,7 +744,7 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs,
struct commit *commit)
{
struct bloom_filter *filter;
- int result;
+ int result = 1, j;
if (!revs->repo->objects->commit_graph)
return -1;
@@ -728,22 +752,19 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs,
if (commit_graph_generation(commit) == GENERATION_NUMBER_INFINITY)
return -1;
- filter = get_bloom_filter(revs->repo, commit, 0);
+ filter = get_bloom_filter(revs->repo, commit);
if (!filter) {
count_bloom_filter_not_present++;
return -1;
}
- if (!filter->len) {
- count_bloom_filter_length_zero++;
- return -1;
+ for (j = 0; result && j < revs->bloom_keys_nr; j++) {
+ result = bloom_filter_contains(filter,
+ &revs->bloom_keys[j],
+ revs->bloom_filter_settings);
}
- result = bloom_filter_contains(filter,
- revs->bloom_key,
- revs->bloom_filter_settings);
-
if (result)
count_bloom_filter_maybe++;
else
@@ -782,7 +803,7 @@ static int rev_compare_tree(struct rev_info *revs,
return REV_TREE_SAME;
}
- if (revs->bloom_key && !nth_parent) {
+ if (revs->bloom_keys_nr && !nth_parent) {
bloom_ret = check_maybe_different_in_bloom_filter(revs, commit);
if (bloom_ret == 0)
@@ -791,9 +812,7 @@ static int rev_compare_tree(struct rev_info *revs,
tree_difference = REV_TREE_SAME;
revs->pruning.flags.has_changes = 0;
- if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
- &revs->pruning) < 0)
- return REV_TREE_DIFFERENT;
+ diff_tree_oid(&t1->object.oid, &t2->object.oid, "", &revs->pruning);
if (!nth_parent)
if (bloom_ret == 1 && tree_difference == REV_TREE_SAME)
@@ -804,7 +823,6 @@ static int rev_compare_tree(struct rev_info *revs,
static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
{
- int retval;
struct tree *t1 = get_commit_tree(commit);
if (!t1)
@@ -812,9 +830,9 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
tree_difference = REV_TREE_SAME;
revs->pruning.flags.has_changes = 0;
- retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
+ diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
- return retval >= 0 && (tree_difference == REV_TREE_SAME);
+ return tree_difference == REV_TREE_SAME;
}
struct treesame_state {
@@ -992,7 +1010,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
ts->treesame[0] = 1;
}
}
- if (parse_commit(p) < 0)
+ if (repo_parse_commit(revs->repo, p) < 0)
die("cannot simplify commit %s (because of %s)",
oid_to_hex(&commit->object.oid),
oid_to_hex(&p->object.oid));
@@ -1037,7 +1055,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
* IOW, we pretend this parent is a
* "root" commit.
*/
- if (parse_commit(p) < 0)
+ if (repo_parse_commit(revs->repo, p) < 0)
die("cannot simplify commit %s (invalid %s)",
oid_to_hex(&commit->object.oid),
oid_to_hex(&p->object.oid));
@@ -1105,7 +1123,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
parent = parent->next;
if (p)
p->object.flags |= UNINTERESTING;
- if (parse_commit_gently(p, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, p, 1) < 0)
continue;
if (p->parents)
mark_parents_uninteresting(p);
@@ -1136,7 +1154,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
struct commit *p = parent->item;
int gently = revs->ignore_missing_links ||
revs->exclude_promisor_objects;
- if (parse_commit_gently(p, gently) < 0) {
+ if (repo_parse_commit_gently(revs->repo, p, gently) < 0) {
if (revs->exclude_promisor_objects &&
is_promisor_object(&p->object.oid)) {
if (revs->first_parent_only)
@@ -1795,7 +1813,7 @@ void repo_init_revisions(struct repository *r,
revs->repo = r;
revs->abbrev = DEFAULT_ABBREV;
- revs->ignore_merges = 1;
+ revs->ignore_merges = -1;
revs->simplify_history = 1;
revs->pruning.repo = r;
revs->pruning.flags.recursive = 1;
@@ -1997,7 +2015,7 @@ static int handle_dotdot(const char *arg,
return ret;
}
-int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
+static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
{
struct object_context oc;
char *mark;
@@ -2072,15 +2090,23 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
return 0;
}
+int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt)
+{
+ int ret = handle_revision_arg_1(arg, revs, flags, revarg_opt);
+ if (!ret)
+ revs->rev_input_given = 1;
+ return ret;
+}
+
static void read_pathspec_from_stdin(struct strbuf *sb,
- struct argv_array *prune)
+ struct strvec *prune)
{
while (strbuf_getline(sb, stdin) != EOF)
- argv_array_push(prune, sb->buf);
+ strvec_push(prune, sb->buf);
}
static void read_revisions_from_stdin(struct rev_info *revs,
- struct argv_array *prune)
+ struct strvec *prune)
{
struct strbuf sb;
int seen_dashdash = 0;
@@ -2315,7 +2341,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--unpacked")) {
revs->unpacked = 1;
} else if (starts_with(arg, "--unpacked=")) {
- die("--unpacked=<packfile> no longer supported.");
+ die(_("--unpacked=<packfile> no longer supported"));
} else if (!strcmp(arg, "-r")) {
revs->diff = 1;
revs->diffopt.flags.recursive = 1;
@@ -2324,7 +2350,22 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->diffopt.flags.recursive = 1;
revs->diffopt.flags.tree_in_recursive = 1;
} else if (!strcmp(arg, "-m")) {
+ /*
+ * To "diff-index", "-m" means "match missing", and to the "log"
+ * family of commands, it means "show full diff for merges". Set
+ * both fields appropriately.
+ */
revs->ignore_merges = 0;
+ revs->match_missing = 1;
+ } else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) {
+ if (!strcmp(optarg, "off")) {
+ revs->ignore_merges = 1;
+ } else {
+ die(_("unknown value for --diff-merges: %s"), optarg);
+ }
+ return argcount;
+ } else if (!strcmp(arg, "--no-diff-merges")) {
+ revs->ignore_merges = 1;
} else if (!strcmp(arg, "-c")) {
revs->diff = 1;
revs->dense_combined_merges = 0;
@@ -2674,8 +2715,8 @@ static void NORETURN diagnose_missing_default(const char *def)
*/
int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
{
- int i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt;
- struct argv_array prune_data = ARGV_ARRAY_INIT;
+ int i, flags, left, seen_dashdash, revarg_opt;
+ struct strvec prune_data = STRVEC_INIT;
const char *submodule = NULL;
int seen_end_of_options = 0;
@@ -2694,7 +2735,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
argv[i] = NULL;
argc = i;
if (argv[i + 1])
- argv_array_pushv(&prune_data, argv + i + 1);
+ strvec_pushv(&prune_data, argv + i + 1);
seen_dashdash = 1;
break;
}
@@ -2760,14 +2801,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
for (j = i; j < argc; j++)
verify_filename(revs->prefix, argv[j], j == i);
- argv_array_pushv(&prune_data, argv + i);
+ strvec_pushv(&prune_data, argv + i);
break;
}
- else
- got_rev_arg = 1;
}
- if (prune_data.argc) {
+ if (prune_data.nr) {
/*
* If we need to introduce the magic "a lone ':' means no
* pathspec whatsoever", here is the place to do so.
@@ -2783,9 +2822,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
* }
*/
parse_pathspec(&revs->prune_data, 0, 0,
- revs->prefix, prune_data.argv);
+ revs->prefix, prune_data.v);
}
- argv_array_clear(&prune_data);
+ strvec_clear(&prune_data);
if (revs->def == NULL)
revs->def = opt ? opt->def : NULL;
@@ -2793,7 +2832,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
opt->tweak(revs, opt);
if (revs->show_merge)
prepare_show_merge(revs);
- if (revs->def && !revs->pending.nr && !revs->rev_input_given && !got_rev_arg) {
+ if (revs->def && !revs->pending.nr && !revs->rev_input_given) {
struct object_id oid;
struct object *object;
struct object_context oc;
@@ -2834,8 +2873,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
copy_pathspec(&revs->diffopt.pathspec,
&revs->prune_data);
}
- if (revs->combine_merges)
+ if (revs->combine_merges && revs->ignore_merges < 0)
revs->ignore_merges = 0;
+ if (revs->ignore_merges < 0)
+ revs->ignore_merges = 1;
if (revs->combined_all_paths && !revs->combine_merges)
die("--combined-all-paths makes no sense without -c or --cc");
@@ -2869,9 +2910,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
die("cannot use --grep-reflog without --walk-reflogs");
- if (revs->first_parent_only && revs->bisect)
- die(_("--first-parent is incompatible with --bisect"));
-
if (revs->line_level_traverse &&
(revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT)))
die(_("-L does not yet support diff formats besides -p and -s"));
@@ -3296,7 +3334,7 @@ static void explore_walk_step(struct rev_info *revs)
if (!c)
return;
- if (parse_commit_gently(c, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
return;
if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
@@ -3334,7 +3372,7 @@ static void indegree_walk_step(struct rev_info *revs)
if (!c)
return;
- if (parse_commit_gently(c, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
return;
explore_to_depth(revs, commit_graph_generation(c));
@@ -3416,7 +3454,7 @@ static void init_topo_walk(struct rev_info *revs)
struct commit *c = list->item;
uint32_t generation;
- if (parse_commit_gently(c, 1))
+ if (repo_parse_commit_gently(revs->repo, c, 1))
continue;
test_flag_and_insert(&info->explore_queue, c, TOPO_WALK_EXPLORED);
@@ -3480,7 +3518,7 @@ static void expand_topo_walk(struct rev_info *revs, struct commit *commit)
if (parent->object.flags & UNINTERESTING)
continue;
- if (parse_commit_gently(parent, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, parent, 1) < 0)
continue;
generation = commit_graph_generation(parent);
diff --git a/revision.h b/revision.h
index f412ae85eb..f6bf860d19 100644
--- a/revision.h
+++ b/revision.h
@@ -188,13 +188,14 @@ struct rev_info {
unsigned int diff:1,
full_diff:1,
show_root_diff:1,
+ match_missing:1,
no_commit_id:1,
verbose_header:1,
- ignore_merges:1,
combine_merges:1,
combined_all_paths:1,
dense_combined_merges:1,
always_show_header:1;
+ int ignore_merges:2;
/* Format info */
int show_notes;
@@ -301,8 +302,10 @@ struct rev_info {
struct topo_walk_info *topo_walk_info;
/* Commit graph bloom filter fields */
- /* The bloom filter key for the pathspec */
- struct bloom_key *bloom_key;
+ /* The bloom filter key(s) for the pathspec */
+ struct bloom_key *bloom_keys;
+ int bloom_keys_nr;
+
/*
* The bloom filter settings used to generate the key.
* This is loaded from the commit-graph being used.
diff --git a/run-command.c b/run-command.c
index a735e380a9..2ee59acdc8 100644
--- a/run-command.c
+++ b/run-command.c
@@ -2,7 +2,7 @@
#include "run-command.h"
#include "exec-cmd.h"
#include "sigchain.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "thread-utils.h"
#include "strbuf.h"
#include "string-list.h"
@@ -11,14 +11,14 @@
void child_process_init(struct child_process *child)
{
memset(child, 0, sizeof(*child));
- argv_array_init(&child->args);
- argv_array_init(&child->env_array);
+ strvec_init(&child->args);
+ strvec_init(&child->env_array);
}
void child_process_clear(struct child_process *child)
{
- argv_array_clear(&child->args);
- argv_array_clear(&child->env_array);
+ strvec_clear(&child->args);
+ strvec_clear(&child->env_array);
}
struct child_to_clean {
@@ -263,31 +263,31 @@ int sane_execvp(const char *file, char * const argv[])
return -1;
}
-static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
+static const char **prepare_shell_cmd(struct strvec *out, const char **argv)
{
if (!argv[0])
BUG("shell command is empty");
if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
#ifndef GIT_WINDOWS_NATIVE
- argv_array_push(out, SHELL_PATH);
+ strvec_push(out, SHELL_PATH);
#else
- argv_array_push(out, "sh");
+ strvec_push(out, "sh");
#endif
- argv_array_push(out, "-c");
+ strvec_push(out, "-c");
/*
* If we have no extra arguments, we do not even need to
* bother with the "$@" magic.
*/
if (!argv[1])
- argv_array_push(out, argv[0]);
+ strvec_push(out, argv[0]);
else
- argv_array_pushf(out, "%s \"$@\"", argv[0]);
+ strvec_pushf(out, "%s \"$@\"", argv[0]);
}
- argv_array_pushv(out, argv);
- return out->argv;
+ strvec_pushv(out, argv);
+ return out->v;
}
#ifndef GIT_WINDOWS_NATIVE
@@ -401,7 +401,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
set_error_routine(old_errfn);
}
-static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
+static int prepare_cmd(struct strvec *out, const struct child_process *cmd)
{
if (!cmd->argv[0])
BUG("command is empty");
@@ -410,14 +410,14 @@ static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
* Add SHELL_PATH so in the event exec fails with ENOEXEC we can
* attempt to interpret the command with 'sh'.
*/
- argv_array_push(out, SHELL_PATH);
+ strvec_push(out, SHELL_PATH);
if (cmd->git_cmd) {
prepare_git_cmd(out, cmd->argv);
} else if (cmd->use_shell) {
prepare_shell_cmd(out, cmd->argv);
} else {
- argv_array_pushv(out, cmd->argv);
+ strvec_pushv(out, cmd->argv);
}
/*
@@ -426,13 +426,13 @@ static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
* there are dir separator characters, we have exec attempt to invoke
* the command directly.
*/
- if (!has_dir_sep(out->argv[1])) {
- char *program = locate_in_PATH(out->argv[1]);
+ if (!has_dir_sep(out->v[1])) {
+ char *program = locate_in_PATH(out->v[1]);
if (program) {
- free((char *)out->argv[1]);
- out->argv[1] = program;
+ free((char *)out->v[1]);
+ out->v[1] = program;
} else {
- argv_array_clear(out);
+ strvec_clear(out);
errno = ENOENT;
return -1;
}
@@ -672,9 +672,9 @@ int start_command(struct child_process *cmd)
char *str;
if (!cmd->argv)
- cmd->argv = cmd->args.argv;
+ cmd->argv = cmd->args.v;
if (!cmd->env)
- cmd->env = cmd->env_array.argv;
+ cmd->env = cmd->env_array.v;
/*
* In case of errors we must keep the promise to close FDs
@@ -742,7 +742,7 @@ fail_pipe:
int notify_pipe[2];
int null_fd = -1;
char **childenv;
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
struct child_err cerr;
struct atfork_state as;
@@ -846,10 +846,10 @@ fail_pipe:
* be used in the event exec failed with ENOEXEC at which point
* we will try to interpret the command using 'sh'.
*/
- execve(argv.argv[1], (char *const *) argv.argv + 1,
+ execve(argv.v[1], (char *const *) argv.v + 1,
(char *const *) childenv);
if (errno == ENOEXEC)
- execve(argv.argv[0], (char *const *) argv.argv,
+ execve(argv.v[0], (char *const *) argv.v,
(char *const *) childenv);
if (errno == ENOENT) {
@@ -888,7 +888,7 @@ fail_pipe:
if (null_fd >= 0)
close(null_fd);
- argv_array_clear(&argv);
+ strvec_clear(&argv);
free(childenv);
}
end_of_spawn:
@@ -897,7 +897,7 @@ end_of_spawn:
{
int fhin = 0, fhout = 1, fherr = 2;
const char **sargv = cmd->argv;
- struct argv_array nargv = ARGV_ARRAY_INIT;
+ struct strvec nargv = STRVEC_INIT;
if (cmd->no_stdin)
fhin = open("/dev/null", O_RDWR);
@@ -935,7 +935,7 @@ end_of_spawn:
if (cmd->clean_on_exit && cmd->pid >= 0)
mark_child_for_cleanup(cmd->pid, cmd);
- argv_array_clear(&nargv);
+ strvec_clear(&nargv);
cmd->argv = sargv;
if (fhin != 0)
close(fhin);
@@ -1352,9 +1352,9 @@ int run_hook_ve(const char *const *env, const char *name, va_list args)
if (!p)
return 0;
- argv_array_push(&hook.args, p);
+ strvec_push(&hook.args, p);
while ((p = va_arg(args, const char *)))
- argv_array_push(&hook.args, p);
+ strvec_push(&hook.args, p);
hook.env = env;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
@@ -1866,15 +1866,13 @@ int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task,
return result;
}
-int run_auto_gc(int quiet)
+int run_auto_maintenance(int quiet)
{
- struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
- int status;
+ struct child_process maint = CHILD_PROCESS_INIT;
- argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
- if (quiet)
- argv_array_push(&argv_gc_auto, "--quiet");
- status = run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
- argv_array_clear(&argv_gc_auto);
- return status;
+ maint.git_cmd = 1;
+ strvec_pushl(&maint.args, "maintenance", "run", "--auto", NULL);
+ strvec_push(&maint.args, quiet ? "--quiet" : "--no-quiet");
+
+ return run_command(&maint);
}
diff --git a/run-command.h b/run-command.h
index ef3071a565..6472b38bde 100644
--- a/run-command.h
+++ b/run-command.h
@@ -3,7 +3,7 @@
#include "thread-utils.h"
-#include "argv-array.h"
+#include "strvec.h"
/**
* The run-command API offers a versatile tool to run sub-processes with
@@ -52,15 +52,15 @@ struct child_process {
* Note that the ownership of the memory pointed to by .argv stays with the
* caller, but it should survive until `finish_command` completes. If the
* .argv member is NULL, `start_command` will point it at the .args
- * `argv_array` (so you may use one or the other, but you must use exactly
+ * `strvec` (so you may use one or the other, but you must use exactly
* one). The memory in .args will be cleaned up automatically during
* `finish_command` (or during `start_command` when it is unsuccessful).
*
*/
const char **argv;
- struct argv_array args;
- struct argv_array env_array;
+ struct strvec args;
+ struct strvec env_array;
pid_t pid;
int trace2_child_id;
@@ -107,7 +107,7 @@ struct child_process {
* variable that will be removed from the child process's environment.
*
* If the .env member is NULL, `start_command` will point it at the
- * .env_array `argv_array` (so you may use one or the other, but not both).
+ * .env_array `strvec` (so you may use one or the other, but not both).
* The memory in .env_array will be cleaned up automatically during
* `finish_command` (or during `start_command` when it is unsuccessful).
*/
@@ -134,7 +134,7 @@ struct child_process {
void *clean_on_exit_handler_cbdata;
};
-#define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
+#define CHILD_PROCESS_INIT { NULL, STRVEC_INIT, STRVEC_INIT }
/**
* The functions: child_process_init, start_command, finish_command,
@@ -221,7 +221,7 @@ int run_hook_ve(const char *const *env, const char *name, va_list args);
/*
* Trigger an auto-gc
*/
-int run_auto_gc(int quiet);
+int run_auto_maintenance(int quiet);
#define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
diff --git a/send-pack.c b/send-pack.c
index d671ab5d05..c9698070fc 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -68,20 +68,20 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
int i;
int rc;
- argv_array_push(&po.args, "pack-objects");
- argv_array_push(&po.args, "--all-progress-implied");
- argv_array_push(&po.args, "--revs");
- argv_array_push(&po.args, "--stdout");
+ strvec_push(&po.args, "pack-objects");
+ strvec_push(&po.args, "--all-progress-implied");
+ strvec_push(&po.args, "--revs");
+ strvec_push(&po.args, "--stdout");
if (args->use_thin_pack)
- argv_array_push(&po.args, "--thin");
+ strvec_push(&po.args, "--thin");
if (args->use_ofs_delta)
- argv_array_push(&po.args, "--delta-base-offset");
+ strvec_push(&po.args, "--delta-base-offset");
if (args->quiet || !args->progress)
- argv_array_push(&po.args, "-q");
+ strvec_push(&po.args, "-q");
if (args->progress)
- argv_array_push(&po.args, "--progress");
+ strvec_push(&po.args, "--progress");
if (is_repository_shallow(the_repository))
- argv_array_push(&po.args, "--shallow");
+ strvec_push(&po.args, "--shallow");
po.in = -1;
po.out = args->stateless_rpc ? -1 : fd;
po.git_cmd = 1;
@@ -154,25 +154,79 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
{
struct ref *hint;
int ret;
+ struct ref_push_report *report = NULL;
+ int new_report = 0;
+ int once = 0;
hint = NULL;
ret = receive_unpack_status(reader);
while (1) {
+ struct object_id old_oid, new_oid;
+ const char *head;
const char *refname;
- char *msg;
+ char *p;
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
break;
- if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) {
- error("invalid ref status from remote: %s", reader->line);
+ head = reader->line;
+ p = strchr(head, ' ');
+ if (!p) {
+ error("invalid status line from remote: %s", reader->line);
ret = -1;
break;
}
+ *p++ = '\0';
- refname = reader->line + 3;
- msg = strchr(refname, ' ');
- if (msg)
- *msg++ = '\0';
+ if (!strcmp(head, "option")) {
+ const char *key, *val;
+ if (!hint || !(report || new_report)) {
+ if (!once++)
+ error("'option' without a matching 'ok/ng' directive");
+ ret = -1;
+ continue;
+ }
+ if (new_report) {
+ if (!hint->report) {
+ hint->report = xcalloc(1, sizeof(struct ref_push_report));
+ report = hint->report;
+ } else {
+ report = hint->report;
+ while (report->next)
+ report = report->next;
+ report->next = xcalloc(1, sizeof(struct ref_push_report));
+ report = report->next;
+ }
+ new_report = 0;
+ }
+ key = p;
+ p = strchr(key, ' ');
+ if (p)
+ *p++ = '\0';
+ val = p;
+ if (!strcmp(key, "refname"))
+ report->ref_name = xstrdup_or_null(val);
+ else if (!strcmp(key, "old-oid") && val &&
+ !parse_oid_hex(val, &old_oid, &val))
+ report->old_oid = oiddup(&old_oid);
+ else if (!strcmp(key, "new-oid") && val &&
+ !parse_oid_hex(val, &new_oid, &val))
+ report->new_oid = oiddup(&new_oid);
+ else if (!strcmp(key, "forced-update"))
+ report->forced_update = 1;
+ continue;
+ }
+
+ report = NULL;
+ new_report = 0;
+ if (strcmp(head, "ok") && strcmp(head, "ng")) {
+ error("invalid ref status from remote: %s", head);
+ ret = -1;
+ break;
+ }
+ refname = p;
+ p = strchr(refname, ' ');
+ if (p)
+ *p++ = '\0';
/* first try searching at our hint, falling back to all refs */
if (hint)
hint = find_ref_by_name(hint, refname);
@@ -180,22 +234,27 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
hint = find_ref_by_name(refs, refname);
if (!hint) {
warning("remote reported status on unknown ref: %s",
- refname);
+ refname);
continue;
}
- if (hint->status != REF_STATUS_EXPECTING_REPORT) {
+ if (hint->status != REF_STATUS_EXPECTING_REPORT &&
+ hint->status != REF_STATUS_OK &&
+ hint->status != REF_STATUS_REMOTE_REJECT) {
warning("remote reported status on unexpected ref: %s",
- refname);
+ refname);
continue;
}
-
- if (reader->line[0] == 'o' && reader->line[1] == 'k')
- hint->status = REF_STATUS_OK;
- else
+ if (!strcmp(head, "ng")) {
hint->status = REF_STATUS_REMOTE_REJECT;
- hint->remote_status = xstrdup_or_null(msg);
- /* start our next search from the next ref */
- hint = hint->next;
+ if (p)
+ hint->remote_status = xstrdup(p);
+ else
+ hint->remote_status = "failed";
+ } else {
+ hint->status = REF_STATUS_OK;
+ hint->remote_status = xstrdup_or_null(p);
+ new_report = 1;
+ }
}
return ret;
}
@@ -244,7 +303,12 @@ static int check_to_send_update(const struct ref *ref, const struct send_pack_ar
return CHECK_REF_STATUS_REJECTED;
case REF_STATUS_UPTODATE:
return CHECK_REF_UPTODATE;
+
default:
+ case REF_STATUS_EXPECTING_REPORT:
+ /* already passed checks on the local side */
+ case REF_STATUS_OK:
+ /* of course this is OK */
return 0;
}
}
@@ -371,7 +435,9 @@ int send_pack(struct send_pack_args *args,
struct packet_reader reader;
/* Does the other end support the reporting? */
- if (server_supports("report-status"))
+ if (server_supports("report-status-v2"))
+ status_report = 2;
+ else if (server_supports("report-status"))
status_report = 1;
if (server_supports("delete-refs"))
allow_deleting_refs = 1;
@@ -423,8 +489,10 @@ int send_pack(struct send_pack_args *args,
use_push_options = push_options_supported && args->push_options;
- if (status_report)
+ if (status_report == 1)
strbuf_addstr(&cap_buf, " report-status");
+ else if (status_report == 2)
+ strbuf_addstr(&cap_buf, " report-status-v2");
if (use_sideband)
strbuf_addstr(&cap_buf, " side-band-64k");
if (quiet_supported && (args->quiet || !args->progress))
@@ -447,13 +515,6 @@ int send_pack(struct send_pack_args *args,
if (ref->deletion && !allow_deleting_refs)
ref->status = REF_STATUS_REJECT_NODELETE;
- if (!args->dry_run)
- advertise_shallow_grafts_buf(&req_buf);
-
- if (!args->dry_run && push_cert_nonce)
- cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
- cap_buf.buf, push_cert_nonce);
-
/*
* Clear the status for each ref and see if we need to send
* the pack data.
@@ -489,31 +550,35 @@ int send_pack(struct send_pack_args *args,
ref->status = REF_STATUS_EXPECTING_REPORT;
}
+ if (!args->dry_run)
+ advertise_shallow_grafts_buf(&req_buf);
+
/*
* Finally, tell the other end!
*/
- for (ref = remote_refs; ref; ref = ref->next) {
- char *old_hex, *new_hex;
-
- if (args->dry_run || push_cert_nonce)
- continue;
-
- if (check_to_send_update(ref, args) < 0)
- continue;
-
- old_hex = oid_to_hex(&ref->old_oid);
- new_hex = oid_to_hex(&ref->new_oid);
- if (!cmds_sent) {
- packet_buf_write(&req_buf,
- "%s %s %s%c%s",
- old_hex, new_hex, ref->name, 0,
- cap_buf.buf);
- cmds_sent = 1;
- } else {
- packet_buf_write(&req_buf, "%s %s %s",
- old_hex, new_hex, ref->name);
+ if (!args->dry_run && push_cert_nonce)
+ cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
+ cap_buf.buf, push_cert_nonce);
+ else if (!args->dry_run)
+ for (ref = remote_refs; ref; ref = ref->next) {
+ char *old_hex, *new_hex;
+
+ if (check_to_send_update(ref, args) < 0)
+ continue;
+
+ old_hex = oid_to_hex(&ref->old_oid);
+ new_hex = oid_to_hex(&ref->new_oid);
+ if (!cmds_sent) {
+ packet_buf_write(&req_buf,
+ "%s %s %s%c%s",
+ old_hex, new_hex, ref->name, 0,
+ cap_buf.buf);
+ cmds_sent = 1;
+ } else {
+ packet_buf_write(&req_buf, "%s %s %s",
+ old_hex, new_hex, ref->name);
+ }
}
- }
if (use_push_options) {
struct string_list_item *item;
diff --git a/sequencer.c b/sequencer.c
index fd7701c88a..e8676e965f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -16,7 +16,7 @@
#include "rerere.h"
#include "merge-recursive.h"
#include "refs.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "quote.h"
#include "trailer.h"
#include "log-tree.h"
@@ -150,6 +150,8 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
* command-line.
*/
static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
+static GIT_PATH_FUNC(rebase_path_cdate_is_adate, "rebase-merge/cdate_is_adate")
+static GIT_PATH_FUNC(rebase_path_ignore_date, "rebase-merge/ignore_date")
static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
@@ -303,6 +305,8 @@ int sequencer_remove_state(struct replay_opts *opts)
}
}
+ free(opts->committer_name);
+ free(opts->committer_email);
free(opts->gpg_sign);
free(opts->strategy);
for (i = 0; i < opts->xopts_nr; i++)
@@ -355,7 +359,7 @@ static int get_message(struct commit *commit, struct commit_message *out)
subject_len = find_commit_subject(out->message, &subject);
out->subject = xmemdupz(subject, subject_len);
- out->label = xstrfmt("%s... %s", abbrev, out->subject);
+ out->label = xstrfmt("%s (%s)", abbrev, out->subject);
out->parent_label = xstrfmt("parent of %s", out->label);
return 0;
@@ -381,7 +385,8 @@ static void print_advice(struct repository *r, int show_hint,
* (typically rebase --interactive) wants to take care
* of the commit itself so remove CHERRY_PICK_HEAD
*/
- unlink(git_path_cherry_pick_head(r));
+ refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
+ NULL, 0);
return;
}
@@ -830,10 +835,10 @@ finish:
/*
* Read a GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL AND GIT_AUTHOR_DATE from a
- * file with shell quoting into struct argv_array. Returns -1 on
+ * file with shell quoting into struct strvec. Returns -1 on
* error, 0 otherwise.
*/
-static int read_env_script(struct argv_array *env)
+static int read_env_script(struct strvec *env)
{
char *name, *email, *date;
@@ -841,9 +846,9 @@ static int read_env_script(struct argv_array *env)
&name, &email, &date, 0))
return -1;
- argv_array_pushf(env, "GIT_AUTHOR_NAME=%s", name);
- argv_array_pushf(env, "GIT_AUTHOR_EMAIL=%s", email);
- argv_array_pushf(env, "GIT_AUTHOR_DATE=%s", date);
+ strvec_pushf(env, "GIT_AUTHOR_NAME=%s", name);
+ strvec_pushf(env, "GIT_AUTHOR_EMAIL=%s", email);
+ strvec_pushf(env, "GIT_AUTHOR_DATE=%s", date);
free(name);
free(email);
free(date);
@@ -863,6 +868,22 @@ static char *get_author(const char *message)
return NULL;
}
+static const char *author_date_from_env_array(const struct strvec *env)
+{
+ int i;
+ const char *date;
+
+ for (i = 0; i < env->nr; i++)
+ if (skip_prefix(env->v[i],
+ "GIT_AUTHOR_DATE=", &date))
+ return date;
+ /*
+ * If GIT_AUTHOR_DATE is missing we should have already errored out when
+ * reading the script
+ */
+ BUG("GIT_AUTHOR_DATE missing from author script");
+}
+
static const char staged_changes_advice[] =
N_("you have staged changes in your working tree\n"
"If these changes are meant to be squashed into the previous commit, run:\n"
@@ -929,34 +950,42 @@ static int run_git_commit(struct repository *r,
gpg_opt, gpg_opt);
}
- argv_array_push(&cmd.args, "commit");
+ if (opts->committer_date_is_author_date)
+ strvec_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s",
+ opts->ignore_date ?
+ "" :
+ author_date_from_env_array(&cmd.env_array));
+ if (opts->ignore_date)
+ strvec_push(&cmd.env_array, "GIT_AUTHOR_DATE=");
+
+ strvec_push(&cmd.args, "commit");
if (!(flags & VERIFY_MSG))
- argv_array_push(&cmd.args, "-n");
+ strvec_push(&cmd.args, "-n");
if ((flags & AMEND_MSG))
- argv_array_push(&cmd.args, "--amend");
+ strvec_push(&cmd.args, "--amend");
if (opts->gpg_sign)
- argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
+ strvec_pushf(&cmd.args, "-S%s", opts->gpg_sign);
else
- argv_array_push(&cmd.args, "--no-gpg-sign");
+ strvec_push(&cmd.args, "--no-gpg-sign");
if (defmsg)
- argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
+ strvec_pushl(&cmd.args, "-F", defmsg, NULL);
else if (!(flags & EDIT_MSG))
- argv_array_pushl(&cmd.args, "-C", "HEAD", NULL);
+ strvec_pushl(&cmd.args, "-C", "HEAD", NULL);
if ((flags & CLEANUP_MSG))
- argv_array_push(&cmd.args, "--cleanup=strip");
+ strvec_push(&cmd.args, "--cleanup=strip");
if ((flags & EDIT_MSG))
- argv_array_push(&cmd.args, "-e");
+ strvec_push(&cmd.args, "-e");
else if (!(flags & CLEANUP_MSG) &&
!opts->signoff && !opts->record_origin &&
!opts->explicit_cleanup)
- argv_array_push(&cmd.args, "--cleanup=verbatim");
+ strvec_push(&cmd.args, "--cleanup=verbatim");
if ((flags & ALLOW_EMPTY))
- argv_array_push(&cmd.args, "--allow-empty");
+ strvec_push(&cmd.args, "--allow-empty");
if (!(flags & EDIT_MSG))
- argv_array_push(&cmd.args, "--allow-empty-message");
+ strvec_push(&cmd.args, "--allow-empty-message");
if (is_rebase_i(opts) && !(flags & EDIT_MSG))
return run_command_silent_on_success(&cmd);
@@ -1308,6 +1337,7 @@ static int try_to_commit(struct repository *r,
struct strbuf err = STRBUF_INIT;
struct strbuf commit_msg = STRBUF_INIT;
char *amend_author = NULL;
+ const char *committer = NULL;
const char *hook_commit = NULL;
enum commit_msg_cleanup_mode cleanup;
int res = 0;
@@ -1399,10 +1429,57 @@ static int try_to_commit(struct repository *r,
goto out;
}
- reset_ident_date();
+ if (opts->committer_date_is_author_date) {
+ struct ident_split id;
+ struct strbuf date = STRBUF_INIT;
+
+ if (!opts->ignore_date) {
+ if (split_ident_line(&id, author, (int)strlen(author)) < 0) {
+ res = error(_("invalid author identity '%s'"),
+ author);
+ goto out;
+ }
+ if (!id.date_begin) {
+ res = error(_(
+ "corrupt author: missing date information"));
+ goto out;
+ }
+ strbuf_addf(&date, "@%.*s %.*s",
+ (int)(id.date_end - id.date_begin),
+ id.date_begin,
+ (int)(id.tz_end - id.tz_begin),
+ id.tz_begin);
+ } else {
+ reset_ident_date();
+ }
+ committer = fmt_ident(opts->committer_name,
+ opts->committer_email,
+ WANT_COMMITTER_IDENT,
+ opts->ignore_date ? NULL : date.buf,
+ IDENT_STRICT);
+ strbuf_release(&date);
+ } else {
+ reset_ident_date();
+ }
+
+ if (opts->ignore_date) {
+ struct ident_split id;
+ char *name, *email;
+
+ if (split_ident_line(&id, author, strlen(author)) < 0) {
+ error(_("invalid author identity '%s'"), author);
+ goto out;
+ }
+ name = xmemdupz(id.name_begin, id.name_end - id.name_begin);
+ email = xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
+ author = fmt_ident(name, email, WANT_AUTHOR_IDENT, NULL,
+ IDENT_STRICT);
+ free(name);
+ free(email);
+ }
- if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
- oid, author, opts->gpg_sign, extra)) {
+ if (commit_tree_extended(msg->buf, msg->len, &tree, parents, oid,
+ author, committer, opts->gpg_sign, extra)) {
res = error(_("failed to write commit object"));
goto out;
}
@@ -1455,7 +1532,8 @@ static int do_commit(struct repository *r,
author, opts, flags, &oid);
strbuf_release(&sb);
if (!res) {
- unlink(git_path_cherry_pick_head(r));
+ refs_delete_ref(get_main_ref_store(r), "",
+ "CHERRY_PICK_HEAD", NULL, 0);
unlink(git_path_merge_msg(r));
if (!is_rebase_i(opts))
print_commit_summary(r, NULL, &oid,
@@ -1966,7 +2044,8 @@ static int do_pick_commit(struct repository *r,
flags |= ALLOW_EMPTY;
} else if (allow == 2) {
drop_commit = 1;
- unlink(git_path_cherry_pick_head(r));
+ refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
+ NULL, 0);
unlink(git_path_merge_msg(r));
fprintf(stderr,
_("dropping %s %s -- patch contents already upstream\n"),
@@ -2305,15 +2384,19 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
struct replay_opts opts = REPLAY_OPTS_INIT;
int need_cleanup = 0;
- if (file_exists(git_path_cherry_pick_head(r))) {
- if (!unlink(git_path_cherry_pick_head(r)) && verbose)
+ if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
+ if (!refs_delete_ref(get_main_ref_store(r), "",
+ "CHERRY_PICK_HEAD", NULL, 0) &&
+ verbose)
warning(_("cancelling a cherry picking in progress"));
opts.action = REPLAY_PICK;
need_cleanup = 1;
}
- if (file_exists(git_path_revert_head(r))) {
- if (!unlink(git_path_revert_head(r)) && verbose)
+ if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
+ if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
+ NULL, 0) &&
+ verbose)
warning(_("cancelling a revert in progress"));
opts.action = REPLAY_REVERT;
need_cleanup = 1;
@@ -2528,6 +2611,16 @@ static int read_populate_opts(struct replay_opts *opts)
opts->signoff = 1;
}
+ if (file_exists(rebase_path_cdate_is_adate())) {
+ opts->allow_ff = 0;
+ opts->committer_date_is_author_date = 1;
+ }
+
+ if (file_exists(rebase_path_ignore_date())) {
+ opts->allow_ff = 0;
+ opts->ignore_date = 1;
+ }
+
if (file_exists(rebase_path_reschedule_failed_exec()))
opts->reschedule_failed_exec = 1;
@@ -2623,6 +2716,10 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
write_file(rebase_path_drop_redundant_commits(), "%s", "");
if (opts->keep_redundant_commits)
write_file(rebase_path_keep_redundant_commits(), "%s", "");
+ if (opts->committer_date_is_author_date)
+ write_file(rebase_path_cdate_is_adate(), "%s", "");
+ if (opts->ignore_date)
+ write_file(rebase_path_ignore_date(), "%s", "");
if (opts->reschedule_failed_exec)
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
@@ -2671,8 +2768,9 @@ static int create_seq_dir(struct repository *r)
enum replay_action action;
const char *in_progress_error = NULL;
const char *in_progress_advice = NULL;
- unsigned int advise_skip = file_exists(git_path_revert_head(r)) ||
- file_exists(git_path_cherry_pick_head(r));
+ unsigned int advise_skip =
+ refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") ||
+ refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD");
if (!sequencer_get_last_command(r, &action)) {
switch (action) {
@@ -2754,15 +2852,15 @@ static int rollback_is_safe(void)
static int reset_merge(const struct object_id *oid)
{
int ret;
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
- argv_array_pushl(&argv, "reset", "--merge", NULL);
+ strvec_pushl(&argv, "reset", "--merge", NULL);
if (!is_null_oid(oid))
- argv_array_push(&argv, oid_to_hex(oid));
+ strvec_push(&argv, oid_to_hex(oid));
- ret = run_command_v_opt(argv.argv, RUN_GIT_CMD);
- argv_array_clear(&argv);
+ ret = run_command_v_opt(argv.v, RUN_GIT_CMD);
+ strvec_clear(&argv);
return ret;
}
@@ -2771,8 +2869,8 @@ static int rollback_single_pick(struct repository *r)
{
struct object_id head_oid;
- if (!file_exists(git_path_cherry_pick_head(r)) &&
- !file_exists(git_path_revert_head(r)))
+ if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
+ !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
return error(_("no cherry-pick or revert in progress"));
if (read_ref_full("HEAD", 0, &head_oid, NULL))
return error(_("cannot resolve HEAD"));
@@ -2866,7 +2964,7 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts)
*/
switch (opts->action) {
case REPLAY_REVERT:
- if (!file_exists(git_path_revert_head(r))) {
+ if (!refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
if (action != REPLAY_REVERT)
return error(_("no revert in progress"));
if (!rollback_is_safe())
@@ -2874,7 +2972,8 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts)
}
break;
case REPLAY_PICK:
- if (!file_exists(git_path_cherry_pick_head(r))) {
+ if (!refs_ref_exists(get_main_ref_store(r),
+ "CHERRY_PICK_HEAD")) {
if (action != REPLAY_PICK)
return error(_("no cherry-pick in progress"));
if (!rollback_is_safe())
@@ -3125,17 +3224,17 @@ static int error_failed_squash(struct repository *r,
static int do_exec(struct repository *r, const char *command_line)
{
- struct argv_array child_env = ARGV_ARRAY_INIT;
+ struct strvec child_env = STRVEC_INIT;
const char *child_argv[] = { NULL, NULL };
int dirty, status;
fprintf(stderr, _("Executing: %s\n"), command_line);
child_argv[0] = command_line;
- argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
- argv_array_pushf(&child_env, "GIT_WORK_TREE=%s",
- absolute_path(get_git_work_tree()));
+ strvec_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+ strvec_pushf(&child_env, "GIT_WORK_TREE=%s",
+ absolute_path(get_git_work_tree()));
status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
- child_env.argv);
+ child_env.v);
/* force re-reading of the cache */
if (discard_index(r->index) < 0 || repo_read_index(r) < 0)
@@ -3165,7 +3264,7 @@ static int do_exec(struct repository *r, const char *command_line)
status = 1;
}
- argv_array_clear(&child_env);
+ strvec_clear(&child_env);
return status;
}
@@ -3543,33 +3642,42 @@ static int do_merge(struct repository *r,
goto leave_merge;
}
+ if (opts->committer_date_is_author_date)
+ strvec_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s",
+ opts->ignore_date ?
+ "" :
+ author_date_from_env_array(&cmd.env_array));
+ if (opts->ignore_date)
+ strvec_push(&cmd.env_array, "GIT_AUTHOR_DATE=");
+
cmd.git_cmd = 1;
- argv_array_push(&cmd.args, "merge");
- argv_array_push(&cmd.args, "-s");
+ strvec_push(&cmd.args, "merge");
+ strvec_push(&cmd.args, "-s");
if (!strategy)
- argv_array_push(&cmd.args, "octopus");
+ strvec_push(&cmd.args, "octopus");
else {
- argv_array_push(&cmd.args, strategy);
+ strvec_push(&cmd.args, strategy);
for (k = 0; k < opts->xopts_nr; k++)
- argv_array_pushf(&cmd.args,
- "-X%s", opts->xopts[k]);
+ strvec_pushf(&cmd.args,
+ "-X%s", opts->xopts[k]);
}
- argv_array_push(&cmd.args, "--no-edit");
- argv_array_push(&cmd.args, "--no-ff");
- argv_array_push(&cmd.args, "--no-log");
- argv_array_push(&cmd.args, "--no-stat");
- argv_array_push(&cmd.args, "-F");
- argv_array_push(&cmd.args, git_path_merge_msg(r));
+ strvec_push(&cmd.args, "--no-edit");
+ strvec_push(&cmd.args, "--no-ff");
+ strvec_push(&cmd.args, "--no-log");
+ strvec_push(&cmd.args, "--no-stat");
+ strvec_push(&cmd.args, "-F");
+ strvec_push(&cmd.args, git_path_merge_msg(r));
if (opts->gpg_sign)
- argv_array_push(&cmd.args, opts->gpg_sign);
+ strvec_push(&cmd.args, opts->gpg_sign);
/* Add the tips to be merged */
for (j = to_merge; j; j = j->next)
- argv_array_push(&cmd.args,
- oid_to_hex(&j->item->object.oid));
+ strvec_push(&cmd.args,
+ oid_to_hex(&j->item->object.oid));
strbuf_release(&ref_name);
- unlink(git_path_cherry_pick_head(r));
+ refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
+ NULL, 0);
rollback_lock_file(&lock);
rollback_lock_file(&lock);
@@ -3694,8 +3802,8 @@ void create_autostash(struct repository *r, const char *path,
struct child_process stash = CHILD_PROCESS_INIT;
struct object_id oid;
- argv_array_pushl(&stash.args,
- "stash", "create", "autostash", NULL);
+ strvec_pushl(&stash.args,
+ "stash", "create", "autostash", NULL);
stash.git_cmd = 1;
stash.no_stdin = 1;
strbuf_reset(&buf);
@@ -3734,9 +3842,9 @@ static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
child.git_cmd = 1;
child.no_stdout = 1;
child.no_stderr = 1;
- argv_array_push(&child.args, "stash");
- argv_array_push(&child.args, "apply");
- argv_array_push(&child.args, stash_oid);
+ strvec_push(&child.args, "stash");
+ strvec_push(&child.args, "apply");
+ strvec_push(&child.args, stash_oid);
ret = run_command(&child);
}
@@ -3746,12 +3854,12 @@ static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
struct child_process store = CHILD_PROCESS_INIT;
store.git_cmd = 1;
- argv_array_push(&store.args, "stash");
- argv_array_push(&store.args, "store");
- argv_array_push(&store.args, "-m");
- argv_array_push(&store.args, "autostash");
- argv_array_push(&store.args, "-q");
- argv_array_push(&store.args, stash_oid);
+ strvec_push(&store.args, "stash");
+ strvec_push(&store.args, "store");
+ strvec_push(&store.args, "-m");
+ strvec_push(&store.args, "autostash");
+ strvec_push(&store.args, "-q");
+ strvec_push(&store.args, stash_oid);
if (run_command(&store))
ret = error(_("cannot store %s"), stash_oid);
else
@@ -3831,9 +3939,9 @@ static int run_git_checkout(struct repository *r, struct replay_opts *opts,
cmd.git_cmd = 1;
- argv_array_push(&cmd.args, "checkout");
- argv_array_push(&cmd.args, commit);
- argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
+ strvec_push(&cmd.args, "checkout");
+ strvec_push(&cmd.args, commit);
+ strvec_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
if (opts->verbose)
ret = run_command(&cmd);
@@ -3906,7 +4014,9 @@ static int pick_commits(struct repository *r,
prev_reflog_action = xstrdup(getenv(GIT_REFLOG_ACTION));
if (opts->allow_ff)
assert(!(opts->signoff || opts->no_commit ||
- opts->record_origin || opts->edit));
+ opts->record_origin || opts->edit ||
+ opts->committer_date_is_author_date ||
+ opts->ignore_date));
if (read_and_refresh_cache(r, opts))
return -1;
@@ -4157,9 +4267,9 @@ cleanup_head_ref:
child.in = open(rebase_path_rewritten_list(), O_RDONLY);
child.git_cmd = 1;
- argv_array_push(&child.args, "notes");
- argv_array_push(&child.args, "copy");
- argv_array_push(&child.args, "--for-rewrite=rebase");
+ strvec_push(&child.args, "notes");
+ strvec_push(&child.args, "copy");
+ strvec_push(&child.args, "--for-rewrite=rebase");
/* we don't care if this copying failed */
run_command(&child);
@@ -4170,8 +4280,8 @@ cleanup_head_ref:
O_RDONLY);
hook.stdout_to_stderr = 1;
hook.trace2_hook_name = "post-rewrite";
- argv_array_push(&hook.args, post_rewrite_hook);
- argv_array_push(&hook.args, "rebase");
+ strvec_push(&hook.args, post_rewrite_hook);
+ strvec_push(&hook.args, "rebase");
/* we don't care if this hook failed */
run_command(&hook);
}
@@ -4201,8 +4311,8 @@ static int continue_single_pick(struct repository *r)
{
const char *argv[] = { "commit", NULL };
- if (!file_exists(git_path_cherry_pick_head(r)) &&
- !file_exists(git_path_revert_head(r)))
+ if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
+ !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
return error(_("no cherry-pick or revert in progress"));
return run_command_v_opt(argv, RUN_GIT_CMD);
}
@@ -4318,9 +4428,10 @@ static int commit_staged_changes(struct repository *r,
}
if (is_clean) {
- const char *cherry_pick_head = git_path_cherry_pick_head(r);
-
- if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
+ if (refs_ref_exists(get_main_ref_store(r),
+ "CHERRY_PICK_HEAD") &&
+ refs_delete_ref(get_main_ref_store(r), "",
+ "CHERRY_PICK_HEAD", NULL, 0))
return error(_("could not remove CHERRY_PICK_HEAD"));
if (!final_fixup)
return 0;
@@ -4347,6 +4458,22 @@ static int commit_staged_changes(struct repository *r,
return 0;
}
+static int init_committer(struct replay_opts *opts)
+{
+ struct ident_split id;
+ const char *committer;
+
+ committer = git_committer_info(IDENT_STRICT);
+ if (split_ident_line(&id, committer, strlen(committer)) < 0)
+ return error(_("invalid committer '%s'"), committer);
+ opts->committer_name =
+ xmemdupz(id.name_begin, id.name_end - id.name_begin);
+ opts->committer_email =
+ xmemdupz(id.mail_begin, id.mail_end - id.mail_end);
+
+ return 0;
+}
+
int sequencer_continue(struct repository *r, struct replay_opts *opts)
{
struct todo_list todo_list = TODO_LIST_INIT;
@@ -4358,6 +4485,9 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
if (read_populate_opts(opts))
return -1;
if (is_rebase_i(opts)) {
+ if (opts->committer_date_is_author_date && init_committer(opts))
+ return -1;
+
if ((res = read_populate_todo(r, &todo_list, opts)))
goto release_todo_list;
@@ -4379,8 +4509,9 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
if (!is_rebase_i(opts)) {
/* Verify that the conflict has been resolved */
- if (file_exists(git_path_cherry_pick_head(r)) ||
- file_exists(git_path_revert_head(r))) {
+ if (refs_ref_exists(get_main_ref_store(r),
+ "CHERRY_PICK_HEAD") ||
+ refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
res = continue_single_pick(r);
if (res)
goto release_todo_list;
@@ -5178,13 +5309,14 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
struct string_list *commands, unsigned autosquash,
struct todo_list *todo_list)
{
- const char *shortonto, *todo_file = rebase_path_todo();
+ char shortonto[GIT_MAX_HEXSZ + 1];
+ const char *todo_file = rebase_path_todo();
struct todo_list new_todo = TODO_LIST_INIT;
struct strbuf *buf = &todo_list->buf, buf2 = STRBUF_INIT;
struct object_id oid = onto->object.oid;
int res;
- shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
+ find_unique_abbrev_r(shortonto, &oid, DEFAULT_ABBREV);
if (buf->len == 0) {
struct todo_item *item = append_new_todo(todo_list);
@@ -5250,6 +5382,9 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
res = -1;
+ if (opts->committer_date_is_author_date && init_committer(opts))
+ goto cleanup;
+
if (checkout_onto(r, opts, onto_name, &oid, orig_head))
goto cleanup;
@@ -5442,7 +5577,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
int sequencer_determine_whence(struct repository *r, enum commit_whence *whence)
{
- if (file_exists(git_path_cherry_pick_head(r))) {
+ if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
struct object_id cherry_pick_head, rebase_head;
if (file_exists(git_path_seq_dir()))
diff --git a/sequencer.h b/sequencer.h
index d31c41f018..b2a501e445 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -45,9 +45,13 @@ struct replay_opts {
int verbose;
int quiet;
int reschedule_failed_exec;
+ int committer_date_is_author_date;
+ int ignore_date;
int mainline;
+ char *committer_name;
+ char *committer_email;
char *gpg_sign;
enum commit_msg_cleanup_mode default_msg_cleanup;
int explicit_cleanup;
diff --git a/serve.c b/serve.c
index fbd2fcdfb5..f6341206c4 100644
--- a/serve.c
+++ b/serve.c
@@ -3,7 +3,7 @@
#include "config.h"
#include "pkt-line.h"
#include "version.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "ls-refs.h"
#include "serve.h"
#include "upload-pack.h"
@@ -56,7 +56,7 @@ struct protocol_capability {
* This field should be NULL for capabilities which are not commands.
*/
int (*command)(struct repository *r,
- struct argv_array *keys,
+ struct strvec *keys,
struct packet_reader *request);
};
@@ -142,13 +142,13 @@ static int is_command(const char *key, struct protocol_capability **command)
return 0;
}
-int has_capability(const struct argv_array *keys, const char *capability,
+int has_capability(const struct strvec *keys, const char *capability,
const char **value)
{
int i;
- for (i = 0; i < keys->argc; i++) {
+ for (i = 0; i < keys->nr; i++) {
const char *out;
- if (skip_prefix(keys->argv[i], capability, &out) &&
+ if (skip_prefix(keys->v[i], capability, &out) &&
(!*out || *out == '=')) {
if (value) {
if (*out == '=')
@@ -162,7 +162,7 @@ int has_capability(const struct argv_array *keys, const char *capability,
return 0;
}
-static void check_algorithm(struct repository *r, struct argv_array *keys)
+static void check_algorithm(struct repository *r, struct strvec *keys)
{
int client = GIT_HASH_SHA1, server = hash_algo_by_ptr(r->hash_algo);
const char *algo_name;
@@ -187,7 +187,7 @@ static int process_request(void)
{
enum request_state state = PROCESS_REQUEST_KEYS;
struct packet_reader reader;
- struct argv_array keys = ARGV_ARRAY_INIT;
+ struct strvec keys = STRVEC_INIT;
struct protocol_capability *command = NULL;
packet_reader_init(&reader, 0, NULL, 0,
@@ -211,7 +211,7 @@ static int process_request(void)
/* collect request; a sequence of keys and values */
if (is_command(reader.line, &command) ||
is_valid_capability(reader.line))
- argv_array_push(&keys, reader.line);
+ strvec_push(&keys, reader.line);
else
die("unknown capability '%s'", reader.line);
@@ -223,7 +223,7 @@ static int process_request(void)
* If no command and no keys were given then the client
* wanted to terminate the connection.
*/
- if (!keys.argc)
+ if (!keys.nr)
return 1;
/*
@@ -254,7 +254,7 @@ static int process_request(void)
command->command(the_repository, &keys, &reader);
- argv_array_clear(&keys);
+ strvec_clear(&keys);
return 0;
}
diff --git a/serve.h b/serve.h
index 42ddca7f8b..fc2683e24d 100644
--- a/serve.h
+++ b/serve.h
@@ -1,8 +1,8 @@
#ifndef SERVE_H
#define SERVE_H
-struct argv_array;
-int has_capability(const struct argv_array *keys, const char *capability,
+struct strvec;
+int has_capability(const struct strvec *keys, const char *capability,
const char **value);
struct serve_options {
diff --git a/setup.c b/setup.c
index 3a81307602..c04cd25a30 100644
--- a/setup.c
+++ b/setup.c
@@ -447,6 +447,63 @@ static int read_worktree_config(const char *var, const char *value, void *vdata)
return 0;
}
+enum extension_result {
+ EXTENSION_ERROR = -1, /* compatible with error(), etc */
+ EXTENSION_UNKNOWN = 0,
+ EXTENSION_OK = 1
+};
+
+/*
+ * Do not add new extensions to this function. It handles extensions which are
+ * respected even in v0-format repositories for historical compatibility.
+ */
+static enum extension_result handle_extension_v0(const char *var,
+ const char *value,
+ const char *ext,
+ struct repository_format *data)
+{
+ if (!strcmp(ext, "noop")) {
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "preciousobjects")) {
+ data->precious_objects = git_config_bool(var, value);
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "partialclone")) {
+ if (!value)
+ return config_error_nonbool(var);
+ data->partial_clone = xstrdup(value);
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "worktreeconfig")) {
+ data->worktree_config = git_config_bool(var, value);
+ return EXTENSION_OK;
+ }
+
+ return EXTENSION_UNKNOWN;
+}
+
+/*
+ * Record any new extensions in this function.
+ */
+static enum extension_result handle_extension(const char *var,
+ const char *value,
+ const char *ext,
+ struct repository_format *data)
+{
+ if (!strcmp(ext, "noop-v1")) {
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "objectformat")) {
+ int format;
+
+ if (!value)
+ return config_error_nonbool(var);
+ format = hash_algo_by_name(value);
+ if (format == GIT_HASH_UNKNOWN)
+ return error("invalid value for 'extensions.objectformat'");
+ data->hash_algo = format;
+ return EXTENSION_OK;
+ }
+ return EXTENSION_UNKNOWN;
+}
+
static int check_repo_format(const char *var, const char *value, void *vdata)
{
struct repository_format *data = vdata;
@@ -455,23 +512,25 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
if (strcmp(var, "core.repositoryformatversion") == 0)
data->version = git_config_int(var, value);
else if (skip_prefix(var, "extensions.", &ext)) {
- /*
- * record any known extensions here; otherwise,
- * we fall through to recording it as unknown, and
- * check_repository_format will complain
- */
- if (!strcmp(ext, "noop"))
- ;
- else if (!strcmp(ext, "preciousobjects"))
- data->precious_objects = git_config_bool(var, value);
- else if (!strcmp(ext, "partialclone")) {
- if (!value)
- return config_error_nonbool(var);
- data->partial_clone = xstrdup(value);
- } else if (!strcmp(ext, "worktreeconfig"))
- data->worktree_config = git_config_bool(var, value);
- else
+ switch (handle_extension_v0(var, value, ext, data)) {
+ case EXTENSION_ERROR:
+ return -1;
+ case EXTENSION_OK:
+ return 0;
+ case EXTENSION_UNKNOWN:
+ break;
+ }
+
+ switch (handle_extension(var, value, ext, data)) {
+ case EXTENSION_ERROR:
+ return -1;
+ case EXTENSION_OK:
+ string_list_append(&data->v1_only_extensions, ext);
+ return 0;
+ case EXTENSION_UNKNOWN:
string_list_append(&data->unknown_extensions, ext);
+ return 0;
+ }
}
return read_worktree_config(var, value, vdata);
@@ -510,6 +569,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
set_repository_format_partial_clone(candidate->partial_clone);
repository_format_worktree_config = candidate->worktree_config;
string_list_clear(&candidate->unknown_extensions, 0);
+ string_list_clear(&candidate->v1_only_extensions, 0);
if (repository_format_worktree_config) {
/*
@@ -588,6 +648,7 @@ int read_repository_format(struct repository_format *format, const char *path)
void clear_repository_format(struct repository_format *format)
{
string_list_clear(&format->unknown_extensions, 0);
+ string_list_clear(&format->v1_only_extensions, 0);
free(format->work_tree);
free(format->partial_clone);
init_repository_format(format);
@@ -613,6 +674,18 @@ int verify_repository_format(const struct repository_format *format,
return -1;
}
+ if (format->version == 0 && format->v1_only_extensions.nr) {
+ int i;
+
+ strbuf_addstr(err,
+ _("repo version is 0, but v1-only extensions found:"));
+
+ for (i = 0; i < format->v1_only_extensions.nr; i++)
+ strbuf_addf(err, "\n\t%s",
+ format->v1_only_extensions.items[i].string);
+ return -1;
+ }
+
return 0;
}
diff --git a/sha1-file.c b/sha1-file.c
index ccd34dd9e8..dd65bd5c68 100644
--- a/sha1-file.c
+++ b/sha1-file.c
@@ -763,18 +763,18 @@ static void fill_alternate_refs_command(struct child_process *cmd,
if (!git_config_get_value("core.alternateRefsCommand", &value)) {
cmd->use_shell = 1;
- argv_array_push(&cmd->args, value);
- argv_array_push(&cmd->args, repo_path);
+ strvec_push(&cmd->args, value);
+ strvec_push(&cmd->args, repo_path);
} else {
cmd->git_cmd = 1;
- argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
- argv_array_push(&cmd->args, "for-each-ref");
- argv_array_push(&cmd->args, "--format=%(objectname)");
+ strvec_pushf(&cmd->args, "--git-dir=%s", repo_path);
+ strvec_push(&cmd->args, "for-each-ref");
+ strvec_push(&cmd->args, "--format=%(objectname)");
if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
- argv_array_push(&cmd->args, "--");
- argv_array_split(&cmd->args, value);
+ strvec_push(&cmd->args, "--");
+ strvec_split(&cmd->args, value);
}
}
@@ -1600,7 +1600,8 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
struct cached_object *co;
hash_object_file(the_hash_algo, buf, len, type_name(type), oid);
- if (has_object_file(oid) || find_cached_object(oid))
+ if (has_object_file_with_flags(oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
+ find_cached_object(oid))
return 0;
ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
co = &cached_objects[cached_object_nr++];
@@ -1988,6 +1989,18 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
return ret;
}
+int has_object(struct repository *r, const struct object_id *oid,
+ unsigned flags)
+{
+ int quick = !(flags & HAS_OBJECT_RECHECK_PACKED);
+ unsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |
+ (quick ? OBJECT_INFO_QUICK : 0);
+
+ if (!startup_info->have_repository)
+ return 0;
+ return oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;
+}
+
int repo_has_object_file_with_flags(struct repository *r,
const struct object_id *oid, int flags)
{
diff --git a/sha1-name.c b/sha1-name.c
index 0b8cb5247a..0b23b86ceb 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -809,7 +809,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
- refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref);
+ refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0);
if (refs_found > 0) {
warning(warn_msg, len, str);
if (advice_object_name_warning)
@@ -860,11 +860,11 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
if (!len && reflog_len)
/* allow "@{...}" to mean the current branch reflog */
- refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref);
+ refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, 0);
else if (reflog_len)
refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
else
- refs_found = repo_dwim_ref(r, str, len, oid, &real_ref);
+ refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, 0);
if (!refs_found)
return -1;
@@ -1427,9 +1427,12 @@ static int reinterpret(struct repository *r,
struct strbuf tmp = STRBUF_INIT;
int used = buf->len;
int ret;
+ struct interpret_branch_name_options options = {
+ .allowed = allowed
+ };
strbuf_add(buf, name + len, namelen - len);
- ret = repo_interpret_branch_name(r, buf->buf, buf->len, &tmp, allowed);
+ ret = repo_interpret_branch_name(r, buf->buf, buf->len, &tmp, &options);
/* that data was not interpreted, remove our cruft */
if (ret < 0) {
strbuf_setlen(buf, used);
@@ -1471,7 +1474,7 @@ static int interpret_branch_mark(struct repository *r,
int (*get_mark)(const char *, int),
const char *(*get_data)(struct branch *,
struct strbuf *),
- unsigned allowed)
+ const struct interpret_branch_name_options *options)
{
int len;
struct branch *branch;
@@ -1493,10 +1496,16 @@ static int interpret_branch_mark(struct repository *r,
branch = branch_get(NULL);
value = get_data(branch, &err);
- if (!value)
- die("%s", err.buf);
+ if (!value) {
+ if (options->nonfatal_dangling_mark) {
+ strbuf_release(&err);
+ return -1;
+ } else {
+ die("%s", err.buf);
+ }
+ }
- if (!branch_interpret_allowed(value, allowed))
+ if (!branch_interpret_allowed(value, options->allowed))
return -1;
set_shortened_ref(r, buf, value);
@@ -1506,7 +1515,7 @@ static int interpret_branch_mark(struct repository *r,
int repo_interpret_branch_name(struct repository *r,
const char *name, int namelen,
struct strbuf *buf,
- unsigned allowed)
+ const struct interpret_branch_name_options *options)
{
char *at;
const char *start;
@@ -1515,7 +1524,7 @@ int repo_interpret_branch_name(struct repository *r,
if (!namelen)
namelen = strlen(name);
- if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
+ if (!options->allowed || (options->allowed & INTERPRET_BRANCH_LOCAL)) {
len = interpret_nth_prior_checkout(r, name, namelen, buf);
if (!len) {
return len; /* syntax Ok, not enough switches */
@@ -1523,7 +1532,8 @@ int repo_interpret_branch_name(struct repository *r,
if (len == namelen)
return len; /* consumed all */
else
- return reinterpret(r, name, namelen, len, buf, allowed);
+ return reinterpret(r, name, namelen, len, buf,
+ options->allowed);
}
}
@@ -1531,22 +1541,22 @@ int repo_interpret_branch_name(struct repository *r,
(at = memchr(start, '@', namelen - (start - name)));
start = at + 1) {
- if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
+ if (!options->allowed || (options->allowed & INTERPRET_BRANCH_HEAD)) {
len = interpret_empty_at(name, namelen, at - name, buf);
if (len > 0)
return reinterpret(r, name, namelen, len, buf,
- allowed);
+ options->allowed);
}
len = interpret_branch_mark(r, name, namelen, at - name, buf,
upstream_mark, branch_get_upstream,
- allowed);
+ options);
if (len > 0)
return len;
len = interpret_branch_mark(r, name, namelen, at - name, buf,
push_mark, branch_get_push,
- allowed);
+ options);
if (len > 0)
return len;
}
@@ -1557,7 +1567,10 @@ int repo_interpret_branch_name(struct repository *r,
void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
{
int len = strlen(name);
- int used = interpret_branch_name(name, len, sb, allowed);
+ struct interpret_branch_name_options options = {
+ .allowed = allowed
+ };
+ int used = interpret_branch_name(name, len, sb, &options);
if (used < 0)
used = 0;
diff --git a/shallow.c b/shallow.c
index b826de9b67..91b9e1073c 100644
--- a/shallow.c
+++ b/shallow.c
@@ -110,6 +110,10 @@ void rollback_shallow_file(struct repository *r, struct shallow_lock *lk)
* supports a "valid" flag.
*/
define_commit_slab(commit_depth, int *);
+static void free_depth_in_slab(int **ptr)
+{
+ FREE_AND_NULL(*ptr);
+}
struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
int shallow_flag, int not_shallow_flag)
{
@@ -176,15 +180,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
}
}
}
- for (i = 0; i < depths.slab_count; i++) {
- int j;
-
- if (!depths.slab[i])
- continue;
- for (j = 0; j < depths.slab_size; j++)
- free(depths.slab[i][j]);
- }
- clear_commit_depth(&depths);
+ deep_clear_commit_depth(&depths, free_depth_in_slab);
return result;
}
diff --git a/sideband.c b/sideband.c
index ef851113c4..0a60662fa6 100644
--- a/sideband.c
+++ b/sideband.c
@@ -147,7 +147,7 @@ int demultiplex_sideband(const char *me, char *buf, int len,
switch (band) {
case 3:
if (die_on_error)
- die("remote error: %s", buf + 1);
+ die(_("remote error: %s"), buf + 1);
strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "",
DISPLAY_PREFIX);
maybe_colorize_sideband(scratch, buf + 1, len);
diff --git a/split-index.c b/split-index.c
index e6154e4ea9..c0e8ad670d 100644
--- a/split-index.c
+++ b/split-index.c
@@ -79,8 +79,10 @@ void move_cache_to_base_index(struct index_state *istate)
if (si->base &&
si->base->ce_mem_pool) {
- if (!istate->ce_mem_pool)
- mem_pool_init(&istate->ce_mem_pool, 0);
+ if (!istate->ce_mem_pool) {
+ istate->ce_mem_pool = xmalloc(sizeof(struct mem_pool));
+ mem_pool_init(istate->ce_mem_pool, 0);
+ }
mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool);
}
diff --git a/strvec.c b/strvec.c
new file mode 100644
index 0000000000..21dce0a7a4
--- /dev/null
+++ b/strvec.c
@@ -0,0 +1,109 @@
+#include "cache.h"
+#include "strvec.h"
+#include "strbuf.h"
+
+const char *empty_strvec[] = { NULL };
+
+void strvec_init(struct strvec *array)
+{
+ array->v = empty_strvec;
+ array->nr = 0;
+ array->alloc = 0;
+}
+
+static void strvec_push_nodup(struct strvec *array, const char *value)
+{
+ if (array->v == empty_strvec)
+ array->v = NULL;
+
+ ALLOC_GROW(array->v, array->nr + 2, array->alloc);
+ array->v[array->nr++] = value;
+ array->v[array->nr] = NULL;
+}
+
+const char *strvec_push(struct strvec *array, const char *value)
+{
+ strvec_push_nodup(array, xstrdup(value));
+ return array->v[array->nr - 1];
+}
+
+const char *strvec_pushf(struct strvec *array, const char *fmt, ...)
+{
+ va_list ap;
+ struct strbuf v = STRBUF_INIT;
+
+ va_start(ap, fmt);
+ strbuf_vaddf(&v, fmt, ap);
+ va_end(ap);
+
+ strvec_push_nodup(array, strbuf_detach(&v, NULL));
+ return array->v[array->nr - 1];
+}
+
+void strvec_pushl(struct strvec *array, ...)
+{
+ va_list ap;
+ const char *arg;
+
+ va_start(ap, array);
+ while ((arg = va_arg(ap, const char *)))
+ strvec_push(array, arg);
+ va_end(ap);
+}
+
+void strvec_pushv(struct strvec *array, const char **items)
+{
+ for (; *items; items++)
+ strvec_push(array, *items);
+}
+
+void strvec_pop(struct strvec *array)
+{
+ if (!array->nr)
+ return;
+ free((char *)array->v[array->nr - 1]);
+ array->v[array->nr - 1] = NULL;
+ array->nr--;
+}
+
+void strvec_split(struct strvec *array, const char *to_split)
+{
+ while (isspace(*to_split))
+ to_split++;
+ for (;;) {
+ const char *p = to_split;
+
+ if (!*p)
+ break;
+
+ while (*p && !isspace(*p))
+ p++;
+ strvec_push_nodup(array, xstrndup(to_split, p - to_split));
+
+ while (isspace(*p))
+ p++;
+ to_split = p;
+ }
+}
+
+void strvec_clear(struct strvec *array)
+{
+ if (array->v != empty_strvec) {
+ int i;
+ for (i = 0; i < array->nr; i++)
+ free((char *)array->v[i]);
+ free(array->v);
+ }
+ strvec_init(array);
+}
+
+const char **strvec_detach(struct strvec *array)
+{
+ if (array->v == empty_strvec)
+ return xcalloc(1, sizeof(const char *));
+ else {
+ const char **ret = array->v;
+ strvec_init(array);
+ return ret;
+ }
+}
diff --git a/argv-array.h b/strvec.h
index a7d3b10707..fdcad75b45 100644
--- a/argv-array.h
+++ b/strvec.h
@@ -1,11 +1,11 @@
-#ifndef ARGV_ARRAY_H
-#define ARGV_ARRAY_H
+#ifndef STRVEC_H
+#define STRVEC_H
/**
- * The argv-array API allows one to dynamically build and store
- * NULL-terminated lists. An argv-array maintains the invariant that the
- * `argv` member always points to a non-NULL array, and that the array is
- * always NULL-terminated at the element pointed to by `argv[argc]`. This
+ * The strvec API allows one to dynamically build and store
+ * NULL-terminated arrays of strings. A strvec maintains the invariant that the
+ * `items` member always points to a non-NULL array, and that the array is
+ * always NULL-terminated at the element pointed to by `items[nr]`. This
* makes the result suitable for passing to functions expecting to receive
* argv from main().
*
@@ -14,42 +14,42 @@
* it contains an item structure with a `util` field that is not compatible
* with the traditional argv interface.
*
- * Each `argv_array` manages its own memory. Any strings pushed into the
- * array are duplicated, and all memory is freed by argv_array_clear().
+ * Each `strvec` manages its own memory. Any strings pushed into the
+ * array are duplicated, and all memory is freed by strvec_clear().
*/
-extern const char *empty_argv[];
+extern const char *empty_strvec[];
/**
* A single array. This should be initialized by assignment from
- * `ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv`
- * member contains the actual array; the `argc` member contains the
+ * `STRVEC_INIT`, or by calling `strvec_init`. The `items`
+ * member contains the actual array; the `nr` member contains the
* number of elements in the array, not including the terminating
* NULL.
*/
-struct argv_array {
- const char **argv;
- int argc;
+struct strvec {
+ const char **v;
+ int nr;
int alloc;
};
-#define ARGV_ARRAY_INIT { empty_argv, 0, 0 }
+#define STRVEC_INIT { empty_strvec, 0, 0 }
/**
* Initialize an array. This is no different than assigning from
- * `ARGV_ARRAY_INIT`.
+ * `STRVEC_INIT`.
*/
-void argv_array_init(struct argv_array *);
+void strvec_init(struct strvec *);
/* Push a copy of a string onto the end of the array. */
-const char *argv_array_push(struct argv_array *, const char *);
+const char *strvec_push(struct strvec *, const char *);
/**
* Format a string and push it onto the end of the array. This is a
- * convenience wrapper combining `strbuf_addf` and `argv_array_push`.
+ * convenience wrapper combining `strbuf_addf` and `strvec_push`.
*/
__attribute__((format (printf,2,3)))
-const char *argv_array_pushf(struct argv_array *, const char *fmt, ...);
+const char *strvec_pushf(struct strvec *, const char *fmt, ...);
/**
* Push a list of strings onto the end of the array. The arguments
@@ -57,33 +57,33 @@ const char *argv_array_pushf(struct argv_array *, const char *fmt, ...);
* argument.
*/
LAST_ARG_MUST_BE_NULL
-void argv_array_pushl(struct argv_array *, ...);
+void strvec_pushl(struct strvec *, ...);
/* Push a null-terminated array of strings onto the end of the array. */
-void argv_array_pushv(struct argv_array *, const char **);
+void strvec_pushv(struct strvec *, const char **);
/**
* Remove the final element from the array. If there are no
* elements in the array, do nothing.
*/
-void argv_array_pop(struct argv_array *);
+void strvec_pop(struct strvec *);
/* Splits by whitespace; does not handle quoted arguments! */
-void argv_array_split(struct argv_array *, const char *);
+void strvec_split(struct strvec *, const char *);
/**
* Free all memory associated with the array and return it to the
* initial, empty state.
*/
-void argv_array_clear(struct argv_array *);
+void strvec_clear(struct strvec *);
/**
- * Disconnect the `argv` member from the `argv_array` struct and
+ * Disconnect the `items` member from the `strvec` struct and
* return it. The caller is responsible for freeing the memory used
* by the array, and by the strings it references. After detaching,
- * the `argv_array` is in a reinitialized state and can be pushed
+ * the `strvec` is in a reinitialized state and can be pushed
* into again.
*/
-const char **argv_array_detach(struct argv_array *);
+const char **strvec_detach(struct strvec *);
-#endif /* ARGV_ARRAY_H */
+#endif /* STRVEC_H */
diff --git a/sub-process.c b/sub-process.c
index 1b1af9dcbd..dfa790d3ff 100644
--- a/sub-process.c
+++ b/sub-process.c
@@ -84,7 +84,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
process = &entry->process;
child_process_init(process);
- argv_array_push(&process->args, cmd);
+ strvec_push(&process->args, cmd);
process->use_shell = 1;
process->in = -1;
process->out = -1;
diff --git a/submodule-config.c b/submodule-config.c
index e175dfbc38..c569e22aa3 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -777,10 +777,14 @@ static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
{
struct fetch_config *config = cb;
if (!strcmp(var, "submodule.fetchjobs")) {
- *(config->max_children) = parse_submodule_fetchjobs(var, value);
+ if (config->max_children)
+ *(config->max_children) =
+ parse_submodule_fetchjobs(var, value);
return 0;
} else if (!strcmp(var, "fetch.recursesubmodules")) {
- *(config->recurse_submodules) = parse_fetch_recurse_submodules_arg(var, value);
+ if (config->recurse_submodules)
+ *(config->recurse_submodules) =
+ parse_fetch_recurse_submodules_arg(var, value);
return 0;
}
diff --git a/submodule.c b/submodule.c
index e2ef5698c8..543b1123ae 100644
--- a/submodule.c
+++ b/submodule.c
@@ -13,7 +13,7 @@
#include "refs.h"
#include "string-list.h"
#include "oid-array.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "blob.h"
#include "thread-utils.h"
#include "quote.h"
@@ -194,7 +194,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
char *key;
key = xstrfmt("submodule.%s.ignore", submodule->name);
- if (repo_config_get_string_const(the_repository, key, &ignore))
+ if (repo_config_get_string_tmp(the_repository, key, &ignore))
ignore = submodule->ignore;
free(key);
@@ -262,17 +262,17 @@ int is_submodule_active(struct repository *repo, const char *path)
sl = repo_config_get_value_multi(repo, "submodule.active");
if (sl) {
struct pathspec ps;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
const struct string_list_item *item;
for_each_string_list_item(item, sl) {
- argv_array_push(&args, item->string);
+ strvec_push(&args, item->string);
}
- parse_pathspec(&ps, 0, 0, NULL, args.argv);
+ parse_pathspec(&ps, 0, 0, NULL, args.v);
ret = match_pathspec(repo->index, &ps, path, strlen(path), 0, NULL, 1);
- argv_array_clear(&args);
+ strvec_clear(&args);
clear_pathspec(&ps);
return ret;
}
@@ -438,13 +438,14 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,
*/
}
-static int prepare_submodule_summary(struct rev_info *rev, const char *path,
- struct commit *left, struct commit *right,
- struct commit_list *merge_bases)
+static int prepare_submodule_diff_summary(struct repository *r, struct rev_info *rev,
+ const char *path,
+ struct commit *left, struct commit *right,
+ struct commit_list *merge_bases)
{
struct commit_list *list;
- repo_init_revisions(the_repository, rev, NULL);
+ repo_init_revisions(r, rev, NULL);
setup_revisions(0, NULL, rev, NULL);
rev->left_right = 1;
rev->first_parent_only = 1;
@@ -459,7 +460,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
return prepare_revision_walk(rev);
}
-static void print_submodule_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
+static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
{
static const char format[] = " %m %s";
struct strbuf sb = STRBUF_INIT;
@@ -481,27 +482,27 @@ static void print_submodule_summary(struct repository *r, struct rev_info *rev,
strbuf_release(&sb);
}
-static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out)
+static void prepare_submodule_repo_env_no_git_dir(struct strvec *out)
{
const char * const *var;
for (var = local_repo_env; *var; var++) {
if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
- argv_array_push(out, *var);
+ strvec_push(out, *var);
}
}
-void prepare_submodule_repo_env(struct argv_array *out)
+void prepare_submodule_repo_env(struct strvec *out)
{
prepare_submodule_repo_env_no_git_dir(out);
- argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
- DEFAULT_GIT_DIR_ENVIRONMENT);
+ strvec_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
+ DEFAULT_GIT_DIR_ENVIRONMENT);
}
-static void prepare_submodule_repo_env_in_gitdir(struct argv_array *out)
+static void prepare_submodule_repo_env_in_gitdir(struct strvec *out)
{
prepare_submodule_repo_env_no_git_dir(out);
- argv_array_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT);
+ strvec_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT);
}
/*
@@ -610,7 +611,7 @@ output_header:
strbuf_release(&sb);
}
-void show_submodule_summary(struct diff_options *o, const char *path,
+void show_submodule_diff_summary(struct diff_options *o, const char *path,
struct object_id *one, struct object_id *two,
unsigned dirty_submodule)
{
@@ -632,12 +633,12 @@ void show_submodule_summary(struct diff_options *o, const char *path,
goto out;
/* Treat revision walker failure the same as missing commits */
- if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
+ if (prepare_submodule_diff_summary(sub, &rev, path, left, right, merge_bases)) {
diff_emit_submodule_error(o, "(revision walker failed)\n");
goto out;
}
- print_submodule_summary(sub, &rev, o);
+ print_submodule_diff_summary(sub, &rev, o);
out:
if (merge_bases)
@@ -681,22 +682,22 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
cp.no_stdin = 1;
/* TODO: other options may need to be passed here. */
- argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL);
- argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
+ strvec_pushl(&cp.args, "diff", "--submodule=diff", NULL);
+ strvec_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
"always" : "never");
if (o->flags.reverse_diff) {
- argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
- o->b_prefix, path);
- argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
- o->a_prefix, path);
+ strvec_pushf(&cp.args, "--src-prefix=%s%s/",
+ o->b_prefix, path);
+ strvec_pushf(&cp.args, "--dst-prefix=%s%s/",
+ o->a_prefix, path);
} else {
- argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
- o->a_prefix, path);
- argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
- o->b_prefix, path);
+ strvec_pushf(&cp.args, "--src-prefix=%s%s/",
+ o->a_prefix, path);
+ strvec_pushf(&cp.args, "--dst-prefix=%s%s/",
+ o->b_prefix, path);
}
- argv_array_push(&cp.args, oid_to_hex(old_oid));
+ strvec_push(&cp.args, oid_to_hex(old_oid));
/*
* If the submodule has modified content, we will diff against the
* work tree, under the assumption that the user has asked for the
@@ -704,7 +705,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
* haven't yet been committed to the submodule yet.
*/
if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
- argv_array_push(&cp.args, oid_to_hex(new_oid));
+ strvec_push(&cp.args, oid_to_hex(new_oid));
prepare_submodule_repo_env(&cp.env_array);
if (start_command(&cp))
@@ -836,13 +837,20 @@ static void collect_changed_submodules_cb(struct diff_queue_struct *q,
*/
static void collect_changed_submodules(struct repository *r,
struct string_list *changed,
- struct argv_array *argv)
+ struct strvec *argv)
{
struct rev_info rev;
const struct commit *commit;
+ int save_warning;
+ struct setup_revision_opt s_r_opt = {
+ .assume_dashdash = 1,
+ };
+ save_warning = warn_on_object_refname_ambiguity;
+ warn_on_object_refname_ambiguity = 0;
repo_init_revisions(r, &rev, NULL);
- setup_revisions(argv->argc, argv->argv, &rev, NULL);
+ setup_revisions(argv->nr, argv->v, &rev, &s_r_opt);
+ warn_on_object_refname_ambiguity = save_warning;
if (prepare_revision_walk(&rev))
die(_("revision walk setup failed"));
@@ -879,8 +887,8 @@ static int has_remote(const char *refname, const struct object_id *oid,
static int append_oid_to_argv(const struct object_id *oid, void *data)
{
- struct argv_array *argv = data;
- argv_array_push(argv, oid_to_hex(oid));
+ struct strvec *argv = data;
+ strvec_push(argv, oid_to_hex(oid));
return 0;
}
@@ -941,9 +949,9 @@ static int submodule_has_commits(struct repository *r,
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf out = STRBUF_INIT;
- argv_array_pushl(&cp.args, "rev-list", "-n", "1", NULL);
+ strvec_pushl(&cp.args, "rev-list", "-n", "1", NULL);
oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
- argv_array_pushl(&cp.args, "--not", "--all", NULL);
+ strvec_pushl(&cp.args, "--not", "--all", NULL);
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
@@ -982,9 +990,9 @@ static int submodule_needs_pushing(struct repository *r,
struct strbuf buf = STRBUF_INIT;
int needs_pushing = 0;
- argv_array_push(&cp.args, "rev-list");
+ strvec_push(&cp.args, "rev-list");
oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
- argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
+ strvec_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
@@ -1012,13 +1020,13 @@ int find_unpushed_submodules(struct repository *r,
{
struct string_list submodules = STRING_LIST_INIT_DUP;
struct string_list_item *name;
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
- /* argv.argv[0] will be ignored by setup_revisions */
- argv_array_push(&argv, "find_unpushed_submodules");
+ /* argv.v[0] will be ignored by setup_revisions */
+ strvec_push(&argv, "find_unpushed_submodules");
oid_array_for_each_unique(commits, append_oid_to_argv, &argv);
- argv_array_push(&argv, "--not");
- argv_array_pushf(&argv, "--remotes=%s", remotes_name);
+ strvec_push(&argv, "--not");
+ strvec_pushf(&argv, "--remotes=%s", remotes_name);
collect_changed_submodules(r, &submodules, &argv);
@@ -1041,7 +1049,7 @@ int find_unpushed_submodules(struct repository *r,
}
free_submodules_oids(&submodules);
- argv_array_clear(&argv);
+ strvec_clear(&argv);
return needs_pushing->nr;
}
@@ -1054,22 +1062,22 @@ static int push_submodule(const char *path,
{
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
struct child_process cp = CHILD_PROCESS_INIT;
- argv_array_push(&cp.args, "push");
+ strvec_push(&cp.args, "push");
if (dry_run)
- argv_array_push(&cp.args, "--dry-run");
+ strvec_push(&cp.args, "--dry-run");
if (push_options && push_options->nr) {
const struct string_list_item *item;
for_each_string_list_item(item, push_options)
- argv_array_pushf(&cp.args, "--push-option=%s",
- item->string);
+ strvec_pushf(&cp.args, "--push-option=%s",
+ item->string);
}
if (remote->origin != REMOTE_UNCONFIGURED) {
int i;
- argv_array_push(&cp.args, remote->name);
+ strvec_push(&cp.args, remote->name);
for (i = 0; i < rs->raw_nr; i++)
- argv_array_push(&cp.args, rs->raw[i]);
+ strvec_push(&cp.args, rs->raw[i]);
}
prepare_submodule_repo_env(&cp.env_array);
@@ -1095,13 +1103,13 @@ static void submodule_push_check(const char *path, const char *head,
struct child_process cp = CHILD_PROCESS_INIT;
int i;
- argv_array_push(&cp.args, "submodule--helper");
- argv_array_push(&cp.args, "push-check");
- argv_array_push(&cp.args, head);
- argv_array_push(&cp.args, remote->name);
+ strvec_push(&cp.args, "submodule--helper");
+ strvec_push(&cp.args, "push-check");
+ strvec_push(&cp.args, head);
+ strvec_push(&cp.args, remote->name);
for (i = 0; i < rs->raw_nr; i++)
- argv_array_push(&cp.args, rs->raw[i]);
+ strvec_push(&cp.args, rs->raw[i]);
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
@@ -1189,17 +1197,17 @@ void check_for_new_submodule_commits(struct object_id *oid)
static void calculate_changed_submodule_paths(struct repository *r,
struct string_list *changed_submodule_names)
{
- struct argv_array argv = ARGV_ARRAY_INIT;
+ struct strvec argv = STRVEC_INIT;
struct string_list_item *name;
/* No need to check if there are no submodules configured */
if (!submodule_from_path(r, NULL, NULL))
return;
- argv_array_push(&argv, "--"); /* argv[0] program name */
+ strvec_push(&argv, "--"); /* argv[0] program name */
oid_array_for_each_unique(&ref_tips_after_fetch,
append_oid_to_argv, &argv);
- argv_array_push(&argv, "--not");
+ strvec_push(&argv, "--not");
oid_array_for_each_unique(&ref_tips_before_fetch,
append_oid_to_argv, &argv);
@@ -1231,7 +1239,7 @@ static void calculate_changed_submodule_paths(struct repository *r,
string_list_remove_empty_items(changed_submodule_names, 1);
- argv_array_clear(&argv);
+ strvec_clear(&argv);
oid_array_clear(&ref_tips_before_fetch);
oid_array_clear(&ref_tips_after_fetch);
initialized_fetch_ref_tips = 0;
@@ -1242,24 +1250,24 @@ int submodule_touches_in_range(struct repository *r,
struct object_id *incl_oid)
{
struct string_list subs = STRING_LIST_INIT_DUP;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
int ret;
/* No need to check if there are no submodules configured */
if (!submodule_from_path(r, NULL, NULL))
return 0;
- argv_array_push(&args, "--"); /* args[0] program name */
- argv_array_push(&args, oid_to_hex(incl_oid));
+ strvec_push(&args, "--"); /* args[0] program name */
+ strvec_push(&args, oid_to_hex(incl_oid));
if (!is_null_oid(excl_oid)) {
- argv_array_push(&args, "--not");
- argv_array_push(&args, oid_to_hex(excl_oid));
+ strvec_push(&args, "--not");
+ strvec_push(&args, oid_to_hex(excl_oid));
}
collect_changed_submodules(r, &subs, &args);
ret = subs.nr;
- argv_array_clear(&args);
+ strvec_clear(&args);
free_submodules_oids(&subs);
return ret;
@@ -1267,7 +1275,7 @@ int submodule_touches_in_range(struct repository *r,
struct submodule_parallel_fetch {
int count;
- struct argv_array args;
+ struct strvec args;
struct repository *r;
const char *prefix;
int command_line_option;
@@ -1283,7 +1291,7 @@ struct submodule_parallel_fetch {
struct strbuf submodules_with_errors;
};
-#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, \
+#define SPF_INIT {0, STRVEC_INIT, NULL, NULL, 0, 0, 0, 0, \
STRING_LIST_INIT_DUP, \
NULL, 0, 0, STRBUF_INIT}
@@ -1299,7 +1307,7 @@ static int get_fetch_recurse_config(const struct submodule *submodule,
int fetch_recurse = submodule->fetch_recurse;
key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
- if (!repo_config_get_string_const(spf->r, key, &value)) {
+ if (!repo_config_get_string_tmp(spf->r, key, &value)) {
fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
}
free(key);
@@ -1452,15 +1460,15 @@ static int get_next_submodule(struct child_process *cp,
if (!spf->quiet)
strbuf_addf(err, _("Fetching submodule %s%s\n"),
spf->prefix, ce->name);
- argv_array_init(&cp->args);
- argv_array_pushv(&cp->args, spf->args.argv);
- argv_array_push(&cp->args, default_argv);
- argv_array_push(&cp->args, "--submodule-prefix");
+ strvec_init(&cp->args);
+ strvec_pushv(&cp->args, spf->args.v);
+ strvec_push(&cp->args, default_argv);
+ strvec_push(&cp->args, "--submodule-prefix");
strbuf_addf(&submodule_prefix, "%s%s/",
spf->prefix,
task->sub->path);
- argv_array_push(&cp->args, submodule_prefix.buf);
+ strvec_push(&cp->args, submodule_prefix.buf);
spf->count++;
*task_cb = task;
@@ -1500,14 +1508,14 @@ static int get_next_submodule(struct child_process *cp,
cp->git_cmd = 1;
cp->dir = task->repo->gitdir;
- argv_array_init(&cp->args);
- argv_array_pushv(&cp->args, spf->args.argv);
- argv_array_push(&cp->args, "on-demand");
- argv_array_push(&cp->args, "--submodule-prefix");
- argv_array_push(&cp->args, submodule_prefix.buf);
+ strvec_init(&cp->args);
+ strvec_pushv(&cp->args, spf->args.v);
+ strvec_push(&cp->args, "on-demand");
+ strvec_push(&cp->args, "--submodule-prefix");
+ strvec_push(&cp->args, submodule_prefix.buf);
/* NEEDSWORK: have get_default_remote from submodule--helper */
- argv_array_push(&cp->args, "origin");
+ strvec_push(&cp->args, "origin");
oid_array_for_each_unique(task->commits,
append_oid_to_argv, &cp->args);
@@ -1598,7 +1606,7 @@ out:
}
int fetch_populated_submodules(struct repository *r,
- const struct argv_array *options,
+ const struct strvec *options,
const char *prefix, int command_line_option,
int default_option,
int quiet, int max_parallel_jobs)
@@ -1618,10 +1626,10 @@ int fetch_populated_submodules(struct repository *r,
if (repo_read_index(r) < 0)
die(_("index file corrupt"));
- argv_array_push(&spf.args, "fetch");
- for (i = 0; i < options->argc; i++)
- argv_array_push(&spf.args, options->argv[i]);
- argv_array_push(&spf.args, "--recurse-submodules-default");
+ strvec_push(&spf.args, "fetch");
+ for (i = 0; i < options->nr; i++)
+ strvec_push(&spf.args, options->v[i]);
+ strvec_push(&spf.args, "--recurse-submodules-default");
/* default value, "--submodule-prefix" and its value are added later */
calculate_changed_submodule_paths(r, &spf.changed_submodule_names);
@@ -1638,7 +1646,7 @@ int fetch_populated_submodules(struct repository *r,
spf.submodules_with_errors.buf);
- argv_array_clear(&spf.args);
+ strvec_clear(&spf.args);
out:
free_submodules_oids(&spf.changed_submodule_names);
return spf.result;
@@ -1666,9 +1674,9 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
}
strbuf_reset(&buf);
- argv_array_pushl(&cp.args, "status", "--porcelain=2", NULL);
+ strvec_pushl(&cp.args, "status", "--porcelain=2", NULL);
if (ignore_untracked)
- argv_array_push(&cp.args, "-uno");
+ strvec_push(&cp.args, "-uno");
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
@@ -1726,14 +1734,6 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
int submodule_uses_gitfile(const char *path)
{
struct child_process cp = CHILD_PROCESS_INIT;
- const char *argv[] = {
- "submodule",
- "foreach",
- "--quiet",
- "--recursive",
- "test -f .git",
- NULL,
- };
struct strbuf buf = STRBUF_INIT;
const char *git_dir;
@@ -1746,7 +1746,10 @@ int submodule_uses_gitfile(const char *path)
strbuf_release(&buf);
/* Now test that all nested submodules use a gitfile too */
- cp.argv = argv;
+ strvec_pushl(&cp.args,
+ "submodule", "foreach", "--quiet", "--recursive",
+ "test -f .git", NULL);
+
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
@@ -1779,16 +1782,16 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
if (!submodule_uses_gitfile(path))
return 1;
- argv_array_pushl(&cp.args, "status", "--porcelain",
- "--ignore-submodules=none", NULL);
+ strvec_pushl(&cp.args, "status", "--porcelain",
+ "--ignore-submodules=none", NULL);
if (flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED)
- argv_array_push(&cp.args, "-uno");
+ strvec_push(&cp.args, "-uno");
else
- argv_array_push(&cp.args, "-uall");
+ strvec_push(&cp.args, "-uall");
if (!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))
- argv_array_push(&cp.args, "--ignored");
+ strvec_push(&cp.args, "--ignored");
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
@@ -1846,8 +1849,8 @@ static int submodule_has_dirty_index(const struct submodule *sub)
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
- argv_array_pushl(&cp.args, "diff-index", "--quiet",
- "--cached", "HEAD", NULL);
+ strvec_pushl(&cp.args, "diff-index", "--quiet",
+ "--cached", "HEAD", NULL);
cp.no_stdin = 1;
cp.no_stdout = 1;
cp.dir = sub->path;
@@ -1866,11 +1869,11 @@ static void submodule_reset_index(const char *path)
cp.no_stdin = 1;
cp.dir = path;
- argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
- get_super_prefix_or_empty(), path);
- argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
+ strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+ get_super_prefix_or_empty(), path);
+ strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
- argv_array_push(&cp.args, empty_tree_oid_hex());
+ strvec_push(&cp.args, empty_tree_oid_hex());
if (run_command(&cp))
die(_("could not reset submodule index"));
@@ -1947,24 +1950,24 @@ int submodule_move_head(const char *path,
cp.no_stdin = 1;
cp.dir = path;
- argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
- get_super_prefix_or_empty(), path);
- argv_array_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
+ strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+ get_super_prefix_or_empty(), path);
+ strvec_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
- argv_array_push(&cp.args, "-n");
+ strvec_push(&cp.args, "-n");
else
- argv_array_push(&cp.args, "-u");
+ strvec_push(&cp.args, "-u");
if (flags & SUBMODULE_MOVE_HEAD_FORCE)
- argv_array_push(&cp.args, "--reset");
+ strvec_push(&cp.args, "--reset");
else
- argv_array_push(&cp.args, "-m");
+ strvec_push(&cp.args, "-m");
if (!(flags & SUBMODULE_MOVE_HEAD_FORCE))
- argv_array_push(&cp.args, old_head ? old_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex());
- argv_array_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
+ strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
if (run_command(&cp)) {
ret = error(_("Submodule '%s' could not be updated."), path);
@@ -1980,8 +1983,8 @@ int submodule_move_head(const char *path,
cp.dir = path;
prepare_submodule_repo_env(&cp.env_array);
- argv_array_pushl(&cp.args, "update-ref", "HEAD",
- "--no-deref", new_head, NULL);
+ strvec_pushl(&cp.args, "update-ref", "HEAD",
+ "--no-deref", new_head, NULL);
if (run_command(&cp)) {
ret = -1;
@@ -2157,9 +2160,9 @@ void absorb_git_dir_into_superproject(const char *path,
cp.dir = path;
cp.git_cmd = 1;
cp.no_stdin = 1;
- argv_array_pushl(&cp.args, "--super-prefix", sb.buf,
- "submodule--helper",
- "absorb-git-dirs", NULL);
+ strvec_pushl(&cp.args, "--super-prefix", sb.buf,
+ "submodule--helper",
+ "absorb-git-dirs", NULL);
prepare_submodule_repo_env(&cp.env_array);
if (run_command(&cp))
die(_("could not recurse into submodule '%s'"), path);
@@ -2194,11 +2197,11 @@ int get_superproject_working_tree(struct strbuf *buf)
strbuf_release(&one_up);
prepare_submodule_repo_env(&cp.env_array);
- argv_array_pop(&cp.env_array);
+ strvec_pop(&cp.env_array);
- argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
- "ls-files", "-z", "--stage", "--full-name", "--",
- subpath, NULL);
+ strvec_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
+ "ls-files", "-z", "--stage", "--full-name", "--",
+ subpath, NULL);
strbuf_reset(&sb);
cp.no_stdin = 1;
diff --git a/submodule.h b/submodule.h
index 4dad649f94..4ac6e31cf1 100644
--- a/submodule.h
+++ b/submodule.h
@@ -1,7 +1,7 @@
#ifndef SUBMODULE_H
#define SUBMODULE_H
-struct argv_array;
+struct strvec;
struct cache_entry;
struct diff_options;
struct index_state;
@@ -69,7 +69,7 @@ int parse_submodule_update_strategy(const char *value,
struct submodule_update_strategy *dst);
const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
void handle_ignore_submodules_arg(struct diff_options *, const char *);
-void show_submodule_summary(struct diff_options *o, const char *path,
+void show_submodule_diff_summary(struct diff_options *o, const char *path,
struct object_id *one, struct object_id *two,
unsigned dirty_submodule);
void show_submodule_inline_diff(struct diff_options *o, const char *path,
@@ -84,7 +84,7 @@ int should_update_submodules(void);
const struct submodule *submodule_from_ce(const struct cache_entry *ce);
void check_for_new_submodule_commits(struct object_id *oid);
int fetch_populated_submodules(struct repository *r,
- const struct argv_array *options,
+ const struct strvec *options,
const char *prefix,
int command_line_option,
int default_option,
@@ -143,7 +143,7 @@ void submodule_unset_core_worktree(const struct submodule *sub);
* a submodule by clearing any repo-specific environment variables, but
* retaining any config in the environment.
*/
-void prepare_submodule_repo_env(struct argv_array *out);
+void prepare_submodule_repo_env(struct strvec *out);
#define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
void absorb_git_dir_into_superproject(const char *path,
diff --git a/t/README b/t/README
index 70ec61cf88..2adaf7c2d2 100644
--- a/t/README
+++ b/t/README
@@ -421,6 +421,10 @@ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=<boolean>, when true (which is
the default when running tests), errors out when an abbreviated option
is used.
+GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to
+use in the test scripts. Recognized values for <hash-algo> are "sha1"
+and "sha256".
+
Naming Tests
------------
diff --git a/t/chainlint.sed b/t/chainlint.sed
index 70df40e34b..8a25c5b855 100644
--- a/t/chainlint.sed
+++ b/t/chainlint.sed
@@ -117,7 +117,7 @@
/^[ ]*!*[ ]*(..*)[ ]*[0-9]*[<>|&]/boneline
# multi-line "(...\n...)"
-/^[ ]*(/bsubshell
+/^[ ]*(/bsubsh
# innocuous line -- print it and advance to next line
b
@@ -130,11 +130,11 @@ b
}
b
-:subshell
+:subsh
# bare "(" line? -- stash for later printing
/^[ ]*([ ]*$/ {
h
- bnextline
+ bnextln
}
# "(..." line -- split off and stash "(", then process "..." as its own line
x
@@ -143,7 +143,7 @@ x
s/(//
bslurp
-:nextline
+:nextln
N
s/.*\n//
@@ -151,10 +151,10 @@ s/.*\n//
# incomplete line "...\"
/\\$/bicmplte
# multi-line quoted string "...\n..."?
-/"/bdqstring
+/"/bdqstr
# multi-line quoted string '...\n...'? (but not contraction in string "it's")
/'/{
- /"[^'"]*'[^'"]*"/!bsqstring
+ /"[^'"]*'[^'"]*"/!bsqstr
}
:folded
# here-doc -- swallow it
@@ -163,8 +163,8 @@ s/.*\n//
# before closing ")", "done", "elsif", "else", or "fi" will need to be
# re-visited to drop "suspect" marking since final line of those constructs
# legitimately lacks "&&", so "suspect" mark must be removed
-/^[ ]*#/bnextline
-/^[ ]*$/bnextline
+/^[ ]*#/bnextln
+/^[ ]*$/bnextln
# in-line comment -- strip it (but not "#" in a string, Bash ${#...} array
# length, or Perforce "//depot/path#42" revision in filespec)
/[ ]#/{
@@ -175,22 +175,22 @@ s/.*\n//
# multi-line "case ... esac"
/^[ ]*case[ ]..*[ ]in/bcase
# multi-line "for ... done" or "while ... done"
-/^[ ]*for[ ]..*[ ]in/bcontinue
-/^[ ]*while[ ]/bcontinue
-/^[ ]*do[ ]/bcontinue
-/^[ ]*do[ ]*$/bcontinue
-/;[ ]*do/bcontinue
+/^[ ]*for[ ]..*[ ]in/bcont
+/^[ ]*while[ ]/bcont
+/^[ ]*do[ ]/bcont
+/^[ ]*do[ ]*$/bcont
+/;[ ]*do/bcont
/^[ ]*done[ ]*&&[ ]*$/bdone
/^[ ]*done[ ]*$/bdone
/^[ ]*done[ ]*[<>|]/bdone
/^[ ]*done[ ]*)/bdone
-/||[ ]*exit[ ]/bcontinue
-/||[ ]*exit[ ]*$/bcontinue
+/||[ ]*exit[ ]/bcont
+/||[ ]*exit[ ]*$/bcont
# multi-line "if...elsif...else...fi"
-/^[ ]*if[ ]/bcontinue
-/^[ ]*then[ ]/bcontinue
-/^[ ]*then[ ]*$/bcontinue
-/;[ ]*then/bcontinue
+/^[ ]*if[ ]/bcont
+/^[ ]*then[ ]/bcont
+/^[ ]*then[ ]*$/bcont
+/;[ ]*then/bcont
/^[ ]*elif[ ]/belse
/^[ ]*elif[ ]*$/belse
/^[ ]*else[ ]/belse
@@ -234,10 +234,10 @@ s/.*\n//
}
}
# line ends with pipe "...|" -- valid; not missing "&&"
-/|[ ]*$/bcontinue
+/|[ ]*$/bcont
# missing end-of-line "&&" -- mark suspect
/&&[ ]*$/!s/^/?!AMP?!/
-:continue
+:cont
# retrieve and print previous line
x
n
@@ -250,7 +250,7 @@ s/\\\n//
bslurp
# check for multi-line double-quoted string "...\n..." -- fold to one line
-:dqstring
+:dqstr
# remove all quote pairs
s/"\([^"]*\)"/@!\1@!/g
# done if no dangling quote
@@ -258,13 +258,13 @@ s/"\([^"]*\)"/@!\1@!/g
# otherwise, slurp next line and try again
N
s/\n//
-bdqstring
+bdqstr
:dqdone
s/@!/"/g
bfolded
# check for multi-line single-quoted string '...\n...' -- fold to one line
-:sqstring
+:sqstr
# remove all quote pairs
s/'\([^']*\)'/@!\1@!/g
# done if no dangling quote
@@ -272,7 +272,7 @@ s/'\([^']*\)'/@!\1@!/g
# otherwise, slurp next line and try again
N
s/\n//
-bsqstring
+bsqstr
:sqdone
s/@!/'/g
bfolded
@@ -282,11 +282,11 @@ bfolded
:heredoc
s/^\(.*\)<<[ ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
s/[ ]*<<//
-:heredsub
+:hdocsub
N
/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
s/\n.*$//
- bheredsub
+ bhdocsub
}
s/^<[^>]*>//
s/\n.*$//
@@ -305,7 +305,7 @@ bcase
x
s/?!AMP?!//
x
-bcontinue
+bcont
# found "done" closing for-loop or while-loop, or "fi" closing if-then -- drop
# "suspect" from final contained line since that line legitimately lacks "&&"
@@ -321,10 +321,10 @@ bchkchn
# found nested multi-line "(...\n...)" -- pass through untouched
:nest
x
-:nstslurp
+:nstslrp
n
# closing ")" on own line -- stop nested slurp
-/^[ ]*)/bnstclose
+/^[ ]*)/bnstcl
# comment -- not closing ")" if in comment
/^[ ]*#/bnstcnt
# "$((...))" -- arithmetic expansion; not closing ")"
@@ -332,11 +332,11 @@ n
# "$(...)" -- command substitution; not closing ")"
/\$([^)][^)]*)[^)]*$/bnstcnt
# closing "...)" -- stop nested slurp
-/)/bnstclose
+/)/bnstcl
:nstcnt
x
-bnstslurp
-:nstclose
+bnstslrp
+:nstcl
s/^/>>/
# is it "))" which closes nested and parent subshells?
/)[ ]*)/bslurp
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
index 48c7bb0bbb..8c2ddcce95 100644
--- a/t/helper/.gitignore
+++ b/t/helper/.gitignore
@@ -1,4 +1,2 @@
/test-tool
/test-fake-ssh
-/test-line-buffer
-/test-svn-fe
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index f0aa80b98e..46e97b04eb 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -39,7 +39,9 @@ static void get_bloom_filter_for_commit(const struct object_id *commit_oid)
struct bloom_filter *filter;
setup_git_directory();
c = lookup_commit(the_repository, commit_oid);
- filter = get_bloom_filter(the_repository, c, 1);
+ filter = get_or_compute_bloom_filter(the_repository, c, 1,
+ &settings,
+ NULL);
print_bloom_filter(filter);
}
@@ -50,6 +52,8 @@ static const char *bloom_usage = "\n"
int cmd__bloom(int argc, const char **argv)
{
+ setup_git_directory();
+
if (argc < 2)
usage(bloom_usage);
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index 234c722b48..a6e936721f 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -126,7 +126,7 @@ int cmd__config(int argc, const char **argv)
goto exit1;
}
} else if (argc == 3 && !strcmp(argv[1], "get_string")) {
- if (!git_config_get_string_const(argv[2], &v)) {
+ if (!git_config_get_string_tmp(argv[2], &v)) {
printf("%s\n", v);
goto exit0;
} else {
diff --git a/t/helper/test-line-buffer.c b/t/helper/test-line-buffer.c
deleted file mode 100644
index 078dd7e29d..0000000000
--- a/t/helper/test-line-buffer.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * test-line-buffer.c: code to exercise the svn importer's input helper
- */
-
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "vcs-svn/line_buffer.h"
-
-static uint32_t strtouint32(const char *s)
-{
- char *end;
- uintmax_t n = strtoumax(s, &end, 10);
- if (*s == '\0' || *end != '\0')
- die("invalid count: %s", s);
- return (uint32_t) n;
-}
-
-static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
-{
- if (starts_with(command, "binary ")) {
- struct strbuf sb = STRBUF_INIT;
- strbuf_addch(&sb, '>');
- buffer_read_binary(buf, &sb, strtouint32(arg));
- fwrite(sb.buf, 1, sb.len, stdout);
- strbuf_release(&sb);
- } else if (starts_with(command, "copy ")) {
- buffer_copy_bytes(buf, strtouint32(arg));
- } else if (starts_with(command, "skip ")) {
- buffer_skip_bytes(buf, strtouint32(arg));
- } else {
- die("unrecognized command: %s", command);
- }
-}
-
-static void handle_line(const char *line, struct line_buffer *stdin_buf)
-{
- const char *arg = strchr(line, ' ');
- if (!arg)
- die("no argument in line: %s", line);
- handle_command(line, arg + 1, stdin_buf);
-}
-
-int cmd_main(int argc, const char **argv)
-{
- struct line_buffer stdin_buf = LINE_BUFFER_INIT;
- struct line_buffer file_buf = LINE_BUFFER_INIT;
- struct line_buffer *input = &stdin_buf;
- const char *filename;
- char *s;
-
- if (argc == 1)
- filename = NULL;
- else if (argc == 2)
- filename = argv[1];
- else
- usage("test-line-buffer [file | &fd] < script");
-
- if (buffer_init(&stdin_buf, NULL))
- die_errno("open error");
- if (filename) {
- if (*filename == '&') {
- if (buffer_fdinit(&file_buf, strtouint32(filename + 1)))
- die_errno("error opening fd %s", filename + 1);
- } else {
- if (buffer_init(&file_buf, filename))
- die_errno("error opening %s", filename);
- }
- input = &file_buf;
- }
-
- while ((s = buffer_read_line(&stdin_buf)))
- handle_line(s, input);
-
- if (filename && buffer_deinit(&file_buf))
- die("error reading from %s", filename);
- if (buffer_deinit(&stdin_buf))
- die("input error");
- if (ferror(stdout))
- die("output error");
- return 0;
-}
diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c
new file mode 100644
index 0000000000..42164d9898
--- /dev/null
+++ b/t/helper/test-proc-receive.c
@@ -0,0 +1,176 @@
+#include "cache.h"
+#include "connect.h"
+#include "parse-options.h"
+#include "pkt-line.h"
+#include "sigchain.h"
+#include "test-tool.h"
+
+static const char *proc_receive_usage[] = {
+ "test-tool proc-receive [<options>...]",
+ NULL
+};
+
+static int die_version;
+static int die_readline;
+static int no_push_options;
+static int use_atomic;
+static int use_push_options;
+static int verbose;
+static int version = 1;
+static struct string_list returns = STRING_LIST_INIT_NODUP;
+
+struct command {
+ struct command *next;
+ const char *error_string;
+ unsigned int skip_update:1,
+ did_not_exist:1;
+ int index;
+ struct object_id old_oid;
+ struct object_id new_oid;
+ char ref_name[FLEX_ARRAY]; /* more */
+};
+
+static void proc_receive_verison(struct packet_reader *reader) {
+ int server_version = 0;
+
+ for (;;) {
+ int linelen;
+
+ if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+ break;
+
+ if (reader->pktlen > 8 && starts_with(reader->line, "version=")) {
+ server_version = atoi(reader->line+8);
+ linelen = strlen(reader->line);
+ if (linelen < reader->pktlen) {
+ const char *feature_list = reader->line + linelen + 1;
+ if (parse_feature_request(feature_list, "atomic"))
+ use_atomic= 1;
+ if (parse_feature_request(feature_list, "push-options"))
+ use_push_options = 1;
+ }
+ }
+ }
+
+ if (server_version != 1 || die_version)
+ die("bad protocol version: %d", server_version);
+
+ packet_write_fmt(1, "version=%d%c%s\n",
+ version, '\0',
+ use_push_options && !no_push_options ? "push-options": "");
+ packet_flush(1);
+}
+
+static void proc_receive_read_commands(struct packet_reader *reader,
+ struct command **commands)
+{
+ struct command **tail = commands;
+
+ for (;;) {
+ struct object_id old_oid, new_oid;
+ struct command *cmd;
+ const char *refname;
+ const char *p;
+
+ if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+ break;
+
+ if (parse_oid_hex(reader->line, &old_oid, &p) ||
+ *p++ != ' ' ||
+ parse_oid_hex(p, &new_oid, &p) ||
+ *p++ != ' ' ||
+ die_readline)
+ die("protocol error: expected 'old new ref', got '%s'",
+ reader->line);
+ refname = p;
+ FLEX_ALLOC_STR(cmd, ref_name, refname);
+ oidcpy(&cmd->old_oid, &old_oid);
+ oidcpy(&cmd->new_oid, &new_oid);
+
+ *tail = cmd;
+ tail = &cmd->next;
+ }
+}
+
+static void proc_receive_read_push_options(struct packet_reader *reader,
+ struct string_list *options)
+{
+
+ if (no_push_options || !use_push_options)
+ return;
+
+ while (1) {
+ if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+ break;
+
+ string_list_append(options, reader->line);
+ }
+}
+
+int cmd__proc_receive(int argc, const char **argv)
+{
+ int nongit_ok = 0;
+ struct packet_reader reader;
+ struct command *commands = NULL;
+ struct string_list push_options = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
+ struct option options[] = {
+ OPT_BOOL(0, "no-push-options", &no_push_options,
+ "disable push options"),
+ OPT_BOOL(0, "die-version", &die_version,
+ "die during version negotiation"),
+ OPT_BOOL(0, "die-readline", &die_readline,
+ "die when readline"),
+ OPT_STRING_LIST('r', "return", &returns, "old/new/ref/status/msg",
+ "return of results"),
+ OPT__VERBOSE(&verbose, "be verbose"),
+ OPT_INTEGER('V', "version", &version,
+ "use this protocol version number"),
+ OPT_END()
+ };
+
+ setup_git_directory_gently(&nongit_ok);
+
+ argc = parse_options(argc, argv, "test-tools", options, proc_receive_usage, 0);
+ if (argc > 0)
+ usage_msg_opt("Too many arguments.", proc_receive_usage, options);
+ packet_reader_init(&reader, 0, NULL, 0,
+ PACKET_READ_CHOMP_NEWLINE |
+ PACKET_READ_DIE_ON_ERR_PACKET);
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+ proc_receive_verison(&reader);
+ proc_receive_read_commands(&reader, &commands);
+ proc_receive_read_push_options(&reader, &push_options);
+
+ if (verbose) {
+ struct command *cmd;
+
+ if (use_push_options || use_atomic)
+ fprintf(stderr, "proc-receive:%s%s\n",
+ use_atomic? " atomic": "",
+ use_push_options ? " push_options": "");
+
+ for (cmd = commands; cmd; cmd = cmd->next)
+ fprintf(stderr, "proc-receive< %s %s %s\n",
+ oid_to_hex(&cmd->old_oid),
+ oid_to_hex(&cmd->new_oid),
+ cmd->ref_name);
+
+ if (push_options.nr > 0)
+ for_each_string_list_item(item, &push_options)
+ fprintf(stderr, "proc-receive< %s\n", item->string);
+
+ if (returns.nr)
+ for_each_string_list_item(item, &returns)
+ fprintf(stderr, "proc-receive> %s\n", item->string);
+ }
+
+ if (returns.nr)
+ for_each_string_list_item(item, &returns)
+ packet_write_fmt(1, "%s\n", item->string);
+ packet_flush(1);
+ sigchain_pop(SIGPIPE);
+
+ return 0;
+}
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 6d0c962438..5f585a1725 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -12,11 +12,12 @@ int cmd__read_graph(int argc, const char **argv)
setup_git_directory();
odb = the_repository->objects->odb;
+ prepare_repo_settings(the_repository);
+
graph = read_commit_graph_one(the_repository, odb);
if (!graph)
return 1;
-
printf("header: %08x %d %d %d %d\n",
ntohl(*(uint32_t*)graph->data),
*(unsigned char*)(graph->data + 4),
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 831b586d02..2430880f78 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -7,14 +7,18 @@
static int read_midx_file(const char *object_dir)
{
uint32_t i;
- struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+ struct multi_pack_index *m;
+
+ setup_git_directory();
+ m = load_multi_pack_index(object_dir, 1);
if (!m)
return 1;
- printf("header: %08x %d %d %d\n",
+ printf("header: %08x %d %d %d %d\n",
m->signature,
m->version,
+ m->hash_len,
m->num_chunks,
m->num_packs);
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index 1646aa25d8..7ae03dc712 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -12,7 +12,7 @@
#include "git-compat-util.h"
#include "cache.h"
#include "run-command.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "strbuf.h"
#include "parse-options.h"
#include "string-list.h"
@@ -31,7 +31,7 @@ static int parallel_next(struct child_process *cp,
if (number_callbacks >= 4)
return 0;
- argv_array_pushv(&cp->args, d->argv);
+ strvec_pushv(&cp->args, d->argv);
strbuf_addstr(err, "preloaded output of a child\n");
number_callbacks++;
return 1;
@@ -72,19 +72,19 @@ static int next_test(struct child_process *cp, struct strbuf *err, void *cb,
return 0;
test = suite->tests.items[suite->next++].string;
- argv_array_pushl(&cp->args, "sh", test, NULL);
+ strvec_pushl(&cp->args, "sh", test, NULL);
if (suite->quiet)
- argv_array_push(&cp->args, "--quiet");
+ strvec_push(&cp->args, "--quiet");
if (suite->immediate)
- argv_array_push(&cp->args, "-i");
+ strvec_push(&cp->args, "-i");
if (suite->verbose)
- argv_array_push(&cp->args, "-v");
+ strvec_push(&cp->args, "-v");
if (suite->verbose_log)
- argv_array_push(&cp->args, "-V");
+ strvec_push(&cp->args, "-V");
if (suite->trace)
- argv_array_push(&cp->args, "-x");
+ strvec_push(&cp->args, "-x");
if (suite->write_junit_xml)
- argv_array_push(&cp->args, "--write-junit-xml");
+ strvec_push(&cp->args, "--write-junit-xml");
strbuf_addf(err, "Output of '%s':\n", test);
*task_cb = (void *)test;
@@ -220,7 +220,7 @@ static int quote_stress_test(int argc, const char **argv)
char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
int i, j, k, trials = 100, skip = 0, msys2 = 0;
struct strbuf out = STRBUF_INIT;
- struct argv_array args = ARGV_ARRAY_INIT;
+ struct strvec args = STRVEC_INIT;
struct option options[] = {
OPT_INTEGER('n', "trials", &trials, "Number of trials"),
OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
@@ -241,20 +241,20 @@ static int quote_stress_test(int argc, const char **argv)
size_t arg_count, arg_offset;
int ret = 0;
- argv_array_clear(&args);
+ strvec_clear(&args);
if (msys2)
- argv_array_pushl(&args, "sh", "-c",
- "printf %s\\\\0 \"$@\"", "skip", NULL);
+ strvec_pushl(&args, "sh", "-c",
+ "printf %s\\\\0 \"$@\"", "skip", NULL);
else
- argv_array_pushl(&args, "test-tool", "run-command",
- "quote-echo", NULL);
- arg_offset = args.argc;
+ strvec_pushl(&args, "test-tool", "run-command",
+ "quote-echo", NULL);
+ arg_offset = args.nr;
if (argc > 0) {
trials = 1;
arg_count = argc;
for (j = 0; j < arg_count; j++)
- argv_array_push(&args, argv[j]);
+ strvec_push(&args, argv[j]);
} else {
arg_count = 1 + (my_random() % 5);
for (j = 0; j < arg_count; j++) {
@@ -268,20 +268,20 @@ static int quote_stress_test(int argc, const char **argv)
ARRAY_SIZE(special)];
buf[arg_len] = '\0';
- argv_array_push(&args, buf);
+ strvec_push(&args, buf);
}
}
if (i < skip)
continue;
- cp.argv = args.argv;
+ cp.argv = args.v;
strbuf_reset(&out);
if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
return error("Failed to spawn child process");
for (j = 0, k = 0; j < arg_count; j++) {
- const char *arg = args.argv[j + arg_offset];
+ const char *arg = args.v[j + arg_offset];
if (strcmp(arg, out.buf + k))
ret = error("incorrectly quoted arg: '%s', "
@@ -298,10 +298,10 @@ static int quote_stress_test(int argc, const char **argv)
fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
for (j = 0; j < arg_count; j++)
fprintf(stderr, "arg #%d: '%s'\n",
- (int)j, args.argv[j + arg_offset]);
+ (int)j, args.v[j + arg_offset]);
strbuf_release(&out);
- argv_array_clear(&args);
+ strvec_clear(&args);
return ret;
}
@@ -311,7 +311,7 @@ static int quote_stress_test(int argc, const char **argv)
}
strbuf_release(&out);
- argv_array_clear(&args);
+ strvec_clear(&args);
return 0;
}
@@ -338,8 +338,8 @@ static int inherit_handle(const char *argv0)
xsnprintf(path, sizeof(path), "out-XXXXXX");
tmp = xmkstemp(path);
- argv_array_pushl(&cp.args,
- "test-tool", argv0, "inherited-handle-child", NULL);
+ strvec_pushl(&cp.args,
+ "test-tool", argv0, "inherited-handle-child", NULL);
cp.in = -1;
cp.no_stdout = cp.no_stderr = 1;
if (start_command(&cp) < 0)
@@ -391,7 +391,7 @@ int cmd__run_command(int argc, const char **argv)
while (!strcmp(argv[1], "env")) {
if (!argv[2])
die("env specifier without a value");
- argv_array_push(&proc.env_array, argv[2]);
+ strvec_push(&proc.env_array, argv[2]);
argv += 2;
argc -= 2;
}
diff --git a/t/helper/test-svn-fe.c b/t/helper/test-svn-fe.c
deleted file mode 100644
index 7667c0803f..0000000000
--- a/t/helper/test-svn-fe.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * test-svn-fe: Code to exercise the svn import lib
- */
-
-#include "git-compat-util.h"
-#include "vcs-svn/svndump.h"
-#include "vcs-svn/svndiff.h"
-#include "vcs-svn/sliding_window.h"
-#include "vcs-svn/line_buffer.h"
-
-static const char test_svnfe_usage[] =
- "test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)";
-
-static int apply_delta(int argc, const char **argv)
-{
- struct line_buffer preimage = LINE_BUFFER_INIT;
- struct line_buffer delta = LINE_BUFFER_INIT;
- struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage, -1);
-
- if (argc != 5)
- usage(test_svnfe_usage);
-
- if (buffer_init(&preimage, argv[2]))
- die_errno("cannot open preimage");
- if (buffer_init(&delta, argv[3]))
- die_errno("cannot open delta");
- if (svndiff0_apply(&delta, (off_t) strtoumax(argv[4], NULL, 0),
- &preimage_view, stdout))
- return 1;
- if (buffer_deinit(&preimage))
- die_errno("cannot close preimage");
- if (buffer_deinit(&delta))
- die_errno("cannot close delta");
- strbuf_release(&preimage_view.buf);
- return 0;
-}
-
-int cmd_main(int argc, const char **argv)
-{
- if (argc == 2) {
- if (svndump_init(argv[1]))
- return 1;
- svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs");
- svndump_deinit();
- svndump_reset();
- return 0;
- }
-
- if (argc >= 2 && !strcmp(argv[1], "-d"))
- return apply_delta(argc, argv);
- usage(test_svnfe_usage);
-}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 590b2efca7..a0d3966b29 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -46,6 +46,7 @@ static struct test_cmd cmds[] = {
{ "path-utils", cmd__path_utils },
{ "pkt-line", cmd__pkt_line },
{ "prio-queue", cmd__prio_queue },
+ { "proc-receive", cmd__proc_receive},
{ "progress", cmd__progress },
{ "reach", cmd__reach },
{ "read-cache", cmd__read_cache },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index ddc8e990e9..07034d3f38 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -35,6 +35,7 @@ int cmd__parse_pathspec_file(int argc, const char** argv);
int cmd__path_utils(int argc, const char **argv);
int cmd__pkt_line(int argc, const char **argv);
int cmd__prio_queue(int argc, const char **argv);
+int cmd__proc_receive(int argc, const char **argv);
int cmd__progress(int argc, const char **argv);
int cmd__reach(int argc, const char **argv);
int cmd__read_cache(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index 197819c872..823f33ceff 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -1,6 +1,6 @@
#include "test-tool.h"
#include "cache.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "run-command.h"
#include "exec-cmd.h"
#include "config.h"
diff --git a/t/lib-pack.sh b/t/lib-pack.sh
index f3463170b3..bb8938ccbe 100644
--- a/t/lib-pack.sh
+++ b/t/lib-pack.sh
@@ -35,8 +35,6 @@ pack_header () {
# have hardcoded some well-known objects. See the case statements below for the
# complete list.
pack_obj () {
- test_oid_init
-
case "$1" in
# empty blob
$EMPTY_BLOB)
@@ -93,6 +91,14 @@ pack_obj () {
;;
esac
;;
+ # blob containing "\3\326"
+ 471819e8c52bf11513f100b2810a8aa0622d5cd3d1c913758a071dd4b3bad8fe)
+ case "$2" in
+ '')
+ printf '\062\170\234\143\276\006\000\000\336\000\332'
+ return
+ ;;
+ esac
esac
# If it's not a delta, we can convince pack-objects to generate a pack
@@ -113,7 +119,6 @@ pack_obj () {
# Compute and append pack trailer to "$1"
pack_trailer () {
- test_oid_init &&
test-tool $(test_oid algo) -b <"$1" >trailer.tmp &&
cat trailer.tmp >>"$1" &&
rm -f trailer.tmp
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 07c822c8ff..87a759149f 100644
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -196,7 +196,6 @@ test_git_directory_exists () {
# the submodule repo if it doesn't exist and configures the most problematic
# settings for diff.ignoreSubmodules.
prolog () {
- test_oid_init &&
(test -d submodule_update_repo || create_lib_submodule_repo) &&
test_config_global diff.ignoreSubmodules all &&
test_config diff.ignoreSubmodules all
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
index b0ed4767e3..fba6778ca3 100644
--- a/t/lib-t6000.sh
+++ b/t/lib-t6000.sh
@@ -1,7 +1,5 @@
: included from 6002 and others
-mkdir -p .git/refs/tags
-
>sed.script
# Answer the sha1 has associated with the tag. The tag must exist under refs/tags
@@ -26,7 +24,8 @@ save_tag () {
_tag=$1
test -n "$_tag" || error "usage: save_tag tag commit-args ..."
shift 1
- "$@" >".git/refs/tags/$_tag"
+
+ git update-ref "refs/tags/$_tag" $("$@")
echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp
cat sed.script >>sed.script.tmp
diff --git a/t/perf/README b/t/perf/README
index c7b70e2d28..bd649afa97 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -84,6 +84,15 @@ You can set the following variables (also in your config.mak):
probably be about linux.git size for optimal results.
Both default to the git.git you are running from.
+ GIT_PERF_EXTRA
+ Boolean to enable additional tests. Most test scripts are
+ written to detect regressions between two versions of Git, and
+ the output will compare timings for individual tests between
+ those versions. Some scripts have additional tests which are not
+ run by default, that show patterns within a single version of
+ Git (e.g., performance of index-pack as the number of threads
+ changes). These can be enabled with GIT_PERF_EXTRA.
+
You can also pass the options taken by ordinary git tests; the most
useful one is:
diff --git a/t/perf/p1400-update-ref.sh b/t/perf/p1400-update-ref.sh
index d275a81248..ce5ac3ed85 100755
--- a/t/perf/p1400-update-ref.sh
+++ b/t/perf/p1400-update-ref.sh
@@ -7,11 +7,13 @@ test_description="Tests performance of update-ref"
test_perf_fresh_repo
test_expect_success "setup" '
+ git init --bare target-repo.git &&
test_commit PRE &&
test_commit POST &&
printf "create refs/heads/%d PRE\n" $(test_seq 1000) >create &&
printf "update refs/heads/%d POST PRE\n" $(test_seq 1000) >update &&
- printf "delete refs/heads/%d POST\n" $(test_seq 1000) >delete
+ printf "delete refs/heads/%d POST\n" $(test_seq 1000) >delete &&
+ git update-ref --stdin <create
'
test_perf "update-ref" '
@@ -24,9 +26,14 @@ test_perf "update-ref" '
'
test_perf "update-ref --stdin" '
- git update-ref --stdin <create &&
git update-ref --stdin <update &&
- git update-ref --stdin <delete
+ git update-ref --stdin <delete &&
+ git update-ref --stdin <create
+'
+
+test_perf "nonatomic push" '
+ git push ./target-repo.git $(test_seq 1000) &&
+ git push --delete ./target-repo.git $(test_seq 1000)
'
test_done
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
index a9b3e112d9..228593d9ad 100755
--- a/t/perf/p5302-pack-index.sh
+++ b/t/perf/p5302-pack-index.sh
@@ -13,35 +13,36 @@ test_expect_success 'repack' '
export PACK
'
-test_perf 'index-pack 0 threads' '
- rm -rf repo.git &&
- git init --bare repo.git &&
- GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK
-'
-
-test_perf 'index-pack 1 thread ' '
- rm -rf repo.git &&
- git init --bare repo.git &&
- GIT_DIR=repo.git GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
+# Rather than counting up and doubling each time, count down from the endpoint,
+# halving each time. That ensures that our final test uses as many threads as
+# CPUs, even if it isn't a power of 2.
+test_expect_success 'set up thread-counting tests' '
+ t=$(test-tool online-cpus) &&
+ threads= &&
+ while test $t -gt 0
+ do
+ threads="$t $threads"
+ t=$((t / 2))
+ done
'
-test_perf 'index-pack 2 threads' '
+test_perf PERF_EXTRA 'index-pack 0 threads' '
rm -rf repo.git &&
git init --bare repo.git &&
- GIT_DIR=repo.git git index-pack --threads=2 --stdin < $PACK
-'
-
-test_perf 'index-pack 4 threads' '
- rm -rf repo.git &&
- git init --bare repo.git &&
- GIT_DIR=repo.git git index-pack --threads=4 --stdin < $PACK
+ GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK
'
-test_perf 'index-pack 8 threads' '
- rm -rf repo.git &&
- git init --bare repo.git &&
- GIT_DIR=repo.git git index-pack --threads=8 --stdin < $PACK
-'
+for t in $threads
+do
+ THREADS=$t
+ export THREADS
+ test_perf PERF_EXTRA "index-pack $t threads" '
+ rm -rf repo.git &&
+ git init --bare repo.git &&
+ GIT_DIR=repo.git GIT_FORCE_THREADS=1 \
+ git index-pack --threads=$THREADS --stdin <$PACK
+ '
+done
test_perf 'index-pack default number of threads' '
rm -rf repo.git &&
diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh
index 7ee791669a..f4c2ab0584 100755
--- a/t/perf/p5303-many-packs.sh
+++ b/t/perf/p5303-many-packs.sh
@@ -73,6 +73,10 @@ do
git rev-list --objects --all >/dev/null
'
+ test_perf "abbrev-commit ($nr_packs)" '
+ git rev-list --abbrev-commit HEAD >/dev/null
+ '
+
# This simulates the interesting part of the repack, which is the
# actual pack generation, without smudging the on-disk setup
# between trials.
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index 13e389367a..821581a885 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -245,3 +245,5 @@ test_at_end_hook_ () {
test_export () {
export "$@"
}
+
+test_lazy_prereq PERF_EXTRA 'test_bool_env GIT_PERF_EXTRA false'
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 2ff176cd5d..923281af93 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -891,10 +891,6 @@ test_expect_success 'test_atexit is run' "
test_path_is_missing also-clean-atexit
"
-test_expect_success 'test_oid setup' '
- test_oid_init
-'
-
test_expect_success 'test_oid provides sane info by default' '
test_oid zero >actual &&
grep "^00*\$" actual &&
@@ -928,6 +924,17 @@ test_expect_success 'test_oid can look up data for SHA-256' '
test "$hexsz" -eq 64
'
+test_expect_success 'test_oid can look up data for a specified algorithm' '
+ rawsz="$(test_oid --hash=sha1 rawsz)" &&
+ hexsz="$(test_oid --hash=sha1 hexsz)" &&
+ test "$rawsz" -eq 20 &&
+ test "$hexsz" -eq 40 &&
+ rawsz="$(test_oid --hash=sha256 rawsz)" &&
+ hexsz="$(test_oid --hash=sha256 hexsz)" &&
+ test "$rawsz" -eq 32 &&
+ test "$hexsz" -eq 64
+'
+
test_expect_success 'test_bool_env' '
(
sane_unset envvar &&
@@ -1271,4 +1278,22 @@ test_expect_success 'very long name in the index handled sanely' '
test $len = 4098
'
+test_expect_success 'test_must_fail on a failing git command' '
+ test_must_fail git notacommand
+'
+
+test_expect_success 'test_must_fail on a failing git command with env' '
+ test_must_fail env var1=a var2=b git notacommand
+'
+
+test_expect_success 'test_must_fail rejects a non-git command' '
+ ! test_must_fail grep ^$ notafile 2>err &&
+ grep -F "test_must_fail: only '"'"'git'"'"' is allowed" err
+'
+
+test_expect_success 'test_must_fail rejects a non-git command with env' '
+ ! test_must_fail env var1=a var2=b grep ^$ notafile 2>err &&
+ grep -F "test_must_fail: only '"'"'git'"'"' is allowed" err
+'
+
test_done
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 6d2467995e..2f7c3dcd0f 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -316,6 +316,28 @@ test_expect_success 'init with separate gitdir' '
test_path_is_dir realgitdir/refs
'
+test_expect_success 'explicit bare & --separate-git-dir incompatible' '
+ test_must_fail git init --bare --separate-git-dir goop.git bare.git 2>err &&
+ test_i18ngrep "mutually exclusive" err
+'
+
+test_expect_success 'implicit bare & --separate-git-dir incompatible' '
+ test_when_finished "rm -rf bare.git" &&
+ mkdir -p bare.git &&
+ test_must_fail env GIT_DIR=. \
+ git -C bare.git init --separate-git-dir goop.git 2>err &&
+ test_i18ngrep "incompatible" err
+'
+
+test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
+ test_when_finished "rm -rf bare.git linkwt seprepo" &&
+ test_commit gumby &&
+ git clone --bare . bare.git &&
+ git -C bare.git worktree add --detach ../linkwt &&
+ test_must_fail git -C linkwt init --separate-git-dir seprepo 2>err &&
+ test_i18ngrep "incompatible" err
+'
+
test_lazy_prereq GETCWD_IGNORES_PERMS '
base=GETCWD_TEST_BASE_DIR &&
mkdir -p $base/dir &&
@@ -392,6 +414,25 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
test_path_is_dir realgitdir/refs
'
+sep_git_dir_worktree () {
+ test_when_finished "rm -rf mainwt linkwt seprepo" &&
+ git init mainwt &&
+ test_commit -C mainwt gumby &&
+ git -C mainwt worktree add --detach ../linkwt &&
+ git -C "$1" init --separate-git-dir ../seprepo &&
+ git -C mainwt rev-parse --git-common-dir >expect &&
+ git -C linkwt rev-parse --git-common-dir >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 're-init to move gitdir with linked worktrees' '
+ sep_git_dir_worktree mainwt
+'
+
+test_expect_success 're-init to move gitdir within linked worktree' '
+ sep_git_dir_worktree linkwt
+'
+
test_expect_success MINGW '.git hidden' '
rm -rf newdir &&
(
@@ -441,6 +482,39 @@ test_expect_success 're-init from a linked worktree' '
)
'
+test_expect_success 'init honors GIT_DEFAULT_HASH' '
+ GIT_DEFAULT_HASH=sha1 git init sha1 &&
+ git -C sha1 rev-parse --show-object-format >actual &&
+ echo sha1 >expected &&
+ test_cmp expected actual &&
+ GIT_DEFAULT_HASH=sha256 git init sha256 &&
+ git -C sha256 rev-parse --show-object-format >actual &&
+ echo sha256 >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'init honors --object-format' '
+ git init --object-format=sha1 explicit-sha1 &&
+ git -C explicit-sha1 rev-parse --show-object-format >actual &&
+ echo sha1 >expected &&
+ test_cmp expected actual &&
+ git init --object-format=sha256 explicit-sha256 &&
+ git -C explicit-sha256 rev-parse --show-object-format >actual &&
+ echo sha256 >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'extensions.objectFormat is not allowed with repo version 0' '
+ git init --object-format=sha256 explicit-v0 &&
+ git -C explicit-v0 config core.repositoryformatversion 0 &&
+ test_must_fail git -C explicit-v0 rev-parse --show-object-format
+'
+
+test_expect_success 'init rejects attempts to initialize with different hash' '
+ test_must_fail git -C sha1 init --object-format=sha256 &&
+ test_must_fail git -C sha256 init --object-format=sha1
+'
+
test_expect_success MINGW 'core.hidedotfiles = false' '
git config --global core.hidedotfiles false &&
rm -rf newdir &&
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index f8178ee4e3..14cafc138b 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -44,7 +44,7 @@ Magic arguments
--no-ambiguous negative ambiguity
Standard options
- --abbrev[=<n>] use <n> digits to display SHA-1s
+ --abbrev[=<n>] use <n> digits to display object names
-v, --verbose be verbose
-n, --dry-run dry run
-q, --quiet be quiet
diff --git a/t/t0081-line-buffer.sh b/t/t0081-line-buffer.sh
deleted file mode 100755
index ce92e6acad..0000000000
--- a/t/t0081-line-buffer.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/sh
-
-test_description="Test the svn importer's input handling routines.
-
-These tests provide some simple checks that the line_buffer API
-behaves as advertised.
-
-While at it, check that input of newlines and null bytes are handled
-correctly.
-"
-. ./test-lib.sh
-
-test_expect_success 'hello world' '
- echo ">HELLO" >expect &&
- test-line-buffer <<-\EOF >actual &&
- binary 6
- HELLO
- EOF
- test_cmp expect actual
-'
-
-test_expect_success '0-length read, send along greeting' '
- echo ">HELLO" >expect &&
- test-line-buffer <<-\EOF >actual &&
- binary 0
- copy 6
- HELLO
- EOF
- test_cmp expect actual
-'
-
-test_expect_success !MINGW 'read from file descriptor' '
- rm -f input &&
- echo hello >expect &&
- echo hello >input &&
- echo copy 6 |
- test-line-buffer "&4" 4<input >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'skip, copy null byte' '
- echo Q | q_to_nul >expect &&
- q_to_nul <<-\EOF | test-line-buffer >actual &&
- skip 2
- Q
- copy 2
- Q
- EOF
- test_cmp expect actual
-'
-
-test_expect_success 'read null byte' '
- echo ">QhelloQ" | q_to_nul >expect &&
- q_to_nul <<-\EOF | test-line-buffer >actual &&
- binary 8
- QhelloQ
- EOF
- test_cmp expect actual
-'
-
-test_expect_success 'long reads are truncated' '
- echo ">foo" >expect &&
- test-line-buffer <<-\EOF >actual &&
- binary 5
- foo
- EOF
- test_cmp expect actual
-'
-
-test_expect_success 'long copies are truncated' '
- printf "%s\n" ">" foo >expect &&
- test-line-buffer <<-\EOF >actual &&
- binary 1
-
- copy 5
- foo
- EOF
- test_cmp expect actual
-'
-
-test_expect_success 'long binary reads are truncated' '
- echo ">foo" >expect &&
- test-line-buffer <<-\EOF >actual &&
- binary 5
- foo
- EOF
- test_cmp expect actual
-'
-
-test_done
diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh
index 232ba2c485..7e4ab1795f 100755
--- a/t/t0095-bloom.sh
+++ b/t/t0095-bloom.sh
@@ -71,8 +71,8 @@ test_expect_success 'get bloom filters for commit with no changes' '
git init &&
git commit --allow-empty -m "c0" &&
cat >expect <<-\EOF &&
- Filter_Length:0
- Filter_Data:
+ Filter_Length:1
+ Filter_Data:00|
EOF
test-tool bloom get_filter_for_commit "$(git rev-parse HEAD)" >actual &&
test_cmp expect actual
@@ -107,8 +107,8 @@ test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' '
git add bigDir &&
git commit -m "commit with 513 changes" &&
cat >expect <<-\EOF &&
- Filter_Length:0
- Filter_Data:
+ Filter_Length:1
+ Filter_Data:ff|
EOF
test-tool bloom get_filter_for_commit "$(git rev-parse HEAD)" >actual &&
test_cmp expect actual
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 6aa0f313bd..584a039b85 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -42,7 +42,7 @@ test_expect_success 'convert shallow clone to partial clone' '
test_cmp_config -C client 1 core.repositoryformatversion
'
-test_expect_success 'convert to partial clone with noop extension' '
+test_expect_success SHA1 'convert to partial clone with noop extension' '
rm -fr server client &&
test_create_repo server &&
test_commit -C server my_commit 1 &&
@@ -53,7 +53,7 @@ test_expect_success 'convert to partial clone with noop extension' '
git -C client fetch --unshallow --filter="blob:none"
'
-test_expect_success 'converting to partial clone fails with unrecognized extension' '
+test_expect_success SHA1 'converting to partial clone fails with unrecognized extension' '
rm -fr server client &&
test_create_repo server &&
test_commit -C server my_commit 1 &&
@@ -183,7 +183,7 @@ test_expect_success 'missing CLI object, but promised, passes fsck' '
'
test_expect_success 'fetching of missing objects' '
- rm -rf repo &&
+ rm -rf repo err &&
test_create_repo server &&
test_commit -C server foo &&
git -C server repack -a -d --write-bitmap-index &&
@@ -194,7 +194,10 @@ test_expect_success 'fetching of missing objects' '
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "origin" &&
- git -C repo cat-file -p "$HASH" &&
+ git -C repo cat-file -p "$HASH" 2>err &&
+
+ # Ensure that no spurious FETCH_HEAD messages are written
+ ! grep FETCH_HEAD err &&
# Ensure that the .promisor file is written, and check that its
# associated packfile contains the object
@@ -214,7 +217,7 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled'
rm -rf repo/.git/objects/* &&
rm -f trace &&
GIT_TRACE_PACKET="$(pwd)/trace" git -C repo cat-file -p "$HASH" &&
- grep "git< fetch=.*ref-in-want" trace
+ grep "fetch< fetch=.*ref-in-want" trace
'
test_expect_success 'fetching of missing objects from another promisor remote' '
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 43c4be1e5e..2f501d2dc9 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -140,8 +140,6 @@ test_expect_success '--batch-check without %(rest) considers whole line' '
test_cmp expect actual
'
-test_oid_init
-
tree_sha1=$(git write-tree)
tree_size=$(($(test_oid rawsz) + 13))
tree_pretty_content="100644 blob $hello_sha1 hello"
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index 6a56d1ca24..61e89a8071 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -12,7 +12,6 @@ file_size () {
}
test_expect_success setup '
- test_oid_init &&
# clone does not allow us to pass core.bigfilethreshold to
# new repos, so set core.bigfilethreshold globally
git config --global core.bigfilethreshold 200k &&
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 7cd45fc139..84acfc48b6 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -369,7 +369,7 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged stat
git clone repo unmerged &&
cat >input <<-EOF &&
- 0 0000000000000000000000000000000000000000 folder1/a
+ 0 $ZERO_OID folder1/a
100644 $(git -C unmerged rev-parse HEAD:folder1/a) 1 folder1/a
EOF
git -C unmerged update-index --index-info <input &&
@@ -396,7 +396,7 @@ test_expect_success 'sparse-checkout reapply' '
echo dirty >tweak/deep/deeper2/a &&
cat >input <<-EOF &&
- 0 0000000000000000000000000000000000000000 folder1/a
+ 0 $ZERO_OID folder1/a
100644 $(git -C tweak rev-parse HEAD:folder1/a) 1 folder1/a
EOF
git -C tweak update-index --index-info <input &&
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 97ebfe1f9d..825d9a184f 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -1836,11 +1836,14 @@ test_expect_success '--show-scope with --show-origin' '
test_cmp expect output
'
-test_expect_success '--local requires a repo' '
- # we expect 128 to ensure that we do not simply
- # fail to find anything and return code "1"
- test_expect_code 128 nongit git config --local foo.bar
-'
+for opt in --local --worktree
+do
+ test_expect_success "$opt requires a repo" '
+ # we expect 128 to ensure that we do not simply
+ # fail to find anything and return code "1"
+ test_expect_code 128 nongit git config $opt foo.bar
+ '
+done
cat >.git/config <<-\EOF &&
[core]
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index d60c042ce8..0acabb6d11 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -87,6 +87,9 @@ allow 1
allow 1 noop
abort 1 no-such-extension
allow 0 no-such-extension
+allow 0 noop
+abort 0 noop-v1
+allow 1 noop-v1
EOF
test_expect_success 'precious-objects allowed' '
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 27171f8261..770e7be363 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -160,10 +160,10 @@ test_expect_success 'core.logAllRefUpdates=always creates reflog by default' '
git reflog exists $outside
'
-test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' '
+test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' '
test_config core.logAllRefUpdates always &&
git update-ref ORIG_HEAD $A &&
- test_must_fail git reflog exists ORIG_HEAD
+ git reflog exists ORIG_HEAD
'
test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' '
@@ -324,7 +324,7 @@ test_expect_success "create $m (logged by config)" '
test_expect_success "update $m (logged by config)" '
test_config core.logAllRefUpdates true &&
GIT_COMMITTER_DATE="2005-05-26 23:33" \
- git update-ref HEAD'" $B $A "'-m "Switch" &&
+ git update-ref HEAD $B $A -m "Switch" &&
test $B = $(git show-ref -s --verify $m)
'
test_expect_success "set $m (logged by config)" '
@@ -475,57 +475,57 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER
test_expect_success 'given old value for missing pseudoref, do not create' '
test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
- test_path_is_missing .git/PSEUDOREF &&
- test_i18ngrep "could not read ref" err
+ test_must_fail git rev-parse PSEUDOREF &&
+ test_i18ngrep "unable to resolve reference" err
'
test_expect_success 'create pseudoref' '
git update-ref PSEUDOREF $A &&
- test $A = $(cat .git/PSEUDOREF)
+ test $A = $(git rev-parse PSEUDOREF)
'
test_expect_success 'overwrite pseudoref with no old value given' '
git update-ref PSEUDOREF $B &&
- test $B = $(cat .git/PSEUDOREF)
+ test $B = $(git rev-parse PSEUDOREF)
'
test_expect_success 'overwrite pseudoref with correct old value' '
git update-ref PSEUDOREF $C $B &&
- test $C = $(cat .git/PSEUDOREF)
+ test $C = $(git rev-parse PSEUDOREF)
'
test_expect_success 'do not overwrite pseudoref with wrong old value' '
test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
- test $C = $(cat .git/PSEUDOREF) &&
- test_i18ngrep "unexpected object ID" err
+ test $C = $(git rev-parse PSEUDOREF) &&
+ test_i18ngrep "cannot lock ref.*expected" err
'
test_expect_success 'delete pseudoref' '
git update-ref -d PSEUDOREF &&
- test_path_is_missing .git/PSEUDOREF
+ test_must_fail git rev-parse PSEUDOREF
'
test_expect_success 'do not delete pseudoref with wrong old value' '
git update-ref PSEUDOREF $A &&
test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
- test $A = $(cat .git/PSEUDOREF) &&
- test_i18ngrep "unexpected object ID" err
+ test $A = $(git rev-parse PSEUDOREF) &&
+ test_i18ngrep "cannot lock ref.*expected" err
'
test_expect_success 'delete pseudoref with correct old value' '
git update-ref -d PSEUDOREF $A &&
- test_path_is_missing .git/PSEUDOREF
+ test_must_fail git rev-parse PSEUDOREF
'
test_expect_success 'create pseudoref with old OID zero' '
git update-ref PSEUDOREF $A $Z &&
- test $A = $(cat .git/PSEUDOREF)
+ test $A = $(git rev-parse PSEUDOREF)
'
test_expect_success 'do not overwrite pseudoref with old OID zero' '
test_when_finished git update-ref -d PSEUDOREF &&
test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
- test $A = $(cat .git/PSEUDOREF) &&
+ test $A = $(git rev-parse PSEUDOREF) &&
test_i18ngrep "already exists" err
'
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
index 331899ddc4..74af927fba 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -31,7 +31,10 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
git rev-parse FOO -- &&
git rev-parse refs/tags/new-tag -- &&
- $RUN delete-refs 0 nothing FOO refs/tags/new-tag &&
+ m=$(git rev-parse master) &&
+ REF_NO_DEREF=1 &&
+ $RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
+ test_must_fail git rev-parse --symbolic-full-name FOO &&
test_must_fail git rev-parse FOO -- &&
test_must_fail git rev-parse refs/tags/new-tag --
'
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 76d9b744a6..730a43d9dd 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -54,7 +54,6 @@ check_dont_have () {
}
test_expect_success setup '
- test_oid_init &&
mkdir -p A/B &&
echo rat >C &&
echo ox >A/D &&
diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh
index da58d867a5..f6e741c6c0 100755
--- a/t/t1416-ref-transaction-hooks.sh
+++ b/t/t1416-ref-transaction-hooks.sh
@@ -7,6 +7,7 @@ test_description='reference transaction hooks'
test_expect_success setup '
mkdir -p .git/hooks &&
test_commit PRE &&
+ PRE_OID=$(git rev-parse PRE) &&
test_commit POST &&
POST_OID=$(git rev-parse POST)
'
@@ -106,4 +107,30 @@ test_expect_success 'hook gets all queued updates in aborted state' '
test_cmp expect actual
'
+test_expect_success 'interleaving hook calls succeed' '
+ test_when_finished "rm -r target-repo.git" &&
+
+ git init --bare target-repo.git &&
+
+ write_script target-repo.git/hooks/reference-transaction <<-\EOF &&
+ echo $0 "$@" >>actual
+ EOF
+
+ write_script target-repo.git/hooks/update <<-\EOF &&
+ echo $0 "$@" >>actual
+ EOF
+
+ cat >expect <<-EOF &&
+ hooks/update refs/tags/PRE $ZERO_OID $PRE_OID
+ hooks/reference-transaction prepared
+ hooks/reference-transaction committed
+ hooks/update refs/tags/POST $ZERO_OID $POST_OID
+ hooks/reference-transaction prepared
+ hooks/reference-transaction committed
+ EOF
+
+ git push ./target-repo.git PRE POST &&
+ test_cmp expect target-repo.git/actual
+'
+
test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 344a2aad82..b17f5c21fb 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -9,7 +9,6 @@ test_description='git fsck random collection of tests
. ./test-lib.sh
test_expect_success setup '
- test_oid_init &&
git config gc.auto 0 &&
git config i18n.commitencoding ISO-8859-1 &&
test_commit A fileA one &&
@@ -714,7 +713,7 @@ test_expect_success 'fsck fails on corrupt packfile' '
# at least one of which is not zero, so setting the first byte to 0 is
# sufficient.)
chmod a+w .git/objects/pack/pack-$pack.pack &&
- printf '\0' | dd of=.git/objects/pack/pack-$pack.pack bs=1 conv=notrunc seek=12 &&
+ printf "\0" | dd of=.git/objects/pack/pack-$pack.pack bs=1 conv=notrunc seek=12 &&
test_when_finished "rm -f .git/objects/pack/pack-$pack.*" &&
remove_object $hsh &&
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 603019b541..408b97d5af 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -59,7 +59,6 @@ test_rev_parse () {
ROOT=$(pwd)
test_expect_success 'setup' '
- test_oid_init &&
mkdir -p sub/dir work &&
cp -R .git repo.git
'
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
index dbf690b9c1..3e657e693b 100755
--- a/t/t1506-rev-parse-diagnosis.sh
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -190,6 +190,24 @@ test_expect_success 'dotdot is not an empty set' '
test_cmp expect actual
'
+test_expect_success 'dotdot does not peel endpoints' '
+ git tag -a -m "annote" annotated HEAD &&
+ A=$(git rev-parse annotated) &&
+ H=$(git rev-parse annotated^0) &&
+ {
+ echo $A && echo ^$A
+ } >expect-with-two-dots &&
+ {
+ echo $A && echo $A && echo ^$H
+ } >expect-with-merge-base &&
+
+ git rev-parse annotated..annotated >actual-with-two-dots &&
+ test_cmp expect-with-two-dots actual-with-two-dots &&
+
+ git rev-parse annotated...annotated >actual-with-merge-base &&
+ test_cmp expect-with-merge-base actual-with-merge-base
+'
+
test_expect_success 'arg before dashdash must be a revision (missing)' '
test_must_fail git rev-parse foobar -- 2>stderr &&
test_i18ngrep "bad revision" stderr
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index f213aa8053..dfc0d96d8a 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -137,7 +137,7 @@ test_expect_success 'merge my-side@{u} records the correct name' '
git branch -t new my-side@{u} &&
git merge -s ours new@{u} &&
git show -s --pretty=tformat:%s >actual &&
- echo "Merge remote-tracking branch ${SQ}origin/side${SQ} into master" >expect &&
+ echo "Merge remote-tracking branch ${SQ}origin/side${SQ}" >expect &&
test_cmp expect actual
)
'
diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh
index 76330cb5ab..fa9e098706 100755
--- a/t/t2025-checkout-no-overlay.sh
+++ b/t/t2025-checkout-no-overlay.sh
@@ -44,4 +44,16 @@ test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
test_path_is_missing file1
'
+test_expect_success 'wildcard pathspec matches file in subdirectory' '
+ git reset --hard &&
+ mkdir subdir &&
+ test_commit file3-1 subdir/file3 &&
+ test_commit file3-2 subdir/file3 &&
+
+ git checkout --no-overlay file3-1 "*file3" &&
+ echo file3-1 >expect &&
+ test_path_is_file subdir/file3 &&
+ test_cmp expect subdir/file3
+'
+
test_done
diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh
index 0d47946e8a..b48345bf95 100755
--- a/t/t2072-restore-pathspec-file.sh
+++ b/t/t2072-restore-pathspec-file.sh
@@ -9,18 +9,21 @@ test_tick
test_expect_success setup '
test_commit file0 &&
+ mkdir dir1 &&
+ echo 1 >dir1/file &&
echo 1 >fileA.t &&
echo 1 >fileB.t &&
echo 1 >fileC.t &&
echo 1 >fileD.t &&
- git add fileA.t fileB.t fileC.t fileD.t &&
+ git add dir1 fileA.t fileB.t fileC.t fileD.t &&
git commit -m "files 1" &&
+ echo 2 >dir1/file &&
echo 2 >fileA.t &&
echo 2 >fileB.t &&
echo 2 >fileC.t &&
echo 2 >fileD.t &&
- git add fileA.t fileB.t fileC.t fileD.t &&
+ git add dir1 fileA.t fileB.t fileC.t fileD.t &&
git commit -m "files 2" &&
git tag checkpoint
@@ -31,7 +34,7 @@ restore_checkpoint () {
}
verify_expect () {
- git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+ git status --porcelain --untracked-files=no -- dir1 fileA.t fileB.t fileC.t fileD.t >actual &&
test_cmp expect actual
}
@@ -161,4 +164,14 @@ test_expect_success 'error conditions' '
test_i18ngrep -e "you must specify path(s) to restore" err
'
+test_expect_success 'wildcard pathspec matches file in subdirectory' '
+ restore_checkpoint &&
+
+ echo "*file" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+ cat >expect <<-\EOF &&
+ M dir1/file
+ EOF
+ verify_expect
+'
+
test_done
diff --git a/t/t2406-worktree-repair.sh b/t/t2406-worktree-repair.sh
new file mode 100755
index 0000000000..1fe468bfe8
--- /dev/null
+++ b/t/t2406-worktree-repair.sh
@@ -0,0 +1,179 @@
+#!/bin/sh
+
+test_description='test git worktree repair'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit init
+'
+
+test_expect_success 'skip missing worktree' '
+ test_when_finished "git worktree prune" &&
+ git worktree add --detach missing &&
+ rm -rf missing &&
+ git worktree repair >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'worktree path not directory' '
+ test_when_finished "git worktree prune" &&
+ git worktree add --detach notdir &&
+ rm -rf notdir &&
+ >notdir &&
+ test_must_fail git worktree repair >out 2>err &&
+ test_must_be_empty out &&
+ test_i18ngrep "not a directory" err
+'
+
+test_expect_success "don't clobber .git repo" '
+ test_when_finished "rm -rf repo && git worktree prune" &&
+ git worktree add --detach repo &&
+ rm -rf repo &&
+ test_create_repo repo &&
+ test_must_fail git worktree repair >out 2>err &&
+ test_must_be_empty out &&
+ test_i18ngrep ".git is not a file" err
+'
+
+test_corrupt_gitfile () {
+ butcher=$1 &&
+ problem=$2 &&
+ repairdir=${3:-.} &&
+ test_when_finished 'rm -rf corrupt && git worktree prune' &&
+ git worktree add --detach corrupt &&
+ git -C corrupt rev-parse --absolute-git-dir >expect &&
+ eval "$butcher" &&
+ git -C "$repairdir" worktree repair >out 2>err &&
+ test_i18ngrep "$problem" out &&
+ test_must_be_empty err &&
+ git -C corrupt rev-parse --absolute-git-dir >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'repair missing .git file' '
+ test_corrupt_gitfile "rm -f corrupt/.git" ".git file broken"
+'
+
+test_expect_success 'repair bogus .git file' '
+ test_corrupt_gitfile "echo \"gitdir: /nowhere\" >corrupt/.git" \
+ ".git file broken"
+'
+
+test_expect_success 'repair incorrect .git file' '
+ test_when_finished "rm -rf other && git worktree prune" &&
+ test_create_repo other &&
+ other=$(git -C other rev-parse --absolute-git-dir) &&
+ test_corrupt_gitfile "echo \"gitdir: $other\" >corrupt/.git" \
+ ".git file incorrect"
+'
+
+test_expect_success 'repair .git file from main/.git' '
+ test_corrupt_gitfile "rm -f corrupt/.git" ".git file broken" .git
+'
+
+test_expect_success 'repair .git file from linked worktree' '
+ test_when_finished "rm -rf other && git worktree prune" &&
+ git worktree add --detach other &&
+ test_corrupt_gitfile "rm -f corrupt/.git" ".git file broken" other
+'
+
+test_expect_success 'repair .git file from bare.git' '
+ test_when_finished "rm -rf bare.git corrupt && git worktree prune" &&
+ git clone --bare . bare.git &&
+ git -C bare.git worktree add --detach ../corrupt &&
+ git -C corrupt rev-parse --absolute-git-dir >expect &&
+ rm -f corrupt/.git &&
+ git -C bare.git worktree repair &&
+ git -C corrupt rev-parse --absolute-git-dir >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'invalid worktree path' '
+ test_must_fail git worktree repair /notvalid >out 2>err &&
+ test_must_be_empty out &&
+ test_i18ngrep "not a valid path" err
+'
+
+test_expect_success 'repo not found; .git not file' '
+ test_when_finished "rm -rf not-a-worktree" &&
+ test_create_repo not-a-worktree &&
+ test_must_fail git worktree repair not-a-worktree >out 2>err &&
+ test_must_be_empty out &&
+ test_i18ngrep ".git is not a file" err
+'
+
+test_expect_success 'repo not found; .git file broken' '
+ test_when_finished "rm -rf orig moved && git worktree prune" &&
+ git worktree add --detach orig &&
+ echo /invalid >orig/.git &&
+ mv orig moved &&
+ test_must_fail git worktree repair moved >out 2>err &&
+ test_must_be_empty out &&
+ test_i18ngrep ".git file broken" err
+'
+
+test_expect_success 'repair broken gitdir' '
+ test_when_finished "rm -rf orig moved && git worktree prune" &&
+ git worktree add --detach orig &&
+ sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
+ rm .git/worktrees/orig/gitdir &&
+ mv orig moved &&
+ git worktree repair moved >out 2>err &&
+ test_cmp expect .git/worktrees/orig/gitdir &&
+ test_i18ngrep "gitdir unreadable" out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'repair incorrect gitdir' '
+ test_when_finished "rm -rf orig moved && git worktree prune" &&
+ git worktree add --detach orig &&
+ sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
+ mv orig moved &&
+ git worktree repair moved >out 2>err &&
+ test_cmp expect .git/worktrees/orig/gitdir &&
+ test_i18ngrep "gitdir incorrect" out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'repair gitdir (implicit) from linked worktree' '
+ test_when_finished "rm -rf orig moved && git worktree prune" &&
+ git worktree add --detach orig &&
+ sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
+ mv orig moved &&
+ git -C moved worktree repair >out 2>err &&
+ test_cmp expect .git/worktrees/orig/gitdir &&
+ test_i18ngrep "gitdir incorrect" out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
+ test_when_finished "rm -rf orig moved && git worktree prune" &&
+ git worktree add --detach orig &&
+ cat .git/worktrees/orig/gitdir >expect &&
+ mv orig moved &&
+ git worktree repair >out 2>err &&
+ test_cmp expect .git/worktrees/orig/gitdir &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'repair multiple gitdir files' '
+ test_when_finished "rm -rf orig1 orig2 moved1 moved2 &&
+ git worktree prune" &&
+ git worktree add --detach orig1 &&
+ git worktree add --detach orig2 &&
+ sed s,orig1/\.git$,moved1/.git, .git/worktrees/orig1/gitdir >expect1 &&
+ sed s,orig2/\.git$,moved2/.git, .git/worktrees/orig2/gitdir >expect2 &&
+ mv orig1 moved1 &&
+ mv orig2 moved2 &&
+ git worktree repair moved1 moved2 >out 2>err &&
+ test_cmp expect1 .git/worktrees/orig1/gitdir &&
+ test_cmp expect2 .git/worktrees/orig2/gitdir &&
+ test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" out &&
+ test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" out &&
+ test_must_be_empty err
+'
+
+test_done
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index ffdfb16f58..740ce56eab 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -152,7 +152,7 @@ test_expect_success 'ls-files -o --directory with mix dir/file pathspecs' '
)
'
-test_expect_success 'ls-files --o --directory with glob filetype match' '
+test_expect_success 'ls-files -o --directory with glob filetype match' '
(
cd nested &&
@@ -168,7 +168,7 @@ test_expect_success 'ls-files --o --directory with glob filetype match' '
)
'
-test_expect_success 'ls-files --o --directory with mix of tracked states' '
+test_expect_success 'ls-files -o --directory with mix of tracked states' '
(
cd nested &&
@@ -184,7 +184,7 @@ test_expect_success 'ls-files --o --directory with mix of tracked states' '
)
'
-test_expect_success 'ls-files --o --directory with glob filetype match only' '
+test_expect_success 'ls-files -o --directory with glob filetype match only' '
(
cd nested &&
@@ -198,7 +198,7 @@ test_expect_success 'ls-files --o --directory with glob filetype match only' '
)
'
-test_expect_success 'ls-files --o --directory to get immediate paths under one dir only' '
+test_expect_success 'ls-files -o --directory to get immediate paths under one dir only' '
(
cd nested &&
@@ -212,4 +212,20 @@ test_expect_success 'ls-files --o --directory to get immediate paths under one d
)
'
+test_expect_success 'ls-files -o avoids listing untracked non-matching gitdir' '
+ test_when_finished "rm -rf nested/untracked/deep/empty" &&
+ (
+ cd nested &&
+
+ git init untracked/deep/empty &&
+ git ls-files --others "untracked/*.c" >actual &&
+
+ cat <<-EOF >expect &&
+ untracked/deep/foo.c
+ EOF
+
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 4c0734157b..c24c6632ee 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1287,6 +1287,7 @@ test_expect_success 'detect typo in branch name when using --edit-description' '
'
test_expect_success 'refuse --edit-description on unborn branch for now' '
+ test_when_finished "git checkout master" &&
write_script editor <<-\EOF &&
echo "New contents" >"$1"
EOF
@@ -1298,10 +1299,6 @@ test_expect_success '--merged catches invalid object names' '
test_must_fail git branch --merged 0000000000000000000000000000000000000000
'
-test_expect_success '--merged is incompatible with --no-merged' '
- test_must_fail git branch --merged HEAD --no-merged HEAD
-'
-
test_expect_success '--list during rebase' '
test_when_finished "reset_rebase" &&
git checkout master &&
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
index 40251c9f8f..efea5c4971 100755
--- a/t/t3201-branch-contains.sh
+++ b/t/t3201-branch-contains.sh
@@ -171,6 +171,69 @@ test_expect_success 'Assert that --contains only works on commits, not trees & b
test_must_fail git branch --no-contains $blob
'
+test_expect_success 'multiple branch --contains' '
+ git checkout -b side2 master &&
+ >feature &&
+ git add feature &&
+ git commit -m "add feature" &&
+ git checkout -b next master &&
+ git merge side &&
+ git branch --contains side --contains side2 >actual &&
+ cat >expect <<-\EOF &&
+ * next
+ side
+ side2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'multiple branch --merged' '
+ git branch --merged next --merged master >actual &&
+ cat >expect <<-\EOF &&
+ master
+ * next
+ side
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'multiple branch --no-contains' '
+ git branch --no-contains side --no-contains side2 >actual &&
+ cat >expect <<-\EOF &&
+ master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'multiple branch --no-merged' '
+ git branch --no-merged next --no-merged master >actual &&
+ cat >expect <<-\EOF &&
+ side2
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'branch --contains combined with --no-contains' '
+ git checkout -b seen master &&
+ git merge side &&
+ git merge side2 &&
+ git branch --contains side --no-contains side2 >actual &&
+ cat >expect <<-\EOF &&
+ next
+ side
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'branch --merged combined with --no-merged' '
+ git branch --merged seen --no-merged next >actual &&
+ cat >expect <<-\EOF &&
+ * seen
+ side2
+ EOF
+ test_cmp expect actual
+'
+
# We want to set up a case where the walk for the tracking info
# of one branch crosses the tip of another branch (and make sure
# that the latter walk does not mess up our flag to see if it was
@@ -200,15 +263,4 @@ test_expect_success 'branch --merged with --verbose' '
test_i18ncmp expect actual
'
-test_expect_success 'branch --contains combined with --no-contains' '
- git branch --contains zzz --no-contains topic >actual &&
- cat >expect <<-\EOF &&
- master
- side
- zzz
- EOF
- test_cmp expect actual
-
-'
-
test_done
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index e024cff65c..6eb344be03 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -252,17 +252,13 @@ test_expect_success 'changed commit with --stat diff option' '
git range-diff --no-color --stat topic...changed >actual &&
cat >expect <<-EOF &&
1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/
- a => b | 0
- 1 file changed, 0 insertions(+), 0 deletions(-)
2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/
- a => b | 0
- 1 file changed, 0 insertions(+), 0 deletions(-)
3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/
- a => b | 0
- 1 file changed, 0 insertions(+), 0 deletions(-)
+ a => b | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/
- a => b | 0
- 1 file changed, 0 insertions(+), 0 deletions(-)
+ a => b | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
EOF
test_cmp expect actual
'
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 3b4753e1b4..94c1b02251 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -7,7 +7,7 @@ test_description='Test that adding/removing many notes triggers automatic fanout
path_has_fanout() {
path=$1 &&
fanout=$2 &&
- after_last_slash=$((40 - $fanout * 2)) &&
+ after_last_slash=$(($(test_oid hexsz) - $fanout * 2)) &&
echo $path | grep -q "^\([0-9a-f]\{2\}/\)\{$fanout\}[0-9a-f]\{$after_last_slash\}$"
}
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
index 790e292966..d69c84c640 100755
--- a/t/t3308-notes-merge.sh
+++ b/t/t3308-notes-merge.sh
@@ -22,7 +22,6 @@ test_expect_success setup '
# Copy notes to remote-notes
git fetch . refs/notes/*:refs/remote-notes/origin/* &&
- test_oid_init &&
test_oid_cache <<-EOF
hash4a sha1:5e93d24084d32e1cb61f7070505b9d2530cca987
hash3a sha1:8366731eeee53787d2bdf8fc1eff7d94757e8da0
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 4a7d21f898..07a1617351 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -256,7 +256,7 @@ test_expect_success 'stop on conflicting pick' '
D
=======
G
- >>>>>>> $commit... G
+ >>>>>>> $commit (G)
EOF
git tag new-branch1 &&
test_must_fail git rebase -i master &&
@@ -1250,7 +1250,7 @@ test_expect_success 'rebase -i error on commits with \ in message' '
test_expect_code 1 grep " emp" error
'
-test_expect_success SHA1 'short SHA-1 setup' '
+test_expect_success 'short commit ID setup' '
test_when_finished "git checkout master" &&
git checkout --orphan collide &&
git rm -rf . &&
@@ -1262,23 +1262,54 @@ test_expect_success SHA1 'short SHA-1 setup' '
)
'
-test_expect_success SHA1 'short SHA-1 collide' '
+if test -n "$GIT_TEST_FIND_COLLIDER"
+then
+ author="$(unset test_tick; test_tick; git var GIT_AUTHOR_IDENT)"
+ committer="$(unset test_tick; test_tick; git var GIT_COMMITTER_IDENT)"
+ blob="$(git rev-parse collide2:collide)"
+ from="$(git rev-parse collide1^0)"
+ repl="commit refs/heads/collider-&\\n"
+ repl="${repl}author $author\\ncommitter $committer\\n"
+ repl="${repl}data <<EOF\\ncollide2 &\\nEOF\\n"
+ repl="${repl}from $from\\nM 100644 $blob collide\\n"
+ test_seq 1 32768 | sed "s|.*|$repl|" >script &&
+ git fast-import <script &&
+ git pack-refs &&
+ git for-each-ref >refs &&
+ grep "^$(test_oid t3404_collision)" <refs >matches &&
+ cat matches &&
+ test_line_count -gt 2 matches || {
+ echo "Could not find a collider" >&2
+ exit 1
+ }
+fi
+
+test_expect_success 'short commit ID collide' '
+ test_oid_cache <<-EOF &&
+ # collision-related constants
+ t3404_collision sha1:6bcd
+ t3404_collision sha256:0161
+ t3404_collider sha1:ac4f2ee
+ t3404_collider sha256:16697
+ EOF
test_when_finished "reset_rebase && git checkout master" &&
git checkout collide &&
- colliding_sha1=6bcda37 &&
- test $colliding_sha1 = "$(git rev-parse HEAD | cut -c 1-7)" &&
+ colliding_id=$(test_oid t3404_collision) &&
+ hexsz=$(test_oid hexsz) &&
+ test $colliding_id = "$(git rev-parse HEAD | cut -c 1-4)" &&
+ test_config core.abbrev 4 &&
(
unset test_tick &&
test_tick &&
set_fake_editor &&
- FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \
+ FAKE_COMMIT_MESSAGE="collide2 $(test_oid t3404_collider)" \
FAKE_LINES="reword 1 break 2" git rebase -i HEAD~2 &&
- test $colliding_sha1 = "$(git rev-parse HEAD | cut -c 1-7)" &&
- grep "^pick $colliding_sha1 " \
+ test $colliding_id = "$(git rev-parse HEAD | cut -c 1-4)" &&
+ grep "^pick $colliding_id " \
.git/rebase-merge/git-rebase-todo.tmp &&
- grep "^pick [0-9a-f]\{40\}" \
+ grep "^pick [0-9a-f]\{$hexsz\}" \
.git/rebase-merge/git-rebase-todo &&
- grep "^pick [0-9a-f]\{40\}" \
+ grep "^pick [0-9a-f]\{$hexsz\}" \
.git/rebase-merge/git-rebase-todo.backup &&
git rebase --continue
) &&
@@ -1760,6 +1791,12 @@ test_expect_success 'correct error message for commit --amend after empty pick'
test_i18ngrep "middle of a rebase -- cannot amend." err
'
+test_expect_success 'todo has correct onto hash' '
+ GIT_SEQUENCE_EDITOR=cat git rebase -i no-conflict-branch~4 no-conflict-branch >actual &&
+ onto=$(git rev-parse --short HEAD~4) &&
+ test_i18ngrep "^# Rebase ..* onto $onto" actual
+'
+
# This must be the last test in this file
test_expect_success '$EDITOR and friends are unchanged' '
test_editor_unchanged
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 50e7960702..c8234062c6 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -61,8 +61,6 @@ test_rebase_am_only () {
}
test_rebase_am_only --whitespace=fix
-test_rebase_am_only --ignore-whitespace
-test_rebase_am_only --committer-date-is-author-date
test_rebase_am_only -C4
test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 6f0452c0ea..a29eda87e9 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -60,15 +60,16 @@ test_rebase_same_head_ () {
fi &&
oldhead=\$(git rev-parse HEAD) &&
test_when_finished 'git reset --hard \$oldhead' &&
- cp .git/logs/HEAD expect &&
+ git reflog HEAD >expect &&
git rebase$flag $* >stdout &&
+ git reflog HEAD >actual &&
if test $what = work
then
old=\$(wc -l <expect) &&
- test_line_count '-gt' \$old .git/logs/HEAD
+ test_line_count '-gt' \$old actual
elif test $what = noop
then
- test_cmp expect .git/logs/HEAD
+ test_cmp expect actual
fi &&
newhead=\$(git rev-parse HEAD) &&
if test $cmp = same
diff --git a/t/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh
new file mode 100755
index 0000000000..996e82787e
--- /dev/null
+++ b/t/t3436-rebase-more-options.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 Rohit Ashiwal
+#
+
+test_description='tests to ensure compatibility between am and interactive backends'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
+export GIT_AUTHOR_DATE
+
+# This is a special case in which both am and interactive backends
+# provide the same output. It was done intentionally because
+# both the backends fall short of optimal behaviour.
+test_expect_success 'setup' '
+ git checkout -b topic &&
+ test_write_lines "line 1" " line 2" "line 3" >file &&
+ git add file &&
+ git commit -m "add file" &&
+
+ test_write_lines "line 1" "new line 2" "line 3" >file &&
+ git commit -am "update file" &&
+ git tag side &&
+ test_commit commit1 foo foo1 &&
+ test_commit commit2 foo foo2 &&
+ test_commit commit3 foo foo3 &&
+
+ git checkout --orphan master &&
+ rm foo &&
+ test_write_lines "line 1" " line 2" "line 3" >file &&
+ git commit -am "add file" &&
+ git tag main &&
+
+ mkdir test-bin &&
+ write_script test-bin/git-merge-test <<-\EOF
+ exec git merge-recursive "$@"
+ EOF
+'
+
+test_expect_success '--ignore-whitespace works with apply backend' '
+ test_must_fail git rebase --apply main side &&
+ git rebase --abort &&
+ git rebase --apply --ignore-whitespace main side &&
+ git diff --exit-code side
+'
+
+test_expect_success '--ignore-whitespace works with merge backend' '
+ test_must_fail git rebase --merge main side &&
+ git rebase --abort &&
+ git rebase --merge --ignore-whitespace main side &&
+ git diff --exit-code side
+'
+
+test_expect_success '--ignore-whitespace is remembered when continuing' '
+ (
+ set_fake_editor &&
+ FAKE_LINES="break 1" git rebase -i --ignore-whitespace \
+ main side &&
+ git rebase --continue
+ ) &&
+ git diff --exit-code side
+'
+
+test_ctime_is_atime () {
+ git log $1 --format=%ai >authortime &&
+ git log $1 --format=%ci >committertime &&
+ test_cmp authortime committertime
+}
+
+test_expect_success '--committer-date-is-author-date works with apply backend' '
+ GIT_AUTHOR_DATE="@1234 +0300" git commit --amend --reset-author &&
+ git rebase --apply --committer-date-is-author-date HEAD^ &&
+ test_ctime_is_atime -1
+'
+
+test_expect_success '--committer-date-is-author-date works with merge backend' '
+ GIT_AUTHOR_DATE="@1234 +0300" git commit --amend --reset-author &&
+ git rebase -m --committer-date-is-author-date HEAD^ &&
+ test_ctime_is_atime -1
+'
+
+test_expect_success '--committer-date-is-author-date works with rebase -r' '
+ git checkout side &&
+ GIT_AUTHOR_DATE="@1234 +0300" git merge --no-ff commit3 &&
+ git rebase -r --root --committer-date-is-author-date &&
+ test_ctime_is_atime
+'
+
+test_expect_success '--committer-date-is-author-date works when forking merge' '
+ git checkout side &&
+ GIT_AUTHOR_DATE="@1234 +0300" git merge --no-ff commit3 &&
+ PATH="./test-bin:$PATH" git rebase -r --root --strategy=test \
+ --committer-date-is-author-date &&
+ test_ctime_is_atime
+'
+
+test_expect_success '--committer-date-is-author-date works when committing conflict resolution' '
+ git checkout commit2 &&
+ GIT_AUTHOR_DATE="@1980 +0000" git commit --amend --only --reset-author &&
+ test_must_fail git rebase -m --committer-date-is-author-date \
+ --onto HEAD^^ HEAD^ &&
+ echo resolved > foo &&
+ git add foo &&
+ git rebase --continue &&
+ test_ctime_is_atime -1
+'
+
+# Checking for +0000 in the author date is sufficient since the
+# default timezone is UTC but the timezone used while committing is
+# +0530. The inverted logic in the grep is necessary to check all the
+# author dates in the file.
+test_atime_is_ignored () {
+ git log $1 --format=%ai >authortime &&
+ ! grep -v +0000 authortime
+}
+
+test_expect_success '--reset-author-date works with apply backend' '
+ git commit --amend --date="$GIT_AUTHOR_DATE" &&
+ git rebase --apply --reset-author-date HEAD^ &&
+ test_atime_is_ignored -1
+'
+
+test_expect_success '--reset-author-date works with merge backend' '
+ git commit --amend --date="$GIT_AUTHOR_DATE" &&
+ git rebase --reset-author-date -m HEAD^ &&
+ test_atime_is_ignored -1
+'
+
+test_expect_success '--reset-author-date works after conflict resolution' '
+ test_must_fail git rebase --reset-author-date -m \
+ --onto commit2^^ commit2^ commit2 &&
+ echo resolved >foo &&
+ git add foo &&
+ git rebase --continue &&
+ test_atime_is_ignored -1
+'
+
+test_expect_success '--reset-author-date works with rebase -r' '
+ git checkout side &&
+ git merge --no-ff commit3 &&
+ git rebase -r --root --reset-author-date &&
+ test_atime_is_ignored
+'
+
+test_expect_success '--reset-author-date with --committer-date-is-author-date works' '
+ test_must_fail git rebase -m --committer-date-is-author-date \
+ --reset-author-date --onto commit2^^ commit2^ commit3 &&
+ git checkout --theirs foo &&
+ git add foo &&
+ git rebase --continue &&
+ test_ctime_is_atime -2 &&
+ test_atime_is_ignored -2
+'
+
+test_expect_success '--reset-author-date --committer-date-is-author-date works when forking merge' '
+ GIT_SEQUENCE_EDITOR="echo \"merge -C $(git rev-parse HEAD) commit3\">" \
+ PATH="./test-bin:$PATH" git rebase -i --strategy=test \
+ --reset-author-date \
+ --committer-date-is-author-date side side &&
+ test_ctime_is_atime -1 &&
+ test_atime_is_ignored -1
+ '
+
+test_expect_success '--ignore-date is an alias for --reset-author-date' '
+ git commit --amend --date="$GIT_AUTHOR_DATE" &&
+ git rebase --apply --ignore-date HEAD^ &&
+ git commit --allow-empty -m empty --date="$GIT_AUTHOR_DATE" &&
+ git rebase -m --ignore-date HEAD^ &&
+ test_atime_is_ignored -2
+'
+
+# This must be the last test in this file
+test_expect_success '$EDITOR and friends are unchanged' '
+ test_editor_unchanged
+'
+
+test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index f038f34b7c..2b8d9cb38e 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -55,4 +55,27 @@ test_expect_success \
expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* - .*"
'
+test_expect_success 'cherry ignores whitespace' '
+ git switch --orphan=upstream-with-space &&
+ test_commit initial file &&
+ >expect &&
+ git switch --create=feature-without-space &&
+
+ # A spaceless file on the feature branch. Expect a match upstream.
+ printf space >file &&
+ git add file &&
+ git commit -m"file without space" &&
+ git log --format="- %H" -1 >>expect &&
+
+ # A further change. Should not match upstream.
+ test_commit change file &&
+ git log --format="+ %H" -1 >>expect &&
+
+ git switch upstream-with-space &&
+ # Same as the spaceless file, just with spaces and on upstream.
+ test_commit "file with space" file "s p a c e" file-with-space &&
+ git cherry upstream-with-space feature-without-space >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 7c1da21df1..3669dfb1be 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -47,7 +47,7 @@ test_expect_success 'cherry-pick --nonsense' '
git diff --exit-code HEAD &&
test_must_fail git cherry-pick --nonsense 2>msg &&
git diff --exit-code HEAD "$pos" &&
- test_i18ngrep '[Uu]sage:' msg
+ test_i18ngrep "[Uu]sage:" msg
'
test_expect_success 'revert --nonsense' '
@@ -56,7 +56,7 @@ test_expect_success 'revert --nonsense' '
git diff --exit-code HEAD &&
test_must_fail git revert --nonsense 2>msg &&
git diff --exit-code HEAD "$pos" &&
- test_i18ngrep '[Uu]sage:' msg
+ test_i18ngrep "[Uu]sage:" msg
'
test_expect_success 'cherry-pick after renaming branch' '
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index 752bc43487..a21adcf0e4 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -283,12 +283,12 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '
a
=======
c
- >>>>>>> objid picked
+ >>>>>>> objid (picked)
EOF
test_must_fail git cherry-pick picked &&
- sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
+ sed "s/[a-f0-9]* (/objid (/" foo >actual &&
test_cmp expected actual
'
@@ -298,16 +298,16 @@ test_expect_success 'diff3 -m style' '
cat <<-EOF >expected &&
<<<<<<< HEAD
a
- ||||||| parent of objid picked
+ ||||||| parent of objid (picked)
b
=======
c
- >>>>>>> objid picked
+ >>>>>>> objid (picked)
EOF
test_must_fail git cherry-pick picked &&
- sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
+ sed "s/[a-f0-9]* (/objid (/" foo >actual &&
test_cmp expected actual
'
@@ -319,7 +319,7 @@ test_expect_success 'revert also handles conflicts sanely' '
a
=======
b
- >>>>>>> parent of objid picked
+ >>>>>>> parent of objid (picked)
EOF
{
git checkout picked -- foo &&
@@ -345,7 +345,7 @@ test_expect_success 'revert also handles conflicts sanely' '
test_must_fail git update-index --refresh -q &&
test_must_fail git diff-index --exit-code HEAD &&
test_cmp expected-stages actual-stages &&
- sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
+ sed "s/[a-f0-9]* (/objid (/" foo >actual &&
test_cmp expected actual
'
@@ -429,16 +429,16 @@ test_expect_success 'revert conflict, diff3 -m style' '
cat <<-EOF >expected &&
<<<<<<< HEAD
a
- ||||||| objid picked
+ ||||||| objid (picked)
c
=======
b
- >>>>>>> parent of objid picked
+ >>>>>>> parent of objid (picked)
EOF
test_must_fail git revert picked &&
- sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
+ sed "s/[a-f0-9]* (/objid (/" foo >actual &&
test_cmp expected actual
'
@@ -512,7 +512,7 @@ test_expect_success 'commit after failed cherry-pick adds -s at the right place'
Signed-off-by: C O Mitter <committer@example.com>
# Conflicts:
EOF
- grep -e "^# Conflicts:" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&
+ grep -e "^# Conflicts:" -e "^Signed-off-by" .git/COMMIT_EDITMSG >actual &&
test_cmp expect actual &&
cat <<-\EOF >expected &&
@@ -541,7 +541,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '
Signed-off-by: C O Mitter <committer@example.com>
Conflicts:
EOF
- grep -e "^Conflicts:" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&
+ grep -e "^Conflicts:" -e "^Signed-off-by" .git/COMMIT_EDITMSG >actual &&
test_cmp expect actual
'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index f2c0168941..efec8d13b6 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -241,7 +241,6 @@ test_expect_success 'refresh index before checking if it is up-to-date' '
'
test_expect_success 'choking "git rm" should not let it die with cruft' '
- test_oid_init &&
git reset -q --hard &&
test_when_finished "rm -f .git/index.lock && git reset -q --hard" &&
i=0 &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 49decbac71..ca04fac417 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -31,7 +31,16 @@ diff_cmp () {
# indicates a dumb terminal, so we set that variable, too.
force_color () {
- env GIT_PAGER_IN_USE=true TERM=vt100 "$@"
+ # The first element of $@ may be a shell function, as a result POSIX
+ # does not guarantee that "one-shot assignment" will not persist after
+ # the function call. Thus, we prevent these variables from escaping
+ # this function's context with this subshell.
+ (
+ GIT_PAGER_IN_USE=true &&
+ TERM=vt100 &&
+ export GIT_PAGER_IN_USE TERM &&
+ "$@"
+ )
}
test_expect_success 'setup (initial)' '
@@ -560,12 +569,20 @@ test_expect_success 'patch mode ignores unmerged entries' '
diff_cmp expected diff
'
+test_expect_success 'index is refreshed after applying patch' '
+ git reset --hard &&
+ echo content >test &&
+ printf y | git add -p &&
+ git diff-files --exit-code
+'
+
test_expect_success 'diffs can be colorized' '
git reset --hard &&
echo content >test &&
printf y >y &&
force_color git add -p >output 2>&1 <y &&
+ git diff-files --exit-code &&
# We do not want to depend on the exact coloring scheme
# git uses for diffs, so just check that we saw some kind of color.
@@ -604,7 +621,7 @@ test_expect_success 'detect bogus diffFilter output' '
echo content >test &&
test_config interactive.diffFilter "sed 1d" &&
printf y >y &&
- test_must_fail force_color git add -p <y
+ force_color test_must_fail git add -p <y
'
test_expect_success 'diff.algorithm is passed to `git diff-files`' '
@@ -805,6 +822,44 @@ test_expect_success 'checkout -p works with pathological context lines' '
test_cmp expect a
'
+# This should be called from a subshell as it sets a temporary editor
+setup_new_file() {
+ write_script new-file-editor.sh <<-\EOF &&
+ sed /^#/d "$1" >patch &&
+ sed /^+c/d patch >"$1"
+ EOF
+ test_set_editor "$(pwd)/new-file-editor.sh" &&
+ test_write_lines a b c d e f >new-file &&
+ test_write_lines a b d e f >new-file-expect &&
+ test_write_lines "@@ -0,0 +1,6 @@" +a +b +c +d +e +f >patch-expect
+}
+
+test_expect_success 'add -N followed by add -p patch editing' '
+ git reset --hard &&
+ (
+ setup_new_file &&
+ git add -N new-file &&
+ test_write_lines e n q | git add -p &&
+ git cat-file blob :new-file >actual &&
+ test_cmp new-file-expect actual &&
+ test_cmp patch-expect patch
+ )
+'
+
+test_expect_success 'checkout -p patch editing of added file' '
+ git reset --hard &&
+ (
+ setup_new_file &&
+ git add new-file &&
+ git commit -m "add new file" &&
+ git rm new-file &&
+ git commit -m "remove new file" &&
+ test_write_lines e n q | git checkout -p HEAD^ &&
+ test_cmp new-file-expect new-file &&
+ test_cmp patch-expect patch
+ )
+'
+
test_expect_success 'show help from add--helper' '
git reset --hard &&
cat >expect <<-EOF &&
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index 64dcc5ec28..d696aa4e52 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -23,7 +23,6 @@ check_verify_failure () {
# first create a commit, so we have a valid object/type
# for the tag.
test_expect_success 'setup' '
- test_oid_init &&
echo Hello >A &&
git update-index --add A &&
git commit -m "Initial commit" &&
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index cbcdd10464..6a9f010197 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -10,8 +10,6 @@ test_description='Test diff raw-output.
. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
-test_oid_init
-
test_oid_cache <<\EOF
aa_1 sha1:ccba72ad3888a3520b39efcf780b9ee64167535d
aa_1 sha256:9febfbf18197819b2735c45291f138525d2476d59470f98239647544586ba403
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index f542d2929d..d18a80493c 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -14,8 +14,8 @@ test_expect_success 'setup reference tree' '
git update-index --add COPYING rezrov &&
tree=$(git write-tree) &&
echo $tree &&
- sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
- sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ sed -e "s/HOWEVER/However/" <COPYING >COPYING.1 &&
+ sed -e "s/GPL/G.P.L/g" <COPYING >COPYING.2 &&
origoid=$(git hash-object COPYING) &&
oid1=$(git hash-object COPYING.1) &&
oid2=$(git hash-object COPYING.2)
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index e5ca359edf..65cc703c65 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -125,7 +125,9 @@ test_expect_success 'setup submodules' '
test_expect_success 'diff-tree ignores trailing slash on submodule path' '
git diff --name-only HEAD^ HEAD submod >expect &&
git diff --name-only HEAD^ HEAD submod/ >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ git diff --name-only HEAD^ HEAD -- submod/whatever >actual &&
+ test_must_be_empty actual
'
test_expect_success 'diff multiple wildcard pathspecs' '
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 43267d6024..5c7b0122b4 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -117,12 +117,12 @@ test_expect_success setup '
: <<\EOF
! [initial] Initial
- * [master] Merge branch 'side' into master
+ * [master] Merge branch 'side'
! [rearrange] Rearranged lines in dir/sub
! [side] Side
----
+ [rearrange] Rearranged lines in dir/sub
- - [master] Merge branch 'side' into master
+ - [master] Merge branch 'side'
* + [side] Side
* [master^] Third
* [master~2] Second
@@ -130,27 +130,45 @@ test_expect_success setup '
EOF
process_diffs () {
- _x04="[0-9a-f][0-9a-f][0-9a-f][0-9a-f]" &&
- _x07="$_x05[0-9a-f][0-9a-f]" &&
- sed -e "s/$OID_REGEX/$ZERO_OID/g" \
- -e "s/From $_x40 /From $ZERO_OID /" \
- -e "s/from $_x40)/from $ZERO_OID)/" \
- -e "s/commit $_x40\$/commit $ZERO_OID/" \
- -e "s/commit $_x40 (/commit $ZERO_OID (/" \
- -e "s/$_x40 $_x40 $_x40/$ZERO_OID $ZERO_OID $ZERO_OID/" \
- -e "s/$_x40 $_x40 /$ZERO_OID $ZERO_OID /" \
- -e "s/^$_x40 $_x40$/$ZERO_OID $ZERO_OID/" \
- -e "s/^$_x40 /$ZERO_OID /" \
- -e "s/^$_x40$/$ZERO_OID/" \
- -e "s/$_x07\.\.$_x07/fffffff..fffffff/g" \
- -e "s/$_x07,$_x07\.\.$_x07/fffffff,fffffff..fffffff/g" \
- -e "s/$_x07 $_x07 $_x07/fffffff fffffff fffffff/g" \
- -e "s/$_x07 $_x07 /fffffff fffffff /g" \
- -e "s/Merge: $_x07 $_x07/Merge: fffffff fffffff/g" \
- -e "s/$_x07\.\.\./fffffff.../g" \
- -e "s/ $_x04\.\.\./ ffff.../g" \
- -e "s/ $_x04/ ffff/g" \
- "$1"
+ perl -e '
+ my $oid_length = length($ARGV[0]);
+ my $x40 = "[0-9a-f]{40}";
+ my $xab = "[0-9a-f]{4,16}";
+ my $orx = "[0-9a-f]" x $oid_length;
+
+ sub munge_oid {
+ my ($oid) = @_;
+ my $x;
+
+ return "" unless length $oid;
+
+ if ($oid =~ /^(100644|100755|120000)$/) {
+ return $oid;
+ }
+
+ if ($oid =~ /^0*$/) {
+ $x = "0";
+ } else {
+ $x = "f";
+ }
+
+ if (length($oid) == 40) {
+ return $x x $oid_length;
+ } else {
+ return $x x length($oid);
+ }
+ }
+
+ while (<STDIN>) {
+ s/($orx)/munge_oid($1)/ge;
+ s/From ($x40)( |\))/"From " . munge_oid($1) . $2/ge;
+ s/commit ($x40)($| \(from )($x40?)/"commit " . munge_oid($1) . $2 . munge_oid($3)/ge;
+ s/\b($x40)( |\.\.|$)/munge_oid($1) . $2/ge;
+ s/^($x40)($| )/munge_oid($1) . $2/e;
+ s/($xab)(\.\.|,| |\.\.\.|$)/munge_oid($1) . $2/ge;
+ print;
+ }
+ ' "$ZERO_OID" <"$1"
}
V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
@@ -221,6 +239,9 @@ diff-tree --root -r --abbrev=4 initial
:noellipses diff-tree --root -r --abbrev=4 initial
diff-tree -p initial
diff-tree --root -p initial
+diff-tree --root -p --abbrev=10 initial
+diff-tree --root -p --full-index initial
+diff-tree --root -p --full-index --abbrev=10 initial
diff-tree --patch-with-stat initial
diff-tree --root --patch-with-stat initial
diff-tree --patch-with-raw initial
@@ -297,6 +318,9 @@ log --root --patch-with-stat --summary master
log --root -c --patch-with-stat --summary master
# improved by Timo's patch
log --root --cc --patch-with-stat --summary master
+log --no-diff-merges -p --first-parent master
+log --diff-merges=off -p --first-parent master
+log --first-parent --diff-merges=off -p master
log -p --first-parent master
log -m -p --first-parent master
log -m -p master
diff --git a/t/t4013/diff.diff-tree_--root_-p_--abbrev=10_initial b/t/t4013/diff.diff-tree_--root_-p_--abbrev=10_initial
new file mode 100644
index 0000000000..7518a9044e
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-p_--abbrev=10_initial
@@ -0,0 +1,29 @@
+$ git diff-tree --root -p --abbrev=10 initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000000..35d242ba79
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000000..01e79c32a8
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000000..01e79c32a8
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_-p_--full-index_--abbrev=10_initial b/t/t4013/diff.diff-tree_--root_-p_--full-index_--abbrev=10_initial
new file mode 100644
index 0000000000..69f913fbe5
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-p_--full-index_--abbrev=10_initial
@@ -0,0 +1,29 @@
+$ git diff-tree --root -p --full-index --abbrev=10 initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000000000000000000000000000000000000..35d242ba79ae89ac695e26b3d4c27a8e6f028f9e
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_-p_--full-index_initial b/t/t4013/diff.diff-tree_--root_-p_--full-index_initial
new file mode 100644
index 0000000000..1b0b6717fa
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-p_--full-index_initial
@@ -0,0 +1,29 @@
+$ git diff-tree --root -p --full-index initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000000000000000000000000000000000000..35d242ba79ae89ac695e26b3d4c27a8e6f028f9e
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
index c56783b985..3f9b872ece 100644
--- a/t/t4013/diff.log_--decorate=full_--all
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -31,7 +31,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index 1cbdc038f4..f5e20e1e14 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -31,7 +31,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side)
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master
new file mode 100644
index 0000000000..194e893c94
--- /dev/null
+++ b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master
@@ -0,0 +1,78 @@
+$ git log --diff-merges=off -p --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master
new file mode 100644
index 0000000000..5d7461a167
--- /dev/null
+++ b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master
@@ -0,0 +1,78 @@
+$ git log --first-parent --diff-merges=off -p master
+commit 80e25ffa65bcdbe82ef654b4d06dbbde7945c37f
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master
new file mode 100644
index 0000000000..597002232e
--- /dev/null
+++ b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master
@@ -0,0 +1,78 @@
+$ git log --no-diff-merges -p --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
index f5b1b6516b..a18f1472a9 100644
--- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master
index af23803cdc..ae425c4672 100644
--- a/t/t4013/diff.log_--patch-with-stat_master
+++ b/t/t4013/diff.log_--patch-with-stat_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
index 814098fbf8..d5207cadf4 100644
--- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
index b927fe4a98..0fc1e8cd71 100644
--- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
index 6db3cea329..dffc09dde9 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master
index 98e9c320c3..55aa98012d 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
index b61b1117ae..019d85f7de 100644
--- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_master
index 345bd9e8a9..b42c334439 100644
--- a/t/t4013/diff.log_--root_-p_master
+++ b/t/t4013/diff.log_--root_-p_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_master
index db56b1fe6b..e8f46159da 100644
--- a/t/t4013/diff.log_--root_master
+++ b/t/t4013/diff.log_--root_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-m_-p_--first-parent_master b/t/t4013/diff.log_-m_-p_--first-parent_master
index bcadb50e26..7a0073f529 100644
--- a/t/t4013/diff.log_-m_-p_--first-parent_master
+++ b/t/t4013/diff.log_-m_-p_--first-parent_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --git a/dir/sub b/dir/sub
index cead32e..992913c 100644
diff --git a/t/t4013/diff.log_-m_-p_master b/t/t4013/diff.log_-m_-p_master
index 2acf43a9fb..9ca62a01ed 100644
--- a/t/t4013/diff.log_-m_-p_master
+++ b/t/t4013/diff.log_-m_-p_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --git a/dir/sub b/dir/sub
index cead32e..992913c 100644
@@ -33,7 +33,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --git a/dir/sub b/dir/sub
index 7289e35..992913c 100644
diff --git a/t/t4013/diff.log_-p_--first-parent_master b/t/t4013/diff.log_-p_--first-parent_master
index c6a5876d80..28840ebea1 100644
--- a/t/t4013/diff.log_-p_--first-parent_master
+++ b/t/t4013/diff.log_-p_--first-parent_master
@@ -4,7 +4,29 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_master
index 1841cded94..bf1326dc36 100644
--- a/t/t4013/diff.log_-p_master
+++ b/t/t4013/diff.log_-p_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_master
index f8ec445eb3..a8f6ce5abd 100644
--- a/t/t4013/diff.log_master
+++ b/t/t4013/diff.log_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.show_--first-parent_master b/t/t4013/diff.show_--first-parent_master
index 94548f4598..3dcbe473a0 100644
--- a/t/t4013/diff.show_--first-parent_master
+++ b/t/t4013/diff.show_--first-parent_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --git a/dir/sub b/dir/sub
index cead32e..992913c 100644
diff --git a/t/t4013/diff.show_-c_master b/t/t4013/diff.show_-c_master
index 1c46ed64fd..81aba8da96 100644
--- a/t/t4013/diff.show_-c_master
+++ b/t/t4013/diff.show_-c_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --combined dir/sub
index cead32e,7289e35..992913c
diff --git a/t/t4013/diff.show_-m_master b/t/t4013/diff.show_-m_master
index 7559fc22f8..4ea2ee453d 100644
--- a/t/t4013/diff.show_-m_master
+++ b/t/t4013/diff.show_-m_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --git a/dir/sub b/dir/sub
index cead32e..992913c 100644
@@ -33,7 +33,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --git a/dir/sub b/dir/sub
index 7289e35..992913c 100644
diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_master
index 57091c5d90..fb08ce0e46 100644
--- a/t/t4013/diff.show_master
+++ b/t/t4013/diff.show_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
diff --cc dir/sub
index cead32e,7289e35..992913c
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
index 5f13a71bb5..30aae7817b 100644
--- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
index 8acb88267b..d1d32bd34c 100644
--- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
@@ -4,7 +4,7 @@ Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
- Merge branch 'side' into master
+ Merge branch 'side'
dir/sub | 2 ++
file0 | 3 +++
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 88d3026894..8bdaa0a693 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -789,7 +789,7 @@ test_expect_success 'checkdiff allows new blank lines' '
git diff --check
'
-test_expect_success 'whitespace-only changes not reported' '
+test_expect_success 'whitespace-only changes not reported (diff)' '
git reset --hard &&
echo >x "hello world" &&
git add x &&
@@ -799,10 +799,44 @@ test_expect_success 'whitespace-only changes not reported' '
test_must_be_empty actual
'
-test_expect_success 'whitespace-only changes reported across renames' '
+test_expect_success 'whitespace-only changes not reported (diffstat)' '
+ # reuse state from previous test
+ git diff --stat -b >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'whitespace changes with modification reported (diffstat)' '
+ git reset --hard &&
+ echo >x "hello world" &&
+ git update-index --chmod=+x x &&
+ git diff --stat --cached -b >actual &&
+ cat <<-EOF >expect &&
+ x | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'whitespace-only changes reported across renames (diffstat)' '
git reset --hard &&
for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
git add x &&
+ git commit -m "base" &&
+ sed -e "5s/^/ /" x >z &&
+ git rm x &&
+ git add z &&
+ git diff -w -M --cached --stat >actual &&
+ cat <<-EOF >expect &&
+ x => z | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'whitespace-only changes reported across renames' '
+ git reset --hard HEAD~1 &&
+ for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+ git add x &&
hash_x=$(git hash-object x) &&
before=$(git rev-parse --short "$hash_x") &&
git commit -m "base" &&
diff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data
new file mode 100644
index 0000000000..63d4e21d0a
--- /dev/null
+++ b/t/t4018/fortran-block-data
@@ -0,0 +1,5 @@
+ BLOCK DATA RIGHT
+
+ COMMON /B/ C, ChangeMe
+ DATA C, ChangeMe / 2.0, 6.0 /
+ END
diff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment
new file mode 100644
index 0000000000..7b10d17658
--- /dev/null
+++ b/t/t4018/fortran-comment
@@ -0,0 +1,13 @@
+ module a
+
+ contains
+
+ ! subroutine wrong
+ subroutine RIGHT
+ ! subroutine wrong
+
+ real ChangeMe
+
+ end subroutine RIGHT
+
+ end module a
diff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword
new file mode 100644
index 0000000000..e9206a5379
--- /dev/null
+++ b/t/t4018/fortran-comment-keyword
@@ -0,0 +1,14 @@
+ module a
+
+ contains
+
+ subroutine RIGHT (funcA, funcB)
+
+ real funcA ! grid function a
+ real funcB ! grid function b
+
+ real ChangeMe
+
+ end subroutine RIGHT
+
+ end module a
diff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy
new file mode 100644
index 0000000000..53cd062c1e
--- /dev/null
+++ b/t/t4018/fortran-comment-legacy
@@ -0,0 +1,13 @@
+ module a
+
+ contains
+
+C subroutine wrong
+ subroutine RIGHT
+C subroutine wrong
+
+ real ChangeMe
+
+ end subroutine RIGHT
+
+ end module a
diff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star
new file mode 100644
index 0000000000..2cbcdc3d8a
--- /dev/null
+++ b/t/t4018/fortran-comment-legacy-star
@@ -0,0 +1,13 @@
+ module a
+
+ contains
+
+* subroutine wrong
+ subroutine RIGHT
+* subroutine wrong
+
+ real ChangeMe
+
+ end subroutine RIGHT
+
+ end module a
diff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function
new file mode 100644
index 0000000000..5a2d85d3aa
--- /dev/null
+++ b/t/t4018/fortran-external-function
@@ -0,0 +1,9 @@
+function RIGHT(a, b) result(c)
+
+integer, intent(in) :: ChangeMe
+integer, intent(in) :: b
+integer, intent(out) :: c
+
+c = a+b
+
+end function RIGHT
diff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine
new file mode 100644
index 0000000000..4ce85fea13
--- /dev/null
+++ b/t/t4018/fortran-external-subroutine
@@ -0,0 +1,5 @@
+subroutine RIGHT
+
+real ChangeMe
+
+end subroutine RIGHT
diff --git a/t/t4018/fortran-module b/t/t4018/fortran-module
new file mode 100644
index 0000000000..c4b737dac3
--- /dev/null
+++ b/t/t4018/fortran-module
@@ -0,0 +1,5 @@
+module RIGHT
+
+use ChangeMe
+
+end module RIGHT
diff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure
new file mode 100644
index 0000000000..1ce6d854c2
--- /dev/null
+++ b/t/t4018/fortran-module-procedure
@@ -0,0 +1,13 @@
+ module RIGHT
+
+ implicit none
+ private
+
+ interface letters ! generic interface
+ module procedure aaaa, &
+ bbbb, &
+ ChangeMe, &
+ dddd
+ end interface
+
+end module RIGHT
diff --git a/t/t4018/fortran-program b/t/t4018/fortran-program
new file mode 100644
index 0000000000..4616895e4b
--- /dev/null
+++ b/t/t4018/fortran-program
@@ -0,0 +1,5 @@
+program RIGHT
+
+call ChangeMe
+
+end program RIGHT
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index e29deaf4a5..d7145ccca4 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -6,7 +6,6 @@ test_description='difference in submodules'
. "$TEST_DIRECTORY"/diff-lib.sh
test_expect_success setup '
- test_oid_init &&
test_tick &&
test_create_repo sub &&
(
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index fb145aa173..0c8fb39ced 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -102,7 +102,7 @@ test_expect_success 'word diff with runs of whitespace' '
'
test_expect_success '--word-diff=porcelain' '
- sed 's/#.*$//' >expect <<-EOF &&
+ sed "s/#.*$//" >expect <<-EOF &&
diff --git a/pre b/post
index $pre..$post 100644
--- a/pre
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index ef8e0e9cb0..804f2a82e8 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -20,7 +20,7 @@ test_expect_success 'git show batches blobs' '
# Ensure that there is exactly 1 negotiation by checking that there is
# only 1 "done" line sent. ("done" marks the end of negotiation.)
GIT_TRACE_PACKET="$(pwd)/trace" git -C client show HEAD &&
- grep "git> done" trace >done_lines &&
+ grep "fetch> done" trace >done_lines &&
test_line_count = 1 done_lines
'
@@ -44,7 +44,7 @@ test_expect_success 'diff batches blobs' '
# Ensure that there is exactly 1 negotiation by checking that there is
# only 1 "done" line sent. ("done" marks the end of negotiation.)
GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD &&
- grep "git> done" trace >done_lines &&
+ grep "fetch> done" trace >done_lines &&
test_line_count = 1 done_lines
'
@@ -127,7 +127,7 @@ test_expect_success 'diff with rename detection batches blobs' '
# only 1 "done" line sent. ("done" marks the end of negotiation.)
GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD >out &&
grep ":100644 100644.*R[0-9][0-9][0-9].*b.*c" out &&
- grep "git> done" trace >done_lines &&
+ grep "fetch> done" trace >done_lines &&
test_line_count = 1 done_lines
'
@@ -175,7 +175,7 @@ test_expect_success 'diff --break-rewrites fetches only if necessary, and batche
# by checking that there is only 1 "done" line sent. ("done" marks the
# end of negotiation.)
GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --break-rewrites --raw -M HEAD^ HEAD &&
- grep "git> done" trace >done_lines &&
+ grep "fetch> done" trace >done_lines &&
test_line_count = 1 done_lines
'
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
index 32e3b0ee0b..71ef4132d1 100755
--- a/t/t4104-apply-boundary.sh
+++ b/t/t4104-apply-boundary.sh
@@ -3,80 +3,55 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git apply boundary tests
+test_description='git apply boundary tests'
-'
. ./test-lib.sh
L="c d e f g h i j k l m n o p q r s t u v w x"
test_expect_success setup '
- for i in b '"$L"' y
- do
- echo $i
- done >victim &&
+ test_write_lines b $L y >victim &&
cat victim >original &&
git update-index --add victim &&
# add to the head
- for i in a b '"$L"' y
- do
- echo $i
- done >victim &&
+ test_write_lines a b $L y >victim &&
cat victim >add-a-expect &&
git diff victim >add-a-patch.with &&
git diff --unified=0 >add-a-patch.without &&
# insert at line two
- for i in b a '"$L"' y
- do
- echo $i
- done >victim &&
+ test_write_lines b a $L y >victim &&
cat victim >insert-a-expect &&
git diff victim >insert-a-patch.with &&
git diff --unified=0 >insert-a-patch.without &&
# modify at the head
- for i in a '"$L"' y
- do
- echo $i
- done >victim &&
+ test_write_lines a $L y >victim &&
cat victim >mod-a-expect &&
git diff victim >mod-a-patch.with &&
git diff --unified=0 >mod-a-patch.without &&
# remove from the head
- for i in '"$L"' y
- do
- echo $i
- done >victim &&
+ test_write_lines $L y >victim &&
cat victim >del-a-expect &&
git diff victim >del-a-patch.with &&
git diff --unified=0 >del-a-patch.without &&
# add to the tail
- for i in b '"$L"' y z
- do
- echo $i
- done >victim &&
+ test_write_lines b $L y z >victim &&
cat victim >add-z-expect &&
git diff victim >add-z-patch.with &&
git diff --unified=0 >add-z-patch.without &&
# modify at the tail
- for i in b '"$L"' z
- do
- echo $i
- done >victim &&
+ test_write_lines b $L z >victim &&
cat victim >mod-z-expect &&
git diff victim >mod-z-patch.with &&
git diff --unified=0 >mod-z-patch.without &&
# remove from the tail
- for i in b '"$L"'
- do
- echo $i
- done >victim &&
+ test_write_lines b $L >victim &&
cat victim >del-z-expect &&
git diff victim >del-z-patch.with &&
git diff --unified=0 >del-z-patch.without
@@ -88,15 +63,15 @@ for with in with without
do
case "$with" in
with) u= ;;
- without) u='--unidiff-zero ' ;;
+ without) u=--unidiff-zero ;;
esac
for kind in add-a add-z insert-a mod-a mod-z del-a del-z
do
test_expect_success "apply $kind-patch $with context" '
cat original >victim &&
git update-index victim &&
- git apply --index '"$u$kind-patch.$with"' &&
- test_cmp '"$kind"'-expect victim
+ git apply --index $u "$kind-patch.$with" &&
+ test_cmp "$kind-expect" victim
'
done
done
@@ -110,13 +85,12 @@ do
test_expect_success "apply non-git $kind-patch without context" '
cat original >victim &&
git update-index victim &&
- git apply --unidiff-zero --index '"$kind-ng.without"' &&
- test_cmp '"$kind"'-expect victim
+ git apply --unidiff-zero --index "$kind-ng.without" &&
+ test_cmp "$kind-expect" victim
'
done
test_expect_success 'two lines' '
-
>file &&
git add file &&
echo aaa >file &&
@@ -125,11 +99,10 @@ test_expect_success 'two lines' '
echo bbb >file &&
git add file &&
test_must_fail git apply --check patch
-
'
test_expect_success 'apply patch with 3 context lines matching at end' '
- { echo a; echo b; echo c; echo d; } >file &&
+ test_write_lines a b c d >file &&
git add file &&
echo e >>file &&
git diff >patch &&
diff --git a/t/t4134-apply-submodule.sh b/t/t4134-apply-submodule.sh
index 99ed4cc546..d1c16ba33c 100755
--- a/t/t4134-apply-submodule.sh
+++ b/t/t4134-apply-submodule.sh
@@ -8,7 +8,6 @@ test_description='git apply submodule tests'
. ./test-lib.sh
test_expect_success setup '
- test_oid_init &&
cat > create-sm.patch <<EOF &&
diff --git a/dir/sm b/dir/sm
new file mode 160000
diff --git a/t/t4140-apply-ita.sh b/t/t4140-apply-ita.sh
new file mode 100755
index 0000000000..c614eaf04c
--- /dev/null
+++ b/t/t4140-apply-ita.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='git apply of i-t-a file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_write_lines 1 2 3 4 5 >blueprint &&
+
+ cat blueprint >test-file &&
+ git add -N test-file &&
+ git diff >creation-patch &&
+ grep "new file mode 100644" creation-patch &&
+
+ rm -f test-file &&
+ git diff >deletion-patch &&
+ grep "deleted file mode 100644" deletion-patch
+'
+
+test_expect_success 'apply creation patch to ita path (--cached)' '
+ git rm -f test-file &&
+ cat blueprint >test-file &&
+ git add -N test-file &&
+
+ git apply --cached creation-patch &&
+ git cat-file blob :test-file >actual &&
+ test_cmp blueprint actual
+'
+
+test_expect_success 'apply creation patch to ita path (--index)' '
+ git rm -f test-file &&
+ cat blueprint >test-file &&
+ git add -N test-file &&
+ rm -f test-file &&
+
+ test_must_fail git apply --index creation-patch
+'
+
+test_expect_success 'apply deletion patch to ita path (--cached)' '
+ git rm -f test-file &&
+ cat blueprint >test-file &&
+ git add -N test-file &&
+
+ git apply --cached deletion-patch &&
+ test_must_fail git ls-files --stage --error-unmatch test-file
+'
+
+test_expect_success 'apply deletion patch to ita path (--index)' '
+ cat blueprint >test-file &&
+ git add -N test-file &&
+
+ test_must_fail git apply --index deletion-patch &&
+ git ls-files --stage --error-unmatch test-file
+'
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index bda4586a79..1da8ab120b 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -989,7 +989,7 @@ test_expect_success 'am -s unexpected trailer block' '
Signed-off-by: J C H <j@c.h>
EOF
git commit -F msg &&
- git cat-file commit HEAD | sed -e '1,/^$/d' >original &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >original &&
git format-patch --stdout -1 >patch &&
git reset --hard HEAD^ &&
@@ -998,7 +998,7 @@ test_expect_success 'am -s unexpected trailer block' '
cat original &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
) >expect &&
- git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
test_cmp expect actual &&
cat >msg <<-\EOF &&
@@ -1009,7 +1009,7 @@ test_expect_success 'am -s unexpected trailer block' '
EOF
git reset HEAD^ &&
git commit -F msg file &&
- git cat-file commit HEAD | sed -e '1,/^$/d' >original &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >original &&
git format-patch --stdout -1 >patch &&
git reset --hard HEAD^ &&
@@ -1020,7 +1020,7 @@ test_expect_success 'am -s unexpected trailer block' '
echo &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
) >expect &&
- git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
test_cmp expect actual
'
@@ -1133,4 +1133,20 @@ test_expect_success 'am and .gitattibutes' '
)
'
+test_expect_success 'apply binary blob in partial clone' '
+ printf "\\000" >binary &&
+ git add binary &&
+ git commit -m "binary blob" &&
+ git format-patch --stdout -m HEAD^ >patch &&
+
+ test_create_repo server &&
+ test_config -C server uploadpack.allowfilter 1 &&
+ test_config -C server uploadpack.allowanysha1inwant 1 &&
+ git clone --filter=blob:none "file://$(pwd)/server" client &&
+ test_when_finished "rm -rf client" &&
+
+ # Exercise to make sure that it works
+ git -C client am ../patch
+'
+
test_done
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index 831d424c47..b12b43e9e9 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -25,7 +25,6 @@ test_description='git rerere
. ./test-lib.sh
test_expect_success 'setup' '
- test_oid_init &&
cat >a1 <<-\EOF &&
Some title
==========
@@ -364,7 +363,7 @@ test_expect_success 'set up an unresolved merge' '
git reset --hard &&
git checkout version2 &&
fifth=$(git rev-parse fifth) &&
- echo "$fifth branch 'fifth' of ." |
+ echo "$fifth branch fifth of ." |
git fmt-merge-msg >msg &&
ancestor=$(git merge-base version2 fifth) &&
test_must_fail git merge-recursive "$ancestor" -- HEAD fifth &&
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index fd9af658af..56d34ed465 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -483,7 +483,7 @@ test_expect_success 'set up merge history' '
'
cat > expect <<\EOF
-* Merge branch 'side' into master
+* Merge branch 'side'
|\
| * side-2
| * side-1
@@ -502,7 +502,7 @@ test_expect_success 'log --graph with merge' '
'
cat > expect <<\EOF
-| | | * Merge branch 'side' into master
+| | | * Merge branch 'side'
| | | |\
| | | | * side-2
| | | | * side-1
@@ -521,7 +521,7 @@ test_expect_success 'log --graph --line-prefix="| | | " with merge' '
'
cat > expect.colors <<\EOF
-* Merge branch 'side' into master
+* Merge branch 'side'
<BLUE>|<RESET><CYAN>\<RESET>
<BLUE>|<RESET> * side-2
<BLUE>|<RESET> * side-1
@@ -555,7 +555,7 @@ cat > expect <<\EOF
|\ Merge: A B
| | Author: A U Thor <author@example.com>
| |
-| | Merge branch 'side' into master
+| | Merge branch 'side'
| |
| * commit tags/side-2
| | Author: A U Thor <author@example.com>
@@ -632,11 +632,11 @@ test_expect_success 'set up more tangled history' '
'
cat > expect <<\EOF
-* Merge tag 'reach' into master
+* Merge tag 'reach'
|\
| \
| \
-*-. \ Merge tags 'octopus-a' and 'octopus-b' into master
+*-. \ Merge tags 'octopus-a' and 'octopus-b'
|\ \ \
* | | | seventh
| | * | octopus-b
@@ -646,14 +646,14 @@ cat > expect <<\EOF
|/ /
| * reach
|/
-* Merge branch 'tangle' into master
+* Merge branch 'tangle'
|\
| * Merge branch 'side' (early part) into tangle
| |\
| * \ Merge branch 'master' (early part) into tangle
| |\ \
| * | | tangle-a
-* | | | Merge branch 'side' into master
+* | | | Merge branch 'side'
|\ \ \ \
| * | | | side-2
| | |_|/
@@ -735,16 +735,16 @@ test_expect_success 'log.decorate configuration' '
test_expect_success 'decorate-refs with glob' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach
+ Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b (octopus-b)
octopus-a (octopus-a)
reach
EOF
cat >expect.no-decorate <<-\EOF &&
- Merge-tag-reach-into-master
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach
+ Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b
octopus-a
@@ -765,8 +765,8 @@ test_expect_success 'decorate-refs with glob' '
test_expect_success 'decorate-refs without globs' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach
+ Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b
octopus-a
@@ -779,8 +779,8 @@ test_expect_success 'decorate-refs without globs' '
test_expect_success 'multiple decorate-refs' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach
+ Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b (octopus-b)
octopus-a (octopus-a)
@@ -794,8 +794,8 @@ test_expect_success 'multiple decorate-refs' '
test_expect_success 'decorate-refs-exclude with glob' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master (HEAD -> master)
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach (HEAD -> master)
+ Merge-tags-octopus-a-and-octopus-b
seventh (tag: seventh)
octopus-b (tag: octopus-b)
octopus-a (tag: octopus-a)
@@ -811,8 +811,8 @@ test_expect_success 'decorate-refs-exclude with glob' '
test_expect_success 'decorate-refs-exclude without globs' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master (HEAD -> master)
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach (HEAD -> master)
+ Merge-tags-octopus-a-and-octopus-b
seventh (tag: seventh)
octopus-b (tag: octopus-b, octopus-b)
octopus-a (tag: octopus-a, octopus-a)
@@ -828,8 +828,8 @@ test_expect_success 'decorate-refs-exclude without globs' '
test_expect_success 'multiple decorate-refs-exclude' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master (HEAD -> master)
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach (HEAD -> master)
+ Merge-tags-octopus-a-and-octopus-b
seventh (tag: seventh)
octopus-b (tag: octopus-b)
octopus-a (tag: octopus-a)
@@ -851,8 +851,8 @@ test_expect_success 'multiple decorate-refs-exclude' '
test_expect_success 'decorate-refs and decorate-refs-exclude' '
cat >expect.no-decorate <<-\EOF &&
- Merge-tag-reach-into-master (master)
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach (master)
+ Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b
octopus-a
@@ -866,8 +866,8 @@ test_expect_success 'decorate-refs and decorate-refs-exclude' '
test_expect_success 'deocrate-refs and log.excludeDecoration' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master (master)
- Merge-tags-octopus-a-and-octopus-b-into-master
+ Merge-tag-reach (master)
+ Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b (octopus-b)
octopus-a (octopus-a)
@@ -881,10 +881,10 @@ test_expect_success 'deocrate-refs and log.excludeDecoration' '
test_expect_success 'decorate-refs-exclude and simplify-by-decoration' '
cat >expect.decorate <<-\EOF &&
- Merge-tag-reach-into-master (HEAD -> master)
+ Merge-tag-reach (HEAD -> master)
reach (tag: reach, reach)
seventh (tag: seventh)
- Merge-branch-tangle-into-master
+ Merge-branch-tangle
Merge-branch-side-early-part-into-tangle (tangle)
tangle-a (tag: tangle-a)
EOF
@@ -1068,7 +1068,7 @@ cat >expect <<\EOF
|\ Merge: MERGE_PARENTS
| | Author: A U Thor <author@example.com>
| |
-| | Merge branch 'tangle' into master
+| | Merge branch 'tangle'
| |
| * commit COMMIT_OBJECT_NAME
| |\ Merge: MERGE_PARENTS
@@ -1102,7 +1102,7 @@ cat >expect <<\EOF
|\ \ \ \ Merge: MERGE_PARENTS
| | | | | Author: A U Thor <author@example.com>
| | | | |
-| | | | | Merge branch 'side' into master
+| | | | | Merge branch 'side'
| | | | |
| * | | | commit COMMIT_OBJECT_NAME
| | |_|/ Author: A U Thor <author@example.com>
@@ -1343,7 +1343,7 @@ cat >expect <<\EOF
*** |\ Merge: MERGE_PARENTS
*** | | Author: A U Thor <author@example.com>
*** | |
-*** | | Merge branch 'tangle' into master
+*** | | Merge branch 'tangle'
*** | |
*** | * commit COMMIT_OBJECT_NAME
*** | |\ Merge: MERGE_PARENTS
@@ -1377,7 +1377,7 @@ cat >expect <<\EOF
*** |\ \ \ \ Merge: MERGE_PARENTS
*** | | | | | Author: A U Thor <author@example.com>
*** | | | | |
-*** | | | | | Merge branch 'side' into master
+*** | | | | | Merge branch 'side'
*** | | | | |
*** | * | | | commit COMMIT_OBJECT_NAME
*** | | |_|/ Author: A U Thor <author@example.com>
@@ -1540,8 +1540,8 @@ cat >expect <<-\EOF
* reach
|
| A reach.t
-* Merge branch 'tangle' into master
-* Merge branch 'side' into master
+* Merge branch 'tangle'
+* Merge branch 'side'
|\
| * side-2
|
@@ -1562,8 +1562,8 @@ cat >expect <<-\EOF
* reach
|
| reach.t
-* Merge branch 'tangle' into master
-* Merge branch 'side' into master
+* Merge branch 'tangle'
+* Merge branch 'side'
|\
| * side-2
|
@@ -1850,6 +1850,16 @@ test_expect_success 'log does not default to HEAD when rev input is given' '
test_must_be_empty actual
'
+test_expect_success 'do not default to HEAD with ignored object on cmdline' '
+ git log --ignore-missing $ZERO_OID >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'do not default to HEAD with ignored object on stdin' '
+ echo $ZERO_OID | git log --ignore-missing --stdin >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'set up --source tests' '
git checkout --orphan source-a &&
test_commit one &&
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index e186c83250..2d1d7b5d19 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -4,7 +4,6 @@ test_description='test log -L'
. ./test-lib.sh
test_expect_success 'setup (import history)' '
- test_oid_init &&
git fast-import < "$TEST_DIRECTORY"/t4211/history.export &&
git reset --hard
'
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index c855bcd3e7..d11040ce41 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -30,12 +30,19 @@ test_expect_success 'setup test - repo, commits, commit graph, log outputs' '
rm file_to_be_deleted &&
git add . &&
git commit -m "file removed" &&
- git commit-graph write --reachable --changed-paths
+ git commit --allow-empty -m "empty" &&
+ git commit-graph write --reachable --changed-paths &&
+
+ test_oid_cache <<-EOF
+ oid_version sha1:1
+ oid_version sha256:2
+ EOF
'
+
graph_read_expect () {
NUM_CHUNKS=5
cat >expect <<- EOF
- header: 43475048 1 1 $NUM_CHUNKS 0
+ header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
num_commits: $1
chunks: oid_fanout oid_lookup commit_metadata bloom_indexes bloom_data
EOF
@@ -44,7 +51,7 @@ graph_read_expect () {
}
test_expect_success 'commit-graph write wrote out the bloom chunks' '
- graph_read_expect 15
+ graph_read_expect 16
'
# Turn off any inherited trace2 settings for this test.
@@ -53,14 +60,14 @@ sane_unset GIT_TRACE2_PERF_BRIEF
sane_unset GIT_TRACE2_CONFIG_PARAMS
setup () {
- rm "$TRASH_DIRECTORY/trace.perf"
+ rm -f "$TRASH_DIRECTORY/trace.perf" &&
git -c core.commitGraph=false log --pretty="format:%s" $1 >log_wo_bloom &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.perf" git -c core.commitGraph=true log --pretty="format:%s" $1 >log_w_bloom
}
test_bloom_filters_used () {
log_args=$1
- bloom_trace_prefix="statistics:{\"filter_not_present\":0,\"zero_length_filter\":0,\"maybe\""
+ bloom_trace_prefix="statistics:{\"filter_not_present\":${2:-0},\"maybe\""
setup "$log_args" &&
grep -q "$bloom_trace_prefix" "$TRASH_DIRECTORY/trace.perf" &&
test_cmp log_wo_bloom log_w_bloom &&
@@ -90,7 +97,9 @@ do
"--ancestry-path side..master"
do
test_expect_success "git log option: $option for path: $path" '
- test_bloom_filters_used "$option -- $path"
+ test_bloom_filters_used "$option -- $path" &&
+ test_config commitgraph.readChangedPaths false &&
+ test_bloom_filters_not_used "$option -- $path"
'
done
done
@@ -112,6 +121,10 @@ test_expect_success 'git log -- multiple path specs does not use Bloom filters'
test_bloom_filters_not_used "-- file4 A/file1"
'
+test_expect_success 'git log -- "." pathspec at root does not use Bloom filters' '
+ test_bloom_filters_not_used "-- ."
+'
+
test_expect_success 'git log with wildcard that resolves to a single path uses Bloom filters' '
test_bloom_filters_used "-- *4" &&
test_bloom_filters_used "-- *renamed"
@@ -126,12 +139,15 @@ test_expect_success 'setup - add commit-graph to the chain without Bloom filters
test_commit c14 A/anotherFile2 &&
test_commit c15 A/B/anotherFile2 &&
test_commit c16 A/B/C/anotherFile2 &&
- GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0 git commit-graph write --reachable --split &&
+ git commit-graph write --reachable --split --no-changed-paths &&
test_line_count = 2 .git/objects/info/commit-graphs/commit-graph-chain
'
-test_expect_success 'Do not use Bloom filters if the latest graph does not have Bloom filters.' '
- test_bloom_filters_not_used "-- A/B"
+test_expect_success 'use Bloom filters even if the latest graph does not have Bloom filters' '
+ # Ensure that the number of empty filters is equal to the number of
+ # filters in the latest graph layer to prove that they are loaded (and
+ # ignored).
+ test_bloom_filters_used "-- A/B" 3
'
test_expect_success 'setup - add commit-graph to the chain with Bloom filters' '
@@ -142,7 +158,7 @@ test_expect_success 'setup - add commit-graph to the chain with Bloom filters' '
test_bloom_filters_used_when_some_filters_are_missing () {
log_args=$1
- bloom_trace_prefix="statistics:{\"filter_not_present\":3,\"zero_length_filter\":0,\"maybe\":8,\"definitely_not\":6"
+ bloom_trace_prefix="statistics:{\"filter_not_present\":3,\"maybe\":6,\"definitely_not\":9"
setup "$log_args" &&
grep -q "$bloom_trace_prefix" "$TRASH_DIRECTORY/trace.perf" &&
test_cmp log_wo_bloom log_w_bloom
@@ -152,4 +168,238 @@ test_expect_success 'Use Bloom filters if they exist in the latest but not all c
test_bloom_filters_used_when_some_filters_are_missing "-- A/B"
'
+test_expect_success 'persist filter settings' '
+ test_when_finished rm -rf .git/objects/info/commit-graph* &&
+ rm -rf .git/objects/info/commit-graph* &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ GIT_TRACE2_EVENT_NESTING=5 \
+ GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=9 \
+ GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY=15 \
+ git commit-graph write --reachable --changed-paths &&
+ grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2-auto.txt" \
+ GIT_TRACE2_EVENT_NESTING=5 \
+ git commit-graph write --reachable --changed-paths &&
+ grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2-auto.txt
+'
+
+test_max_changed_paths () {
+ grep "\"max_changed_paths\":$1" $2
+}
+
+test_filter_not_computed () {
+ grep "\"key\":\"filter-not-computed\",\"value\":\"$1\"" $2
+}
+
+test_filter_computed () {
+ grep "\"key\":\"filter-computed\",\"value\":\"$1\"" $2
+}
+
+test_filter_trunc_empty () {
+ grep "\"key\":\"filter-trunc-empty\",\"value\":\"$1\"" $2
+}
+
+test_filter_trunc_large () {
+ grep "\"key\":\"filter-trunc-large\",\"value\":\"$1\"" $2
+}
+
+test_expect_success 'correctly report changes over limit' '
+ git init limits &&
+ (
+ cd limits &&
+ mkdir d &&
+ mkdir d/e &&
+
+ for i in $(test_seq 1 2)
+ do
+ printf $i >d/file$i.txt &&
+ printf $i >d/e/file$i.txt || return 1
+ done &&
+
+ mkdir mode &&
+ printf bash >mode/script.sh &&
+
+ mkdir foo &&
+ touch foo/bar &&
+ touch foo.txt &&
+
+ git add d foo foo.txt mode &&
+ git commit -m "files" &&
+
+ # Commit has 7 file and 4 directory adds
+ GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS=10 \
+ GIT_TRACE2_EVENT="$(pwd)/trace" \
+ git commit-graph write --reachable --changed-paths &&
+ test_max_changed_paths 10 trace &&
+ test_filter_computed 1 trace &&
+ test_filter_trunc_large 1 trace &&
+
+ for path in $(git ls-tree -r --name-only HEAD)
+ do
+ git -c commitGraph.readChangedPaths=false log \
+ -- $path >expect &&
+ git log -- $path >actual &&
+ test_cmp expect actual || return 1
+ done &&
+
+ # Make a variety of path changes
+ printf new1 >d/e/file1.txt &&
+ printf new2 >d/file2.txt &&
+ rm d/e/file2.txt &&
+ rm -r foo &&
+ printf text >foo &&
+ mkdir f &&
+ printf new1 >f/file1.txt &&
+
+ # including a mode-only change (counts as modified)
+ git update-index --chmod=+x mode/script.sh &&
+
+ git add foo d f &&
+ git commit -m "complicated" &&
+
+ # start from scratch and rebuild
+ rm -f .git/objects/info/commit-graph &&
+ GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS=10 \
+ GIT_TRACE2_EVENT="$(pwd)/trace-edit" \
+ git commit-graph write --reachable --changed-paths &&
+ test_max_changed_paths 10 trace-edit &&
+ test_filter_computed 2 trace-edit &&
+ test_filter_trunc_large 2 trace-edit &&
+
+ for path in $(git ls-tree -r --name-only HEAD)
+ do
+ git -c commitGraph.readChangedPaths=false log \
+ -- $path >expect &&
+ git log -- $path >actual &&
+ test_cmp expect actual || return 1
+ done &&
+
+ # start from scratch and rebuild
+ rm -f .git/objects/info/commit-graph &&
+ GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS=11 \
+ GIT_TRACE2_EVENT="$(pwd)/trace-update" \
+ git commit-graph write --reachable --changed-paths &&
+ test_max_changed_paths 11 trace-update &&
+ test_filter_computed 2 trace-update &&
+ test_filter_trunc_large 0 trace-update &&
+
+ for path in $(git ls-tree -r --name-only HEAD)
+ do
+ git -c commitGraph.readChangedPaths=false log \
+ -- $path >expect &&
+ git log -- $path >actual &&
+ test_cmp expect actual || return 1
+ done
+ )
+'
+
+test_expect_success 'correctly report commits with no changed paths' '
+ git init empty &&
+ test_when_finished "rm -fr empty" &&
+ (
+ cd empty &&
+
+ git commit --allow-empty -m "initial commit" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ git commit-graph write --reachable --changed-paths &&
+ test_filter_computed 1 trace.event &&
+ test_filter_not_computed 0 trace.event &&
+ test_filter_trunc_empty 1 trace.event &&
+ test_filter_trunc_large 0 trace.event
+ )
+'
+
+test_expect_success 'Bloom generation is limited by --max-new-filters' '
+ (
+ cd limits &&
+ test_commit c2 filter &&
+ test_commit c3 filter &&
+ test_commit c4 no-filter &&
+
+ rm -f trace.event &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ git commit-graph write --reachable --split=replace \
+ --changed-paths --max-new-filters=2 &&
+
+ test_filter_computed 2 trace.event &&
+ test_filter_not_computed 3 trace.event &&
+ test_filter_trunc_empty 0 trace.event &&
+ test_filter_trunc_large 0 trace.event
+ )
+'
+
+test_expect_success 'Bloom generation backfills previously-skipped filters' '
+ # Check specifying commitGraph.maxNewFilters over "git config" works.
+ test_config -C limits commitGraph.maxNewFilters 1 &&
+ (
+ cd limits &&
+
+ rm -f trace.event &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ git commit-graph write --reachable --changed-paths \
+ --split=replace &&
+ test_filter_computed 1 trace.event &&
+ test_filter_not_computed 4 trace.event &&
+ test_filter_trunc_empty 0 trace.event &&
+ test_filter_trunc_large 0 trace.event
+ )
+'
+
+test_expect_success '--max-new-filters overrides configuration' '
+ git init override &&
+ test_when_finished "rm -fr override" &&
+ test_config -C override commitGraph.maxNewFilters 2 &&
+ (
+ cd override &&
+ test_commit one &&
+ test_commit two &&
+
+ rm -f trace.event &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ git commit-graph write --reachable --changed-paths \
+ --max-new-filters=1 &&
+ test_filter_computed 1 trace.event &&
+ test_filter_not_computed 1 trace.event &&
+ test_filter_trunc_empty 0 trace.event &&
+ test_filter_trunc_large 0 trace.event
+ )
+'
+
+test_expect_success 'Bloom generation backfills empty commits' '
+ git init empty &&
+ test_when_finished "rm -fr empty" &&
+ (
+ cd empty &&
+ for i in $(test_seq 1 6)
+ do
+ git commit --allow-empty -m "$i"
+ done &&
+
+ # Generate Bloom filters for empty commits 1-6, two at a time.
+ for i in $(test_seq 1 3)
+ do
+ rm -f trace.event &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ git commit-graph write --reachable \
+ --changed-paths --max-new-filters=2 &&
+ test_filter_computed 2 trace.event &&
+ test_filter_not_computed 4 trace.event &&
+ test_filter_trunc_empty 2 trace.event &&
+ test_filter_trunc_large 0 trace.event
+ done &&
+
+ # Finally, make sure that once all commits have filters, that
+ # none are subsequently recomputed.
+ rm -f trace.event &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+ git commit-graph write --reachable \
+ --changed-paths --max-new-filters=2 &&
+ test_filter_computed 0 trace.event &&
+ test_filter_not_computed 6 trace.event &&
+ test_filter_trunc_empty 0 trace.event &&
+ test_filter_trunc_large 0 trace.event
+ )
+'
+
test_done
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 746cdb626e..392201cabd 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -12,8 +12,7 @@ TRASH=$(pwd)
test_expect_success \
'setup' \
- 'test_oid_init &&
- rm -f .git/index* &&
+ 'rm -f .git/index* &&
perl -e "print \"a\" x 4096;" > a &&
perl -e "print \"b\" x 4096;" > b &&
perl -e "print \"c\" x 4096;" > c &&
@@ -497,4 +496,40 @@ test_expect_success 'make sure index-pack detects the SHA1 collision (large blob
)
'
+test_expect_success 'prefetch objects' '
+ rm -rf server client &&
+
+ git init server &&
+ test_config -C server uploadpack.allowanysha1inwant 1 &&
+ test_config -C server uploadpack.allowfilter 1 &&
+ test_config -C server protocol.version 2 &&
+
+ echo one >server/one &&
+ git -C server add one &&
+ git -C server commit -m one &&
+ git -C server branch one_branch &&
+
+ echo two_a >server/two_a &&
+ echo two_b >server/two_b &&
+ git -C server add two_a two_b &&
+ git -C server commit -m two &&
+
+ echo three >server/three &&
+ git -C server add three &&
+ git -C server commit -m three &&
+ git -C server branch three_branch &&
+
+ # Clone, fetch "two" with blobs excluded, and re-push it. This requires
+ # the client to have the blobs of "two" - verify that these are
+ # prefetched in one batch.
+ git clone --filter=blob:none --single-branch -b one_branch \
+ "file://$(pwd)/server" client &&
+ test_config -C client protocol.version 2 &&
+ TWO=$(git -C server rev-parse three_branch^) &&
+ git -C client fetch --filter=blob:none origin "$TWO" &&
+ GIT_TRACE_PACKET=$(pwd)/trace git -C client push origin "$TWO":refs/heads/two_branch &&
+ grep "fetch> done" trace >donelines &&
+ test_line_count = 1 donelines
+'
+
test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 8981c9b90e..c92e553a2f 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -7,7 +7,6 @@ test_description='pack index with 64-bit offsets and object CRC'
. ./test-lib.sh
test_expect_success 'setup' '
- test_oid_init &&
rawsz=$(test_oid rawsz) &&
rm -rf .git &&
git init &&
@@ -15,7 +14,7 @@ test_expect_success 'setup' '
i=1 &&
while test $i -le 100
do
- iii=$(printf '%03i' $i)
+ iii=$(printf "%03i" $i)
test-tool genrandom "bar" 200 > wide_delta_$iii &&
test-tool genrandom "baz $iii" 50 >> wide_delta_$iii &&
test-tool genrandom "foo"$i 100 > deep_delta_$iii &&
diff --git a/t/t5308-pack-detect-duplicates.sh b/t/t5308-pack-detect-duplicates.sh
index 6845c1f3c3..693b2411c8 100755
--- a/t/t5308-pack-detect-duplicates.sh
+++ b/t/t5308-pack-detect-duplicates.sh
@@ -4,23 +4,27 @@ test_description='handling of duplicate objects in incoming packfiles'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
-if ! test_have_prereq SHA1
-then
- skip_all='not using SHA-1 for objects'
- test_done
-fi
+test_expect_success 'setup' '
+ test_oid_cache <<-EOF
+ lo_oid sha1:e68fe8129b546b101aee9510c5328e7f21ca1d18
+ lo_oid sha256:471819e8c52bf11513f100b2810a8aa0622d5cd3d1c913758a071dd4b3bad8fe
+
+ missing_oid sha1:e69d000000000000000000000000000000000000
+ missing_oid sha256:4720000000000000000000000000000000000000000000000000000000000000
+ EOF
+'
# The sha1s we have in our pack. It's important that these have the same
# starting byte, so that they end up in the same fanout section of the index.
# That lets us make sure we are exercising the binary search with both sets.
-LO_SHA1=e68fe8129b546b101aee9510c5328e7f21ca1d18
-HI_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+LO_SHA1=$(test_oid lo_oid)
+HI_SHA1=$EMPTY_BLOB
# And here's a "missing sha1" which will produce failed lookups. It must also
# be in the same fanout section, and should be between the two (so that during
# our binary search, we are sure to end up looking at one or the other of the
# duplicate runs).
-MISSING_SHA1='e69d000000000000000000000000000000000000'
+MISSING_SHA1=$(test_oid missing_oid)
# git will never intentionally create packfiles with
# duplicate objects, so we have to construct them by hand.
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 2a4557efc2..535313e4dc 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -45,7 +45,6 @@ extended_table () {
}
test_expect_success 'setup' '
- test_oid_init &&
test_oid_cache <<-EOF
oid000 sha1:1485
oid000 sha256:4222
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 26f332d6a3..2ed0c1544d 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -11,7 +11,11 @@ test_expect_success 'setup full repo' '
git init &&
git config core.commitGraph true &&
objdir=".git/objects" &&
- test_oid_init
+
+ test_oid_cache <<-EOF
+ oid_version sha1:1
+ oid_version sha256:2
+ EOF
'
test_expect_success POSIXPERM 'tweak umask for modebit tests' '
@@ -78,7 +82,7 @@ graph_read_expect() {
NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
fi
cat >expect <<- EOF
- header: 43475048 1 1 $NUM_CHUNKS 0
+ header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
num_commits: $1
chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
EOF
@@ -413,6 +417,35 @@ test_expect_success 'replace-objects invalidates commit-graph' '
)
'
+test_expect_success 'warn on improper hash version' '
+ git init --object-format=sha1 sha1 &&
+ (
+ cd sha1 &&
+ test_commit 1 &&
+ git commit-graph write --reachable &&
+ mv .git/objects/info/commit-graph ../cg-sha1
+ ) &&
+ git init --object-format=sha256 sha256 &&
+ (
+ cd sha256 &&
+ test_commit 1 &&
+ git commit-graph write --reachable &&
+ mv .git/objects/info/commit-graph ../cg-sha256
+ ) &&
+ (
+ cd sha1 &&
+ mv ../cg-sha256 .git/objects/info/commit-graph &&
+ git log -1 2>err &&
+ test_i18ngrep "commit-graph hash version 2 does not match version 1" err
+ ) &&
+ (
+ cd sha256 &&
+ mv ../cg-sha1 .git/objects/info/commit-graph &&
+ git log -1 2>err &&
+ test_i18ngrep "commit-graph hash version 1 does not match version 2" err
+ )
+'
+
# the verify tests below expect the commit-graph to contain
# exactly the commits reachable from the commits/8 branch.
# If the file changes the set of commits in the list, then the
@@ -476,7 +509,7 @@ corrupt_graph_verify() {
cp $objdir/info/commit-graph commit-graph-pre-write-test
fi &&
git status --short &&
- GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD=true git commit-graph write &&
+ GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git commit-graph write &&
chmod u+w $objdir/info/commit-graph &&
git commit-graph verify
}
@@ -529,7 +562,7 @@ test_expect_success 'detect bad hash version' '
'
test_expect_success 'detect low chunk count' '
- corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\02" \
+ corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\01" \
"missing the .* chunk"
'
@@ -615,7 +648,8 @@ test_expect_success 'detect invalid checksum hash' '
test_expect_success 'detect incorrect chunk count' '
corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\377" \
- "chunk lookup table entry missing" $GRAPH_CHUNK_LOOKUP_OFFSET
+ "commit-graph file is too small to hold [0-9]* chunks" \
+ $GRAPH_CHUNK_LOOKUP_OFFSET
'
test_expect_success 'git fsck (checks commit-graph)' '
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 7214cab36c..f340b376bc 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -5,6 +5,8 @@ test_description='multi-pack-indexes'
objdir=.git/objects
+HASH_LEN=$(test_oid rawsz)
+
midx_read_expect () {
NUM_PACKS=$1
NUM_OBJECTS=$2
@@ -13,7 +15,7 @@ midx_read_expect () {
EXTRA_CHUNKS="$5"
{
cat <<-EOF &&
- header: 4d494458 1 $NUM_CHUNKS $NUM_PACKS
+ header: 4d494458 1 $HASH_LEN $NUM_CHUNKS $NUM_PACKS
chunks: pack-names oid-fanout oid-lookup object-offsets$EXTRA_CHUNKS
num_objects: $NUM_OBJECTS
packs:
@@ -29,7 +31,6 @@ midx_read_expect () {
}
test_expect_success 'setup' '
- test_oid_init &&
test_oid_cache <<-EOF
idxoff sha1:2999
idxoff sha256:3739
@@ -47,7 +48,7 @@ test_expect_success "don't write midx with no packs" '
test_path_is_missing pack/multi-pack-index
'
-test_expect_success "Warn if a midx contains no oid" '
+test_expect_success SHA1 'warn if a midx contains no oid' '
cp "$TEST_DIRECTORY"/t5319/no-objects.midx $objdir/pack/multi-pack-index &&
test_must_fail git multi-pack-index verify &&
rm $objdir/pack/multi-pack-index
@@ -199,6 +200,40 @@ test_expect_success 'write midx with twelve packs' '
compare_results_with_midx "twelve packs"
+test_expect_success 'warn on improper hash version' '
+ git init --object-format=sha1 sha1 &&
+ (
+ cd sha1 &&
+ git config core.multiPackIndex true &&
+ test_commit 1 &&
+ git repack -a &&
+ git multi-pack-index write &&
+ mv .git/objects/pack/multi-pack-index ../mpi-sha1
+ ) &&
+ git init --object-format=sha256 sha256 &&
+ (
+ cd sha256 &&
+ git config core.multiPackIndex true &&
+ test_commit 1 &&
+ git repack -a &&
+ git multi-pack-index write &&
+ mv .git/objects/pack/multi-pack-index ../mpi-sha256
+ ) &&
+ (
+ cd sha1 &&
+ mv ../mpi-sha256 .git/objects/pack/multi-pack-index &&
+ git log -1 2>err &&
+ test_i18ngrep "multi-pack-index hash version 2 does not match version 1" err
+ ) &&
+ (
+ cd sha256 &&
+ mv ../mpi-sha1 .git/objects/pack/multi-pack-index &&
+ git log -1 2>err &&
+ test_i18ngrep "multi-pack-index hash version 1 does not match version 2" err
+ )
+'
+
+
test_expect_success 'verify multi-pack-index success' '
git multi-pack-index verify --object-dir=$objdir
'
@@ -244,7 +279,6 @@ test_expect_success 'verify bad signature' '
"multi-pack-index signature"
'
-HASH_LEN=$(test_oid rawsz)
NUM_OBJECTS=74
MIDX_BYTE_VERSION=4
MIDX_BYTE_OID_VERSION=5
@@ -273,7 +307,7 @@ test_expect_success 'verify bad version' '
'
test_expect_success 'verify bad OID version' '
- corrupt_midx_and_verify $MIDX_BYTE_OID_VERSION "\02" $objdir \
+ corrupt_midx_and_verify $MIDX_BYTE_OID_VERSION "\03" $objdir \
"hash version"
'
@@ -348,12 +382,52 @@ test_expect_success 'repack with the --no-progress option' '
test_line_count = 0 err
'
-test_expect_success 'repack removes multi-pack-index' '
+test_expect_success 'repack removes multi-pack-index when deleting packs' '
test_path_is_file $objdir/pack/multi-pack-index &&
- GIT_TEST_MULTI_PACK_INDEX=0 git repack -adf &&
+ # Set GIT_TEST_MULTI_PACK_INDEX to 0 to avoid writing a new
+ # multi-pack-index after repacking, but set "core.multiPackIndex" to
+ # true so that "git repack" can read the existing MIDX.
+ GIT_TEST_MULTI_PACK_INDEX=0 git -c core.multiPackIndex repack -adf &&
test_path_is_missing $objdir/pack/multi-pack-index
'
+test_expect_success 'repack preserves multi-pack-index when creating packs' '
+ git init preserve &&
+ test_when_finished "rm -fr preserve" &&
+ (
+ cd preserve &&
+ packdir=.git/objects/pack &&
+ midx=$packdir/multi-pack-index &&
+
+ test_commit 1 &&
+ pack1=$(git pack-objects --all $packdir/pack) &&
+ touch $packdir/pack-$pack1.keep &&
+ test_commit 2 &&
+ pack2=$(git pack-objects --revs $packdir/pack) &&
+ touch $packdir/pack-$pack2.keep &&
+
+ git multi-pack-index write &&
+ cp $midx $midx.bak &&
+
+ cat >pack-input <<-EOF &&
+ HEAD
+ ^HEAD~1
+ EOF
+ test_commit 3 &&
+ pack3=$(git pack-objects --revs $packdir/pack <pack-input) &&
+ test_commit 4 &&
+ pack4=$(git pack-objects --revs $packdir/pack <pack-input) &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 git -c core.multiPackIndex repack -ad &&
+ ls -la $packdir &&
+ test_path_is_file $packdir/pack-$pack1.pack &&
+ test_path_is_file $packdir/pack-$pack2.pack &&
+ test_path_is_missing $packdir/pack-$pack3.pack &&
+ test_path_is_missing $packdir/pack-$pack4.pack &&
+ test_cmp_bin $midx.bak $midx
+ )
+'
+
compare_results_with_midx "after repack"
test_expect_success 'multi-pack-index and pack-bitmap' '
@@ -643,6 +717,7 @@ test_expect_success 'expire respects .keep files' '
'
test_expect_success 'repack --batch-size=0 repacks everything' '
+ cp -r dup dup2 &&
(
cd dup &&
rm .git/objects/pack/*.keep &&
@@ -662,4 +737,21 @@ test_expect_success 'repack --batch-size=0 repacks everything' '
)
'
+test_expect_success 'repack --batch-size=<large> repacks everything' '
+ (
+ cd dup2 &&
+ rm .git/objects/pack/*.keep &&
+ ls .git/objects/pack/*idx >idx-list &&
+ test_line_count = 2 idx-list &&
+ git multi-pack-index repack --batch-size=2000000 &&
+ ls .git/objects/pack/*idx >idx-list &&
+ test_line_count = 3 idx-list &&
+ test-tool read-midx .git/objects | grep idx >midx-list &&
+ test_line_count = 3 midx-list &&
+ git multi-pack-index expire &&
+ ls -al .git/objects/pack/*idx >idx-list &&
+ test_line_count = 1 idx-list
+ )
+'
+
test_done
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
index 269d0964a3..c334ee9155 100755
--- a/t/t5324-split-commit-graph.sh
+++ b/t/t5324-split-commit-graph.sh
@@ -12,13 +12,15 @@ test_expect_success 'setup repo' '
git config gc.writeCommitGraph false &&
infodir=".git/objects/info" &&
graphdir="$infodir/commit-graphs" &&
- test_oid_init &&
test_oid_cache <<-EOM
shallow sha1:1760
shallow sha256:2064
base sha1:1376
base sha256:1496
+
+ oid_version sha1:1
+ oid_version sha256:2
EOM
'
@@ -29,7 +31,7 @@ graph_read_expect() {
NUM_BASE=$2
fi
cat >expect <<- EOF
- header: 43475048 1 1 3 $NUM_BASE
+ header: 43475048 1 $(test_oid oid_version) 3 $NUM_BASE
num_commits: $1
chunks: oid_fanout oid_lookup commit_metadata
EOF
@@ -399,7 +401,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'handles file descriptor exhaustion'
for i in $(test_seq 64)
do
test_commit $i &&
- test_might_fail run_with_limited_open_files git commit-graph write \
+ run_with_limited_open_files test_might_fail git commit-graph write \
--split=no-merge --reachable || return 1
done
)
@@ -425,4 +427,17 @@ done <<\EOF
0600 -r--------
EOF
+test_expect_success '--split=replace with partial Bloom data' '
+ rm -rf $graphdir $infodir/commit-graph &&
+ git reset --hard commits/3 &&
+ git rev-list -1 HEAD~2 >a &&
+ git rev-list -1 HEAD~1 >b &&
+ git commit-graph write --split=no-merge --stdin-commits --changed-paths <a &&
+ git commit-graph write --split=no-merge --stdin-commits <b &&
+ git commit-graph write --split=replace --stdin-commits --changed-paths <c &&
+ ls $graphdir/graph-*.graph >graph-files &&
+ test_line_count = 1 graph-files &&
+ verify_chain_files_exist $graphdir
+'
+
test_done
diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh
new file mode 100755
index 0000000000..746487286f
--- /dev/null
+++ b/t/t5411-proc-receive-hook.sh
@@ -0,0 +1,117 @@
+#!/bin/sh
+#
+# Copyright (c) 2020 Jiang Xin
+#
+
+test_description='Test proc-receive hook'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/t5411/common-functions.sh
+
+setup_upstream_and_workbench () {
+ # Refs of upstream : master(A)
+ # Refs of workbench: master(A) tags/v123
+ test_expect_success "setup upstream and workbench" '
+ rm -rf upstream.git &&
+ rm -rf workbench &&
+ git init --bare upstream.git &&
+ git init workbench &&
+ create_commits_in workbench A B &&
+ (
+ cd workbench &&
+ # Try to make a stable fixed width for abbreviated commit ID,
+ # this fixed-width oid will be replaced with "<OID>".
+ git config core.abbrev 7 &&
+ git tag -m "v123" v123 $A &&
+ git remote add origin ../upstream.git &&
+ git push origin master &&
+ git update-ref refs/heads/master $A $B &&
+ git -C ../upstream.git update-ref \
+ refs/heads/master $A $B
+ ) &&
+ TAG=$(git -C workbench rev-parse v123) &&
+
+ # setup pre-receive hook
+ write_script upstream.git/hooks/pre-receive <<-\EOF &&
+ exec >&2
+ echo "# pre-receive hook"
+ while read old new ref
+ do
+ echo "pre-receive< $old $new $ref"
+ done
+ EOF
+
+ # setup post-receive hook
+ write_script upstream.git/hooks/post-receive <<-\EOF &&
+ exec >&2
+ echo "# post-receive hook"
+ while read old new ref
+ do
+ echo "post-receive< $old $new $ref"
+ done
+ EOF
+
+ upstream=upstream.git
+ '
+}
+
+run_proc_receive_hook_test() {
+ case $1 in
+ http)
+ PROTOCOL="HTTP protocol"
+ URL_PREFIX="http://.*"
+ ;;
+ local)
+ PROTOCOL="builtin protocol"
+ URL_PREFIX="\.\."
+ ;;
+ esac
+
+ # Include test cases for both file and HTTP protocol
+ for t in "$TEST_DIRECTORY"/t5411/test-*.sh
+ do
+ . "$t"
+ done
+}
+
+# Initialize the upstream repository and local workbench.
+setup_upstream_and_workbench
+
+# Load test cases that only need to be executed once.
+for t in "$TEST_DIRECTORY"/t5411/once-*.sh
+do
+ . "$t"
+done
+
+# Initialize the upstream repository and local workbench.
+setup_upstream_and_workbench
+
+# Run test cases for 'proc-receive' hook on local file protocol.
+run_proc_receive_hook_test local
+
+ROOT_PATH="$PWD"
+. "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
+start_httpd
+
+# Re-initialize the upstream repository and local workbench.
+setup_upstream_and_workbench
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "setup for HTTP protocol" '
+ git -C upstream.git config http.receivepack true &&
+ upstream="$HTTPD_DOCUMENT_ROOT_PATH/upstream.git" &&
+ mv upstream.git "$upstream" &&
+ git -C workbench remote set-url origin "$HTTPD_URL/auth-push/smart/upstream.git" &&
+ set_askpass user@host pass@host
+'
+
+setup_askpass_helper
+
+# Run test cases for 'proc-receive' hook on HTTP protocol.
+run_proc_receive_hook_test http
+
+test_done
diff --git a/t/t5411/common-functions.sh b/t/t5411/common-functions.sh
new file mode 100644
index 0000000000..6580bebd8e
--- /dev/null
+++ b/t/t5411/common-functions.sh
@@ -0,0 +1,56 @@
+# Create commits in <repo> and assign each commit's oid to shell variables
+# given in the arguments (A, B, and C). E.g.:
+#
+# create_commits_in <repo> A B C
+#
+# NOTE: Never calling this function from a subshell since variable
+# assignments will disappear when subshell exits.
+create_commits_in () {
+ repo="$1" &&
+ if ! parent=$(git -C "$repo" rev-parse HEAD^{} --)
+ then
+ parent=
+ fi &&
+ T=$(git -C "$repo" write-tree) &&
+ shift &&
+ while test $# -gt 0
+ do
+ name=$1 &&
+ test_tick &&
+ if test -z "$parent"
+ then
+ oid=$(echo $name | git -C "$repo" commit-tree $T)
+ else
+ oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T)
+ fi &&
+ eval $name=$oid &&
+ parent=$oid &&
+ shift ||
+ return 1
+ done &&
+ git -C "$repo" update-ref refs/heads/master $oid
+}
+
+# Format the output of git-push, git-show-ref and other commands to make a
+# user-friendly and stable text. We can easily prepare the expect text
+# without having to worry about future changes of the commit ID and spaces
+# of the output. Single quotes are replaced with double quotes, because
+# it is boring to prepare unquoted single quotes in expect text. We also
+# remove some locale error messages, which break test if we turn on
+# `GIT_TEST_GETTEXT_POISON=true` in order to test unintentional translations
+# on plumbing commands.
+make_user_friendly_and_stable_output () {
+ sed \
+ -e "s/ *\$//" \
+ -e "s/ */ /g" \
+ -e "s/'/\"/g" \
+ -e "s/ / /g" \
+ -e "s/$A/<COMMIT-A>/g" \
+ -e "s/$B/<COMMIT-B>/g" \
+ -e "s/$TAG/<TAG-v123>/g" \
+ -e "s/$ZERO_OID/<ZERO-OID>/g" \
+ -e "s/$(echo $A | cut -c1-7)[0-9a-f]*/<OID-A>/g" \
+ -e "s/$(echo $B | cut -c1-7)[0-9a-f]*/<OID-B>/g" \
+ -e "s#To $URL_PREFIX/upstream.git#To <URL/of/upstream.git>#" \
+ -e "/^error: / d"
+}
diff --git a/t/t5411/once-0010-report-status-v1.sh b/t/t5411/once-0010-report-status-v1.sh
new file mode 100644
index 0000000000..dc2cf4a522
--- /dev/null
+++ b/t/t5411/once-0010-report-status-v1.sh
@@ -0,0 +1,94 @@
+test_expect_success "setup receive.procReceiveRefs" '
+ git -C "$upstream" config --add receive.procReceiveRefs refs/for
+'
+
+test_expect_success "setup proc-receive hook" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic1" \
+ -r "option fall-through" \
+ -r "ok refs/for/master/topic2" \
+ -r "option refname refs/for/changes/23/123/1" \
+ -r "option new-oid $A" \
+ -r "ok refs/for/master/topic2" \
+ -r "option refname refs/for/changes/24/124/2" \
+ -r "option old-oid $B" \
+ -r "option new-oid $A" \
+ -r "option forced-update" \
+ -r "ng refs/for/next/topic target branch not exist"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : (B) refs/for/master/topic1(A) foo(A) refs/for/next/topic(A) refs/for/master/topic2(A)
+test_expect_success "proc-receive: report status v1" '
+ {
+ if test -z "$GIT_DEFAULT_HASH" || test "$GIT_DEFAULT_HASH" = "sha1"
+ then
+ printf "%s %s refs/heads/master\0report-status\n" \
+ $A $B | packetize
+ else
+ printf "%s %s refs/heads/master\0report-status object-format=$GIT_DEFAULT_HASH\n" \
+ $A $B | packetize
+ fi &&
+ printf "%s %s refs/for/master/topic1\n" \
+ $ZERO_OID $A | packetize &&
+ printf "%s %s refs/heads/foo\n" \
+ $ZERO_OID $A | packetize &&
+ printf "%s %s refs/for/next/topic\n" \
+ $ZERO_OID $A | packetize &&
+ printf "%s %s refs/for/master/topic2\n" \
+ $ZERO_OID $A | packetize &&
+ printf 0000 &&
+ printf "" | git -C "$upstream" pack-objects --stdout
+ } | git receive-pack "$upstream" --stateless-rpc \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ # pre-receive hook
+ pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic1
+ pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/foo
+ pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic2
+ # proc-receive hook
+ proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic1
+ proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic2
+ proc-receive> ok refs/for/master/topic1
+ proc-receive> option fall-through
+ proc-receive> ok refs/for/master/topic2
+ proc-receive> option refname refs/for/changes/23/123/1
+ proc-receive> option new-oid <COMMIT-A>
+ proc-receive> ok refs/for/master/topic2
+ proc-receive> option refname refs/for/changes/24/124/2
+ proc-receive> option old-oid <COMMIT-B>
+ proc-receive> option new-oid <COMMIT-A>
+ proc-receive> option forced-update
+ proc-receive> ng refs/for/next/topic target branch not exist
+ 000eunpack ok
+ 0019ok refs/heads/master
+ 001eok refs/for/master/topic1
+ 0016ok refs/heads/foo
+ 0033ng refs/for/next/topic target branch not exist
+ 001eok refs/for/master/topic2
+ 0000# post-receive hook
+ post-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ post-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic1
+ post-receive< <ZERO-OID> <COMMIT-A> refs/heads/foo
+ post-receive< <ZERO-OID> <COMMIT-A> refs/for/changes/23/123/1
+ post-receive< <COMMIT-B> <COMMIT-A> refs/for/changes/24/124/2
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/for/master/topic1
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0000-standard-git-push.sh b/t/t5411/test-0000-standard-git-push.sh
new file mode 100644
index 0000000000..e206587348
--- /dev/null
+++ b/t/t5411/test-0000-standard-git-push.sh
@@ -0,0 +1,143 @@
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git-push : master(B) next(A)
+test_expect_success "git-push ($PROTOCOL)" '
+ git -C workbench push origin \
+ $B:refs/heads/master \
+ HEAD:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> <COMMIT-B> -> master
+ * [new branch] HEAD -> next
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) next(A)
+# Refs of workbench: master(A) tags/v123
+# git-push --atomic: master(A) next(B)
+test_expect_success "git-push --atomic ($PROTOCOL)" '
+ test_must_fail git -C workbench push --atomic origin \
+ master \
+ $B:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out |
+ sed -n \
+ -e "/^To / { s/ */ /g; p; }" \
+ -e "/^ ! / { s/ */ /g; p; }" \
+ >actual &&
+ cat >expect <<-EOF &&
+ To <URL/of/upstream.git>
+ ! [rejected] master -> master (non-fast-forward)
+ ! [rejected] <COMMIT-B> -> next (atomic push failed)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) next(A)
+# Refs of workbench: master(A) tags/v123
+# git-push : master(A) next(B)
+test_expect_success "non-fast-forward git-push ($PROTOCOL)" '
+ test_must_fail git \
+ -C workbench \
+ -c advice.pushUpdateRejected=false \
+ push origin \
+ master \
+ $B:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> <COMMIT-B> -> next
+ ! [rejected] master -> master (non-fast-forward)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ <COMMIT-B> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) next(B)
+# Refs of workbench: master(A) tags/v123
+# git-push -f : master(A) NULL tags/v123 refs/review/master/topic(A) a/b/c(A)
+test_expect_success "git-push -f ($PROTOCOL)" '
+ git -C workbench push -f origin \
+ refs/tags/v123 \
+ :refs/heads/next \
+ master \
+ master:refs/review/master/topic \
+ HEAD:refs/heads/a/b/c \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: pre-receive< <COMMIT-B> <ZERO-OID> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <TAG-v123> refs/tags/v123
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/review/master/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/a/b/c
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: post-receive< <COMMIT-B> <ZERO-OID> refs/heads/next
+ remote: post-receive< <ZERO-OID> <TAG-v123> refs/tags/v123
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/review/master/topic
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/a/b/c
+ To <URL/of/upstream.git>
+ + <OID-B>...<OID-A> master -> master (forced update)
+ - [deleted] next
+ * [new tag] v123 -> v123
+ * [new reference] master -> refs/review/master/topic
+ * [new branch] HEAD -> a/b/c
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/a/b/c
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/review/master/topic
+ <TAG-v123> refs/tags/v123
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) tags/v123 refs/review/master/topic(A) a/b/c(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ (
+ cd "$upstream" &&
+ git update-ref -d refs/review/master/topic &&
+ git update-ref -d refs/tags/v123 &&
+ git update-ref -d refs/heads/a/b/c
+ )
+'
diff --git a/t/t5411/test-0001-standard-git-push--porcelain.sh b/t/t5411/test-0001-standard-git-push--porcelain.sh
new file mode 100644
index 0000000000..48f6fcc846
--- /dev/null
+++ b/t/t5411/test-0001-standard-git-push--porcelain.sh
@@ -0,0 +1,147 @@
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git-push : master(B) next(A)
+test_expect_success "git-push ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ $B:refs/heads/master \
+ HEAD:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ To <URL/of/upstream.git>
+ <COMMIT-B>:refs/heads/master <OID-A>..<OID-B>
+ * HEAD:refs/heads/next [new branch]
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) next(A)
+# Refs of workbench: master(A) tags/v123
+# git-push --atomic: master(A) next(B)
+test_expect_success "git-push --atomic ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --atomic --porcelain origin \
+ master \
+ $B:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out |
+ sed -n \
+ -e "s/^# GETTEXT POISON #//" \
+ -e "/^To / { s/ */ /g; p; }" \
+ -e "/^! / { s/ */ /g; p; }" \
+ >actual &&
+ cat >expect <<-EOF &&
+ To <URL/of/upstream.git>
+ ! refs/heads/master:refs/heads/master [rejected] (non-fast-forward)
+ ! <COMMIT-B>:refs/heads/next [rejected] (atomic push failed)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) next(A)
+# Refs of workbench: master(A) tags/v123
+# git-push : master(A) next(B)
+test_expect_success "non-fast-forward git-push ($PROTOCOL/porcelain)" '
+ test_must_fail git \
+ -C workbench \
+ -c advice.pushUpdateRejected=false \
+ push --porcelain origin \
+ master \
+ $B:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
+ To <URL/of/upstream.git>
+ <COMMIT-B>:refs/heads/next <OID-A>..<OID-B>
+ ! refs/heads/master:refs/heads/master [rejected] (non-fast-forward)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ <COMMIT-B> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) next(B)
+# Refs of workbench: master(A) tags/v123
+# git-push -f : master(A) NULL tags/v123 refs/review/master/topic(A) a/b/c(A)
+test_expect_success "git-push -f ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain -f origin \
+ refs/tags/v123 \
+ :refs/heads/next \
+ master \
+ master:refs/review/master/topic \
+ HEAD:refs/heads/a/b/c \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: pre-receive< <COMMIT-B> <ZERO-OID> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <TAG-v123> refs/tags/v123
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/review/master/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/a/b/c
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: post-receive< <COMMIT-B> <ZERO-OID> refs/heads/next
+ remote: post-receive< <ZERO-OID> <TAG-v123> refs/tags/v123
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/review/master/topic
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/a/b/c
+ To <URL/of/upstream.git>
+ + refs/heads/master:refs/heads/master <OID-B>...<OID-A> (forced update)
+ - :refs/heads/next [deleted]
+ * refs/tags/v123:refs/tags/v123 [new tag]
+ * refs/heads/master:refs/review/master/topic [new reference]
+ * HEAD:refs/heads/a/b/c [new branch]
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/a/b/c
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/review/master/topic
+ <TAG-v123> refs/tags/v123
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) tags/v123 refs/review/master/topic(A) a/b/c(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ (
+ cd "$upstream" &&
+ git update-ref -d refs/review/master/topic &&
+ git update-ref -d refs/tags/v123 &&
+ git update-ref -d refs/heads/a/b/c
+ )
+'
diff --git a/t/t5411/test-0002-pre-receive-declined.sh b/t/t5411/test-0002-pre-receive-declined.sh
new file mode 100644
index 0000000000..c246f7e68e
--- /dev/null
+++ b/t/t5411/test-0002-pre-receive-declined.sh
@@ -0,0 +1,33 @@
+test_expect_success "setup pre-receive hook ($PROTOCOL)" '
+ mv "$upstream/hooks/pre-receive" "$upstream/hooks/pre-receive.ok" &&
+ write_script "$upstream/hooks/pre-receive" <<-EOF
+ exit 1
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git-push : master(B) next(A)
+test_expect_success "git-push is declined ($PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ $B:refs/heads/master \
+ HEAD:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ To <URL/of/upstream.git>
+ ! [remote rejected] <COMMIT-B> -> master (pre-receive hook declined)
+ ! [remote rejected] HEAD -> next (pre-receive hook declined)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "cleanup ($PROTOCOL)" '
+ mv "$upstream/hooks/pre-receive.ok" "$upstream/hooks/pre-receive"
+'
diff --git a/t/t5411/test-0003-pre-receive-declined--porcelain.sh b/t/t5411/test-0003-pre-receive-declined--porcelain.sh
new file mode 100644
index 0000000000..b14894de81
--- /dev/null
+++ b/t/t5411/test-0003-pre-receive-declined--porcelain.sh
@@ -0,0 +1,34 @@
+test_expect_success "setup pre-receive hook ($PROTOCOL/porcelain)" '
+ mv "$upstream/hooks/pre-receive" "$upstream/hooks/pre-receive.ok" &&
+ write_script "$upstream/hooks/pre-receive" <<-EOF
+ exit 1
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git-push : master(B) next(A)
+test_expect_success "git-push is declined ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ $B:refs/heads/master \
+ HEAD:refs/heads/next \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ To <URL/of/upstream.git>
+ ! <COMMIT-B>:refs/heads/master [remote rejected] (pre-receive hook declined)
+ ! HEAD:refs/heads/next [remote rejected] (pre-receive hook declined)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ mv "$upstream/hooks/pre-receive.ok" "$upstream/hooks/pre-receive"
+'
diff --git a/t/t5411/test-0010-proc-receive-settings.sh b/t/t5411/test-0010-proc-receive-settings.sh
new file mode 100644
index 0000000000..a36809927b
--- /dev/null
+++ b/t/t5411/test-0010-proc-receive-settings.sh
@@ -0,0 +1,7 @@
+test_expect_success "add two receive.procReceiveRefs settings" '
+ (
+ cd "$upstream" &&
+ git config --add receive.procReceiveRefs refs/for &&
+ git config --add receive.procReceiveRefs refs/review/
+ )
+'
diff --git a/t/t5411/test-0011-no-hook-error.sh b/t/t5411/test-0011-no-hook-error.sh
new file mode 100644
index 0000000000..bb6ec92a92
--- /dev/null
+++ b/t/t5411/test-0011-no-hook-error.sh
@@ -0,0 +1,64 @@
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : next(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: no hook, fail to push special ref ($PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:next \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: error: cannot find hook "proc-receive"
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ To <URL/of/upstream.git>
+ * [new branch] HEAD -> next
+ ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) next(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ git -C "$upstream" update-ref -d refs/heads/next
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push --atomic: (B) next(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: no hook, all failed for atomic push ($PROTOCOL)" '
+ test_must_fail git -C workbench push --atomic origin \
+ $B:master \
+ HEAD:next \
+ HEAD:refs/for/master/topic >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: error: cannot find hook "proc-receive"
+ To <URL/of/upstream.git>
+ ! [remote rejected] <COMMIT-B> -> master (fail to run proc-receive hook)
+ ! [remote rejected] HEAD -> next (fail to run proc-receive hook)
+ ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0012-no-hook-error--porcelain.sh b/t/t5411/test-0012-no-hook-error--porcelain.sh
new file mode 100644
index 0000000000..4814f74dc2
--- /dev/null
+++ b/t/t5411/test-0012-no-hook-error--porcelain.sh
@@ -0,0 +1,66 @@
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : next(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: no hook, fail to push special ref ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:next \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: error: cannot find hook "proc-receive"
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ To <URL/of/upstream.git>
+ * HEAD:refs/heads/next [new branch]
+ ! HEAD:refs/for/master/topic [remote rejected] (fail to run proc-receive hook)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) next(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ git -C "$upstream" update-ref -d refs/heads/next
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push --atomic: (B) next(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: no hook, all failed for atomic push ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain --atomic origin \
+ $B:master \
+ HEAD:next \
+ HEAD:refs/for/master/topic >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: error: cannot find hook "proc-receive"
+ To <URL/of/upstream.git>
+ ! <COMMIT-B>:refs/heads/master [remote rejected] (fail to run proc-receive hook)
+ ! HEAD:refs/heads/next [remote rejected] (fail to run proc-receive hook)
+ ! HEAD:refs/for/master/topic [remote rejected] (fail to run proc-receive hook)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0013-bad-protocol.sh b/t/t5411/test-0013-bad-protocol.sh
new file mode 100644
index 0000000000..c5fe4cb37b
--- /dev/null
+++ b/t/t5411/test-0013-bad-protocol.sh
@@ -0,0 +1,217 @@
+test_expect_success "setup proc-receive hook (unknown version, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v --version 2
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+
+ # Check status report for git-push
+ sed -n \
+ -e "/^To / { p; n; p; }" \
+ <actual >actual-report &&
+ cat >expect <<-EOF &&
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook)
+ EOF
+ test_cmp expect actual-report &&
+
+ # Check error message from "receive-pack", but ignore unstable fatal error
+ # message ("remote: fatal: the remote end hung up unexpectedly") which
+ # is different from the remote HTTP server with different locale settings.
+ grep "^remote: error:" <actual >actual-error &&
+ cat >expect <<-EOF &&
+ remote: error: proc-receive version "2" is not supported
+ EOF
+ test_cmp expect actual-error &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-version, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v --die-version
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-version, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: fatal: bad protocol version: 1
+ remote: error: proc-receive version "0" is not supported
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook)
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (hook --die-readline, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v --die-readline
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: bad protocol (hook --die-readline, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+
+ grep "remote: fatal: protocol error: expected \"old new ref\", got \"<ZERO-OID> <COMMIT-A> refs/for/master/topic\"" actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (no report, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : next(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/heads/next \
+ HEAD:refs/for/master/topic >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ To <URL/of/upstream.git>
+ * [new branch] HEAD -> next
+ ! [remote rejected] HEAD -> refs/for/master/topic (proc-receive failed to report status)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) next(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ git -C "$upstream" update-ref -d refs/heads/next
+
+'
+
+test_expect_success "setup proc-receive hook (no ref, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic\
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok
+ remote: error: proc-receive reported incomplete status line: "ok"
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/master/topic (proc-receive failed to report status)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "xx refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> xx refs/for/master/topic
+ remote: error: proc-receive reported bad status "xx" on ref "refs/for/master/topic"
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/master/topic (proc-receive failed to report status)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0014-bad-protocol--porcelain.sh b/t/t5411/test-0014-bad-protocol--porcelain.sh
new file mode 100644
index 0000000000..53b47b0185
--- /dev/null
+++ b/t/t5411/test-0014-bad-protocol--porcelain.sh
@@ -0,0 +1,160 @@
+test_expect_success "setup proc-receive hook (unknown version, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v --version 2
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+
+ # Check status report for git-push
+ sed -n \
+ -e "/^To / { p; n; p; n; p; }" \
+ <actual >actual-report &&
+ cat >expect <<-EOF &&
+ To <URL/of/upstream.git>
+ ! HEAD:refs/for/master/topic [remote rejected] (fail to run proc-receive hook)
+ Done
+ EOF
+ test_cmp expect actual-report &&
+
+ # Check error message from "receive-pack", but ignore unstable fatal error
+ # message ("remote: fatal: the remote end hung up unexpectedly") which
+ # is different from the remote HTTP server with different locale settings.
+ grep "^remote: error:" <actual >actual-error &&
+ cat >expect <<-EOF &&
+ remote: error: proc-receive version "2" is not supported
+ EOF
+ test_cmp expect actual-error &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (no report, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : next(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/heads/next \
+ HEAD:refs/for/master/topic >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ To <URL/of/upstream.git>
+ * HEAD:refs/heads/next [new branch]
+ ! HEAD:refs/for/master/topic [remote rejected] (proc-receive failed to report status)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) next(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ git -C "$upstream" update-ref -d refs/heads/next
+
+'
+
+test_expect_success "setup proc-receive hook (no ref, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic\
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok
+ remote: error: proc-receive reported incomplete status line: "ok"
+ To <URL/of/upstream.git>
+ ! HEAD:refs/for/master/topic [remote rejected] (proc-receive failed to report status)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "xx refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> xx refs/for/master/topic
+ remote: error: proc-receive reported bad status "xx" on ref "refs/for/master/topic"
+ To <URL/of/upstream.git>
+ ! HEAD:refs/for/master/topic [remote rejected] (proc-receive failed to report status)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0020-report-ng.sh b/t/t5411/test-0020-report-ng.sh
new file mode 100644
index 0000000000..f726b7ca9c
--- /dev/null
+++ b/t/t5411/test-0020-report-ng.sh
@@ -0,0 +1,67 @@
+test_expect_success "setup proc-receive hook (ng, no message, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ng refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ng refs/for/master/topic
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/master/topic (failed)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (ng message, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ng refs/for/master/topic error msg"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ng refs/for/master/topic error msg
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/master/topic (error msg)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0021-report-ng--porcelain.sh b/t/t5411/test-0021-report-ng--porcelain.sh
new file mode 100644
index 0000000000..fbf5569103
--- /dev/null
+++ b/t/t5411/test-0021-report-ng--porcelain.sh
@@ -0,0 +1,69 @@
+test_expect_success "setup proc-receive hook (ng, no message, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ng refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ng refs/for/master/topic
+ To <URL/of/upstream.git>
+ ! HEAD:refs/for/master/topic [remote rejected] (failed)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (ng message, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ng refs/for/master/topic error msg"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ng refs/for/master/topic error msg
+ To <URL/of/upstream.git>
+ ! HEAD:refs/for/master/topic [remote rejected] (error msg)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0022-report-unexpect-ref.sh b/t/t5411/test-0022-report-unexpect-ref.sh
new file mode 100644
index 0000000000..92a415b929
--- /dev/null
+++ b/t/t5411/test-0022-report-unexpect-ref.sh
@@ -0,0 +1,45 @@
+test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/heads/master"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : (B) refs/for/master/topic
+test_expect_success "proc-receive: report unexpected ref ($PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ $B:refs/heads/master \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/heads/master
+ remote: error: proc-receive reported status on unexpected ref: refs/heads/master
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> <COMMIT-B> -> master
+ ! [remote rejected] HEAD -> refs/for/master/topic (proc-receive failed to report status)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ git -C "$upstream" update-ref refs/heads/master $A
+'
diff --git a/t/t5411/test-0023-report-unexpect-ref--porcelain.sh b/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
new file mode 100644
index 0000000000..acbf93e40a
--- /dev/null
+++ b/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
@@ -0,0 +1,46 @@
+test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/heads/master"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : (B) refs/for/master/topic
+test_expect_success "proc-receive: report unexpected ref ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ $B:refs/heads/master \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/heads/master
+ remote: error: proc-receive reported status on unexpected ref: refs/heads/master
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ To <URL/of/upstream.git>
+ <COMMIT-B>:refs/heads/master <OID-A>..<OID-B>
+ ! HEAD:refs/for/master/topic [remote rejected] (proc-receive failed to report status)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ git -C "$upstream" update-ref refs/heads/master $A
+'
diff --git a/t/t5411/test-0024-report-unknown-ref.sh b/t/t5411/test-0024-report-unknown-ref.sh
new file mode 100644
index 0000000000..c3946f329a
--- /dev/null
+++ b/t/t5411/test-0024-report-unknown-ref.sh
@@ -0,0 +1,34 @@
+test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/a/b/c/my/topic
+test_expect_success "proc-receive: report unknown reference ($PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/a/b/c/my/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: error: proc-receive reported status on unknown ref: refs/for/master/topic
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/a/b/c/my/topic (proc-receive failed to report status)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0025-report-unknown-ref--porcelain.sh b/t/t5411/test-0025-report-unknown-ref--porcelain.sh
new file mode 100644
index 0000000000..d093b1a579
--- /dev/null
+++ b/t/t5411/test-0025-report-unknown-ref--porcelain.sh
@@ -0,0 +1,35 @@
+test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/a/b/c/my/topic
+test_expect_success "proc-receive: report unknown reference ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/for/a/b/c/my/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: error: proc-receive reported status on unknown ref: refs/for/master/topic
+ To <URL/of/upstream.git>
+ ! HEAD:refs/for/a/b/c/my/topic [remote rejected] (proc-receive failed to report status)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0026-push-options.sh b/t/t5411/test-0026-push-options.sh
new file mode 100644
index 0000000000..d0c4da8b23
--- /dev/null
+++ b/t/t5411/test-0026-push-options.sh
@@ -0,0 +1,79 @@
+test_expect_success "setup proc-receive hook and disable push-options ($PROTOCOL)" '
+ git -C "$upstream" config receive.advertisePushOptions false &&
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push -o ... : refs/for/master/topic
+test_expect_success "proc-receive: not support push options ($PROTOCOL)" '
+ test_must_fail git -C workbench push \
+ -o issue=123 \
+ -o reviewer=user1 \
+ origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ test_i18ngrep "fatal: the receiving end does not support push options" \
+ actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "enable push options ($PROTOCOL)" '
+ git -C "$upstream" config receive.advertisePushOptions true
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push -o ... : next(A) refs/for/master/topic
+test_expect_success "proc-receive: push with options ($PROTOCOL)" '
+ git -C workbench push \
+ --atomic \
+ -o issue=123 \
+ -o reviewer=user1 \
+ origin \
+ HEAD:refs/heads/next \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive: atomic push_options
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive< issue=123
+ remote: proc-receive< reviewer=user1
+ remote: proc-receive> ok refs/for/master/topic
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ To <URL/of/upstream.git>
+ * [new branch] HEAD -> next
+ * [new reference] HEAD -> refs/for/master/topic
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) next(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ git -C "$upstream" update-ref -d refs/heads/next
+'
diff --git a/t/t5411/test-0027-push-options--porcelain.sh b/t/t5411/test-0027-push-options--porcelain.sh
new file mode 100644
index 0000000000..c89a1e7c57
--- /dev/null
+++ b/t/t5411/test-0027-push-options--porcelain.sh
@@ -0,0 +1,82 @@
+test_expect_success "setup proc-receive hook and disable push-options ($PROTOCOL/porcelain)" '
+ git -C "$upstream" config receive.advertisePushOptions false &&
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push -o ... : refs/for/master/topic
+test_expect_success "proc-receive: not support push options ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push \
+ --porcelain \
+ -o issue=123 \
+ -o reviewer=user1 \
+ origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ test_i18ngrep "fatal: the receiving end does not support push options" \
+ actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "enable push options ($PROTOCOL/porcelain)" '
+ git -C "$upstream" config receive.advertisePushOptions true
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push -o ... : next(A) refs/for/master/topic
+test_expect_success "proc-receive: push with options ($PROTOCOL/porcelain)" '
+ git -C workbench push \
+ --porcelain \
+ --atomic \
+ -o issue=123 \
+ -o reviewer=user1 \
+ origin \
+ HEAD:refs/heads/next \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive: atomic push_options
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive< issue=123
+ remote: proc-receive< reviewer=user1
+ remote: proc-receive> ok refs/for/master/topic
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ To <URL/of/upstream.git>
+ * HEAD:refs/heads/next [new branch]
+ * HEAD:refs/for/master/topic [new reference]
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) next(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ git -C "$upstream" update-ref -d refs/heads/next
+'
diff --git a/t/t5411/test-0030-report-ok.sh b/t/t5411/test-0030-report-ok.sh
new file mode 100644
index 0000000000..44c99d3831
--- /dev/null
+++ b/t/t5411/test-0030-report-ok.sh
@@ -0,0 +1,35 @@
+test_expect_success "setup proc-receive hook (ok, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: ok ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ To <URL/of/upstream.git>
+ * [new reference] HEAD -> refs/for/master/topic
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0031-report-ok--porcelain.sh b/t/t5411/test-0031-report-ok--porcelain.sh
new file mode 100644
index 0000000000..3223b26184
--- /dev/null
+++ b/t/t5411/test-0031-report-ok--porcelain.sh
@@ -0,0 +1,36 @@
+test_expect_success "setup proc-receive hook (ok, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic
+test_expect_success "proc-receive: ok ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ To <URL/of/upstream.git>
+ * HEAD:refs/for/master/topic [new reference]
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0032-report-with-options.sh b/t/t5411/test-0032-report-with-options.sh
new file mode 100644
index 0000000000..b77b78c49f
--- /dev/null
+++ b/t/t5411/test-0032-report-with-options.sh
@@ -0,0 +1,256 @@
+test_expect_success "setup proc-receive hook (option without matching ok, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option without matching ok ($PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: error: proc-receive reported "option" without a matching "ok/ng" directive
+ To <URL/of/upstream.git>
+ ! [remote rejected] HEAD -> refs/for/master/topic (proc-receive failed to report status)
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option refname, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option refname ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/pull/123/head
+ To <URL/of/upstream.git>
+ * [new reference] HEAD -> refs/pull/123/head
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option refname and forced-update, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "option forced-update"
+ EOF
+'
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option refname and forced-update ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option forced-update
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/pull/123/head
+ To <URL/of/upstream.git>
+ * [new reference] HEAD -> refs/pull/123/head
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option refname and old-oid, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option refname and old-oid ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/pull/123/head
+ To <URL/of/upstream.git>
+ <OID-B>..<OID-A> HEAD -> refs/pull/123/head
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option old-oid, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option old-oid ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/for/master/topic
+ To <URL/of/upstream.git>
+ <OID-B>..<OID-A> HEAD -> refs/for/master/topic
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option old-oid and new-oid, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option old-oid and new-oid ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> HEAD -> refs/for/master/topic
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (report with multiple rewrites, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/a/b/c/topic" \
+ -r "ok refs/for/next/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/124/head" \
+ -r "option old-oid $B" \
+ -r "option forced-update" \
+ -r "option new-oid $A"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report with multiple rewrites ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/next/topic \
+ HEAD:refs/for/a/b/c/topic \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/a/b/c/topic
+ remote: proc-receive> ok refs/for/next/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/124/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: proc-receive> option forced-update
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/pull/123/head
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/topic
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/pull/124/head
+ To <URL/of/upstream.git>
+ * [new reference] HEAD -> refs/pull/123/head
+ * [new reference] HEAD -> refs/for/a/b/c/topic
+ + <OID-B>...<OID-A> HEAD -> refs/pull/124/head (forced update)
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0033-report-with-options--porcelain.sh b/t/t5411/test-0033-report-with-options--porcelain.sh
new file mode 100644
index 0000000000..1fe352b686
--- /dev/null
+++ b/t/t5411/test-0033-report-with-options--porcelain.sh
@@ -0,0 +1,265 @@
+test_expect_success "setup proc-receive hook (option without matching ok, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option without matching ok ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: error: proc-receive reported "option" without a matching "ok/ng" directive
+ To <URL/of/upstream.git>
+ ! HEAD:refs/for/master/topic [remote rejected] (proc-receive failed to report status)
+ Done
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option refname, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option refname ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/pull/123/head
+ To <URL/of/upstream.git>
+ * HEAD:refs/pull/123/head [new reference]
+ Done
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option refname and forced-update, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "option forced-update"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option refname and forced-update ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option forced-update
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/pull/123/head
+ To <URL/of/upstream.git>
+ * HEAD:refs/pull/123/head [new reference]
+ Done
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option refname and old-oid, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option refname and old-oid ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/pull/123/head
+ To <URL/of/upstream.git>
+ HEAD:refs/pull/123/head <OID-B>..<OID-A>
+ Done
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option old-oid, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option old-oid ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/for/master/topic
+ To <URL/of/upstream.git>
+ HEAD:refs/for/master/topic <OID-B>..<OID-A>
+ Done
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (option old-oid and new-oid, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report option old-oid and new-oid ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ To <URL/of/upstream.git>
+ HEAD:refs/for/master/topic <OID-A>..<OID-B>
+ Done
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (report with multiple rewrites, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/a/b/c/topic" \
+ -r "ok refs/for/next/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/124/head" \
+ -r "option old-oid $B" \
+ -r "option forced-update" \
+ -r "option new-oid $A"
+
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/next/topic(A) refs/for/a/b/c/topic(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report with multiple rewrites ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/next/topic \
+ HEAD:refs/for/a/b/c/topic \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/a/b/c/topic
+ remote: proc-receive> ok refs/for/next/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/124/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: proc-receive> option forced-update
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/pull/123/head
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/topic
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/pull/124/head
+ To <URL/of/upstream.git>
+ * HEAD:refs/pull/123/head [new reference]
+ * HEAD:refs/for/a/b/c/topic [new reference]
+ + HEAD:refs/pull/124/head <OID-B>...<OID-A> (forced update)
+ Done
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0034-report-ft.sh b/t/t5411/test-0034-report-ft.sh
new file mode 100644
index 0000000000..aca2b0676c
--- /dev/null
+++ b/t/t5411/test-0034-report-ft.sh
@@ -0,0 +1,44 @@
+test_expect_success "setup proc-receive hook (ft, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option fall-through"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(B)
+test_expect_success "proc-receive: fall throught, let receive-pack to execute ($PROTOCOL)" '
+ git -C workbench push origin \
+ $B:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-B> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-B> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option fall-through
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-B> refs/for/master/topic
+ To <URL/of/upstream.git>
+ * [new reference] <COMMIT-B> -> refs/for/master/topic
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/for/master/topic
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) refs/for/master/topic(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ git -C "$upstream" update-ref -d refs/for/master/topic
+'
diff --git a/t/t5411/test-0035-report-ft--porcelain.sh b/t/t5411/test-0035-report-ft--porcelain.sh
new file mode 100644
index 0000000000..30ffffb352
--- /dev/null
+++ b/t/t5411/test-0035-report-ft--porcelain.sh
@@ -0,0 +1,45 @@
+test_expect_success "setup proc-receive hook (fall-through, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option fall-through"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(B)
+test_expect_success "proc-receive: fall throught, let receive-pack to execute ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ $B:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-B> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-B> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option fall-through
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-B> refs/for/master/topic
+ To <URL/of/upstream.git>
+ * <COMMIT-B>:refs/for/master/topic [new reference]
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/for/master/topic
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) refs/for/master/topic(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ git -C "$upstream" update-ref -d refs/for/master/topic
+'
diff --git a/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh b/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
new file mode 100644
index 0000000000..73283d81e8
--- /dev/null
+++ b/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
@@ -0,0 +1,227 @@
+test_expect_success "setup git config for remote-tracking of special refs" '
+ (
+ cd workbench &&
+ if ! git config --get-all remote.origin.fetch | grep refs/for/
+ then
+ git config --add remote.origin.fetch \
+ "+refs/for/*:refs/t/for/*" &&
+ git config --add remote.origin.fetch \
+ "+refs/pull/*:refs/t/pull/*" &&
+ git config --add remote.origin.fetch \
+ "+refs/changes/*:refs/t/changes/*"
+ fi
+ )
+'
+
+test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 1st rewrite, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/24/124/1" \
+ -r "option old-oid $ZERO_OID" \
+ -r "option new-oid $A" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/25/125/1" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: multiple rewrite for one ref, no refname for the 1st rewrite ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/24/124/1
+ remote: proc-receive> option old-oid <ZERO-OID>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/25/125/1
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/changes/24/124/1
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/changes/25/125/1
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> HEAD -> refs/for/master/topic
+ * [new reference] HEAD -> refs/changes/24/124/1
+ <OID-A>..<OID-B> HEAD -> refs/changes/25/125/1
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "proc-receive: check remote-tracking #1 ($PROTOCOL)" '
+ git -C workbench show-ref |
+ grep -v -e refs/remotes -e refs/heads -e refs/tags >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/t/changes/24/124/1
+ <COMMIT-B> refs/t/changes/25/125/1
+ <COMMIT-B> refs/t/for/master/topic
+ EOF
+ test_cmp expect actual &&
+ git -C workbench update-ref -d refs/t/for/master/topic &&
+ git -C workbench update-ref -d refs/t/changes/24/124/1 &&
+ git -C workbench update-ref -d refs/t/changes/25/125/1
+'
+
+test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 2nd rewrite, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/24/124/1" \
+ -r "option old-oid $ZERO_OID" \
+ -r "option new-oid $A" \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/25/125/1" \
+ -r "option old-oid $B" \
+ -r "option new-oid $A" \
+ -r "option forced-update"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: multiple rewrites for one ref, no refname for the 2nd rewrite ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/24/124/1
+ remote: proc-receive> option old-oid <ZERO-OID>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/25/125/1
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> option forced-update
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/changes/24/124/1
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/changes/25/125/1
+ To <URL/of/upstream.git>
+ * [new reference] HEAD -> refs/changes/24/124/1
+ <OID-A>..<OID-B> HEAD -> refs/for/master/topic
+ + <OID-B>...<OID-A> HEAD -> refs/changes/25/125/1 (forced update)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "proc-receive: check remote-tracking #2 ($PROTOCOL)" '
+ git -C workbench show-ref |
+ grep -v -e refs/remotes -e refs/heads -e refs/tags >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/t/changes/24/124/1
+ <COMMIT-A> refs/t/changes/25/125/1
+ <COMMIT-B> refs/t/for/master/topic
+ EOF
+ test_cmp expect actual &&
+ git -C workbench update-ref -d refs/t/for/master/topic &&
+ git -C workbench update-ref -d refs/t/changes/24/124/1 &&
+ git -C workbench update-ref -d refs/t/changes/25/125/1
+'
+
+test_expect_success "setup proc-receive hook (multiple rewrites for one ref, $PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/23/123/1" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/24/124/2" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: multiple rewrites for one ref ($PROTOCOL)" '
+ git -C workbench push origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/23/123/1
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/24/124/2
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/changes/23/123/1
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/changes/24/124/2
+ To <URL/of/upstream.git>
+ * [new reference] HEAD -> refs/changes/23/123/1
+ <OID-A>..<OID-B> HEAD -> refs/changes/24/124/2
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "proc-receive: check remote-tracking #3 ($PROTOCOL)" '
+ git -C workbench show-ref |
+ grep -v -e refs/remotes -e refs/heads -e refs/tags >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/t/changes/23/123/1
+ <COMMIT-B> refs/t/changes/24/124/2
+ EOF
+ test_cmp expect actual &&
+ git -C workbench update-ref -d refs/t/changes/24/124/1 &&
+ git -C workbench update-ref -d refs/t/changes/25/125/2
+'
diff --git a/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh b/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
new file mode 100644
index 0000000000..77b5b22ed4
--- /dev/null
+++ b/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
@@ -0,0 +1,172 @@
+test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 1st rewrite, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/24/124/1" \
+ -r "option old-oid $ZERO_OID" \
+ -r "option new-oid $A" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/25/125/1" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: multiple rewrite for one ref, no refname for the 1st rewrite ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/24/124/1
+ remote: proc-receive> option old-oid <ZERO-OID>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/25/125/1
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/changes/24/124/1
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/changes/25/125/1
+ To <URL/of/upstream.git>
+ HEAD:refs/for/master/topic <OID-A>..<OID-B>
+ * HEAD:refs/changes/24/124/1 [new reference]
+ HEAD:refs/changes/25/125/1 <OID-A>..<OID-B>
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 2nd rewrite, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/24/124/1" \
+ -r "option old-oid $ZERO_OID" \
+ -r "option new-oid $A" \
+ -r "ok refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/25/125/1" \
+ -r "option old-oid $B" \
+ -r "option new-oid $A" \
+ -r "option forced-update"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: multiple rewrites for one ref, no refname for the 2nd rewrite ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/24/124/1
+ remote: proc-receive> option old-oid <ZERO-OID>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/25/125/1
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> option forced-update
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/changes/24/124/1
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/changes/25/125/1
+ To <URL/of/upstream.git>
+ * HEAD:refs/changes/24/124/1 [new reference]
+ HEAD:refs/for/master/topic <OID-A>..<OID-B>
+ + HEAD:refs/changes/25/125/1 <OID-B>...<OID-A> (forced update)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook (multiple rewrites for one ref, $PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/23/123/1" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/changes/24/124/2" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : refs/for/master/topic(A)
+test_expect_success "proc-receive: multiple rewrites for one ref ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain origin \
+ HEAD:refs/for/master/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/23/123/1
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/changes/24/124/2
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/changes/23/123/1
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/changes/24/124/2
+ To <URL/of/upstream.git>
+ * HEAD:refs/changes/23/123/1 [new reference]
+ HEAD:refs/changes/24/124/2 <OID-A>..<OID-B>
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5411/test-0038-report-mixed-refs.sh b/t/t5411/test-0038-report-mixed-refs.sh
new file mode 100644
index 0000000000..a74a2cb449
--- /dev/null
+++ b/t/t5411/test-0038-report-mixed-refs.sh
@@ -0,0 +1,89 @@
+test_expect_success "setup proc-receive hook ($PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/next/topic2" \
+ -r "ng refs/for/next/topic1 fail to call Web API" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : (B) bar(A) baz(A) refs/for/next/topic(A) foo(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report update of mixed refs ($PROTOCOL)" '
+ test_must_fail git -C workbench push origin \
+ $B:refs/heads/master \
+ HEAD:refs/heads/bar \
+ HEAD:refs/heads/baz \
+ HEAD:refs/for/next/topic2 \
+ HEAD:refs/for/next/topic1 \
+ HEAD:refs/heads/foo \
+ HEAD:refs/for/master/topic \
+ HEAD:refs/for/next/topic3 \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/bar
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/baz
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic2
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic1
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/foo
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic3
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic2
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic1
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic3
+ remote: proc-receive> ok refs/for/next/topic2
+ remote: proc-receive> ng refs/for/next/topic1 fail to call Web API
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/bar
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/baz
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic2
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/foo
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> <COMMIT-B> -> master
+ * [new branch] HEAD -> bar
+ * [new branch] HEAD -> baz
+ * [new reference] HEAD -> refs/for/next/topic2
+ * [new branch] HEAD -> foo
+ <OID-A>..<OID-B> HEAD -> refs/for/master/topic
+ ! [remote rejected] HEAD -> refs/for/next/topic1 (fail to call Web API)
+ ! [remote rejected] HEAD -> refs/for/next/topic3 (proc-receive failed to report status)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) foo(A) bar(A)) baz(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ (
+ cd "$upstream" &&
+ git update-ref refs/heads/master $A &&
+ git update-ref -d refs/heads/foo &&
+ git update-ref -d refs/heads/bar &&
+ git update-ref -d refs/heads/baz
+ )
+'
diff --git a/t/t5411/test-0039-report-mixed-refs--porcelain.sh b/t/t5411/test-0039-report-mixed-refs--porcelain.sh
new file mode 100644
index 0000000000..e4baa13ea3
--- /dev/null
+++ b/t/t5411/test-0039-report-mixed-refs--porcelain.sh
@@ -0,0 +1,91 @@
+test_expect_success "setup proc-receive hook ($PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/for/next/topic2" \
+ -r "ng refs/for/next/topic1 fail to call Web API" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/for/master/topic" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : (B) bar(A) baz(A) refs/for/next/topic(A) foo(A) refs/for/master/topic(A)
+test_expect_success "proc-receive: report update of mixed refs ($PROTOCOL/porcelain)" '
+ test_must_fail git -C workbench push --porcelain origin \
+ $B:refs/heads/master \
+ HEAD:refs/heads/bar \
+ HEAD:refs/heads/baz \
+ HEAD:refs/for/next/topic2 \
+ HEAD:refs/for/next/topic1 \
+ HEAD:refs/heads/foo \
+ HEAD:refs/for/master/topic \
+ HEAD:refs/for/next/topic3 \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/bar
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/baz
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic2
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic1
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/foo
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic3
+ remote: # proc-receive hook
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic2
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic1
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic3
+ remote: proc-receive> ok refs/for/next/topic2
+ remote: proc-receive> ng refs/for/next/topic1 fail to call Web API
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/for/master/topic
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/bar
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/baz
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic2
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/foo
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/for/master/topic
+ To <URL/of/upstream.git>
+ <COMMIT-B>:refs/heads/master <OID-A>..<OID-B>
+ * HEAD:refs/heads/bar [new branch]
+ * HEAD:refs/heads/baz [new branch]
+ * HEAD:refs/for/next/topic2 [new reference]
+ * HEAD:refs/heads/foo [new branch]
+ HEAD:refs/for/master/topic <OID-A>..<OID-B>
+ ! HEAD:refs/for/next/topic1 [remote rejected] (fail to call Web API)
+ ! HEAD:refs/for/next/topic3 [remote rejected] (proc-receive failed to report status)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(B) foo(A) bar(A)) baz(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ (
+ cd "$upstream" &&
+ git update-ref refs/heads/master $A &&
+ git update-ref -d refs/heads/foo &&
+ git update-ref -d refs/heads/bar &&
+ git update-ref -d refs/heads/baz
+ )
+
+'
diff --git a/t/t5411/test-0040-process-all-refs.sh b/t/t5411/test-0040-process-all-refs.sh
new file mode 100644
index 0000000000..b07c999f53
--- /dev/null
+++ b/t/t5411/test-0040-process-all-refs.sh
@@ -0,0 +1,113 @@
+test_expect_success "config receive.procReceiveRefs = refs ($PROTOCOL)" '
+ git -C "$upstream" config --unset-all receive.procReceiveRefs &&
+ git -C "$upstream" config --add receive.procReceiveRefs refs
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "setup upstream branches ($PROTOCOL)" '
+ (
+ cd "$upstream" &&
+ git update-ref refs/heads/master $B &&
+ git update-ref refs/heads/foo $A &&
+ git update-ref refs/heads/bar $A &&
+ git update-ref refs/heads/baz $A
+ )
+
+'
+
+test_expect_success "setup proc-receive hook ($PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/heads/master" \
+ -r "option fall-through" \
+ -r "ok refs/heads/foo" \
+ -r "option fall-through" \
+ -r "ok refs/heads/bar" \
+ -r "option fall-through" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B" \
+ -r "ok refs/for/next/topic" \
+ -r "option refname refs/pull/124/head" \
+ -r "option old-oid $B" \
+ -r "option new-oid $A" \
+ -r "option forced-update"
+ EOF
+'
+
+# Refs of upstream : master(B) foo(A) bar(A)) baz(A)
+# Refs of workbench: master(A) tags/v123
+# git push -f : master(A) (NULL) (B) refs/for/master/topic(A) refs/for/next/topic(A)
+test_expect_success "proc-receive: process all refs ($PROTOCOL)" '
+ git -C workbench push -f origin \
+ HEAD:refs/heads/master \
+ :refs/heads/foo \
+ $B:refs/heads/bar \
+ HEAD:refs/for/master/topic \
+ HEAD:refs/for/next/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/bar
+ remote: pre-receive< <COMMIT-A> <ZERO-OID> refs/heads/foo
+ remote: pre-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <COMMIT-A> <COMMIT-B> refs/heads/bar
+ remote: proc-receive< <COMMIT-A> <ZERO-OID> refs/heads/foo
+ remote: proc-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: proc-receive> ok refs/heads/master
+ remote: proc-receive> option fall-through
+ remote: proc-receive> ok refs/heads/foo
+ remote: proc-receive> option fall-through
+ remote: proc-receive> ok refs/heads/bar
+ remote: proc-receive> option fall-through
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: proc-receive> ok refs/for/next/topic
+ remote: proc-receive> option refname refs/pull/124/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> option forced-update
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/bar
+ remote: post-receive< <COMMIT-A> <ZERO-OID> refs/heads/foo
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/pull/123/head
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/pull/124/head
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> <COMMIT-B> -> bar
+ - [deleted] foo
+ + <OID-B>...<OID-A> HEAD -> master (forced update)
+ <OID-A>..<OID-B> HEAD -> refs/pull/123/head
+ + <OID-B>...<OID-A> HEAD -> refs/pull/124/head (forced update)
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) bar(A) baz(B)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL)" '
+ (
+ cd "$upstream" &&
+ git update-ref -d refs/heads/bar &&
+ git update-ref -d refs/heads/baz
+ )
+'
diff --git a/t/t5411/test-0041-process-all-refs--porcelain.sh b/t/t5411/test-0041-process-all-refs--porcelain.sh
new file mode 100644
index 0000000000..0dd9824616
--- /dev/null
+++ b/t/t5411/test-0041-process-all-refs--porcelain.sh
@@ -0,0 +1,114 @@
+test_expect_success "config receive.procReceiveRefs = refs ($PROTOCOL/porcelain)" '
+ git -C "$upstream" config --unset-all receive.procReceiveRefs &&
+ git -C "$upstream" config --add receive.procReceiveRefs refs
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "setup upstream branches ($PROTOCOL/porcelain)" '
+ (
+ cd "$upstream" &&
+ git update-ref refs/heads/master $B &&
+ git update-ref refs/heads/foo $A &&
+ git update-ref refs/heads/bar $A &&
+ git update-ref refs/heads/baz $A
+ )
+
+'
+
+test_expect_success "setup proc-receive hook ($PROTOCOL/porcelain)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/heads/master" \
+ -r "option fall-through" \
+ -r "ok refs/heads/foo" \
+ -r "option fall-through" \
+ -r "ok refs/heads/bar" \
+ -r "option fall-through" \
+ -r "ok refs/for/master/topic" \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B" \
+ -r "ok refs/for/next/topic" \
+ -r "option refname refs/pull/124/head" \
+ -r "option old-oid $B" \
+ -r "option new-oid $A" \
+ -r "option forced-update"
+ EOF
+'
+
+# Refs of upstream : master(B) foo(A) bar(A)) baz(A)
+# Refs of workbench: master(A) tags/v123
+# git push -f : master(A) (NULL) (B) refs/for/master/topic(A) refs/for/next/topic(A)
+test_expect_success "proc-receive: process all refs ($PROTOCOL/porcelain)" '
+ git -C workbench push --porcelain -f origin \
+ HEAD:refs/heads/master \
+ :refs/heads/foo \
+ $B:refs/heads/bar \
+ HEAD:refs/for/master/topic \
+ HEAD:refs/for/next/topic \
+ >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/bar
+ remote: pre-receive< <COMMIT-A> <ZERO-OID> refs/heads/foo
+ remote: pre-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: # proc-receive hook
+ remote: proc-receive< <COMMIT-A> <COMMIT-B> refs/heads/bar
+ remote: proc-receive< <COMMIT-A> <ZERO-OID> refs/heads/foo
+ remote: proc-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/master/topic
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/next/topic
+ remote: proc-receive> ok refs/heads/master
+ remote: proc-receive> option fall-through
+ remote: proc-receive> ok refs/heads/foo
+ remote: proc-receive> option fall-through
+ remote: proc-receive> ok refs/heads/bar
+ remote: proc-receive> option fall-through
+ remote: proc-receive> ok refs/for/master/topic
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: proc-receive> ok refs/for/next/topic
+ remote: proc-receive> option refname refs/pull/124/head
+ remote: proc-receive> option old-oid <COMMIT-B>
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: proc-receive> option forced-update
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/bar
+ remote: post-receive< <COMMIT-A> <ZERO-OID> refs/heads/foo
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/heads/master
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/pull/123/head
+ remote: post-receive< <COMMIT-B> <COMMIT-A> refs/pull/124/head
+ To <URL/of/upstream.git>
+ <COMMIT-B>:refs/heads/bar <OID-A>..<OID-B>
+ - :refs/heads/foo [deleted]
+ + HEAD:refs/heads/master <OID-B>...<OID-A> (forced update)
+ HEAD:refs/pull/123/head <OID-A>..<OID-B>
+ + HEAD:refs/pull/124/head <OID-B>...<OID-A> (forced update)
+ Done
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-B> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A) bar(A) baz(B)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "cleanup ($PROTOCOL/porcelain)" '
+ (
+ cd "$upstream" &&
+ git update-ref -d refs/heads/bar &&
+ git update-ref -d refs/heads/baz
+ )
+'
diff --git a/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh b/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
new file mode 100644
index 0000000000..c22849cbe2
--- /dev/null
+++ b/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
@@ -0,0 +1,135 @@
+test_expect_success "config receive.procReceiveRefs with modifiers ($PROTOCOL)" '
+ (
+ cd "$upstream" &&
+ git config --unset-all receive.procReceiveRefs &&
+ git config --add receive.procReceiveRefs m:refs/heads/master &&
+ git config --add receive.procReceiveRefs ad:refs/heads &&
+ git config --add receive.procReceiveRefs "a!:refs/heads"
+ )
+'
+
+test_expect_success "setup proc-receive hook ($PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/heads/master" \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $A" \
+ -r "option new-oid $B" \
+ -r "ok refs/tags/v123 " \
+ -r "option refname refs/pull/124/head"
+ EOF
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+# git push : master(B) tags/v123
+test_expect_success "proc-receive: update branch and new tag ($PROTOCOL)" '
+ git -C workbench push origin \
+ $B:refs/heads/master \
+ v123 >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: pre-receive< <ZERO-OID> <TAG-v123> refs/tags/v123
+ remote: # proc-receive hook
+ remote: proc-receive< <COMMIT-A> <COMMIT-B> refs/heads/master
+ remote: proc-receive< <ZERO-OID> <TAG-v123> refs/tags/v123
+ remote: proc-receive> ok refs/heads/master
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <COMMIT-B>
+ remote: proc-receive> ok refs/tags/v123
+ remote: proc-receive> option refname refs/pull/124/head
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/pull/123/head
+ remote: post-receive< <ZERO-OID> <TAG-v123> refs/pull/124/head
+ To <URL/of/upstream.git>
+ <OID-A>..<OID-B> <COMMIT-B> -> refs/pull/123/head
+ * [new reference] v123 -> refs/pull/124/head
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ EOF
+ test_cmp expect actual
+'
+
+# Refs of upstream : master(A)
+# Refs of workbench: master(A) tags/v123
+test_expect_success "setup upstream: create tags/v123 ($PROTOCOL)" '
+ git -C "$upstream" update-ref refs/heads/topic $A &&
+ git -C "$upstream" update-ref refs/tags/v123 $TAG &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-A> refs/heads/topic
+ <TAG-v123> refs/tags/v123
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success "setup proc-receive hook ($PROTOCOL)" '
+ write_script "$upstream/hooks/proc-receive" <<-EOF
+ printf >&2 "# proc-receive hook\n"
+ test-tool proc-receive -v \
+ -r "ok refs/heads/master" \
+ -r "option refname refs/pull/123/head" \
+ -r "option old-oid $A" \
+ -r "option new-oid $ZERO_OID" \
+ -r "ok refs/heads/next" \
+ -r "option refname refs/pull/124/head" \
+ -r "option new-oid $A"
+ EOF
+'
+
+# Refs of upstream : master(A) topic(A) tags/v123
+# Refs of workbench: master(A) tags/v123
+# git push : NULL topic(B) NULL next(A)
+test_expect_success "proc-receive: create/delete branch, and delete tag ($PROTOCOL)" '
+ git -C workbench push origin \
+ :refs/heads/master \
+ $B:refs/heads/topic \
+ $A:refs/heads/next \
+ :refs/tags/v123 >out 2>&1 &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ remote: # pre-receive hook
+ remote: pre-receive< <COMMIT-A> <ZERO-OID> refs/heads/master
+ remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/topic
+ remote: pre-receive< <TAG-v123> <ZERO-OID> refs/tags/v123
+ remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: # proc-receive hook
+ remote: proc-receive< <COMMIT-A> <ZERO-OID> refs/heads/master
+ remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
+ remote: proc-receive> ok refs/heads/master
+ remote: proc-receive> option refname refs/pull/123/head
+ remote: proc-receive> option old-oid <COMMIT-A>
+ remote: proc-receive> option new-oid <ZERO-OID>
+ remote: proc-receive> ok refs/heads/next
+ remote: proc-receive> option refname refs/pull/124/head
+ remote: proc-receive> option new-oid <COMMIT-A>
+ remote: # post-receive hook
+ remote: post-receive< <COMMIT-A> <ZERO-OID> refs/pull/123/head
+ remote: post-receive< <COMMIT-A> <COMMIT-B> refs/heads/topic
+ remote: post-receive< <TAG-v123> <ZERO-OID> refs/tags/v123
+ remote: post-receive< <ZERO-OID> <COMMIT-A> refs/pull/124/head
+ To <URL/of/upstream.git>
+ - [deleted] refs/pull/123/head
+ <OID-A>..<OID-B> <COMMIT-B> -> topic
+ - [deleted] v123
+ * [new reference] <COMMIT-A> -> refs/pull/124/head
+ EOF
+ test_cmp expect actual &&
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/master
+ <COMMIT-B> refs/heads/topic
+ EOF
+ test_cmp expect actual
+'
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index a32efe2b6c..1a16ac4c0d 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -4,7 +4,6 @@ test_description='fetch/receive strict mode'
. ./test-lib.sh
test_expect_success 'setup and inject "corrupt or missing" object' '
- test_oid_init &&
echo hello >greetings &&
git add greetings &&
git commit -m greetings &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index a66dbe0bde..dbc724e4c0 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -213,7 +213,7 @@ test_expect_success 'fetch tags when there is no tags' '
test_expect_success 'fetch following tags' '
cd "$D" &&
- git tag -a -m 'annotated' anno HEAD &&
+ git tag -a -m "annotated" anno HEAD &&
git tag light HEAD &&
mkdir four &&
@@ -281,15 +281,19 @@ test_expect_success 'create bundle 1' '
cd "$D" &&
echo >file updated again by origin &&
git commit -a -m "tip" &&
- git bundle create bundle1 master^..master
+ git bundle create --version=3 bundle1 master^..master
'
test_expect_success 'header of bundle looks right' '
- head -n 4 "$D"/bundle1 &&
- head -n 1 "$D"/bundle1 | grep "^#" &&
- head -n 2 "$D"/bundle1 | grep "^-$OID_REGEX " &&
- head -n 3 "$D"/bundle1 | grep "^$OID_REGEX " &&
- head -n 4 "$D"/bundle1 | grep "^$"
+ cat >expect <<-EOF &&
+ # v3 git bundle
+ @object-format=$(test_oid algo)
+ -OID updated by origin
+ OID refs/heads/master
+
+ EOF
+ sed -e "s/$OID_REGEX/OID/g" -e "5q" "$D"/bundle1 >actual &&
+ test_cmp expect actual
'
test_expect_success 'create bundle 2' '
@@ -331,7 +335,7 @@ test_expect_success 'bundle does not prerequisite objects' '
test_expect_success 'bundle should be able to create a full history' '
cd "$D" &&
- git tag -a -m '1.0' v1.0 master &&
+ git tag -a -m "1.0" v1.0 master &&
git bundle create bundle4 v1.0
'
@@ -539,10 +543,23 @@ test_expect_success 'fetch into the current branch with --update-head-ok' '
'
-test_expect_success 'fetch --dry-run' '
+test_expect_success 'fetch --dry-run does not touch FETCH_HEAD, but still prints what would be written' '
+ rm -f .git/FETCH_HEAD err &&
+ git fetch --dry-run . 2>err &&
+ ! test -f .git/FETCH_HEAD &&
+ grep FETCH_HEAD err
+'
+
+test_expect_success '--no-write-fetch-head does not touch FETCH_HEAD, and does not print what would be written' '
+ rm -f .git/FETCH_HEAD err &&
+ git fetch --no-write-fetch-head . 2>err &&
+ ! test -f .git/FETCH_HEAD &&
+ ! grep FETCH_HEAD err
+'
+test_expect_success '--write-fetch-head gets defeated by --dry-run' '
rm -f .git/FETCH_HEAD &&
- git fetch --dry-run . &&
+ git fetch --dry-run --write-fetch-head . &&
! test -f .git/FETCH_HEAD
'
@@ -797,7 +814,7 @@ test_configured_prune true true unset unset pruned pruned \
"--prune origin refs/tags/*:refs/tags/* +refs/heads/*:refs/remotes/origin/*"
# --prune-tags on its own does nothing, needs --prune as well, same
-# for for fetch.pruneTags without fetch.prune
+# for fetch.pruneTags without fetch.prune
test_configured_prune unset unset unset unset kept kept "--prune-tags"
test_configured_prune unset unset true unset kept kept ""
test_configured_prune unset unset unset true kept kept ""
@@ -919,7 +936,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
git config fetch.unpackLimit 1 &&
git config gc.autoPackLimit 1 &&
git config gc.autoDetach false &&
- GIT_ASK_YESNO="$D/askyesno" git fetch >fetch.out 2>&1 &&
+ GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 &&
test_i18ngrep "Auto packing the repository" fetch.out &&
! grep "Should I try again" fetch.out
)
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index de8e2f1531..bd202ec6f3 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -108,7 +108,7 @@ test_expect_success 'git fetch --multiple (two remotes)' '
GIT_TRACE=1 git fetch --multiple one two 2>trace &&
git branch -r > output &&
test_cmp ../expect output &&
- grep "built-in: git gc" trace >gc &&
+ grep "built-in: git maintenance" trace >gc &&
test_line_count = 1 gc
)
'
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 36ad20a849..d11382f769 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1039,7 +1039,7 @@ test_force_fetch_tag "annotated tag" "-f -a -m'tag message'"
test_expect_success 'push --porcelain' '
mk_empty testrepo &&
echo >.git/foo "To testrepo" &&
- echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new branch]" &&
+ echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new reference]" &&
echo >>.git/foo "Done" &&
git push >.git/bar --porcelain testrepo refs/heads/master:refs/remotes/origin/master &&
(
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 159afa7ac8..db1a381cd9 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -85,6 +85,13 @@ test_expect_success 'git pull --cleanup errors early on invalid argument' '
test -s err)
'
+test_expect_success 'git pull --no-write-fetch-head fails' '
+ mkdir clonedwfh &&
+ (cd clonedwfh && git init &&
+ test_expect_code 129 git pull --no-write-fetch-head "../parent" >out 2>err &&
+ test_must_be_empty out &&
+ test_i18ngrep "no-write-fetch-head" err)
+'
test_expect_success 'git pull --force' '
mkdir clonedoldstyle &&
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 4ce9a9f704..205a2631e7 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -14,7 +14,6 @@ corrupt_repo () {
}
test_expect_success 'setup and corrupt repository' '
- test_oid_init &&
echo file >file &&
git add file &&
git rev-parse :file &&
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index 030331f1c5..7e928aff66 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -273,4 +273,27 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
test_cmp expect dst/push-cert-status
'
+test_expect_success GPG 'failed atomic push does not execute GPG' '
+ prepare_dst &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script gpg <<-EOF &&
+ # should check atomic push locally before running GPG.
+ exit 1
+ EOF
+ test_must_fail env PATH="$TRASH_DIRECTORY:$PATH" git push \
+ --signed --atomic --porcelain \
+ dst noop ff noff >out 2>&1 &&
+
+ test_i18ngrep ! "gpg failed to sign" out &&
+ sed -n -e "/^To dst/,$ p" out >actual &&
+ cat >expect <<-EOF &&
+ To dst
+ = refs/heads/noop:refs/heads/noop [up to date]
+ ! refs/heads/ff:refs/heads/ff [rejected] (atomic push failed)
+ ! refs/heads/noff:refs/heads/noff [rejected] (non-fast-forward)
+ Done
+ EOF
+ test_i18ncmp expect actual
+'
+
test_done
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh
index c0d02dee89..82aa99ae87 100755
--- a/t/t5539-fetch-http-shallow.sh
+++ b/t/t5539-fetch-http-shallow.sh
@@ -9,10 +9,12 @@ start_httpd
commit() {
echo "$1" >tracked &&
git add tracked &&
+ test_tick &&
git commit -m "$1"
}
test_expect_success 'setup shallow clone' '
+ test_tick=1500000000 &&
commit 1 &&
commit 2 &&
commit 3 &&
@@ -48,7 +50,6 @@ EOF
test_expect_success 'no shallow lines after receiving ACK ready' '
(
cd shallow &&
- test_tick &&
for i in $(test_seq 15)
do
git checkout --orphan unrelated$i &&
@@ -66,6 +67,7 @@ test_expect_success 'no shallow lines after receiving ACK ready' '
(
cd clone &&
git checkout --orphan newnew &&
+ test_tick=1400000000 &&
test_commit new-too &&
# NEEDSWORK: If the overspecification of the expected result is reduced, we
# might be able to run this test in all protocol versions.
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index 463d0f12e5..187454f5dd 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -479,6 +479,21 @@ test_expect_success 'clone/fetch scrubs password from reflogs' '
! grep "$HTTPD_URL_USER_PASS" reflog
'
+test_expect_success 'Non-ASCII branch name can be used with --force-with-lease' '
+ cd "$ROOT_PATH" &&
+ git clone "$HTTPD_URL_USER_PASS/smart/test_repo.git" non-ascii &&
+ cd non-ascii &&
+ git checkout -b rama-de-árbol &&
+ test_commit F &&
+ git push --force-with-lease origin rama-de-árbol &&
+ git ls-remote origin refs/heads/rama-de-árbol >actual &&
+ git ls-remote . refs/heads/rama-de-árbol >expect &&
+ test_cmp expect actual &&
+ git push --delete --force-with-lease origin rama-de-árbol &&
+ git ls-remote origin refs/heads/rama-de-árbol >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'colorize errors/hints' '
cd "$ROOT_PATH"/test_repo_clone &&
test_must_fail git -c color.transport=always -c color.advice=always \
diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh
index 81975ad8f9..7622981cbf 100755
--- a/t/t5553-set-upstream.sh
+++ b/t/t5553-set-upstream.sh
@@ -81,7 +81,7 @@ test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails
test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' '
clear_config other other2 &&
- url="file://'"$PWD"'" &&
+ url="file://$PWD" &&
git fetch --set-upstream "$url" &&
check_config master "$url" HEAD &&
check_config_missing other &&
@@ -158,7 +158,7 @@ test_expect_success 'pull --set-upstream upstream with more than one branch does
test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' '
clear_config master other other2 &&
git checkout master &&
- url="file://'"$PWD"'" &&
+ url="file://$PWD" &&
git pull --set-upstream "$url" &&
check_config master "$url" HEAD &&
check_config_missing other &&
@@ -168,7 +168,7 @@ test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' '
test_expect_success 'pull --set-upstream with valid URL and branch sets branch' '
clear_config master other other2 &&
git checkout master &&
- url="file://'"$PWD"'" &&
+ url="file://$PWD" &&
git pull --set-upstream "$url" master &&
check_config master "$url" refs/heads/master &&
check_config_missing other &&
diff --git a/t/t5554-noop-fetch-negotiator.sh b/t/t5554-noop-fetch-negotiator.sh
new file mode 100755
index 0000000000..2ac7b5859e
--- /dev/null
+++ b/t/t5554-noop-fetch-negotiator.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description='test noop fetch negotiator'
+. ./test-lib.sh
+
+test_expect_success 'noop negotiator does not emit any "have"' '
+ rm -f trace &&
+
+ test_create_repo server &&
+ test_commit -C server to_fetch &&
+
+ test_create_repo client &&
+ test_commit -C client we_have &&
+
+ test_config -C client fetch.negotiationalgorithm noop &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch "$(pwd)/server" &&
+
+ ! grep "fetch> have" trace &&
+ grep "fetch> done" trace
+'
+
+test_done
diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh
index c6ec625497..e5d3d15ba8 100755
--- a/t/t5562-http-backend-content-length.sh
+++ b/t/t5562-http-backend-content-length.sh
@@ -46,7 +46,6 @@ ssize_b100dots() {
}
test_expect_success 'setup' '
- test_oid_init &&
HTTP_CONTENT_ENCODING="identity" &&
export HTTP_CONTENT_ENCODING &&
git config http.receivepack true &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 84ea2a3eb7..b6c8312da1 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -271,7 +271,9 @@ test_expect_success 'fetch from gitfile parent' '
test_expect_success 'clone separate gitdir where target already exists' '
rm -rf dst &&
- test_must_fail git clone --separate-git-dir realgitdir src dst
+ echo foo=bar >>realgitdir/config &&
+ test_must_fail git clone --separate-git-dir realgitdir src dst &&
+ grep foo=bar realgitdir/config
'
test_expect_success 'clone --reference from original' '
@@ -629,6 +631,20 @@ test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
test_i18ngrep "the following paths have collided" icasefs/warning
'
+test_expect_success 'clone with GIT_DEFAULT_HASH' '
+ (
+ sane_unset GIT_DEFAULT_HASH &&
+ git init --object-format=sha1 test-sha1 &&
+ git init --object-format=sha256 test-sha256
+ ) &&
+ test_commit -C test-sha1 foo &&
+ test_commit -C test-sha256 foo &&
+ GIT_DEFAULT_HASH=sha1 git clone test-sha256 test-clone-sha256 &&
+ GIT_DEFAULT_HASH=sha256 git clone test-sha1 test-clone-sha1 &&
+ git -C test-clone-sha1 status &&
+ git -C test-clone-sha256 status
+'
+
partial_clone_server () {
SERVER="$1" &&
@@ -702,7 +718,7 @@ test_expect_success 'batch missing blob request during checkout' '
# Ensure that there is only one negotiation by checking that there is
# only "done" line sent. ("done" marks the end of negotiation.)
GIT_TRACE_PACKET="$(pwd)/trace" git -C client checkout HEAD^ &&
- grep "git> done" trace >done_lines &&
+ grep "fetch> done" trace >done_lines &&
test_line_count = 1 done_lines
'
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
index 6d5a977fcb..26985f4b44 100755
--- a/t/t5607-clone-bundle.sh
+++ b/t/t5607-clone-bundle.sh
@@ -4,6 +4,10 @@ test_description='some bundle related tests'
. ./test-lib.sh
test_expect_success 'setup' '
+ test_oid_cache <<-EOF &&
+ version sha1:2
+ version sha256:3
+ EOF
test_commit initial &&
test_tick &&
git tag -m tag tag &&
@@ -94,4 +98,31 @@ test_expect_success 'fetch SHA-1 from bundle' '
git fetch --no-tags foo/tip.bundle "$(cat hash)"
'
+test_expect_success 'git bundle uses expected default format' '
+ git bundle create bundle HEAD^.. &&
+ head -n1 bundle | grep "^# v$(test_oid version) git bundle$"
+'
+
+test_expect_success 'git bundle v3 has expected contents' '
+ git branch side HEAD &&
+ git bundle create --version=3 bundle HEAD^..side &&
+ head -n2 bundle >actual &&
+ cat >expect <<-EOF &&
+ # v3 git bundle
+ @object-format=$(test_oid algo)
+ EOF
+ test_cmp expect actual &&
+ git bundle verify bundle
+'
+
+test_expect_success 'git bundle v3 rejects unknown capabilities' '
+ cat >new <<-EOF &&
+ # v3 git bundle
+ @object-format=$(test_oid algo)
+ @unknown=silly
+ EOF
+ test_must_fail git bundle verify new 2>output &&
+ test_i18ngrep "unknown capability .unknown=silly." output
+'
+
test_done
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 8a27452a51..f4d49d8335 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -163,6 +163,22 @@ test_expect_success 'manual prefetch of missing objects' '
test_line_count = 0 observed.oids
'
+test_expect_success 'partial clone with transfer.fsckobjects=1 works with submodules' '
+ test_create_repo submodule &&
+ test_commit -C submodule mycommit &&
+
+ test_create_repo src_with_sub &&
+ test_config -C src_with_sub uploadpack.allowfilter 1 &&
+ test_config -C src_with_sub uploadpack.allowanysha1inwant 1 &&
+
+ git -C src_with_sub submodule add "file://$(pwd)/submodule" mysub &&
+ git -C src_with_sub commit -m "commit with submodule" &&
+
+ git -c transfer.fsckobjects=1 \
+ clone --filter="blob:none" "file://$(pwd)/src_with_sub" dst &&
+ test_when_finished rm -rf dst
+'
+
test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack --fsck-objects' '
git init src &&
test_commit -C src x &&
@@ -235,6 +251,39 @@ test_expect_success 'implicitly construct combine: filter with repeated flags' '
test_cmp unique_types.expected unique_types.actual
'
+test_expect_success 'upload-pack fails banned object filters' '
+ test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
+ "file://$(pwd)/srv.bare" pc3 2>err &&
+ test_i18ngrep "filter '\''blob:none'\'' not supported" err
+'
+
+test_expect_success 'upload-pack fails banned combine object filters' '
+ test_config -C srv.bare uploadpackfilter.allow false &&
+ test_config -C srv.bare uploadpackfilter.combine.allow true &&
+ test_config -C srv.bare uploadpackfilter.tree.allow true &&
+ test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
+ --filter=blob:none "file://$(pwd)/srv.bare" pc3 2>err &&
+ test_i18ngrep "filter '\''blob:none'\'' not supported" err
+'
+
+test_expect_success 'upload-pack fails banned object filters with fallback' '
+ test_config -C srv.bare uploadpackfilter.allow false &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
+ "file://$(pwd)/srv.bare" pc3 2>err &&
+ test_i18ngrep "filter '\''blob:none'\'' not supported" err
+'
+
+test_expect_success 'upload-pack limits tree depth filters' '
+ test_config -C srv.bare uploadpackfilter.allow false &&
+ test_config -C srv.bare uploadpackfilter.tree.allow true &&
+ test_config -C srv.bare uploadpackfilter.tree.maxDepth 0 &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
+ "file://$(pwd)/srv.bare" pc3 2>err &&
+ test_i18ngrep "tree filter allows max depth 0, but got 1" err
+'
+
test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
rm -rf src dst &&
git init src &&
@@ -384,6 +433,26 @@ test_expect_success 'fetch lazy-fetches only to resolve deltas, protocol v2' '
grep "want $(cat hash)" trace
'
+test_expect_success 'fetch does not lazy-fetch missing targets of its refs' '
+ rm -rf server client trace &&
+
+ test_create_repo server &&
+ test_config -C server uploadpack.allowfilter 1 &&
+ test_config -C server uploadpack.allowanysha1inwant 1 &&
+ test_commit -C server foo &&
+
+ git clone --filter=blob:none "file://$(pwd)/server" client &&
+ # Make all refs point to nothing by deleting all objects.
+ rm client/.git/objects/pack/* &&
+
+ test_commit -C server bar &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --no-tags --recurse-submodules=no \
+ origin refs/tags/bar &&
+ FOO_HASH=$(git -C server rev-parse foo) &&
+ ! grep "want $FOO_HASH" trace
+'
+
# The following two tests must be in this order. It is important that
# the srv.bare repository did not have tags during clone, but has tags
# in the fetch.
@@ -422,6 +491,44 @@ test_expect_success 'single-branch tag following respects partial clone' '
test_must_fail git -C single rev-parse --verify refs/tags/C
'
+test_expect_success 'fetch from a partial clone, protocol v0' '
+ rm -rf server client trace &&
+
+ # Pretend that the server is a partial clone
+ git init server &&
+ git -C server remote add a_remote "file://$(pwd)/" &&
+ test_config -C server core.repositoryformatversion 1 &&
+ test_config -C server extensions.partialclone a_remote &&
+ test_config -C server protocol.version 0 &&
+ test_commit -C server foo &&
+
+ # Fetch from the server
+ git init client &&
+ test_config -C client protocol.version 0 &&
+ test_commit -C client bar &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch "file://$(pwd)/server" &&
+ ! grep "version 2" trace
+'
+
+test_expect_success 'fetch from a partial clone, protocol v2' '
+ rm -rf server client trace &&
+
+ # Pretend that the server is a partial clone
+ git init server &&
+ git -C server remote add a_remote "file://$(pwd)/" &&
+ test_config -C server core.repositoryformatversion 1 &&
+ test_config -C server extensions.partialclone a_remote &&
+ test_config -C server protocol.version 2 &&
+ test_commit -C server foo &&
+
+ # Fetch from the server
+ git init client &&
+ test_config -C client protocol.version 2 &&
+ test_commit -C client bar &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch "file://$(pwd)/server" &&
+ grep "version 2" trace
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 1b54c35b01..7d5b17909b 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -13,7 +13,6 @@ start_git_daemon --export-all --enable=receive-pack
daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent
test_expect_success 'create repo to be served by git-daemon' '
- test_oid_init &&
git init "$daemon_parent" &&
test_commit -C "$daemon_parent" one
'
@@ -829,7 +828,7 @@ test_expect_success 'part of packfile response provided as URI' '
# Ensure that my-blob and other-blob are in separate packfiles.
for idx in http_child/.git/objects/pack/*.idx
do
- git verify-pack --verbose $idx >out &&
+ git verify-pack --object-format=$(test_oid algo) --verbose $idx >out &&
{
grep "^[0-9a-f]\{16,\} " out || :
} >out.objectlist &&
@@ -884,6 +883,59 @@ test_expect_success 'fetching with valid packfile URI but invalid hash fails' '
test_i18ngrep "pack downloaded from.*does not match expected hash" err
'
+test_expect_success 'packfile-uri with transfer.fsckobjects' '
+ P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+ rm -rf "$P" http_child log &&
+
+ git init "$P" &&
+ git -C "$P" config "uploadpack.allowsidebandall" "true" &&
+
+ echo my-blob >"$P/my-blob" &&
+ git -C "$P" add my-blob &&
+ git -C "$P" commit -m x &&
+
+ configure_exclusion "$P" my-blob >h &&
+
+ sane_unset GIT_TEST_SIDEBAND_ALL &&
+ git -c protocol.version=2 -c transfer.fsckobjects=1 \
+ -c fetch.uriprotocols=http,https \
+ clone "$HTTPD_URL/smart/http_parent" http_child &&
+
+ # Ensure that there are exactly 4 files (2 .pack and 2 .idx).
+ ls http_child/.git/objects/pack/* >filelist &&
+ test_line_count = 4 filelist
+'
+
+test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object' '
+ P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+ rm -rf "$P" http_child log &&
+
+ git init "$P" &&
+ git -C "$P" config "uploadpack.allowsidebandall" "true" &&
+
+ cat >bogus-commit <<-EOF &&
+ tree $EMPTY_TREE
+ author Bugs Bunny 1234567890 +0000
+ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000
+
+ This commit object intentionally broken
+ EOF
+ BOGUS=$(git -C "$P" hash-object -t commit -w --stdin <bogus-commit) &&
+ git -C "$P" branch bogus-branch "$BOGUS" &&
+
+ echo my-blob >"$P/my-blob" &&
+ git -C "$P" add my-blob &&
+ git -C "$P" commit -m x &&
+
+ configure_exclusion "$P" my-blob >h &&
+
+ sane_unset GIT_TEST_SIDEBAND_ALL &&
+ test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
+ -c fetch.uriprotocols=http,https \
+ clone "$HTTPD_URL/smart/http_parent" http_child 2>error &&
+ test_i18ngrep "invalid author/committer line - missing email" error
+'
+
# DO NOT add non-httpd-specific tests here, because the last part of this
# test script is only executed when httpd is available and enabled.
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index 748282f058..d9ecf0f4a9 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -43,7 +43,6 @@ write_command () {
# \ | /
# a
test_expect_success 'setup repository' '
- test_oid_init &&
test_commit a &&
git checkout -b o/foo &&
test_commit b &&
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 3dc1ad8f71..fc4d55dcb2 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -8,6 +8,7 @@ test_expect_success setup '
echo content1 >wanted_file &&
echo content2 >unwanted_file &&
git add wanted_file unwanted_file &&
+ test_tick &&
git commit -m one
'
@@ -21,6 +22,7 @@ test_expect_success 'rev-list --objects with pathspecs and deeper paths' '
mkdir foo &&
>foo/file &&
git add foo/file &&
+ test_tick &&
git commit -m two &&
git rev-list --objects HEAD -- foo >output &&
@@ -69,6 +71,7 @@ test_expect_success '--no-object-names and --object-names are last-one-wins' '
'
test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
+ test_tick &&
git commit --allow-empty -m another &&
git tag -a -m "annotated" v1.0 &&
git rev-list --objects ^v1.0^ v1.0 >expect &&
@@ -84,10 +87,10 @@ test_expect_success 'propagate uninteresting flag down correctly' '
test_expect_success 'symleft flag bit is propagated down from tag' '
git log --format="%m %s" --left-right v1.0...master >actual &&
cat >expect <<-\EOF &&
- > two
- > one
< another
< that
+ > two
+ > one
EOF
test_cmp expect actual
'
@@ -125,8 +128,8 @@ test_expect_success 'rev-list can negate index objects' '
test_cmp expect actual
'
-test_expect_success '--bisect and --first-parent can not be combined' '
- test_must_fail git rev-list --bisect --first-parent HEAD
+test_expect_success '--bisect and --first-parent can be combined' '
+ git rev-list --bisect --first-parent HEAD
'
test_expect_success '--header shows a NUL after each commit' '
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index a661408038..b95a0212ad 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -263,4 +263,49 @@ test_expect_success 'rev-parse --bisect can default to good/bad refs' '
test_cmp expect.sorted actual.sorted
'
+test_output_expect_success '--bisect --first-parent' 'git rev-list --bisect --first-parent E ^F' <<EOF
+e4
+EOF
+
+test_output_expect_success '--first-parent' 'git rev-list --first-parent E ^F' <<EOF
+E
+e1
+e2
+e3
+e4
+e5
+e6
+e7
+e8
+EOF
+
+test_output_expect_success '--bisect-vars --first-parent' 'git rev-list --bisect-vars --first-parent E ^F' <<EOF
+bisect_rev='e5'
+bisect_nr=4
+bisect_good=4
+bisect_bad=3
+bisect_all=9
+bisect_steps=2
+EOF
+
+test_expect_success '--bisect-all --first-parent' '
+ cat >expect.unsorted <<-EOF &&
+ $(git rev-parse E) (tag: E, dist=0)
+ $(git rev-parse e1) (tag: e1, dist=1)
+ $(git rev-parse e2) (tag: e2, dist=2)
+ $(git rev-parse e3) (tag: e3, dist=3)
+ $(git rev-parse e4) (tag: e4, dist=4)
+ $(git rev-parse e5) (tag: e5, dist=4)
+ $(git rev-parse e6) (tag: e6, dist=3)
+ $(git rev-parse e7) (tag: e7, dist=2)
+ $(git rev-parse e8) (tag: e8, dist=1)
+ EOF
+
+ # expect results to be ordered by distance (descending),
+ # commit hash (ascending)
+ sort -k4,4r -k1,1 expect.unsorted >expect &&
+ git rev-list --bisect-all --first-parent E ^F >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 7e82e43a63..bc95da8a5f 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -32,7 +32,6 @@ changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t $test_encoding)
truncate_count=20
test_expect_success 'setup' '
- test_oid_init &&
: >foo &&
git add foo &&
git config i18n.commitEncoding $test_encoding &&
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index bb5aeac07f..b31ff7eeec 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -345,6 +345,11 @@ test_expect_success 'rev-list should succeed with empty output with empty glob'
test_must_be_empty actual
'
+test_expect_success 'rev-list should succeed with empty output when ignoring missing' '
+ git rev-list --ignore-missing $ZERO_OID >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'shortlog accepts --glob/--tags/--remotes' '
compare shortlog "subspace/one subspace/two" --branches=subspace &&
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 36d9b2b2e4..aa226381be 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -82,6 +82,13 @@ test_expect_success 'bisect fails if given any junk instead of revs' '
git bisect bad $HASH4
'
+test_expect_success 'bisect start without -- takes unknown arg as pathspec' '
+ git bisect reset &&
+ git bisect start foo bar &&
+ grep foo ".git/BISECT_NAMES" &&
+ grep bar ".git/BISECT_NAMES"
+'
+
test_expect_success 'bisect reset: back in the master branch' '
git bisect reset &&
echo "* master" > branch.expect &&
@@ -243,32 +250,30 @@ test_expect_success 'bisect skip: with commit both bad and skipped' '
'
# We want to automatically find the commit that
-# introduced "Another" into hello.
-test_expect_success \
- '"git bisect run" simple case' \
- 'echo "#"\!"/bin/sh" > test_script.sh &&
- echo "grep Another hello > /dev/null" >> test_script.sh &&
- echo "test \$? -ne 0" >> test_script.sh &&
- chmod +x test_script.sh &&
- git bisect start &&
- git bisect good $HASH1 &&
- git bisect bad $HASH4 &&
- git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
- git bisect reset'
+# added "Another" into hello.
+test_expect_success '"git bisect run" simple case' '
+ write_script test_script.sh <<-\EOF &&
+ ! grep Another hello >/dev/null
+ EOF
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
+ git bisect reset
+'
# We want to automatically find the commit that
-# introduced "Ciao" into hello.
-test_expect_success \
- '"git bisect run" with more complex "git bisect start"' \
- 'echo "#"\!"/bin/sh" > test_script.sh &&
- echo "grep Ciao hello > /dev/null" >> test_script.sh &&
- echo "test \$? -ne 0" >> test_script.sh &&
- chmod +x test_script.sh &&
- git bisect start $HASH4 $HASH1 &&
- git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
- git bisect reset'
+# added "Ciao" into hello.
+test_expect_success '"git bisect run" with more complex "git bisect start"' '
+ write_script test_script.sh <<-\EOF &&
+ ! grep Ciao hello >/dev/null
+ EOF
+ git bisect start $HASH4 $HASH1 &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
+ git bisect reset
+'
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
# but $HASH4 is good,
@@ -295,24 +300,17 @@ HASH6=
test_expect_success 'bisect run & skip: cannot tell between 2' '
add_line_into_file "6: Yet a line." hello &&
HASH6=$(git rev-parse --verify HEAD) &&
- echo "#"\!"/bin/sh" > test_script.sh &&
- echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
- echo "grep line hello > /dev/null" >> test_script.sh &&
- echo "test \$? -ne 0" >> test_script.sh &&
- chmod +x test_script.sh &&
+ write_script test_script.sh <<-\EOF &&
+ sed -ne \$p hello | grep Ciao >/dev/null && exit 125
+ ! grep line hello >/dev/null
+ EOF
git bisect start $HASH6 $HASH1 &&
- if git bisect run ./test_script.sh > my_bisect_log.txt
- then
- echo Oops, should have failed.
- false
- else
- test $? -eq 2 &&
- grep "first bad commit could be any of" my_bisect_log.txt &&
- ! grep $HASH3 my_bisect_log.txt &&
- ! grep $HASH6 my_bisect_log.txt &&
- grep $HASH4 my_bisect_log.txt &&
- grep $HASH5 my_bisect_log.txt
- fi
+ test_expect_code 2 git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH3 my_bisect_log.txt &&
+ ! grep $HASH6 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ grep $HASH5 my_bisect_log.txt
'
HASH7=
@@ -320,14 +318,13 @@ test_expect_success 'bisect run & skip: find first bad' '
git bisect reset &&
add_line_into_file "7: Should be the last line." hello &&
HASH7=$(git rev-parse --verify HEAD) &&
- echo "#"\!"/bin/sh" > test_script.sh &&
- echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
- echo "sed -ne \\\$p hello | grep day > /dev/null && exit 125" >> test_script.sh &&
- echo "grep Yet hello > /dev/null" >> test_script.sh &&
- echo "test \$? -ne 0" >> test_script.sh &&
- chmod +x test_script.sh &&
+ write_script test_script.sh <<-\EOF &&
+ sed -ne \$p hello | grep Ciao >/dev/null && exit 125
+ sed -ne \$p hello | grep day >/dev/null && exit 125
+ ! grep Yet hello >/dev/null
+ EOF
git bisect start $HASH7 $HASH1 &&
- git bisect run ./test_script.sh > my_bisect_log.txt &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
grep "$HASH6 is the first bad commit" my_bisect_log.txt
'
@@ -458,6 +455,24 @@ test_expect_success 'many merge bases creation' '
grep "$SIDE_HASH5" merge_bases.txt
'
+# We want to automatically find the merge that
+# added "line" into hello.
+test_expect_success '"git bisect run --first-parent" simple case' '
+ git rev-list --first-parent $B_HASH ^$HASH4 >first_parent_chain.txt &&
+ write_script test_script.sh <<-\EOF &&
+ grep $(git rev-parse HEAD) first_parent_chain.txt || exit -1
+ ! grep line hello >/dev/null
+ EOF
+ git bisect start --first-parent &&
+ test_path_is_file ".git/BISECT_FIRST_PARENT" &&
+ git bisect good $HASH4 &&
+ git bisect bad $B_HASH &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "$B_HASH is the first bad commit" my_bisect_log.txt &&
+ git bisect reset &&
+ test_path_is_missing .git/BISECT_FIRST_PARENT
+'
+
test_expect_success 'good merge bases when good and bad are siblings' '
git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index b2bb0a7f61..e934bc239c 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -22,7 +22,7 @@ test_expect_success 'setup a commit history with trees, blobs' '
test_expect_success 'rev-list --in-commit-order' '
git rev-list --in-commit-order --objects HEAD >actual.raw &&
- cut -c 1-40 >actual <actual.raw &&
+ cut -d" " -f1 >actual <actual.raw &&
git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
HEAD^{commit}
@@ -49,7 +49,7 @@ test_expect_success 'rev-list --in-commit-order' '
test_expect_success 'rev-list lists blobs and trees after commits' '
git rev-list --objects HEAD >actual.raw &&
- cut -c 1-40 >actual <actual.raw &&
+ cut -d" " -f1 >actual <actual.raw &&
git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
HEAD^{commit}
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 7683e4a114..7531262a5e 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -34,7 +34,7 @@ test_expect_success 'setup' '
'
test_expect_success 'start is valid' '
- git rev-parse start | grep "^[0-9a-f]\{40\}$"
+ git rev-parse start | grep "^$OID_REGEX$"
'
test_expect_success 'start^0' '
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index 2b3fd498d0..7d549748ef 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -79,7 +79,7 @@ test_expect_success GPG 'set up a signed tag' '
'
test_expect_success 'message for merging local branch' '
- echo "Merge branch ${apos}left${apos} into master" >expected &&
+ echo "Merge branch ${apos}left${apos}" >expected &&
git checkout master &&
git fetch . left &&
@@ -107,7 +107,7 @@ test_expect_success GPG 'message for merging local tag signed by unknown key' '
'
test_expect_success 'message for merging external branch' '
- echo "Merge branch ${apos}left${apos} of $(pwd) into master" >expected &&
+ echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
git checkout master &&
git fetch "$(pwd)" left &&
@@ -118,7 +118,7 @@ test_expect_success 'message for merging external branch' '
test_expect_success '[merge] summary/log configuration' '
cat >expected <<-EOF &&
- Merge branch ${apos}left${apos} into master
+ Merge branch ${apos}left${apos}
# By Another Author (3) and A U Thor (2)
# Via Another Committer
@@ -160,7 +160,7 @@ test_expect_success 'setup FETCH_HEAD' '
test_expect_success 'merge.log=3 limits shortlog length' '
cat >expected <<-EOF &&
- Merge branch ${apos}left${apos} into master
+ Merge branch ${apos}left${apos}
# By Another Author (3) and A U Thor (2)
# Via Another Committer
@@ -177,7 +177,7 @@ test_expect_success 'merge.log=3 limits shortlog length' '
test_expect_success 'merge.log=5 shows all 5 commits' '
cat >expected <<-EOF &&
- Merge branch ${apos}left${apos} into master
+ Merge branch ${apos}left${apos}
# By Another Author (3) and A U Thor (2)
# Via Another Committer
@@ -195,7 +195,7 @@ test_expect_success 'merge.log=5 shows all 5 commits' '
test_expect_success '--log=5 with custom comment character' '
cat >expected <<-EOF &&
- Merge branch ${apos}left${apos} into master
+ Merge branch ${apos}left${apos}
x By Another Author (3) and A U Thor (2)
x Via Another Committer
@@ -212,14 +212,14 @@ test_expect_success '--log=5 with custom comment character' '
'
test_expect_success 'merge.log=0 disables shortlog' '
- echo "Merge branch ${apos}left${apos} into master" >expected &&
+ echo "Merge branch ${apos}left${apos}" >expected &&
git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
test_cmp expected actual
'
test_expect_success '--log=3 limits shortlog length' '
cat >expected <<-EOF &&
- Merge branch ${apos}left${apos} into master
+ Merge branch ${apos}left${apos}
# By Another Author (3) and A U Thor (2)
# Via Another Committer
@@ -236,7 +236,7 @@ test_expect_success '--log=3 limits shortlog length' '
test_expect_success '--log=5 shows all 5 commits' '
cat >expected <<-EOF &&
- Merge branch ${apos}left${apos} into master
+ Merge branch ${apos}left${apos}
# By Another Author (3) and A U Thor (2)
# Via Another Committer
@@ -253,13 +253,13 @@ test_expect_success '--log=5 shows all 5 commits' '
'
test_expect_success '--no-log disables shortlog' '
- echo "Merge branch ${apos}left${apos} into master" >expected &&
+ echo "Merge branch ${apos}left${apos}" >expected &&
git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
test_cmp expected actual
'
test_expect_success '--log=0 disables shortlog' '
- echo "Merge branch ${apos}left${apos} into master" >expected &&
+ echo "Merge branch ${apos}left${apos}" >expected &&
git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
test_cmp expected actual
'
@@ -300,7 +300,7 @@ test_expect_success 'fmt-merge-msg -m' '
test_expect_success 'setup: expected shortlog for two branches' '
cat >expected <<-EOF
- Merge branches ${apos}left${apos} and ${apos}right${apos} into master
+ Merge branches ${apos}left${apos} and ${apos}right${apos}
# By Another Author (3) and A U Thor (2)
# Via Another Committer
@@ -397,7 +397,7 @@ test_expect_success 'merge-msg with nothing to merge' '
test_expect_success 'merge-msg tag' '
cat >expected <<-EOF &&
- Merge tag ${apos}tag-r3${apos} into master
+ Merge tag ${apos}tag-r3${apos}
* tag ${apos}tag-r3${apos}:
Right #3
@@ -418,7 +418,7 @@ test_expect_success 'merge-msg tag' '
test_expect_success 'merge-msg two tags' '
cat >expected <<-EOF &&
- Merge tags ${apos}tag-r3${apos} and ${apos}tag-l5${apos} into master
+ Merge tags ${apos}tag-r3${apos} and ${apos}tag-l5${apos}
* tag ${apos}tag-r3${apos}:
Right #3
@@ -448,7 +448,7 @@ test_expect_success 'merge-msg two tags' '
test_expect_success 'merge-msg tag and branch' '
cat >expected <<-EOF &&
- Merge branch ${apos}left${apos}, tag ${apos}tag-r3${apos} into master
+ Merge branch ${apos}left${apos}, tag ${apos}tag-r3${apos}
* tag ${apos}tag-r3${apos}:
Right #3
@@ -479,7 +479,7 @@ test_expect_success 'merge-msg tag and branch' '
test_expect_success 'merge-msg lots of commits' '
{
cat <<-EOF &&
- Merge branch ${apos}long${apos} into master
+ Merge branch ${apos}long${apos}
* long: (35 commits)
EOF
@@ -516,7 +516,7 @@ test_expect_success 'merge-msg with "merging" an annotated tag' '
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
{
cat <<-\EOF
- Merge tag '\''annote'\'' into master
+ Merge tag '\''annote'\''
An annotated one
@@ -531,7 +531,7 @@ test_expect_success 'merge-msg with "merging" an annotated tag' '
git merge --no-commit --no-ff $annote &&
{
cat <<-EOF
- Merge tag '\''$annote'\'' into master
+ Merge tag '\''$annote'\''
An annotated one
@@ -542,4 +542,24 @@ test_expect_success 'merge-msg with "merging" an annotated tag' '
test_cmp expected .git/MERGE_MSG
'
+test_expect_success 'merge.suppressDest configuration' '
+ git checkout -B side master &&
+ git commit --allow-empty -m "One step ahead" &&
+ git checkout master &&
+ git fetch . side &&
+
+ git -c merge.suppressDest="" fmt-merge-msg <.git/FETCH_HEAD >full.1 &&
+ head -n1 full.1 >actual &&
+ grep -e "Merge branch .side. into master" actual &&
+
+ git -c merge.suppressDest="mast" fmt-merge-msg <.git/FETCH_HEAD >full.2 &&
+ head -n1 full.2 >actual &&
+ grep -e "Merge branch .side. into master$" actual &&
+
+ git -c merge.suppressDest="ma??er" fmt-merge-msg <.git/FETCH_HEAD >full.3 &&
+ head -n1 full.3 >actual &&
+ grep -e "Merge branch .side." actual &&
+ ! grep -e " into master$" actual
+'
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index da59fadc5d..b359023189 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -52,6 +52,25 @@ test_atom() {
sanitize_pgp <actual >actual.clean &&
test_cmp expected actual.clean
"
+ # Automatically test "contents:size" atom after testing "contents"
+ if test "$2" = "contents"
+ then
+ case $(git cat-file -t "$ref") in
+ tag)
+ # We cannot use $3 as it expects sanitize_pgp to run
+ expect=$(git cat-file tag $ref | tail -n +6 | wc -c) ;;
+ tree | blob)
+ expect='' ;;
+ commit)
+ expect=$(printf '%s' "$3" | wc -c) ;;
+ esac
+ # Leave $expect unquoted to lose possible leading whitespaces
+ echo $expect >expected
+ test_expect_${4:-success} $PREREQ "basic atom: $1 contents:size" '
+ git for-each-ref --format="%(contents:size)" "$ref" >actual &&
+ test_cmp expected actual
+ '
+ fi
}
hexlen=$(test_oid hexsz)
@@ -97,7 +116,13 @@ test_atom head objectname:short $(git rev-parse --short refs/heads/master)
test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)
test_atom head tree $(git rev-parse refs/heads/master^{tree})
+test_atom head tree:short $(git rev-parse --short refs/heads/master^{tree})
+test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/master^{tree})
+test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/master^{tree})
test_atom head parent ''
+test_atom head parent:short ''
+test_atom head parent:short=1 ''
+test_atom head parent:short=10 ''
test_atom head numparent 0
test_atom head object ''
test_atom head type ''
@@ -106,19 +131,26 @@ test_atom head '*objecttype' ''
test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
test_atom head authorname 'A U Thor'
test_atom head authoremail '<author@example.com>'
+test_atom head authoremail:trim 'author@example.com'
+test_atom head authoremail:localpart 'author'
test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'
test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'
test_atom head committername 'C O Mitter'
test_atom head committeremail '<committer@example.com>'
+test_atom head committeremail:trim 'committer@example.com'
+test_atom head committeremail:localpart 'committer'
test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'
test_atom head tag ''
test_atom head tagger ''
test_atom head taggername ''
test_atom head taggeremail ''
+test_atom head taggeremail:trim ''
+test_atom head taggeremail:localpart ''
test_atom head taggerdate ''
test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'
test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'
test_atom head subject 'Initial'
+test_atom head subject:sanitize 'Initial'
test_atom head contents:subject 'Initial'
test_atom head body ''
test_atom head contents:body ''
@@ -142,7 +174,13 @@ test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)
test_atom tag tree ''
+test_atom tag tree:short ''
+test_atom tag tree:short=1 ''
+test_atom tag tree:short=10 ''
test_atom tag parent ''
+test_atom tag parent:short ''
+test_atom tag parent:short=1 ''
+test_atom tag parent:short=10 ''
test_atom tag numparent ''
test_atom tag object $(git rev-parse refs/tags/testtag^0)
test_atom tag type 'commit'
@@ -151,19 +189,26 @@ test_atom tag '*objecttype' 'commit'
test_atom tag author ''
test_atom tag authorname ''
test_atom tag authoremail ''
+test_atom tag authoremail:trim ''
+test_atom tag authoremail:localpart ''
test_atom tag authordate ''
test_atom tag committer ''
test_atom tag committername ''
test_atom tag committeremail ''
+test_atom tag committeremail:trim ''
+test_atom tag committeremail:localpart ''
test_atom tag committerdate ''
test_atom tag tag 'testtag'
test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'
test_atom tag taggername 'C O Mitter'
test_atom tag taggeremail '<committer@example.com>'
+test_atom tag taggeremail:trim 'committer@example.com'
+test_atom tag taggeremail:localpart 'committer'
test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
test_atom tag subject 'Tagging at 1151968727'
+test_atom tag subject:sanitize 'Tagging-at-1151968727'
test_atom tag contents:subject 'Tagging at 1151968727'
test_atom tag body ''
test_atom tag contents:body ''
@@ -545,10 +590,14 @@ test_atom refs/tags/taggerless tag 'taggerless'
test_atom refs/tags/taggerless tagger ''
test_atom refs/tags/taggerless taggername ''
test_atom refs/tags/taggerless taggeremail ''
+test_atom refs/tags/taggerless taggeremail:trim ''
+test_atom refs/tags/taggerless taggeremail:localpart ''
test_atom refs/tags/taggerless taggerdate ''
test_atom refs/tags/taggerless committer ''
test_atom refs/tags/taggerless committername ''
test_atom refs/tags/taggerless committeremail ''
+test_atom refs/tags/taggerless committeremail:trim ''
+test_atom refs/tags/taggerless committeremail:localpart ''
test_atom refs/tags/taggerless committerdate ''
test_atom refs/tags/taggerless subject 'Broken tag'
@@ -572,6 +621,7 @@ test_expect_success 'create tag with subject and body content' '
git tag -F msg subject-body
'
test_atom refs/tags/subject-body subject 'the subject line'
+test_atom refs/tags/subject-body subject:sanitize 'the-subject-line'
test_atom refs/tags/subject-body body 'first body line
second body line
'
@@ -592,6 +642,7 @@ test_expect_success 'create tag with multiline subject' '
git tag -F msg multiline
'
test_atom refs/tags/multiline subject 'first subject line second subject line'
+test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line'
test_atom refs/tags/multiline contents:subject 'first subject line second subject line'
test_atom refs/tags/multiline body 'first body line
second body line
@@ -624,6 +675,7 @@ sig='-----BEGIN PGP SIGNATURE-----
PREREQ=GPG
test_atom refs/tags/signed-empty subject ''
+test_atom refs/tags/signed-empty subject:sanitize ''
test_atom refs/tags/signed-empty contents:subject ''
test_atom refs/tags/signed-empty body "$sig"
test_atom refs/tags/signed-empty contents:body ''
@@ -631,6 +683,7 @@ test_atom refs/tags/signed-empty contents:signature "$sig"
test_atom refs/tags/signed-empty contents "$sig"
test_atom refs/tags/signed-short subject 'subject line'
+test_atom refs/tags/signed-short subject:sanitize 'subject-line'
test_atom refs/tags/signed-short contents:subject 'subject line'
test_atom refs/tags/signed-short body "$sig"
test_atom refs/tags/signed-short contents:body ''
@@ -639,6 +692,7 @@ test_atom refs/tags/signed-short contents "subject line
$sig"
test_atom refs/tags/signed-long subject 'subject line'
+test_atom refs/tags/signed-long subject:sanitize 'subject-line'
test_atom refs/tags/signed-long contents:subject 'subject line'
test_atom refs/tags/signed-long body "body contents
$sig"
@@ -650,6 +704,25 @@ test_atom refs/tags/signed-long contents "subject line
body contents
$sig"
+test_expect_success 'set up refs pointing to tree and blob' '
+ git update-ref refs/mytrees/first refs/heads/master^{tree} &&
+ git update-ref refs/myblobs/first refs/heads/master:one
+'
+
+test_atom refs/mytrees/first subject ""
+test_atom refs/mytrees/first contents:subject ""
+test_atom refs/mytrees/first body ""
+test_atom refs/mytrees/first contents:body ""
+test_atom refs/mytrees/first contents:signature ""
+test_atom refs/mytrees/first contents ""
+
+test_atom refs/myblobs/first subject ""
+test_atom refs/myblobs/first contents:subject ""
+test_atom refs/myblobs/first body ""
+test_atom refs/myblobs/first contents:body ""
+test_atom refs/myblobs/first contents:signature ""
+test_atom refs/myblobs/first contents ""
+
test_expect_success 'set up multiple-sort tags' '
for when in 100000 200000
do
@@ -738,61 +811,40 @@ test_expect_success 'set up trailers for next test' '
'
test_expect_success '%(trailers:unfold) unfolds trailers' '
- git for-each-ref --format="%(trailers:unfold)" refs/heads/master >actual &&
{
unfold <trailers
echo
} >expect &&
+ git for-each-ref --format="%(trailers:unfold)" refs/heads/master >actual &&
+ test_cmp expect actual &&
+ git for-each-ref --format="%(contents:trailers:unfold)" refs/heads/master >actual &&
test_cmp expect actual
'
test_expect_success '%(trailers:only) shows only "key: value" trailers' '
- git for-each-ref --format="%(trailers:only)" refs/heads/master >actual &&
{
grep -v patch.description <trailers &&
echo
} >expect &&
+ git for-each-ref --format="%(trailers:only)" refs/heads/master >actual &&
+ test_cmp expect actual &&
+ git for-each-ref --format="%(contents:trailers:only)" refs/heads/master >actual &&
test_cmp expect actual
'
test_expect_success '%(trailers:only) and %(trailers:unfold) work together' '
- git for-each-ref --format="%(trailers:only,unfold)" refs/heads/master >actual &&
- git for-each-ref --format="%(trailers:unfold,only)" refs/heads/master >reverse &&
- test_cmp actual reverse &&
{
grep -v patch.description <trailers | unfold &&
echo
} >expect &&
- test_cmp expect actual
-'
-
-test_expect_success '%(contents:trailers:unfold) unfolds trailers' '
- git for-each-ref --format="%(contents:trailers:unfold)" refs/heads/master >actual &&
- {
- unfold <trailers
- echo
- } >expect &&
- test_cmp expect actual
-'
-
-test_expect_success '%(contents:trailers:only) shows only "key: value" trailers' '
- git for-each-ref --format="%(contents:trailers:only)" refs/heads/master >actual &&
- {
- grep -v patch.description <trailers &&
- echo
- } >expect &&
- test_cmp expect actual
-'
-
-test_expect_success '%(contents:trailers:only) and %(contents:trailers:unfold) work together' '
+ git for-each-ref --format="%(trailers:only,unfold)" refs/heads/master >actual &&
+ test_cmp expect actual &&
+ git for-each-ref --format="%(trailers:unfold,only)" refs/heads/master >actual &&
+ test_cmp actual actual &&
git for-each-ref --format="%(contents:trailers:only,unfold)" refs/heads/master >actual &&
- git for-each-ref --format="%(contents:trailers:unfold,only)" refs/heads/master >reverse &&
- test_cmp actual reverse &&
- {
- grep -v patch.description <trailers | unfold &&
- echo
- } >expect &&
- test_cmp expect actual
+ test_cmp expect actual &&
+ git for-each-ref --format="%(contents:trailers:unfold,only)" refs/heads/master >actual &&
+ test_cmp actual actual
'
test_expect_success '%(trailers) rejects unknown trailers arguments' '
@@ -801,15 +853,16 @@ test_expect_success '%(trailers) rejects unknown trailers arguments' '
fatal: unknown %(trailers) argument: unsupported
EOF
test_must_fail git for-each-ref --format="%(trailers:unsupported)" 2>actual &&
+ test_i18ncmp expect actual &&
+ test_must_fail git for-each-ref --format="%(contents:trailers:unsupported)" 2>actual &&
test_i18ncmp expect actual
'
-test_expect_success '%(contents:trailers) rejects unknown trailers arguments' '
- # error message cannot be checked under i18n
+test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '
cat >expect <<-EOF &&
- fatal: unknown %(trailers) argument: unsupported
+ fatal: unrecognized %(contents) argument: trailersonly
EOF
- test_must_fail git for-each-ref --format="%(contents:trailers:unsupported)" 2>actual &&
+ test_must_fail git for-each-ref --format="%(contents:trailersonly)" 2>actual &&
test_i18ncmp expect actual
'
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index 49cc65bb58..809854fc0c 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -5,9 +5,9 @@ test_description='for-each-ref errors for broken refs'
. ./test-lib.sh
ZEROS=$ZERO_OID
-MISSING=abababababababababababababababababababab
test_expect_success setup '
+ MISSING=$(test_oid deadbeef) &&
git commit --allow-empty -m "Initial" &&
git tag testtag &&
git for-each-ref >full-list &&
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 35408d53fd..781e470aea 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -437,8 +437,8 @@ test_expect_success 'check %(if:notequals=<string>)' '
test_cmp expect actual
'
-test_expect_success '--merged is incompatible with --no-merged' '
- test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
+test_expect_success '--merged is compatible with --no-merged' '
+ git for-each-ref --merged HEAD --no-merged HEAD
'
test_expect_success 'validate worktree atom' '
diff --git a/t/t6020-merge-df.sh b/t/t6400-merge-df.sh
index 400a4cd139..400a4cd139 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6400-merge-df.sh
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6401-merge-criss-cross.sh
index 9d5e992878..9d5e992878 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6401-merge-criss-cross.sh
diff --git a/t/t6022-merge-rename.sh b/t/t6402-merge-rename.sh
index bbbba3dcbf..bbbba3dcbf 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
diff --git a/t/t6023-merge-file.sh b/t/t6403-merge-file.sh
index 2f421d967a..2f421d967a 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6403-merge-file.sh
diff --git a/t/t6024-recursive-merge.sh b/t/t6404-recursive-merge.sh
index 332cfc53fd..332cfc53fd 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6404-recursive-merge.sh
diff --git a/t/t6025-merge-symlinks.sh b/t/t6405-merge-symlinks.sh
index 6c0a90d044..6c0a90d044 100755
--- a/t/t6025-merge-symlinks.sh
+++ b/t/t6405-merge-symlinks.sh
diff --git a/t/t6026-merge-attr.sh b/t/t6406-merge-attr.sh
index 5900358ce9..76a55f838c 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -122,7 +122,7 @@ test_expect_success 'custom merge backend' '
o=$(git unpack-file master^:text) &&
a=$(git unpack-file side^:text) &&
b=$(git unpack-file master:text) &&
- sh -c "./custom-merge $o $a $b 0 'text'" &&
+ sh -c "./custom-merge $o $a $b 0 text" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
rm -f $o $a $b
@@ -149,7 +149,7 @@ test_expect_success 'custom merge backend' '
o=$(git unpack-file master^:text) &&
a=$(git unpack-file anchor:text) &&
b=$(git unpack-file master:text) &&
- sh -c "./custom-merge $o $a $b 0 'text'" &&
+ sh -c "./custom-merge $o $a $b 0 text" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
sed -e 1,3d -e 4q $a >check-3 &&
diff --git a/t/t6027-merge-binary.sh b/t/t6407-merge-binary.sh
index 4e6c7cb77e..4e6c7cb77e 100755
--- a/t/t6027-merge-binary.sh
+++ b/t/t6407-merge-binary.sh
diff --git a/t/t6028-merge-up-to-date.sh b/t/t6408-merge-up-to-date.sh
index 7763c1ba98..7763c1ba98 100755
--- a/t/t6028-merge-up-to-date.sh
+++ b/t/t6408-merge-up-to-date.sh
diff --git a/t/t6029-merge-subtree.sh b/t/t6409-merge-subtree.sh
index 793f0c8bf3..793f0c8bf3 100755
--- a/t/t6029-merge-subtree.sh
+++ b/t/t6409-merge-subtree.sh
diff --git a/t/t6031-merge-filemode.sh b/t/t6411-merge-filemode.sh
index 87741efad3..87741efad3 100755
--- a/t/t6031-merge-filemode.sh
+++ b/t/t6411-merge-filemode.sh
diff --git a/t/t6032-merge-large-rename.sh b/t/t6412-merge-large-rename.sh
index 80777386dc..80777386dc 100755
--- a/t/t6032-merge-large-rename.sh
+++ b/t/t6412-merge-large-rename.sh
diff --git a/t/t6033-merge-crlf.sh b/t/t6413-merge-crlf.sh
index e8d65eefb5..e8d65eefb5 100755
--- a/t/t6033-merge-crlf.sh
+++ b/t/t6413-merge-crlf.sh
diff --git a/t/t6034-merge-rename-nocruft.sh b/t/t6414-merge-rename-nocruft.sh
index a25e730460..a25e730460 100755
--- a/t/t6034-merge-rename-nocruft.sh
+++ b/t/t6414-merge-rename-nocruft.sh
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6415-merge-dir-to-symlink.sh
index 2eddcc7664..2eddcc7664 100755
--- a/t/t6035-merge-dir-to-symlink.sh
+++ b/t/t6415-merge-dir-to-symlink.sh
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh
index b3bf462617..fd98989b14 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6416-recursive-corner-cases.sh
@@ -452,7 +452,7 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
#
# So choice 5 at least provides some kind of conflict for the original case,
# and can merge cleanly as expected with D1 and E3. It also made things just
-# slightly funny for merging D1 and e$, where E4 is defined as:
+# slightly funny for merging D1 and E4, where E4 is defined as:
# Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
# in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE
# gets renamed to 'a' in D1 and to 'a2' in E4. But that's better than having
@@ -1144,7 +1144,7 @@ test_expect_failure 'check symlink add/add' '
test_must_fail git merge -s recursive E^0 &&
git ls-files -s >out &&
- test_line_count = 2 out &&
+ test_line_count = 3 out &&
git ls-files -u >out &&
test_line_count = 2 out &&
git ls-files -o >out &&
diff --git a/t/t6037-merge-ours-theirs.sh b/t/t6417-merge-ours-theirs.sh
index 0aebc6c028..0aebc6c028 100755
--- a/t/t6037-merge-ours-theirs.sh
+++ b/t/t6417-merge-ours-theirs.sh
diff --git a/t/t6038-merge-text-auto.sh b/t/t6418-merge-text-auto.sh
index 5e8d5fa50c..30983d18b1 100755
--- a/t/t6038-merge-text-auto.sh
+++ b/t/t6418-merge-text-auto.sh
@@ -158,7 +158,7 @@ test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
compare_files expected file.fuzzy
'
-test_expect_failure 'checkout -m after setting text=auto' '
+test_expect_success 'checkout -m after setting text=auto' '
cat <<-\EOF >expected &&
first line
same line
@@ -168,12 +168,12 @@ test_expect_failure 'checkout -m after setting text=auto' '
git rm -fr . &&
rm -f .gitattributes &&
git reset --hard initial &&
- git checkout a -- . &&
+ git restore --source=a -- . &&
git checkout -m b &&
- compare_files expected file
+ git diff --no-index --ignore-cr-at-eol expected file
'
-test_expect_failure 'checkout -m addition of text=auto' '
+test_expect_success 'checkout -m addition of text=auto' '
cat <<-\EOF >expected &&
first line
same line
@@ -183,23 +183,9 @@ test_expect_failure 'checkout -m addition of text=auto' '
git rm -fr . &&
rm -f .gitattributes file &&
git reset --hard initial &&
- git checkout b -- . &&
+ git restore --source=b -- . &&
git checkout -m a &&
- compare_files expected file
-'
-
-test_expect_failure 'cherry-pick patch from after text=auto was added' '
- append_cr <<-\EOF >expected &&
- first line
- same line
- EOF
-
- git config merge.renormalize true &&
- git rm -fr . &&
- git reset --hard b &&
- test_must_fail git cherry-pick a >err 2>&1 &&
- grep "[Nn]othing added" err &&
- compare_files expected file
+ git diff --no-index --ignore-cr-at-eol expected file
'
test_expect_success 'Test delete/normalize conflict' '
@@ -211,7 +197,8 @@ test_expect_success 'Test delete/normalize conflict' '
git commit -m "remove file" &&
git checkout master &&
git reset --hard a^ &&
- git merge side
+ git merge side &&
+ test_path_is_missing file
'
test_done
diff --git a/t/t6039-merge-ignorecase.sh b/t/t6419-merge-ignorecase.sh
index 531850d834..531850d834 100755
--- a/t/t6039-merge-ignorecase.sh
+++ b/t/t6419-merge-ignorecase.sh
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index f163893ff9..3375eaf4e7 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -457,7 +457,7 @@ test_expect_success 'handle rename-with-content-merge vs. add' '
git checkout A^0 &&
test_must_fail git merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/add)" out &&
+ test_i18ngrep "CONFLICT (.*/add)" out &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -503,7 +503,7 @@ test_expect_success 'handle rename-with-content-merge vs. add, merge other way'
git checkout B^0 &&
test_must_fail git merge -s recursive A^0 >out &&
- test_i18ngrep "CONFLICT (rename/add)" out &&
+ test_i18ngrep "CONFLICT (.*/add)" out &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -583,7 +583,7 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
git checkout B^0 &&
test_must_fail git merge -s recursive C^0 >out &&
- test_i18ngrep "CONFLICT (rename/rename)" out &&
+ test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
git ls-files -s >out &&
test_line_count = 2 out &&
@@ -886,12 +886,17 @@ test_expect_failure 'rad-check: rename/add/delete conflict' '
git checkout B^0 &&
test_must_fail git merge -s recursive A^0 >out 2>err &&
- # Not sure whether the output should contain just one
- # "CONFLICT (rename/add/delete)" line, or if it should break
- # it into a pair of "CONFLICT (rename/delete)" and
- # "CONFLICT (rename/add)"; allow for either.
- test_i18ngrep "CONFLICT (rename.*add)" out &&
- test_i18ngrep "CONFLICT (rename.*delete)" out &&
+ # Instead of requiring the output to contain one combined line
+ # CONFLICT (rename/add/delete)
+ # or perhaps two lines:
+ # CONFLICT (rename/add): new file collides with rename target
+ # CONFLICT (rename/delete): rename source removed on other side
+ # and instead of requiring "rename/add" instead of "add/add",
+ # be flexible in the type of console output message(s) reported
+ # for this particular case; we will be more stringent about the
+ # contents of the index and working directory.
+ test_i18ngrep "CONFLICT (.*/add)" out &&
+ test_i18ngrep "CONFLICT (rename.*/delete)" out &&
test_must_be_empty err &&
git ls-files -s >file_count &&
@@ -899,14 +904,14 @@ test_expect_failure 'rad-check: rename/add/delete conflict' '
git ls-files -u >file_count &&
test_line_count = 2 file_count &&
git ls-files -o >file_count &&
- test_line_count = 2 file_count &&
+ test_line_count = 3 file_count &&
git rev-parse >actual \
:2:bar :3:bar &&
git rev-parse >expect \
B:bar A:bar &&
- test_cmp file_is_missing foo &&
+ test_path_is_missing foo &&
# bar should have two-way merged contents of the different
# versions of bar; check that content from both sides is
# present.
@@ -954,11 +959,17 @@ test_expect_failure 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
git checkout A^0 &&
test_must_fail git merge -s recursive B^0 >out 2>err &&
- # Not sure whether the output should contain just one
- # "CONFLICT (rename/rename/delete/delete)" line, or if it
- # should break it into three: "CONFLICT (rename/rename)" and
- # two "CONFLICT (rename/delete)" lines; allow for either.
- test_i18ngrep "CONFLICT (rename/rename)" out &&
+ # Instead of requiring the output to contain one combined line
+ # CONFLICT (rename/rename/delete/delete)
+ # or perhaps two lines:
+ # CONFLICT (rename/rename): ...
+ # CONFLICT (rename/delete): info about pair 1
+ # CONFLICT (rename/delete): info about pair 2
+ # and instead of requiring "rename/rename" instead of "add/add",
+ # be flexible in the type of console output message(s) reported
+ # for this particular case; we will be more stringent about the
+ # contents of the index and working directory.
+ test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
test_i18ngrep "CONFLICT (rename.*delete)" out &&
test_must_be_empty err &&
@@ -967,15 +978,15 @@ test_expect_failure 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
git ls-files -u >file_count &&
test_line_count = 2 file_count &&
git ls-files -o >file_count &&
- test_line_count = 2 file_count &&
+ test_line_count = 3 file_count &&
git rev-parse >actual \
:2:baz :3:baz &&
git rev-parse >expect \
O:foo O:bar &&
- test_cmp file_is_missing foo &&
- test_cmp file_is_missing bar &&
+ test_path_is_missing foo &&
+ test_path_is_missing bar &&
# baz should have two-way merged contents of the original
# contents of foo and bar; check that content from both sides
# is present.
@@ -1042,25 +1053,25 @@ test_expect_failure 'mod6-check: chains of rename/rename(1to2) and rename/rename
test_must_be_empty err &&
git ls-files -s >file_count &&
- test_line_count = 6 file_count &&
+ test_line_count = 9 file_count &&
git ls-files -u >file_count &&
- test_line_count = 6 file_count &&
+ test_line_count = 9 file_count &&
git ls-files -o >file_count &&
test_line_count = 3 file_count &&
test_seq 10 20 >merged-one &&
test_seq 51 60 >merged-five &&
# Determine what the merge of three would give us.
- test_seq 30 40 >three-side-A &&
+ test_seq 31 39 >three-base &&
+ test_seq 31 40 >three-side-A &&
test_seq 31 39 >three-side-B &&
- echo forty >three-side-B &&
- >empty &&
+ echo forty >>three-side-B &&
test_must_fail git merge-file \
- -L "HEAD" \
+ -L "HEAD:four" \
-L "" \
- -L "B^0" \
- three-side-A empty three-side-B &&
- sed -e "s/^\([<=>]\)/\1\1\1/" three-side-A >merged-three &&
+ -L "B^0:two" \
+ three-side-A three-base three-side-B &&
+ sed -e "s/^\([<=>]\)/\1\1/" three-side-A >merged-three &&
# Verify the index is as expected
git rev-parse >actual \
@@ -1075,6 +1086,7 @@ test_expect_failure 'mod6-check: chains of rename/rename(1to2) and rename/rename
git cat-file -p :2:two >expect &&
git cat-file -p :3:two >other &&
+ >empty &&
test_must_fail git merge-file \
-L "HEAD" -L "" -L "B^0" \
expect empty other &&
diff --git a/t/t6043-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index 83792c5ef1..f7ecbb886d 100755
--- a/t/t6043-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -275,7 +275,7 @@ test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict'
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/rename)" out &&
+ test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
git ls-files -s >out &&
test_line_count = 8 out &&
@@ -1686,7 +1686,7 @@ test_expect_success '7b: rename/rename(2to1), but only due to transitive rename'
git checkout A^0 &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
- test_i18ngrep "CONFLICT (rename/rename)" out &&
+ test_i18ngrep "CONFLICT (\(.*\)/\1)" out &&
git ls-files -s >out &&
test_line_count = 4 out &&
@@ -2260,24 +2260,23 @@ test_expect_success '8d: rename/delete...or not?' '
# Commit B: w/{b,c}, z/d
#
# Possible Resolutions:
-# w/o dir-rename detection: z/d, CONFLICT(z/b -> y/b vs. w/b),
-# CONFLICT(z/c -> y/c vs. w/c)
-# Currently expected: y/d, CONFLICT(z/b -> y/b vs. w/b),
-# CONFLICT(z/c -> y/c vs. w/c)
-# Optimal: ??
+# if z not considered renamed: z/d, CONFLICT(z/b -> y/b vs. w/b),
+# CONFLICT(z/c -> y/c vs. w/c)
+# if z->y rename considered: y/d, CONFLICT(z/b -> y/b vs. w/b),
+# CONFLICT(z/c -> y/c vs. w/c)
+# Optimal: ??
#
# Notes: In commit A, directory z got renamed to y. In commit B, directory z
# did NOT get renamed; the directory is still present; instead it is
# considered to have just renamed a subset of paths in directory z
-# elsewhere. Therefore, the directory rename done in commit A to z/
-# applies to z/d and maps it to y/d.
+# elsewhere. However, this is much like testcase 6b (where commit B
+# moves all the original paths out of z/ but opted to keep d
+# within z/). This makes it hard to judge where d should end up.
#
# It's possible that users would get confused about this, but what
-# should we do instead? Silently leaving at z/d seems just as bad or
-# maybe even worse. Perhaps we could print a big warning about z/d
-# and how we're moving to y/d in this case, but when I started thinking
-# about the ramifications of doing that, I didn't know how to rule out
-# that opening other weird edge and corner cases so I just punted.
+# should we do instead? It's not at all clear to me whether z/d or
+# y/d or something else is a better resolution here, and other cases
+# start getting really tricky, so I just picked one.
test_setup_8e () {
test_create_repo 8e &&
@@ -2844,6 +2843,14 @@ test_expect_success '9f: Renamed directory that only contained immediate subdirs
# Commit A: priority/{alpha,bravo}/$more_files
# Commit B: goal/{a,b}/$more_files, goal/c
# Expected: priority/{alpha,bravo}/$more_files, priority/c
+# We currently fail this test because the directory renames we detect are
+# goal/a/ -> priority/alpha/
+# goal/b/ -> priority/bravo/
+# We do not detect
+# goal/ -> priority/
+# because of no files found within goal/, and the fact that "a" != "alpha"
+# and "b" != "bravo". But I'm not sure it's really a failure given that
+# viewpoint...
test_setup_9g () {
test_create_repo 9g &&
@@ -2880,6 +2887,7 @@ test_setup_9g () {
}
test_expect_failure '9g: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' '
+ test_setup_9g &&
(
cd 9g &&
@@ -3362,6 +3370,7 @@ test_setup_10e () {
}
test_expect_failure '10e: Does git complain about untracked file that is not really in the way?' '
+ test_setup_10e &&
(
cd 10e &&
@@ -4403,7 +4412,7 @@ test_expect_success '13b(info): messages for transitive rename with conflicted c
# Commit O: z/{b,c}, x/{d,e}
# Commit A: y/{b,c,d}, x/e
# Commit B: z/{b,c,d}, x/e
-# Expected: y/{b,c,d}, with info or conflict messages for d (
+# Expected: y/{b,c,d}, x/e, with info or conflict messages for d
# A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d
# One could argue A had partial knowledge of what was done with
# d and B had full knowledge, but that's a slippery slope as
diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh
index 5e3779ebc9..5e3779ebc9 100755
--- a/t/t6044-merge-unrelated-index-changes.sh
+++ b/t/t6424-merge-unrelated-index-changes.sh
diff --git a/t/t6045-merge-rename-delete.sh b/t/t6425-merge-rename-delete.sh
index 5d33577d2f..f79d021590 100755
--- a/t/t6045-merge-rename-delete.sh
+++ b/t/t6425-merge-rename-delete.sh
@@ -17,7 +17,8 @@ test_expect_success 'rename/delete' '
git commit -m "delete" &&
test_must_fail git merge --strategy=recursive rename >output &&
- test_i18ngrep "CONFLICT (rename/delete): A deleted in HEAD and renamed to B in rename. Version rename of B left in tree." output
+ test_i18ngrep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output &&
+ test_i18ngrep "CONFLICT (rename/delete): A.*deleted in HEAD." output
'
test_done
diff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index 1ddc9e6626..699813671c 100755
--- a/t/t6046-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -374,7 +374,7 @@ test_expect_success '2c: Modify b & add c VS rename b->c' '
export GIT_MERGE_VERBOSITY &&
test_must_fail git merge -s recursive B^0 >out 2>err &&
- test_i18ngrep "CONFLICT (rename/add): Rename b->c" out &&
+ test_i18ngrep "CONFLICT (.*/add):" out &&
test_must_be_empty err &&
# Make sure c WAS updated
@@ -661,7 +661,7 @@ test_setup_4a () {
}
# NOTE: For as long as we continue using unpack_trees() without index_only
-# set to true, it will error out on a case like this claiming the the locally
+# set to true, it will error out on a case like this claiming that the locally
# modified file would be overwritten by the merge. Getting this testcase
# correct requires doing the merge in-memory first, then realizing that no
# updates to the file are necessary, and thus that we can just leave the path
diff --git a/t/t6047-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh
index f4655bb358..f4655bb358 100755
--- a/t/t6047-diff3-conflict-markers.sh
+++ b/t/t6427-diff3-conflict-markers.sh
diff --git a/t/t3030-merge-recursive.sh b/t/t6430-merge-recursive.sh
index d48d211a95..d48d211a95 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t6430-merge-recursive.sh
diff --git a/t/t3031-merge-criscross.sh b/t/t6431-merge-criscross.sh
index 3824756a02..3824756a02 100755
--- a/t/t3031-merge-criscross.sh
+++ b/t/t6431-merge-criscross.sh
diff --git a/t/t3032-merge-recursive-space-options.sh b/t/t6432-merge-recursive-space-options.sh
index b56180ee4a..b56180ee4a 100755
--- a/t/t3032-merge-recursive-space-options.sh
+++ b/t/t6432-merge-recursive-space-options.sh
diff --git a/t/t3033-merge-toplevel.sh b/t/t6433-merge-toplevel.sh
index e29c284b9b..e29c284b9b 100755
--- a/t/t3033-merge-toplevel.sh
+++ b/t/t6433-merge-toplevel.sh
diff --git a/t/t3034-merge-recursive-rename-options.sh b/t/t6434-merge-recursive-rename-options.sh
index 3d9fae68c4..3d9fae68c4 100755
--- a/t/t3034-merge-recursive-rename-options.sh
+++ b/t/t6434-merge-recursive-rename-options.sh
diff --git a/t/t3035-merge-sparse.sh b/t/t6435-merge-sparse.sh
index 74562e1235..74562e1235 100755
--- a/t/t3035-merge-sparse.sh
+++ b/t/t6435-merge-sparse.sh
diff --git a/t/t7607-merge-overwrite.sh b/t/t6436-merge-overwrite.sh
index dd8ab7ede1..dd8ab7ede1 100755
--- a/t/t7607-merge-overwrite.sh
+++ b/t/t6436-merge-overwrite.sh
diff --git a/t/t7405-submodule-merge.sh b/t/t6437-submodule-merge.sh
index aa33978ed2..6a1e5f8232 100755
--- a/t/t7405-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -195,7 +195,7 @@ test_expect_success 'git submodule status should display the merge conflict prop
url = $TRASH_DIRECTORY/sub
EOF
cat >expect <<EOF &&
-U0000000000000000000000000000000000000000 sub
+U$ZERO_OID sub
EOF
git submodule status > actual &&
test_cmp expect actual &&
@@ -214,7 +214,7 @@ test_expect_success 'git submodule status should display the merge conflict prop
url = $TRASH_DIRECTORY/sub
EOF
cat >expect <<EOF &&
-U0000000000000000000000000000000000000000 sub
+U$ZERO_OID sub
EOF
git submodule status > actual &&
test_cmp expect actual &&
diff --git a/t/t7613-merge-submodule.sh b/t/t6438-submodule-directory-file-conflicts.sh
index 04bf4be7d7..04bf4be7d7 100755
--- a/t/t7613-merge-submodule.sh
+++ b/t/t6438-submodule-directory-file-conflicts.sh
diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t6439-merge-co-error-msgs.sh
index 5c8894d94f..5c8894d94f 100755
--- a/t/t7609-merge-co-error-msgs.sh
+++ b/t/t6439-merge-co-error-msgs.sh
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 0a69a67117..4a3b8f48ac 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -10,7 +10,24 @@ test_expect_success 'setup' '
# do not let the amount of physical memory affects gc
# behavior, make sure we always pack everything to one pack by
# default
- git config gc.bigPackThreshold 2g
+ git config gc.bigPackThreshold 2g &&
+
+ # These are simply values which, when hashed as a blob with a newline,
+ # produce a hash where the first byte is 0x17 in their respective
+ # algorithms.
+ test_oid_cache <<-EOF
+ obj1 sha1:263
+ obj1 sha256:34
+
+ obj2 sha1:410
+ obj2 sha256:174
+
+ obj3 sha1:523
+ obj3 sha256:313
+
+ obj4 sha1:790
+ obj4 sha256:481
+ EOF
'
test_expect_success 'gc empty repository' '
@@ -85,13 +102,13 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
# We need to create two object whose sha1s start with 17
# since this is what git gc counts. As it happens, these
# two blobs will do so.
- test_commit 263 &&
- test_commit 410 &&
+ test_commit "$(test_oid obj1)" &&
+ test_commit "$(test_oid obj2)" &&
# Our first gc will create a pack; our second will create a second pack
git gc --auto &&
ls .git/objects/pack | sort >existing_packs &&
- test_commit 523 &&
- test_commit 790 &&
+ test_commit "$(test_oid obj3)" &&
+ test_commit "$(test_oid obj4)" &&
git gc --auto 2>err &&
test_i18ngrep ! "^warning:" err &&
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index f30b4849b6..8a3bb4105b 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -128,9 +128,9 @@ for repack in '' true; do
done
test_expect_success 'do not complain about existing broken links (commit)' '
- cat >broken-commit <<-\EOF &&
- tree 0000000000000000000000000000000000000001
- parent 0000000000000000000000000000000000000002
+ cat >broken-commit <<-EOF &&
+ tree $(test_oid 001)
+ parent $(test_oid 002)
author whatever <whatever@example.com> 1234 -0000
committer whatever <whatever@example.com> 1234 -0000
@@ -143,8 +143,8 @@ test_expect_success 'do not complain about existing broken links (commit)' '
'
test_expect_success 'do not complain about existing broken links (tree)' '
- cat >broken-tree <<-\EOF &&
- 100644 blob 0000000000000000000000000000000000000003 foo
+ cat >broken-tree <<-EOF &&
+ 100644 blob $(test_oid 003) foo
EOF
tree=$(git mktree --missing <broken-tree) &&
git gc -q 2>stderr &&
@@ -153,8 +153,8 @@ test_expect_success 'do not complain about existing broken links (tree)' '
'
test_expect_success 'do not complain about existing broken links (tag)' '
- cat >broken-tag <<-\EOF &&
- object 0000000000000000000000000000000000000004
+ cat >broken-tag <<-EOF &&
+ object $(test_oid 004)
type commit
tag broken
tagger whatever <whatever@example.com> 1234 -0000
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 36b50d0b4c..63d5f41a12 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -177,7 +177,7 @@ test_expect_success "Sergey Vlasov's test case" '
date >ab.c &&
date >ab/d &&
git add ab.c ab &&
- git commit -m 'initial' &&
+ git commit -m "initial" &&
git mv ab a
'
@@ -248,6 +248,23 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
rm -f dirty dirty2
+# NB: This test is about the error message
+# as well as the failure.
+test_expect_success 'git mv error on conflicted file' '
+ rm -fr .git &&
+ git init &&
+ >conflict &&
+ test_when_finished "rm -f conflict" &&
+ cfhash=$(git hash-object -w conflict) &&
+ q_to_tab <<-EOF | git update-index --index-info &&
+ 0 $cfhash 0Qconflict
+ 100644 $cfhash 1Qconflict
+ EOF
+
+ test_must_fail git mv conflict newname 2>actual &&
+ test_i18ngrep "conflicted" actual
+'
+
test_expect_success 'git mv should overwrite symlink to a file' '
rm -fr .git &&
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index e23de7d0b5..36477cb1f4 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -463,10 +463,11 @@ test_expect_success 'rewrite submodule with another content' '
'
test_expect_success 'replace submodule revision' '
+ invalid=$(test_oid numeric) &&
git reset --hard original &&
git filter-branch -f --tree-filter \
"if git ls-files --error-unmatch -- submod > /dev/null 2>&1
- then git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submod
+ then git update-index --cacheinfo 160000 $invalid submod
fi" HEAD &&
test $orig_head != $(git show-ref --hash --head HEAD)
'
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 74b637deb2..05f411c821 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -2015,8 +2015,8 @@ test_expect_success '--merged can be used in non-list mode' '
test_cmp expect actual
'
-test_expect_success '--merged is incompatible with --no-merged' '
- test_must_fail git tag --merged HEAD --no-merged HEAD
+test_expect_success '--merged is compatible with --no-merged' '
+ git tag --merged HEAD --no-merged HEAD
'
test_expect_success '--merged shows merged tags' '
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
index e4cf5484f9..2f9bea9793 100755
--- a/t/t7061-wtstatus-ignore.sh
+++ b/t/t7061-wtstatus-ignore.sh
@@ -30,6 +30,31 @@ test_expect_success 'same with gitignore starting with BOM' '
test_cmp expected actual
'
+test_expect_success 'status untracked files --ignored with pathspec (no match)' '
+ git status --porcelain --ignored -- untracked/i >actual &&
+ test_must_be_empty actual &&
+ git status --porcelain --ignored -- untracked/u >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'status untracked files --ignored with pathspec (literal match)' '
+ git status --porcelain --ignored -- untracked/ignored >actual &&
+ echo "!! untracked/ignored" >expected &&
+ test_cmp expected actual &&
+ git status --porcelain --ignored -- untracked/uncommitted >actual &&
+ echo "?? untracked/uncommitted" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'status untracked files --ignored with pathspec (glob match)' '
+ git status --porcelain --ignored -- untracked/i\* >actual &&
+ echo "!! untracked/ignored" >expected &&
+ test_cmp expected actual &&
+ git status --porcelain --ignored -- untracked/u\* >actual &&
+ echo "?? untracked/uncommitted" >expected &&
+ test_cmp expected actual
+'
+
cat >expected <<\EOF
?? .gitignore
?? actual
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
index 428cff9cf3..a682a3d826 100755
--- a/t/t7063-status-untracked-cache.sh
+++ b/t/t7063-status-untracked-cache.sh
@@ -75,14 +75,24 @@ test_expect_success 'setup' '
touch one two three done/one dtwo/two dthree/three &&
git add one two done/one &&
: >.git/info/exclude &&
- git update-index --untracked-cache
+ git update-index --untracked-cache &&
+ test_oid_cache <<-EOF
+ root sha1:e6fcc8f2ee31bae321d66afd183fcb7237afae6e
+ root sha256:b90c672088c015b9c83876e919da311bad4cd39639fb139f988af6a11493b974
+
+ exclude sha1:13263c0978fb9fad16b2d580fb800b6d811c3ff0
+ exclude sha256:fe4aaa1bbbbce4cb8f73426748a14c5ad6026b26f90505a0bf2494b165a5b76c
+
+ done sha1:1946f0437f90c5005533cbe1736a6451ca301714
+ done sha256:7f079501d79f665b3acc50f5e0e9e94509084d5032ac20113a37dd5029b757cc
+ EOF
'
test_expect_success 'untracked cache is empty' '
test-tool dump-untracked-cache >../actual &&
cat >../expect-empty <<EOF &&
-info/exclude 0000000000000000000000000000000000000000
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $ZERO_OID
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
EOF
@@ -100,17 +110,17 @@ EOF
cat >../dump.expect <<EOF &&
info/exclude $EMPTY_BLOB
-core.excludesfile 0000000000000000000000000000000000000000
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ 0000000000000000000000000000000000000000 recurse valid
+/ $ZERO_OID recurse valid
dthree/
dtwo/
three
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
three
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
@@ -190,18 +200,18 @@ test_expect_success 'verify untracked cache dump' '
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude $EMPTY_BLOB
-core.excludesfile 0000000000000000000000000000000000000000
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ 0000000000000000000000000000000000000000 recurse valid
+/ $ZERO_OID recurse valid
dthree/
dtwo/
four
three
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
three
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -239,18 +249,18 @@ test_expect_success 'verify untracked cache dump' '
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
info/exclude $EMPTY_BLOB
-core.excludesfile 0000000000000000000000000000000000000000
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+/ $(test_oid root) recurse valid
.gitignore
dthree/
dtwo/
three
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
three
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -284,16 +294,16 @@ EOF
test_expect_success 'verify untracked cache dump' '
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+/ $(test_oid root) recurse valid
.gitignore
dtwo/
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -303,14 +313,14 @@ test_expect_success 'move two from tracked to untracked' '
git rm --cached two &&
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/ $(test_oid root) recurse
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -342,17 +352,17 @@ EOF
test_expect_success 'verify untracked cache dump' '
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+/ $(test_oid root) recurse valid
.gitignore
dtwo/
two
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -362,14 +372,14 @@ test_expect_success 'move two from untracked to tracked' '
git add two &&
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/ $(test_oid root) recurse
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -401,16 +411,16 @@ EOF
test_expect_success 'verify untracked cache dump' '
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+/ $(test_oid root) recurse valid
.gitignore
dtwo/
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -447,16 +457,16 @@ EOF
test_expect_success 'untracked cache correct after commit' '
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+/ $(test_oid root) recurse valid
.gitignore
dtwo/
-/done/ 0000000000000000000000000000000000000000 recurse valid
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/ $ZERO_OID recurse valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -508,17 +518,17 @@ EOF
test_expect_success 'untracked cache correct after status' '
test-tool dump-untracked-cache >../actual &&
cat >../expect <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+/ $(test_oid root) recurse valid
.gitignore
dtwo/
-/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
+/done/ $(test_oid done) recurse valid
five
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect ../actual
@@ -580,22 +590,22 @@ EOF
test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
test-tool dump-untracked-cache >../actual &&
cat >../expect-from-test-dump <<EOF &&
-info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
-core.excludesfile 0000000000000000000000000000000000000000
+info/exclude $(test_oid exclude)
+core.excludesfile $ZERO_OID
exclude_per_dir .gitignore
flags 00000006
-/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+/ $(test_oid root) recurse valid
.gitignore
dtwo/
-/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
+/done/ $(test_oid done) recurse valid
five
sub/
-/done/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/sub/ $ZERO_OID recurse check_only valid
sub/
-/done/sub/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
+/done/sub/sub/ $ZERO_OID recurse check_only valid
file
-/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
-/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dthree/ $ZERO_OID recurse check_only valid
+/dtwo/ $ZERO_OID recurse check_only valid
two
EOF
test_cmp ../expect-from-test-dump ../actual
@@ -806,8 +816,8 @@ test_expect_success '"status" after file replacement should be clean with UC=tru
test-tool dump-untracked-cache >../actual &&
grep -F "recurse valid" ../actual >../actual.grep &&
cat >../expect.grep <<EOF &&
-/ 0000000000000000000000000000000000000000 recurse valid
-/two/ 0000000000000000000000000000000000000000 recurse valid
+/ $ZERO_OID recurse valid
+/two/ $ZERO_OID recurse valid
EOF
status_is_clean &&
test_cmp ../expect.grep ../actual.grep
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 97be0d968d..22161b3b2d 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -37,17 +37,23 @@ test_expect_success 'creating initial files and commits' '
echo "2nd line 1st file" >>first &&
git commit -a -m "modify 1st file" &&
+ head5p2=$(git rev-parse --verify HEAD) &&
+ head5p2f=$(git rev-parse --short HEAD:first) &&
git rm first &&
git mv second secondfile &&
git commit -a -m "remove 1st and rename 2nd" &&
+ head5p1=$(git rev-parse --verify HEAD) &&
+ head5p1s=$(git rev-parse --short HEAD:secondfile) &&
echo "1st line 2nd file" >secondfile &&
echo "2nd line 2nd file" >>secondfile &&
# "git commit -m" would break MinGW, as Windows refuse to pass
# $test_encoding encoded parameter to git.
commit_msg $test_encoding | git -c "i18n.commitEncoding=$test_encoding" commit -a -F - &&
- head5=$(git rev-parse --verify HEAD)
+ head5=$(git rev-parse --verify HEAD) &&
+ head5s=$(git rev-parse --short HEAD:secondfile) &&
+ head5sl=$(git rev-parse HEAD:secondfile)
'
# git log --pretty=oneline # to see those SHA1 involved
@@ -94,7 +100,7 @@ test_expect_success 'giving a non existing revision should fail' '
test_expect_success 'reset --soft with unmerged index should fail' '
touch .git/MERGE_HEAD &&
- echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" |
+ echo "100644 $head5sl 1 un" |
git update-index --index-info &&
test_must_fail git reset --soft HEAD &&
rm .git/MERGE_HEAD &&
@@ -192,7 +198,7 @@ test_expect_success \
>.diff_expect
cat >.cached_expect <<EOF
diff --git a/secondfile b/secondfile
-index 1bbba79..44c5b58 100644
+index $head5p1s..$head5s 100644
--- a/secondfile
+++ b/secondfile
@@ -1 +1,2 @@
@@ -207,7 +213,7 @@ secondfile:
EOF
test_expect_success '--soft reset only should show changes in diff --cached' '
git reset --soft HEAD^ &&
- check_changes d1a4bc3abce4829628ae2dcb0d60ef3d1a78b1c4 &&
+ check_changes $head5p1 &&
test "$(git rev-parse ORIG_HEAD)" = \
$head5
'
@@ -242,7 +248,7 @@ EOF
test_expect_success \
'--hard reset should change the files and undo commits permanently' '
git reset --hard HEAD~2 &&
- check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+ check_changes $head5p2 &&
test "$(git rev-parse ORIG_HEAD)" = \
$head4
'
@@ -251,7 +257,7 @@ test_expect_success \
cat >.cached_expect <<EOF
diff --git a/first b/first
deleted file mode 100644
-index 8206c22..0000000
+index $head5p2f..0000000
--- a/first
+++ /dev/null
@@ -1,2 +0,0 @@
@@ -259,14 +265,14 @@ index 8206c22..0000000
-2nd line 1st file
diff --git a/second b/second
deleted file mode 100644
-index 1bbba79..0000000
+index $head5p1s..0000000
--- a/second
+++ /dev/null
@@ -1 +0,0 @@
-2nd file
diff --git a/secondfile b/secondfile
new file mode 100644
-index 0000000..44c5b58
+index 0000000..$head5s
--- /dev/null
+++ b/secondfile
@@ -0,0 +1,2 @@
@@ -286,13 +292,13 @@ test_expect_success \
echo "1st line 2nd file" >secondfile &&
echo "2nd line 2nd file" >>secondfile &&
git add secondfile &&
- check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+ check_changes $head5p2
'
cat >.diff_expect <<EOF
diff --git a/first b/first
deleted file mode 100644
-index 8206c22..0000000
+index $head5p2f..0000000
--- a/first
+++ /dev/null
@@ -1,2 +0,0 @@
@@ -300,7 +306,7 @@ index 8206c22..0000000
-2nd line 1st file
diff --git a/second b/second
deleted file mode 100644
-index 1bbba79..0000000
+index $head5p1s..0000000
--- a/second
+++ /dev/null
@@ -1 +0,0 @@
@@ -314,9 +320,8 @@ secondfile:
EOF
test_expect_success '--mixed reset to HEAD should unadd the files' '
git reset &&
- check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
- test "$(git rev-parse ORIG_HEAD)" = \
- ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+ check_changes $head5p2 &&
+ test "$(git rev-parse ORIG_HEAD)" = $head5p2
'
>.diff_expect
@@ -328,7 +333,7 @@ secondfile:
EOF
test_expect_success 'redoing the last two commits should succeed' '
git add secondfile &&
- git reset --hard ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+ git reset --hard $head5p2 &&
git rm first &&
git mv second secondfile &&
@@ -389,47 +394,55 @@ test_expect_success \
check_changes $head5
'
-cat > expect << EOF
-diff --git a/file1 b/file1
-index d00491f..7ed6ff8 100644
---- a/file1
-+++ b/file1
-@@ -1 +1 @@
--1
-+5
-diff --git a/file2 b/file2
-deleted file mode 100644
-index 0cfbf08..0000000
---- a/file2
-+++ /dev/null
-@@ -1 +0,0 @@
--2
-EOF
-cat > cached_expect << EOF
-diff --git a/file4 b/file4
-new file mode 100644
-index 0000000..b8626c4
---- /dev/null
-+++ b/file4
-@@ -0,0 +1 @@
-+4
-EOF
test_expect_success 'test --mixed <paths>' '
echo 1 > file1 &&
echo 2 > file2 &&
git add file1 file2 &&
test_tick &&
git commit -m files &&
+ before1=$(git rev-parse --short HEAD:file1) &&
+ before2=$(git rev-parse --short HEAD:file2) &&
git rm file2 &&
echo 3 > file3 &&
echo 4 > file4 &&
echo 5 > file1 &&
+ after1=$(git rev-parse --short $(git hash-object file1)) &&
+ after4=$(git rev-parse --short $(git hash-object file4)) &&
git add file1 file3 file4 &&
git reset HEAD -- file1 file2 file3 &&
test_must_fail git diff --quiet &&
git diff > output &&
+
+ cat > expect <<-EOF &&
+ diff --git a/file1 b/file1
+ index $before1..$after1 100644
+ --- a/file1
+ +++ b/file1
+ @@ -1 +1 @@
+ -1
+ +5
+ diff --git a/file2 b/file2
+ deleted file mode 100644
+ index $before2..0000000
+ --- a/file2
+ +++ /dev/null
+ @@ -1 +0,0 @@
+ -2
+ EOF
+
test_cmp expect output &&
git diff --cached > output &&
+
+ cat > cached_expect <<-EOF &&
+ diff --git a/file4 b/file4
+ new file mode 100644
+ index 0000000..$after4
+ --- /dev/null
+ +++ b/file4
+ @@ -0,0 +1 @@
+ +4
+ EOF
+
test_cmp cached_expect output
'
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
index cad3a9de9e..15ccb14f7e 100755
--- a/t/t7107-reset-pathspec-file.sh
+++ b/t/t7107-reset-pathspec-file.sh
@@ -22,7 +22,12 @@ restore_checkpoint () {
verify_expect () {
git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
- test_cmp expect actual
+ if test "x$1" = 'x!'
+ then
+ ! test_cmp expect actual
+ else
+ test_cmp expect actual
+ fi
}
test_expect_success '--pathspec-from-file from stdin' '
@@ -131,7 +136,7 @@ test_expect_success 'quotes not compatible with --pathspec-file-nul' '
cat >expect <<-\EOF &&
D fileA.t
EOF
- test_must_fail verify_expect
+ verify_expect !
'
test_expect_success 'only touches what was listed' '
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index b696bae5f5..4d62b9b00f 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -230,9 +230,10 @@ test_expect_success 'switch to another branch while carrying a deletion' '
test_expect_success 'checkout to detach HEAD (with advice declined)' '
git config advice.detachedHead false &&
+ rev=$(git rev-parse --short renamer^) &&
git checkout -f renamer && git clean -f &&
git checkout renamer^ 2>messages &&
- test_i18ngrep "HEAD is now at 7329388" messages &&
+ test_i18ngrep "HEAD is now at $rev" messages &&
test_line_count = 1 messages &&
H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) &&
@@ -248,9 +249,10 @@ test_expect_success 'checkout to detach HEAD (with advice declined)' '
test_expect_success 'checkout to detach HEAD' '
git config advice.detachedHead true &&
+ rev=$(git rev-parse --short renamer^) &&
git checkout -f renamer && git clean -f &&
GIT_TEST_GETTEXT_POISON=false git checkout renamer^ 2>messages &&
- grep "HEAD is now at 7329388" messages &&
+ grep "HEAD is now at $rev" messages &&
test_line_count -gt 1 messages &&
H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) &&
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 956e17abb3..fec7e0299d 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -1231,7 +1231,7 @@ test_expect_success 'submodule helper list is not confused by common prefixes' '
git submodule add /dir1/b dir1/b &&
git submodule add /dir2/b dir2/b &&
git commit -m "first submodule commit" &&
- git submodule--helper list dir1/b |cut -c51- >actual &&
+ git submodule--helper list dir1/b | cut -f 2 >actual &&
echo "dir1/b" >expect &&
test_cmp expect actual
'
@@ -1260,7 +1260,7 @@ test_expect_success 'submodule update --init with a specification' '
pwd=$(pwd) &&
git clone file://"$pwd"/multisuper multisuper_clone &&
git -C multisuper_clone submodule update --init . ":(exclude)sub0" &&
- git -C multisuper_clone submodule status |cut -c 1,43- >actual &&
+ git -C multisuper_clone submodule status | sed "s/$OID_REGEX //" >actual &&
test_cmp expect actual
'
@@ -1271,7 +1271,7 @@ test_expect_success 'submodule update --init with submodule.active set' '
git -C multisuper_clone config submodule.active "." &&
git -C multisuper_clone config --add submodule.active ":(exclude)sub0" &&
git -C multisuper_clone submodule update --init &&
- git -C multisuper_clone submodule status |cut -c 1,43- >actual &&
+ git -C multisuper_clone submodule status | sed "s/$OID_REGEX //" >actual &&
test_cmp expect actual
'
@@ -1290,7 +1290,7 @@ test_expect_success 'submodule update and setting submodule.<name>.active' '
-sub3
EOF
git -C multisuper_clone submodule update &&
- git -C multisuper_clone submodule status |cut -c 1,43- >actual &&
+ git -C multisuper_clone submodule status | sed "s/$OID_REGEX //" >actual &&
test_cmp expect actual
'
@@ -1307,12 +1307,12 @@ test_expect_success 'clone active submodule without submodule url set' '
git submodule update &&
git submodule status >actual_raw &&
- cut -c 1,43- actual_raw >actual &&
+ cut -d" " -f3- actual_raw >actual &&
cat >expect <<-\EOF &&
- sub0 (test2)
- sub1 (test2)
- sub2 (test2)
- sub3 (test2)
+ sub0 (test2)
+ sub1 (test2)
+ sub2 (test2)
+ sub3 (test2)
EOF
test_cmp expect actual
)
@@ -1328,7 +1328,7 @@ test_expect_success 'clone --recurse-submodules with a pathspec works' '
EOF
git clone --recurse-submodules="sub0" multisuper multisuper_clone &&
- git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+ git -C multisuper_clone submodule status | sed "s/$OID_REGEX //" >actual &&
test_cmp expected actual
'
@@ -1345,7 +1345,7 @@ test_expect_success 'clone with multiple --recurse-submodules options' '
--recurse-submodules=":(exclude)sub0" \
--recurse-submodules=":(exclude)sub2" \
multisuper multisuper_clone &&
- git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+ git -C multisuper_clone submodule status | sed "s/$OID_REGEX //" >actual &&
test_cmp expect actual
'
@@ -1373,7 +1373,7 @@ test_expect_success 'clone and subsequent updates correctly auto-initialize subm
--recurse-submodules=":(exclude)sub4" \
multisuper multisuper_clone &&
- git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+ git -C multisuper_clone submodule status | sed "s/$OID_REGEX //" >actual &&
test_cmp expect actual &&
git -C multisuper submodule add ../sub1 sub4 &&
@@ -1382,7 +1382,7 @@ test_expect_success 'clone and subsequent updates correctly auto-initialize subm
# obtain the new superproject
git -C multisuper_clone pull &&
git -C multisuper_clone submodule update --init &&
- git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+ git -C multisuper_clone submodule status | sed "s/$OID_REGEX //" >actual &&
test_cmp expect2 actual
'
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 9bc841d085..7608814708 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -5,9 +5,18 @@
test_description='Summary support for submodules
-This test tries to verify the sanity of summary subcommand of git submodule.
+This test script tries to verify the sanity of summary subcommand of git submodule.
'
+# NOTE: This test script uses 'git add' instead of 'git submodule add' to add
+# submodules to the superproject. Some submodule subcommands such as init and
+# deinit might not work as expected in this script. t7421 does not have this
+# caveat.
+#
+# NEEDSWORK: This test script is old fashioned and may need a big cleanup due to
+# various reasons, one of them being that there are lots of commands taking place
+# outside of 'test_expect_success' block, which is no longer in good-style.
+
. ./test-lib.sh
add_file () {
@@ -16,12 +25,12 @@ add_file () {
owd=$(pwd)
cd "$sm"
for name; do
- echo "$name" > "$name" &&
+ echo "$name" >"$name" &&
git add "$name" &&
test_tick &&
git commit -m "Add $name"
done >/dev/null
- git rev-parse --verify HEAD | cut -c1-7
+ git rev-parse --short HEAD
cd "$owd"
}
commit_file () {
@@ -38,10 +47,10 @@ test_expect_success 'added submodule' "
git add sm1 &&
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 0000000...$head1 (2):
- > Add foo2
+ * sm1 0000000...$head1 (2):
+ > Add foo2
-EOF
+ EOF
test_cmp expected actual
"
@@ -52,10 +61,10 @@ test_expect_success 'added submodule (subdirectory)' "
git submodule summary >../actual
) &&
cat >expected <<-EOF &&
-* ../sm1 0000000...$head1 (2):
- > Add foo2
+ * ../sm1 0000000...$head1 (2):
+ > Add foo2
-EOF
+ EOF
test_cmp expected actual
"
@@ -73,10 +82,10 @@ test_expect_success 'added submodule (subdirectory with explicit path)' "
git submodule summary ../sm1 >../actual
) &&
cat >expected <<-EOF &&
-* ../sm1 0000000...$head1 (2):
- > Add foo2
+ * ../sm1 0000000...$head1 (2):
+ > Add foo2
-EOF
+ EOF
test_cmp expected actual
"
@@ -86,20 +95,20 @@ head2=$(add_file sm1 foo3)
test_expect_success 'modified submodule(forward)' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head1...$head2 (1):
- > Add foo3
+ * sm1 $head1...$head2 (1):
+ > Add foo3
-EOF
+ EOF
test_cmp expected actual
"
test_expect_success 'modified submodule(forward), --files' "
git submodule summary --files >actual &&
cat >expected <<-EOF &&
-* sm1 $head1...$head2 (1):
- > Add foo3
+ * sm1 $head1...$head2 (1):
+ > Add foo3
-EOF
+ EOF
test_cmp expected actual
"
@@ -110,10 +119,10 @@ test_expect_success 'no ignore=all setting has any effect' "
git config diff.ignoreSubmodules all &&
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head1...$head2 (1):
- > Add foo3
+ * sm1 $head1...$head2 (1):
+ > Add foo3
-EOF
+ EOF
test_cmp expected actual &&
git config --unset diff.ignoreSubmodules &&
git config --remove-section submodule.sm1 &&
@@ -125,17 +134,17 @@ commit_file sm1 &&
head3=$(
cd sm1 &&
git reset --hard HEAD~2 >/dev/null &&
- git rev-parse --verify HEAD | cut -c1-7
+ git rev-parse --short HEAD
)
test_expect_success 'modified submodule(backward)' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head2...$head3 (2):
- < Add foo3
- < Add foo2
+ * sm1 $head2...$head3 (2):
+ < Add foo3
+ < Add foo2
-EOF
+ EOF
test_cmp expected actual
"
@@ -144,25 +153,25 @@ head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
test_expect_success 'modified submodule(backward and forward)' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head2...$head4 (4):
- > Add foo5
- > Add foo4
- < Add foo3
- < Add foo2
+ * sm1 $head2...$head4 (4):
+ > Add foo5
+ > Add foo4
+ < Add foo3
+ < Add foo2
-EOF
+ EOF
test_cmp expected actual
"
test_expect_success '--summary-limit' "
git submodule summary -n 3 >actual &&
cat >expected <<-EOF &&
-* sm1 $head2...$head4 (4):
- > Add foo5
- > Add foo4
- < Add foo3
+ * sm1 $head2...$head4 (4):
+ > Add foo5
+ > Add foo4
+ < Add foo3
-EOF
+ EOF
test_cmp expected actual
"
@@ -177,21 +186,21 @@ mv sm1-bak sm1
test_expect_success 'typechanged submodule(submodule->blob), --cached' "
git submodule summary --cached >actual &&
cat >expected <<-EOF &&
-* sm1 $head4(submodule)->$head5(blob) (3):
- < Add foo5
+ * sm1 $head4(submodule)->$head5(blob) (3):
+ < Add foo5
-EOF
- test_i18ncmp actual expected
+ EOF
+ test_i18ncmp expected actual
"
test_expect_success 'typechanged submodule(submodule->blob), --files' "
git submodule summary --files >actual &&
cat >expected <<-EOF &&
-* sm1 $head5(blob)->$head4(submodule) (3):
- > Add foo5
+ * sm1 $head5(blob)->$head4(submodule) (3):
+ > Add foo5
-EOF
- test_i18ncmp actual expected
+ EOF
+ test_i18ncmp expected actual
"
rm -rf sm1 &&
@@ -199,10 +208,10 @@ git checkout-index sm1
test_expect_success 'typechanged submodule(submodule->blob)' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head4(submodule)->$head5(blob):
+ * sm1 $head4(submodule)->$head5(blob):
-EOF
- test_i18ncmp actual expected
+ EOF
+ test_i18ncmp expected actual
"
rm -f sm1 &&
@@ -211,21 +220,21 @@ head6=$(add_file sm1 foo6 foo7)
test_expect_success 'nonexistent commit' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head4...$head6:
- Warn: sm1 doesn't contain commit $head4_full
+ * sm1 $head4...$head6:
+ Warn: sm1 doesn't contain commit $head4_full
-EOF
- test_i18ncmp actual expected
+ EOF
+ test_i18ncmp expected actual
"
commit_file
test_expect_success 'typechanged submodule(blob->submodule)' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head5(blob)->$head6(submodule) (2):
- > Add foo7
+ * sm1 $head5(blob)->$head6(submodule) (2):
+ > Add foo7
-EOF
+ EOF
test_i18ncmp expected actual
"
@@ -234,9 +243,9 @@ rm -rf sm1
test_expect_success 'deleted submodule' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head6...0000000:
+ * sm1 $head6...0000000:
-EOF
+ EOF
test_cmp expected actual
"
@@ -249,22 +258,22 @@ test_expect_success 'create second submodule' '
test_expect_success 'multiple submodules' "
git submodule summary >actual &&
cat >expected <<-EOF &&
-* sm1 $head6...0000000:
+ * sm1 $head6...0000000:
-* sm2 0000000...$head7 (2):
- > Add foo9
+ * sm2 0000000...$head7 (2):
+ > Add foo9
-EOF
+ EOF
test_cmp expected actual
"
test_expect_success 'path filter' "
git submodule summary sm2 >actual &&
cat >expected <<-EOF &&
-* sm2 0000000...$head7 (2):
- > Add foo9
+ * sm2 0000000...$head7 (2):
+ > Add foo9
-EOF
+ EOF
test_cmp expected actual
"
@@ -272,24 +281,24 @@ commit_file sm2
test_expect_success 'given commit' "
git submodule summary HEAD^ >actual &&
cat >expected <<-EOF &&
-* sm1 $head6...0000000:
+ * sm1 $head6...0000000:
-* sm2 0000000...$head7 (2):
- > Add foo9
+ * sm2 0000000...$head7 (2):
+ > Add foo9
-EOF
+ EOF
test_cmp expected actual
"
test_expect_success '--for-status' "
git submodule summary --for-status HEAD^ >actual &&
- test_i18ncmp actual - <<EOF
-* sm1 $head6...0000000:
+ test_i18ncmp - actual <<-EOF
+ * sm1 $head6...0000000:
-* sm2 0000000...$head7 (2):
- > Add foo9
+ * sm2 0000000...$head7 (2):
+ > Add foo9
-EOF
+ EOF
"
test_expect_success 'fail when using --files together with --cached' "
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
new file mode 100755
index 0000000000..b070f13714
--- /dev/null
+++ b/t/t7421-submodule-summary-add.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (C) 2020 Shourya Shukla
+#
+
+test_description='Summary support for submodules, adding them using git submodule add
+
+This test script tries to verify the sanity of summary subcommand of git submodule
+while making sure to add submodules using `git submodule add` instead of
+`git add` as done in t7401.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'summary test environment setup' '
+ git init sm &&
+ test_commit -C sm "add file" file file-content file-tag &&
+
+ git submodule add ./sm my-subm &&
+ test_tick &&
+ git commit -m "add submodule"
+'
+
+test_expect_success 'submodule summary output for initialized submodule' '
+ test_commit -C sm "add file2" file2 file2-content file2-tag &&
+ git submodule update --remote &&
+ test_tick &&
+ git commit -m "update submodule" my-subm &&
+ git submodule summary HEAD^ >actual &&
+ rev1=$(git -C sm rev-parse --short HEAD^) &&
+ rev2=$(git -C sm rev-parse --short HEAD) &&
+ cat >expected <<-EOF &&
+ * my-subm ${rev1}...${rev2} (1):
+ > add file2
+
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'submodule summary output for deinitialized submodule' '
+ git submodule deinit my-subm &&
+ git submodule summary HEAD^ >actual &&
+ test_must_be_empty actual &&
+ git submodule update --init my-subm &&
+ git submodule summary HEAD^ >actual &&
+ rev1=$(git -C sm rev-parse --short HEAD^) &&
+ rev2=$(git -C sm rev-parse --short HEAD) &&
+ cat >expected <<-EOF &&
+ * my-subm ${rev1}...${rev2} (1):
+ > add file2
+
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'submodule summary output for submodules with changed paths' '
+ git mv my-subm subm &&
+ git commit -m "change submodule path" &&
+ rev=$(git -C sm rev-parse --short HEAD^) &&
+ git submodule summary HEAD^^ -- my-subm >actual 2>err &&
+ test_must_be_empty err &&
+ cat >expected <<-EOF &&
+ * my-subm ${rev}...0000000:
+
+ EOF
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 08629a6e70..3fcb44767f 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -22,6 +22,10 @@ sanitize_output () {
mv output2 output
}
+sanitize_diff () {
+ sed -e "/^index [0-9a-f,]*\.\.[0-9a-f]*/d" "$1"
+}
+
test_expect_success 'setup' '
test_create_repo_with_commit sub &&
@@ -269,7 +273,6 @@ short_sha1_merge_sub1=$(cd sub1 && git rev-parse --short HEAD)
short_sha1_merge_sub2=$(cd sub2 && git rev-parse --short HEAD)
cat >diff_expect <<\EOF
diff --cc .gitmodules
-index badaa4c,44f999a..0000000
--- a/.gitmodules
+++ b/.gitmodules
@@@ -1,3 -1,3 +1,9 @@@
@@ -286,7 +289,6 @@ EOF
cat >diff_submodule_expect <<\EOF
diff --cc .gitmodules
-index badaa4c,44f999a..0000000
--- a/.gitmodules
+++ b/.gitmodules
@@@ -1,3 -1,3 +1,9 @@@
@@ -306,7 +308,8 @@ test_expect_success 'diff with merge conflict in .gitmodules' '
cd super &&
git diff >../diff_actual 2>&1
) &&
- test_cmp diff_expect diff_actual
+ sanitize_diff diff_actual >diff_sanitized &&
+ test_cmp diff_expect diff_sanitized
'
test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
@@ -314,7 +317,8 @@ test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
cd super &&
git diff --submodule >../diff_submodule_actual 2>&1
) &&
- test_cmp diff_submodule_expect diff_submodule_actual
+ sanitize_diff diff_submodule_actual >diff_sanitized &&
+ test_cmp diff_submodule_expect diff_sanitized
'
# We'll setup different cases for further testing:
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 8e969f3e36..963fed6821 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -814,6 +814,33 @@ test_expect_success 'status -s without relative paths' '
'
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+A "file with spaces"
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? "file with spaces 2"
+?? untracked
+EOF
+
+test_expect_success 'status -s without relative paths' '
+ test_when_finished "git rm --cached \"file with spaces\"; rm -f file*" &&
+ >"file with spaces" &&
+ >"file with spaces 2" &&
+ >"expect with spaces" &&
+ git add "file with spaces" &&
+
+ git status -s >output &&
+ test_cmp expect output &&
+
+ git status -s --ignored >output &&
+ grep "^!! \"expect with spaces\"$" output &&
+ grep -v "^!! " output >output-wo-ignored &&
+ test_cmp expect output-wo-ignored
+'
+
test_expect_success 'dry-run of partial commit excluding new file in index' '
cat >expect <<EOF &&
On branch master
@@ -837,7 +864,7 @@ EOF
'
cat >expect <<EOF
-:100644 100644 $EMPTY_BLOB 0000000000000000000000000000000000000000 M dir1/modified
+:100644 100644 $EMPTY_BLOB $ZERO_OID M dir1/modified
EOF
test_expect_success 'status refreshes the index' '
touch dir2/added &&
@@ -846,6 +873,18 @@ test_expect_success 'status refreshes the index' '
test_cmp expect output
'
+test_expect_success 'status shows detached HEAD properly after checking out non-local upstream branch' '
+ test_when_finished rm -rf upstream downstream actual &&
+
+ test_create_repo upstream &&
+ test_commit -C upstream foo &&
+
+ git clone upstream downstream &&
+ git -C downstream checkout @{u} &&
+ git -C downstream status >actual &&
+ test_i18ngrep "HEAD detached at [0-9a-f]\\+" actual
+'
+
test_expect_success 'setup status submodule summary' '
test_create_repo sm && (
cd sm &&
diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh
index b22f631261..dc3e9c8c88 100755
--- a/t/t7518-ident-corner-cases.sh
+++ b/t/t7518-ident-corner-cases.sh
@@ -29,7 +29,18 @@ test_expect_success 'empty configured name does not auto-detect' '
sane_unset GIT_AUTHOR_NAME &&
test_must_fail \
git -c user.name= commit --allow-empty -m foo 2>err &&
- test_i18ngrep "empty ident name" err
+ test_i18ngrep "empty ident name" err &&
+ test_i18ngrep "Author identity unknown" err
+ )
+'
+
+test_expect_success 'empty configured name does not auto-detect for committer' '
+ (
+ sane_unset GIT_COMMITTER_NAME &&
+ test_must_fail \
+ git -c user.name= commit --allow-empty -m foo 2>err &&
+ test_i18ngrep "empty ident name" err &&
+ test_i18ngrep "Committer identity unknown" err
)
'
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 1d45f9a4ed..1c85f75555 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -14,9 +14,9 @@ Testing basic merge operations/option parsing.
! [c4] c4
! [c5] c5
! [c6] c6
- * [master] Merge commit 'c1' into master
+ * [master] Merge commit 'c1'
--------
- - [master] Merge commit 'c1' into master
+ - [master] Merge commit 'c1'
+ * [c1] commit 1
+ [c6] c6
+ [c5] c5
@@ -44,8 +44,8 @@ test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9
test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z
create_merge_msgs () {
- echo "Merge tag 'c2' into master" >msg.1-5 &&
- echo "Merge tags 'c2' and 'c3' into master" >msg.1-5-9 &&
+ echo "Merge tag 'c2'" >msg.1-5 &&
+ echo "Merge tags 'c2' and 'c3'" >msg.1-5-9 &&
{
echo "Squashed commit of the following:" &&
echo &&
@@ -246,7 +246,7 @@ test_expect_success 'merge --squash c3 with c7' '
# file
EOF
git cat-file commit HEAD >raw &&
- sed -e '1,/^$/d' raw >actual &&
+ sed -e "1,/^$/d" raw >actual &&
test_cmp expect actual
'
@@ -258,7 +258,7 @@ test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '
git commit --no-edit -a &&
cat >expect <<-\EOF &&
- Merge tag '"'"'c7'"'"' into master
+ Merge tag '"'"'c7'"'"'
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
@@ -268,7 +268,7 @@ test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '
# file
EOF
git cat-file commit HEAD >raw &&
- sed -e '1,/^$/d' raw >actual &&
+ sed -e "1,/^$/d" raw >actual &&
test_i18ncmp expect actual
'
@@ -292,7 +292,7 @@ test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '
# file
EOF
git cat-file commit HEAD >raw &&
- sed -e '1,/^$/d' raw >actual &&
+ sed -e "1,/^$/d" raw >actual &&
test_i18ncmp expect actual
'
@@ -808,10 +808,10 @@ test_expect_success 'merge with conflicted --autostash changes' '
'
cat >expected.branch <<\EOF
-Merge branch 'c5-branch' (early part) into master
+Merge branch 'c5-branch' (early part)
EOF
cat >expected.tag <<\EOF
-Merge commit 'c5~1' into master
+Merge commit 'c5~1'
EOF
test_expect_success 'merge early part of c2' '
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 0f97828cd0..c5c4ea5fc0 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -33,11 +33,18 @@ test_expect_success 'pull.rebase not set' '
test_i18ngrep "Pulling without specifying how to reconcile" err
'
+test_expect_success 'pull.rebase not set and pull.ff=true' '
+ git reset --hard c0 &&
+ test_config pull.ff true &&
+ git pull . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
test_expect_success 'pull.rebase not set and pull.ff=false' '
git reset --hard c0 &&
test_config pull.ff false &&
git pull . c1 2>err &&
- test_i18ngrep "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
'
test_expect_success 'pull.rebase not set and pull.ff=only' '
@@ -59,6 +66,18 @@ test_expect_success 'pull.rebase not set and --no-rebase given' '
test_i18ngrep ! "Pulling without specifying how to reconcile" err
'
+test_expect_success 'pull.rebase not set and --ff given' '
+ git reset --hard c0 &&
+ git pull --ff . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --no-ff given' '
+ git reset --hard c0 &&
+ git pull --no-ff . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
test_expect_success 'pull.rebase not set and --ff-only given' '
git reset --hard c0 &&
git pull --ff-only . c1 2>err &&
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
index 2af33f195b..8e7e0a5865 100755
--- a/t/t7608-merge-messages.sh
+++ b/t/t7608-merge-messages.sh
@@ -16,7 +16,7 @@ test_expect_success 'merge local branch' '
git checkout master &&
test_commit master-2 &&
git merge local-branch &&
- check_oneline "Merge branch Qlocal-branchQ into master"
+ check_oneline "Merge branch Qlocal-branchQ"
'
test_expect_success 'merge octopus branches' '
@@ -26,7 +26,7 @@ test_expect_success 'merge octopus branches' '
test_commit octopus-2 &&
git checkout master &&
git merge octopus-a octopus-b &&
- check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ into master"
+ check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ"
'
test_expect_success 'merge tag' '
@@ -35,7 +35,7 @@ test_expect_success 'merge tag' '
git checkout master &&
test_commit master-3 &&
git merge tag-1 &&
- check_oneline "Merge tag Qtag-1Q into master"
+ check_oneline "Merge tag Qtag-1Q"
'
test_expect_success 'ambiguous tag' '
@@ -44,7 +44,7 @@ test_expect_success 'ambiguous tag' '
git checkout master &&
test_commit master-4 &&
git merge ambiguous &&
- check_oneline "Merge tag QambiguousQ into master"
+ check_oneline "Merge tag QambiguousQ"
'
test_expect_success 'remote-tracking branch' '
@@ -54,7 +54,7 @@ test_expect_success 'remote-tracking branch' '
git checkout master &&
test_commit master-5 &&
git merge origin/master &&
- check_oneline "Merge remote-tracking branch Qorigin/masterQ into master"
+ check_oneline "Merge remote-tracking branch Qorigin/masterQ"
'
test_done
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
new file mode 100755
index 0000000000..53c883531e
--- /dev/null
+++ b/t/t7900-maintenance.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+test_description='git maintenance builtin'
+
+. ./test-lib.sh
+
+GIT_TEST_COMMIT_GRAPH=0
+
+test_expect_success 'help text' '
+ test_expect_code 129 git maintenance -h 2>err &&
+ test_i18ngrep "usage: git maintenance run" err &&
+ test_expect_code 128 git maintenance barf 2>err &&
+ test_i18ngrep "invalid subcommand: barf" err &&
+ test_expect_code 129 git maintenance 2>err &&
+ test_i18ngrep "usage: git maintenance" err
+'
+
+test_expect_success 'run [--auto|--quiet]' '
+ GIT_TRACE2_EVENT="$(pwd)/run-no-auto.txt" \
+ git maintenance run 2>/dev/null &&
+ GIT_TRACE2_EVENT="$(pwd)/run-auto.txt" \
+ git maintenance run --auto 2>/dev/null &&
+ GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
+ git maintenance run --no-quiet 2>/dev/null &&
+ test_subcommand git gc --quiet <run-no-auto.txt &&
+ test_subcommand ! git gc --auto --quiet <run-auto.txt &&
+ test_subcommand git gc --no-quiet <run-no-quiet.txt
+'
+
+test_expect_success 'maintenance.<task>.enabled' '
+ git config maintenance.gc.enabled false &&
+ git config maintenance.commit-graph.enabled true &&
+ GIT_TRACE2_EVENT="$(pwd)/run-config.txt" git maintenance run 2>err &&
+ test_subcommand ! git gc --quiet <run-config.txt &&
+ test_subcommand git commit-graph write --split --reachable --no-progress <run-config.txt
+'
+
+test_expect_success 'run --task=<task>' '
+ GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
+ git maintenance run --task=commit-graph 2>/dev/null &&
+ GIT_TRACE2_EVENT="$(pwd)/run-gc.txt" \
+ git maintenance run --task=gc 2>/dev/null &&
+ GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
+ git maintenance run --task=commit-graph 2>/dev/null &&
+ GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
+ git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
+ test_subcommand ! git gc --quiet <run-commit-graph.txt &&
+ test_subcommand git gc --quiet <run-gc.txt &&
+ test_subcommand git gc --quiet <run-both.txt &&
+ test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
+ test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
+ test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
+'
+
+test_expect_success 'run --task=bogus' '
+ test_must_fail git maintenance run --task=bogus 2>err &&
+ test_i18ngrep "is not a valid task" err
+'
+
+test_expect_success 'run --task duplicate' '
+ test_must_fail git maintenance run --task=gc --task=gc 2>err &&
+ test_i18ngrep "cannot be selected multiple times" err
+'
+
+test_done
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index eea048e52c..015973e8fe 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -6,6 +6,10 @@ test_description='git blame'
PROG='git blame -c'
. "$TEST_DIRECTORY"/annotate-tests.sh
+test_expect_success 'setup' '
+ hexsz=$(test_oid hexsz)
+'
+
test_expect_success 'blame untracked file in empty repo' '
>untracked &&
test_must_fail git blame untracked
@@ -105,21 +109,32 @@ test_expect_success 'blame --abbrev=<n> works' '
'
test_expect_success 'blame -l aligns regular and boundary commits' '
- check_abbrev 40 -l HEAD &&
- check_abbrev 39 -l ^HEAD
+ check_abbrev $hexsz -l HEAD &&
+ check_abbrev $((hexsz - 1)) -l ^HEAD
'
-test_expect_success 'blame --abbrev=40 behaves like -l' '
- check_abbrev 40 --abbrev=40 HEAD &&
- check_abbrev 39 --abbrev=40 ^HEAD
+test_expect_success 'blame --abbrev with full length behaves like -l' '
+ check_abbrev $hexsz --abbrev=$hexsz HEAD &&
+ check_abbrev $((hexsz - 1)) --abbrev=$hexsz ^HEAD
'
-test_expect_success '--no-abbrev works like --abbrev=40' '
- check_abbrev 40 --no-abbrev
+test_expect_success '--no-abbrev works like --abbrev with full length' '
+ check_abbrev $hexsz --no-abbrev
'
test_expect_success '--exclude-promisor-objects does not BUG-crash' '
test_must_fail git blame --exclude-promisor-objects one
'
+test_expect_success 'blame with uncommitted edits in partial clone does not crash' '
+ git init server &&
+ echo foo >server/file.txt &&
+ git -C server add file.txt &&
+ git -C server commit -m file &&
+
+ git clone --filter=blob:none "file://$(pwd)/server" client &&
+ echo bar >>client/file.txt &&
+ git -C client blame file.txt
+'
+
test_done
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index 9130b887d2..ba8013b002 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -6,7 +6,6 @@ test_description='git blame corner cases'
pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
test_expect_success setup '
-
echo A A A A A >one &&
echo B B B B B >two &&
echo C C C C C >tres &&
@@ -274,18 +273,14 @@ test_expect_success 'blame file with CRLF core.autocrlf=true' '
grep "A U Thor" actual
'
-# Tests the splitting and merging of blame entries in blame_coalesce().
-# The output of blame is the same, regardless of whether blame_coalesce() runs
-# or not, so we'd likely only notice a problem if blame crashes or assigned
-# blame to the "splitting" commit ('SPLIT' below).
-test_expect_success 'blame coalesce' '
+test_expect_success 'setup coalesce tests' '
cat >giraffe <<-\EOF &&
ABC
DEF
EOF
git add giraffe &&
git commit -m "original file" &&
- oid=$(git rev-parse HEAD) &&
+ orig=$(git rev-parse HEAD) &&
cat >giraffe <<-\EOF &&
ABC
@@ -294,6 +289,7 @@ test_expect_success 'blame coalesce' '
EOF
git add giraffe &&
git commit -m "interior SPLIT line" &&
+ split=$(git rev-parse HEAD) &&
cat >giraffe <<-\EOF &&
ABC
@@ -301,12 +297,25 @@ test_expect_success 'blame coalesce' '
EOF
git add giraffe &&
git commit -m "same contents as original" &&
+ final=$(git rev-parse HEAD)
+'
+
+test_expect_success 'blame coalesce' '
+ cat >expect <<-EOF &&
+ $orig 1 1 2
+ $orig 2 2
+ EOF
+ git blame --porcelain $final giraffe >actual.raw &&
+ grep "^$orig" actual.raw >actual &&
+ test_cmp expect actual
+'
+test_expect_success 'blame does not coalesce non-adjacent result lines' '
cat >expect <<-EOF &&
- $oid 1) ABC
- $oid 2) DEF
+ $orig 1) ABC
+ $orig 3) DEF
EOF
- git -c core.abbrev=40 blame -s giraffe >actual &&
+ git blame --no-abbrev -s -L1,1 -L3,3 $split giraffe >actual &&
test_cmp expect actual
'
diff --git a/t/t8011-blame-split-file.sh b/t/t8011-blame-split-file.sh
index 831125047b..bdda0c03fe 100755
--- a/t/t8011-blame-split-file.sh
+++ b/t/t8011-blame-split-file.sh
@@ -54,7 +54,7 @@ test_expect_success 'setup simulated porcelain' '
cat >read-porcelain.pl <<-\EOF
my $field = shift;
while (<>) {
- if (/^[0-9a-f]{40} /) {
+ if (/^[0-9a-f]{40,} /) {
flush();
$hash = $&;
} elsif (/^$field (.*)/) {
diff --git a/t/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index 36dc31eb39..24ae5018e8 100755
--- a/t/t8013-blame-ignore-revs.sh
+++ b/t/t8013-blame-ignore-revs.sh
@@ -21,6 +21,7 @@ test_expect_success setup '
test_tick &&
git commit -m X &&
git tag X &&
+ git tag -a -m "X (annotated)" XT &&
git blame --line-porcelain file >blame_raw &&
@@ -31,20 +32,36 @@ test_expect_success setup '
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
git rev-parse X >expect &&
test_cmp expect actual
+'
+
+# Ensure bogus --ignore-rev requests are caught
+test_expect_success 'validate --ignore-rev' '
+ test_must_fail git blame --ignore-rev X^{tree} file
+'
+
+# Ensure bogus --ignore-revs-file requests are caught
+test_expect_success 'validate --ignore-revs-file' '
+ git rev-parse X^{tree} >ignore_x &&
+ test_must_fail git blame --ignore-revs-file ignore_x file
+'
+
+for I in X XT
+do
+ # Ignore X (or XT), make sure A is blamed for line 1 and B for line 2.
+ # Giving X (i.e. commit) and XT (i.e. annotated tag to commit) should
+ # produce the same result.
+ test_expect_success "ignore_rev_changing_lines ($I)" '
+ git blame --line-porcelain --ignore-rev $I file >blame_raw &&
+
+ grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ git rev-parse A >expect &&
+ test_cmp expect actual &&
+
+ grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ git rev-parse B >expect &&
+ test_cmp expect actual
'
-
-# Ignore X, make sure A is blamed for line 1 and B for line 2.
-test_expect_success ignore_rev_changing_lines '
- git blame --line-porcelain --ignore-rev X file >blame_raw &&
-
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
- git rev-parse A >expect &&
- test_cmp expect actual &&
-
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
- git rev-parse B >expect &&
- test_cmp expect actual
- '
+done
# For ignored revs that have added 'unblamable' lines, attribute those to the
# ignored commit.
@@ -67,7 +84,7 @@ test_expect_success ignore_rev_adding_unblamable_lines '
grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
test_cmp expect actual
- '
+'
# Ignore X and Y, both in separate files. Lines 1 == A, 2 == B.
test_expect_success ignore_revs_from_files '
@@ -82,7 +99,7 @@ test_expect_success ignore_revs_from_files '
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
git rev-parse B >expect &&
test_cmp expect actual
- '
+'
# Ignore X from the config option, Y from a file.
test_expect_success ignore_revs_from_configs_and_files '
@@ -96,7 +113,7 @@ test_expect_success ignore_revs_from_configs_and_files '
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
git rev-parse B >expect &&
test_cmp expect actual
- '
+'
# Override blame.ignoreRevsFile (ignore_x) with an empty string. X should be
# blamed now for lines 1 and 2, since we are no longer ignoring X.
@@ -120,7 +137,7 @@ test_expect_success bad_files_and_revs '
echo NOREV >ignore_norev &&
test_must_fail git blame file --ignore-revs-file ignore_norev 2>err &&
test_i18ngrep "invalid object name: NOREV" err
- '
+'
# For ignored revs that have added 'unblamable' lines, mark those lines with a
# '*'
@@ -138,7 +155,7 @@ test_expect_success mark_unblamable_lines '
sed -n "4p" blame_raw | cut -c1 >actual &&
test_cmp expect actual
- '
+'
# Commit Z will touch the first two lines. Y touched all four.
# A--B--X--Y--Z
@@ -171,7 +188,7 @@ test_expect_success mark_ignored_lines '
sed -n "4p" blame_raw | cut -c1 >actual &&
! test_cmp expect actual
- '
+'
# For ignored revs that added 'unblamable' lines and more recent commits changed
# the blamable lines, mark the unblamable lines with a
@@ -190,7 +207,7 @@ test_expect_success mark_unblamable_lines_intermediate '
sed -n "4p" blame_raw | cut -c1 >actual &&
test_cmp expect actual
- '
+'
# The heuristic called by guess_line_blames() tries to find the size of a
# blame_entry 'e' in the parent's address space. Those calculations need to
@@ -227,7 +244,7 @@ test_expect_success ignored_chunk_negative_parent_size '
git tag C &&
git blame file --ignore-rev B >blame_raw
- '
+'
# Resetting the repo and creating:
#
@@ -269,6 +286,6 @@ test_expect_success ignore_merge '
grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
git rev-parse C >expect &&
test_cmp expect actual
- '
+'
test_done
diff --git a/t/t8014-blame-ignore-fuzzy.sh b/t/t8014-blame-ignore-fuzzy.sh
index 6e61882b6f..e68e6115a6 100755
--- a/t/t8014-blame-ignore-fuzzy.sh
+++ b/t/t8014-blame-ignore-fuzzy.sh
@@ -248,7 +248,7 @@ Final
EOF
# The first line of b matches best with the last line of a, but the overall
-# match is better if we match it with the the first line of a.
+# match is better if we match it with the first line of a.
title11="Piggy in the middle"
cat <<EOF >a11
abcdefg
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index ec261085ec..a08f72596a 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1551,7 +1551,7 @@ test_expect_success $PREREQ '8-bit and sendemail.transferencoding=quoted-printab
--smtp-server="$(pwd)/fake.sendmail" \
email-using-8bit \
2>errors >out &&
- sed '1,/^$/d' msgtxt1 >actual &&
+ sed "1,/^$/d" msgtxt1 >actual &&
test_cmp expected actual
'
@@ -1568,7 +1568,7 @@ test_expect_success $PREREQ '8-bit and sendemail.transferencoding=base64' '
--smtp-server="$(pwd)/fake.sendmail" \
email-using-8bit \
2>errors >out &&
- sed '1,/^$/d' msgtxt1 >actual &&
+ sed "1,/^$/d" msgtxt1 >actual &&
test_cmp expected actual
'
@@ -1594,7 +1594,7 @@ test_expect_success $PREREQ 'convert from quoted-printable to base64' '
--smtp-server="$(pwd)/fake.sendmail" \
email-using-qp \
2>errors >out &&
- sed '1,/^$/d' msgtxt1 >actual &&
+ sed "1,/^$/d" msgtxt1 >actual &&
test_cmp expected actual
'
@@ -1624,7 +1624,7 @@ test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=quoted-printabl
--smtp-server="$(pwd)/fake.sendmail" \
email-using-crlf \
2>errors >out &&
- sed '1,/^$/d' msgtxt1 >actual &&
+ sed "1,/^$/d" msgtxt1 >actual &&
test_cmp expected actual
'
@@ -1641,7 +1641,7 @@ test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=base64' '
--smtp-server="$(pwd)/fake.sendmail" \
email-using-crlf \
2>errors >out &&
- sed '1,/^$/d' msgtxt1 >actual &&
+ sed "1,/^$/d" msgtxt1 >actual &&
test_cmp expected actual
'
@@ -2142,4 +2142,33 @@ test_expect_success $PREREQ 'test that send-email works outside a repo' '
"$(pwd)/0001-add-master.patch"
'
+test_expect_success $PREREQ 'test that sendmail config is rejected' '
+ test_config sendmail.program sendmail &&
+ test_must_fail git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ HEAD^ 2>err &&
+ test_i18ngrep "found configuration options for '"'"sendmail"'"'" err
+'
+
+test_expect_success $PREREQ 'test that sendmail config rejection is specific' '
+ test_config resendmail.program sendmail &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ HEAD^
+'
+
+test_expect_success $PREREQ 'test forbidSendmailVariables behavior override' '
+ test_config sendmail.program sendmail &&
+ test_config sendemail.forbidSendmailVariables false &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ HEAD^
+'
+
test_done
diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh
deleted file mode 100755
index 83f8f5cacb..0000000000
--- a/t/t9010-svn-fe.sh
+++ /dev/null
@@ -1,1105 +0,0 @@
-#!/bin/sh
-
-test_description='check svn dumpfile importer'
-
-. ./test-lib.sh
-
-if test_have_prereq !PIPE
-then
- skip_all="svn dumpfile importer testing requires the PIPE prerequisite"
- test_done
-fi
-
-reinit_git () {
- rm -fr .git &&
- rm -f stream backflow &&
- git init &&
- mkfifo stream backflow
-}
-
-try_dump () {
- input=$1 &&
- maybe_fail_svnfe=${2:+test_$2} &&
- maybe_fail_fi=${3:+test_$3} &&
-
- {
- $maybe_fail_svnfe test-svn-fe "$input" >stream 3<backflow &
- } &&
- $maybe_fail_fi git fast-import --cat-blob-fd=3 <stream 3>backflow &&
- wait $!
-}
-
-properties () {
- while test "$#" -ne 0
- do
- property="$1" &&
- value="$2" &&
- printf "%s\n" "K ${#property}" &&
- printf "%s\n" "$property" &&
- printf "%s\n" "V ${#value}" &&
- printf "%s\n" "$value" &&
- shift 2 ||
- return 1
- done
-}
-
-text_no_props () {
- text="$1
-" &&
- printf "%s\n" "Prop-content-length: 10" &&
- printf "%s\n" "Text-content-length: ${#text}" &&
- printf "%s\n" "Content-length: $((${#text} + 10))" &&
- printf "%s\n" "" "PROPS-END" &&
- printf "%s\n" "$text"
-}
-
-test_expect_success 'empty dump' '
- reinit_git &&
- echo "SVN-fs-dump-format-version: 2" >input &&
- try_dump input
-'
-
-test_expect_success 'v4 dumps not supported' '
- reinit_git &&
- echo "SVN-fs-dump-format-version: 4" >v4.dump &&
- try_dump v4.dump must_fail
-'
-
-test_expect_failure 'empty revision' '
- reinit_git &&
- printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
- cat >emptyrev.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 0
- Content-length: 0
-
- Revision-number: 2
- Prop-content-length: 0
- Content-length: 0
-
- EOF
- try_dump emptyrev.dump &&
- git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'empty properties' '
- reinit_git &&
- printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
- cat >emptyprop.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Revision-number: 2
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
- EOF
- try_dump emptyprop.dump &&
- git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'author name and commit message' '
- reinit_git &&
- echo "<author@example.com, author@example.com@local>" >expect.author &&
- cat >message <<-\EOF &&
- A concise summary of the change
-
- A detailed description of the change, why it is needed, what
- was broken and why applying this is the best course of action.
-
- * file.c
- Details pertaining to an individual file.
- EOF
- {
- properties \
- svn:author author@example.com \
- svn:log "$(cat message)" &&
- echo PROPS-END
- } >props &&
- {
- echo "SVN-fs-dump-format-version: 3" &&
- echo &&
- echo "Revision-number: 1" &&
- echo Prop-content-length: $(wc -c <props) &&
- echo Content-length: $(wc -c <props) &&
- echo &&
- cat props
- } >log.dump &&
- try_dump log.dump &&
- git log -p --format="%B" HEAD >actual.log &&
- git log --format="<%an, %ae>" >actual.author &&
- test_cmp message actual.log &&
- test_cmp expect.author actual.author
-'
-
-test_expect_success 'unsupported properties are ignored' '
- reinit_git &&
- echo author >expect &&
- cat >extraprop.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 56
- Content-length: 56
-
- K 8
- nonsense
- V 1
- y
- K 10
- svn:author
- V 6
- author
- PROPS-END
- EOF
- try_dump extraprop.dump &&
- git log -p --format=%an HEAD >actual &&
- test_cmp expect actual
-'
-
-test_expect_failure 'timestamp and empty file' '
- echo author@example.com >expect.author &&
- echo 1999-01-01 >expect.date &&
- echo file >expect.files &&
- reinit_git &&
- {
- properties \
- svn:author author@example.com \
- svn:date "1999-01-01T00:01:002.000000Z" \
- svn:log "add empty file" &&
- echo PROPS-END
- } >props &&
- {
- cat <<-EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- EOF
- echo Prop-content-length: $(wc -c <props) &&
- echo Content-length: $(wc -c <props) &&
- echo &&
- cat props &&
- cat <<-\EOF
-
- Node-path: empty-file
- Node-kind: file
- Node-action: add
- Content-length: 0
-
- EOF
- } >emptyfile.dump &&
- try_dump emptyfile.dump &&
- git log --format=%an HEAD >actual.author &&
- git log --date=short --format=%ad HEAD >actual.date &&
- git ls-tree -r --name-only HEAD >actual.files &&
- test_cmp expect.author actual.author &&
- test_cmp expect.date actual.date &&
- test_cmp expect.files actual.files &&
- git checkout HEAD empty-file &&
- test_must_be_empty file
-'
-
-test_expect_success 'directory with files' '
- reinit_git &&
- printf "%s\n" directory/file1 directory/file2 >expect.files &&
- echo hi >hi &&
- echo hello >hello &&
- {
- properties \
- svn:author author@example.com \
- svn:date "1999-02-01T00:01:002.000000Z" \
- svn:log "add directory with some files in it" &&
- echo PROPS-END
- } >props &&
- {
- cat <<-EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- EOF
- echo Prop-content-length: $(wc -c <props) &&
- echo Content-length: $(wc -c <props) &&
- echo &&
- cat props &&
- cat <<-\EOF &&
-
- Node-path: directory
- Node-kind: dir
- Node-action: add
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: directory/file1
- Node-kind: file
- Node-action: add
- EOF
- text_no_props hello &&
- cat <<-\EOF &&
- Node-path: directory/file2
- Node-kind: file
- Node-action: add
- EOF
- text_no_props hi
- } >directory.dump &&
- try_dump directory.dump &&
-
- git ls-tree -r --name-only HEAD >actual.files &&
- git checkout HEAD directory &&
- test_cmp expect.files actual.files &&
- test_cmp hello directory/file1 &&
- test_cmp hi directory/file2
-'
-
-test_expect_success 'branch name with backslash' '
- reinit_git &&
- sort <<-\EOF >expect.branch-files &&
- trunk/file1
- trunk/file2
- "branches/UpdateFOPto094\\/file1"
- "branches/UpdateFOPto094\\/file2"
- EOF
-
- echo hi >hi &&
- echo hello >hello &&
- {
- properties \
- svn:author author@example.com \
- svn:date "1999-02-02T00:01:02.000000Z" \
- svn:log "add directory with some files in it" &&
- echo PROPS-END
- } >props.setup &&
- {
- properties \
- svn:author brancher@example.com \
- svn:date "2007-12-06T21:38:34.000000Z" \
- svn:log "Updating fop to .94 and adjust fo-stylesheets" &&
- echo PROPS-END
- } >props.branch &&
- {
- cat <<-EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- EOF
- echo Prop-content-length: $(wc -c <props.setup) &&
- echo Content-length: $(wc -c <props.setup) &&
- echo &&
- cat props.setup &&
- cat <<-\EOF &&
-
- Node-path: trunk
- Node-kind: dir
- Node-action: add
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: branches
- Node-kind: dir
- Node-action: add
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: trunk/file1
- Node-kind: file
- Node-action: add
- EOF
- text_no_props hello &&
- cat <<-\EOF &&
- Node-path: trunk/file2
- Node-kind: file
- Node-action: add
- EOF
- text_no_props hi &&
- cat <<-\EOF &&
-
- Revision-number: 2
- EOF
- echo Prop-content-length: $(wc -c <props.branch) &&
- echo Content-length: $(wc -c <props.branch) &&
- echo &&
- cat props.branch &&
- cat <<-\EOF
-
- Node-path: branches/UpdateFOPto094\
- Node-kind: dir
- Node-action: add
- Node-copyfrom-rev: 1
- Node-copyfrom-path: trunk
-
- Node-kind: dir
- Node-action: add
- Prop-content-length: 34
- Content-length: 34
-
- K 13
- svn:mergeinfo
- V 0
-
- PROPS-END
- EOF
- } >branch.dump &&
- try_dump branch.dump &&
-
- git ls-tree -r --name-only HEAD |
- sort >actual.branch-files &&
- test_cmp expect.branch-files actual.branch-files
-'
-
-test_expect_success 'node without action' '
- reinit_git &&
- cat >inaction.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: directory
- Node-kind: dir
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
- EOF
- try_dump inaction.dump must_fail
-'
-
-test_expect_success 'action: add node without text' '
- reinit_git &&
- cat >textless.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: textless
- Node-kind: file
- Node-action: add
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
- EOF
- try_dump textless.dump must_fail
-'
-
-test_expect_failure 'change file mode but keep old content' '
- reinit_git &&
- cat >expect <<-\EOF &&
- OBJID
- :120000 100644 OBJID OBJID T greeting
- OBJID
- :100644 120000 OBJID OBJID T greeting
- OBJID
- :000000 100644 OBJID OBJID A greeting
- EOF
- echo "link hello" >expect.blob &&
- echo hello >hello &&
- cat >filemode.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: add
- Prop-content-length: 10
- Text-content-length: 11
- Content-length: 21
-
- PROPS-END
- link hello
-
- Revision-number: 2
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: change
- Prop-content-length: 33
- Content-length: 33
-
- K 11
- svn:special
- V 1
- *
- PROPS-END
-
- Revision-number: 3
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: change
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
- EOF
- try_dump filemode.dump &&
- {
- git rev-list HEAD |
- git diff-tree --root --stdin |
- sed "s/$OID_REGEX/OBJID/g"
- } >actual &&
- git show HEAD:greeting >actual.blob &&
- git show HEAD^:greeting >actual.target &&
- test_cmp expect actual &&
- test_cmp expect.blob actual.blob &&
- test_cmp hello actual.target
-'
-
-test_expect_success 'NUL in property value' '
- reinit_git &&
- echo "commit message" >expect.message &&
- {
- properties \
- unimportant "something with a NUL (Q)" \
- svn:log "commit message" &&
- echo PROPS-END
- } |
- q_to_nul >props &&
- {
- cat <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- EOF
- echo Prop-content-length: $(wc -c <props) &&
- echo Content-length: $(wc -c <props) &&
- echo &&
- cat props
- } >nulprop.dump &&
- try_dump nulprop.dump &&
- git diff-tree --always -s --format=%s HEAD >actual.message &&
- test_cmp expect.message actual.message
-'
-
-test_expect_success 'NUL in log message, file content, and property name' '
- # Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the
- # svn:specialQnotreally example.
- reinit_git &&
- cat >expect <<-\EOF &&
- OBJID
- :100644 100644 OBJID OBJID M greeting
- OBJID
- :000000 100644 OBJID OBJID A greeting
- EOF
- printf "\n%s\n" "something with an ASCII NUL (Q)" >expect.message &&
- printf "%s\n" "helQo" >expect.hello1 &&
- printf "%s\n" "link hello" >expect.hello2 &&
- {
- properties svn:log "something with an ASCII NUL (Q)" &&
- echo PROPS-END
- } |
- q_to_nul >props &&
- {
- q_to_nul <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: add
- Prop-content-length: 10
- Text-content-length: 6
- Content-length: 16
-
- PROPS-END
- helQo
-
- Revision-number: 2
- EOF
- echo Prop-content-length: $(wc -c <props) &&
- echo Content-length: $(wc -c <props) &&
- echo &&
- cat props &&
- q_to_nul <<-\EOF
-
- Node-path: greeting
- Node-kind: file
- Node-action: change
- Prop-content-length: 43
- Text-content-length: 11
- Content-length: 54
-
- K 21
- svn:specialQnotreally
- V 1
- *
- PROPS-END
- link hello
- EOF
- } >8bitclean.dump &&
- try_dump 8bitclean.dump &&
- {
- git rev-list HEAD |
- git diff-tree --root --stdin |
- sed "s/$OID_REGEX/OBJID/g"
- } >actual &&
- {
- git cat-file commit HEAD | nul_to_q &&
- echo
- } |
- sed -ne "/^\$/,\$ p" >actual.message &&
- git cat-file blob HEAD^:greeting | nul_to_q >actual.hello1 &&
- git cat-file blob HEAD:greeting | nul_to_q >actual.hello2 &&
- test_cmp expect actual &&
- test_cmp expect.message actual.message &&
- test_cmp expect.hello1 actual.hello1 &&
- test_cmp expect.hello2 actual.hello2
-'
-
-test_expect_success 'change file mode and reiterate content' '
- reinit_git &&
- cat >expect <<-\EOF &&
- OBJID
- :120000 100644 OBJID OBJID T greeting
- OBJID
- :100644 120000 OBJID OBJID T greeting
- OBJID
- :000000 100644 OBJID OBJID A greeting
- EOF
- echo "link hello" >expect.blob &&
- echo hello >hello &&
- cat >filemode2.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: add
- Prop-content-length: 10
- Text-content-length: 11
- Content-length: 21
-
- PROPS-END
- link hello
-
- Revision-number: 2
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: change
- Prop-content-length: 33
- Text-content-length: 11
- Content-length: 44
-
- K 11
- svn:special
- V 1
- *
- PROPS-END
- link hello
-
- Revision-number: 3
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: change
- Prop-content-length: 10
- Text-content-length: 11
- Content-length: 21
-
- PROPS-END
- link hello
- EOF
- try_dump filemode2.dump &&
- {
- git rev-list HEAD |
- git diff-tree --root --stdin |
- sed "s/$OID_REGEX/OBJID/g"
- } >actual &&
- git show HEAD:greeting >actual.blob &&
- git show HEAD^:greeting >actual.target &&
- test_cmp expect actual &&
- test_cmp expect.blob actual.blob &&
- test_cmp hello actual.target
-'
-
-test_expect_success 'deltas supported' '
- reinit_git &&
- {
- # (old) h + (inline) ello + (old) \n
- printf "SVNQ%b%b%s" "Q\003\006\005\004" "\001Q\0204\001\002" "ello" |
- q_to_nul
- } >delta &&
- {
- properties \
- svn:author author@example.com \
- svn:date "1999-01-05T00:01:002.000000Z" \
- svn:log "add greeting" &&
- echo PROPS-END
- } >props &&
- {
- properties \
- svn:author author@example.com \
- svn:date "1999-01-06T00:01:002.000000Z" \
- svn:log "change it" &&
- echo PROPS-END
- } >props2 &&
- {
- echo SVN-fs-dump-format-version: 3 &&
- echo &&
- echo Revision-number: 1 &&
- echo Prop-content-length: $(wc -c <props) &&
- echo Content-length: $(wc -c <props) &&
- echo &&
- cat props &&
- cat <<-\EOF &&
-
- Node-path: hello
- Node-kind: file
- Node-action: add
- Prop-content-length: 10
- Text-content-length: 3
- Content-length: 13
-
- PROPS-END
- hi
-
- EOF
- echo Revision-number: 2 &&
- echo Prop-content-length: $(wc -c <props2) &&
- echo Content-length: $(wc -c <props2) &&
- echo &&
- cat props2 &&
- cat <<-\EOF &&
-
- Node-path: hello
- Node-kind: file
- Node-action: change
- Text-delta: true
- Prop-content-length: 10
- EOF
- echo Text-content-length: $(wc -c <delta) &&
- echo Content-length: $((10 + $(wc -c <delta))) &&
- echo &&
- echo PROPS-END &&
- cat delta
- } >delta.dump &&
- try_dump delta.dump
-'
-
-test_expect_success 'property deltas supported' '
- reinit_git &&
- cat >expect <<-\EOF &&
- OBJID
- :100755 100644 OBJID OBJID M script.sh
- EOF
- {
- properties \
- svn:author author@example.com \
- svn:date "1999-03-06T00:01:002.000000Z" \
- svn:log "make an executable, or chmod -x it" &&
- echo PROPS-END
- } >revprops &&
- {
- echo SVN-fs-dump-format-version: 3 &&
- echo &&
- echo Revision-number: 1 &&
- echo Prop-content-length: $(wc -c <revprops) &&
- echo Content-length: $(wc -c <revprops) &&
- echo &&
- cat revprops &&
- echo &&
- cat <<-\EOF &&
- Node-path: script.sh
- Node-kind: file
- Node-action: add
- Text-content-length: 0
- Prop-content-length: 39
- Content-length: 39
-
- K 14
- svn:executable
- V 4
- true
- PROPS-END
-
- EOF
- echo Revision-number: 2 &&
- echo Prop-content-length: $(wc -c <revprops) &&
- echo Content-length: $(wc -c <revprops) &&
- echo &&
- cat revprops &&
- echo &&
- cat <<-\EOF
- Node-path: script.sh
- Node-kind: file
- Node-action: change
- Prop-delta: true
- Prop-content-length: 30
- Content-length: 30
-
- D 14
- svn:executable
- PROPS-END
- EOF
- } >propdelta.dump &&
- try_dump propdelta.dump &&
- {
- git rev-list HEAD |
- git diff-tree --stdin |
- sed "s/$OID_REGEX/OBJID/g"
- } >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'properties on /' '
- reinit_git &&
- cat <<-\EOF >expect &&
- OBJID
- OBJID
- :000000 100644 OBJID OBJID A greeting
- EOF
- sed -e "s/X$//" <<-\EOF >changeroot.dump &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: greeting
- Node-kind: file
- Node-action: add
- Text-content-length: 0
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Revision-number: 2
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: X
- Node-kind: dir
- Node-action: change
- Prop-delta: true
- Prop-content-length: 43
- Content-length: 43
-
- K 10
- svn:ignore
- V 11
- build-area
-
- PROPS-END
- EOF
- try_dump changeroot.dump &&
- {
- git rev-list HEAD |
- git diff-tree --root --always --stdin |
- sed "s/$OID_REGEX/OBJID/g"
- } >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'deltas for typechange' '
- reinit_git &&
- cat >expect <<-\EOF &&
- OBJID
- :120000 100644 OBJID OBJID T test-file
- OBJID
- :100755 120000 OBJID OBJID T test-file
- OBJID
- :000000 100755 OBJID OBJID A test-file
- EOF
- cat >deleteprop.dump <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: test-file
- Node-kind: file
- Node-action: add
- Prop-delta: true
- Prop-content-length: 35
- Text-content-length: 17
- Content-length: 52
-
- K 14
- svn:executable
- V 0
-
- PROPS-END
- link testing 123
-
- Revision-number: 2
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: test-file
- Node-kind: file
- Node-action: change
- Prop-delta: true
- Prop-content-length: 53
- Text-content-length: 17
- Content-length: 70
-
- K 11
- svn:special
- V 1
- *
- D 14
- svn:executable
- PROPS-END
- link testing 231
-
- Revision-number: 3
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: test-file
- Node-kind: file
- Node-action: change
- Prop-delta: true
- Prop-content-length: 27
- Text-content-length: 17
- Content-length: 44
-
- D 11
- svn:special
- PROPS-END
- link testing 321
- EOF
- try_dump deleteprop.dump &&
- {
- git rev-list HEAD |
- git diff-tree --root --stdin |
- sed "s/$OID_REGEX/OBJID/g"
- } >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'deltas need not consume the whole preimage' '
- reinit_git &&
- cat >expect <<-\EOF &&
- OBJID
- :120000 100644 OBJID OBJID T postimage
- OBJID
- :100644 120000 OBJID OBJID T postimage
- OBJID
- :000000 100644 OBJID OBJID A postimage
- EOF
- echo "first preimage" >expect.1 &&
- printf target >expect.2 &&
- printf lnk >expect.3 &&
- {
- printf "SVNQ%b%b%b" "QQ\017\001\017" "\0217" "first preimage\n" |
- q_to_nul
- } >delta.1 &&
- {
- properties svn:special "*" &&
- echo PROPS-END
- } >symlink.props &&
- {
- printf "SVNQ%b%b%b" "Q\002\013\004\012" "\0201\001\001\0211" "lnk target" |
- q_to_nul
- } >delta.2 &&
- {
- printf "SVNQ%b%b" "Q\004\003\004Q" "\001Q\002\002" |
- q_to_nul
- } >delta.3 &&
- {
- cat <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: postimage
- Node-kind: file
- Node-action: add
- Text-delta: true
- Prop-content-length: 10
- EOF
- echo Text-content-length: $(wc -c <delta.1) &&
- echo Content-length: $((10 + $(wc -c <delta.1))) &&
- echo &&
- echo PROPS-END &&
- cat delta.1 &&
- cat <<-\EOF &&
-
- Revision-number: 2
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: postimage
- Node-kind: file
- Node-action: change
- Text-delta: true
- EOF
- echo Prop-content-length: $(wc -c <symlink.props) &&
- echo Text-content-length: $(wc -c <delta.2) &&
- echo Content-length: $(($(wc -c <symlink.props) + $(wc -c <delta.2))) &&
- echo &&
- cat symlink.props &&
- cat delta.2 &&
- cat <<-\EOF &&
-
- Revision-number: 3
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: postimage
- Node-kind: file
- Node-action: change
- Text-delta: true
- Prop-content-length: 10
- EOF
- echo Text-content-length: $(wc -c <delta.3) &&
- echo Content-length: $((10 + $(wc -c <delta.3))) &&
- echo &&
- echo PROPS-END &&
- cat delta.3 &&
- echo
- } >deltapartial.dump &&
- try_dump deltapartial.dump &&
- {
- git rev-list HEAD |
- git diff-tree --root --stdin |
- sed "s/$OID_REGEX/OBJID/g"
- } >actual &&
- test_cmp expect actual &&
- git show HEAD:postimage >actual.3 &&
- git show HEAD^:postimage >actual.2 &&
- git show HEAD^^:postimage >actual.1 &&
- test_cmp expect.1 actual.1 &&
- test_cmp expect.2 actual.2 &&
- test_cmp expect.3 actual.3
-'
-
-test_expect_success 'no hang for delta trying to read past end of preimage' '
- reinit_git &&
- {
- # COPY 1
- printf "SVNQ%b%b" "Q\001\001\002Q" "\001Q" |
- q_to_nul
- } >greedy.delta &&
- {
- cat <<-\EOF &&
- SVN-fs-dump-format-version: 3
-
- Revision-number: 1
- Prop-content-length: 10
- Content-length: 10
-
- PROPS-END
-
- Node-path: bootstrap
- Node-kind: file
- Node-action: add
- Text-delta: true
- Prop-content-length: 10
- EOF
- echo Text-content-length: $(wc -c <greedy.delta) &&
- echo Content-length: $((10 + $(wc -c <greedy.delta))) &&
- echo &&
- echo PROPS-END &&
- cat greedy.delta &&
- echo
- } >greedydelta.dump &&
- try_dump greedydelta.dump must_fail might_fail
-'
-
-test_expect_success 'set up svn repo' '
- svnconf=$PWD/svnconf &&
- mkdir -p "$svnconf" &&
-
- if
- svnadmin -h >/dev/null 2>&1 &&
- svnadmin create simple-svn &&
- svnadmin load simple-svn <"$TEST_DIRECTORY/t9135/svn.dump" &&
- svn export --config-dir "$svnconf" "file://$PWD/simple-svn" simple-svnco
- then
- test_set_prereq SVNREPO
- fi
-'
-
-test_expect_success SVNREPO 't9135/svn.dump' '
- mkdir -p simple-git &&
- (
- cd simple-git &&
- reinit_git &&
- try_dump "$TEST_DIRECTORY/t9135/svn.dump"
- ) &&
- (
- cd simple-svnco &&
- git init &&
- git add . &&
- git fetch ../simple-git master &&
- git diff --exit-code FETCH_HEAD
- )
-'
-
-test_done
diff --git a/t/t9011-svn-da.sh b/t/t9011-svn-da.sh
deleted file mode 100755
index ab1ef28fd9..0000000000
--- a/t/t9011-svn-da.sh
+++ /dev/null
@@ -1,248 +0,0 @@
-#!/bin/sh
-
-test_description='test parsing of svndiff0 files
-
-Using the "test-svn-fe -d" helper, check that svn-fe correctly
-interprets deltas using various facilities (some from the spec,
-some only learned from practice).
-'
-. ./test-lib.sh
-
->empty
-printf foo >preimage
-
-test_expect_success 'reject empty delta' '
- test_must_fail test-svn-fe -d preimage empty 0
-'
-
-test_expect_success 'delta can empty file' '
- printf "SVNQ" | q_to_nul >clear.delta &&
- test-svn-fe -d preimage clear.delta 4 >actual &&
- test_must_be_empty actual
-'
-
-test_expect_success 'reject svndiff2' '
- printf "SVN\002" >bad.filetype &&
- test_must_fail test-svn-fe -d preimage bad.filetype 4
-'
-
-test_expect_success 'one-window empty delta' '
- printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
- test-svn-fe -d preimage clear.onewindow 9 >actual &&
- test_must_be_empty actual
-'
-
-test_expect_success 'reject incomplete window header' '
- printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
- printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
- test_must_fail test-svn-fe -d preimage clear.onewindow 6 &&
- test_must_fail test-svn-fe -d preimage clear.partialwindow 6
-'
-
-test_expect_success 'reject declared delta longer than actual delta' '
- printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
- printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
- test_must_fail test-svn-fe -d preimage clear.onewindow 14 &&
- test_must_fail test-svn-fe -d preimage clear.partialwindow 9
-'
-
-test_expect_success 'two-window empty delta' '
- printf "SVNQ%s%s" "QQQQQ" "QQQQQ" | q_to_nul >clear.twowindow &&
- test-svn-fe -d preimage clear.twowindow 14 >actual &&
- test_must_fail test-svn-fe -d preimage clear.twowindow 13 &&
- test_must_be_empty actual
-'
-
-test_expect_success 'noisy zeroes' '
- printf "SVNQ%s" \
- "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQ" |
- tr R "\200" |
- q_to_nul >clear.noisy &&
- len=$(wc -c <clear.noisy) &&
- test-svn-fe -d preimage clear.noisy $len &&
- test_must_be_empty actual
-'
-
-test_expect_success 'reject variable-length int in magic' '
- printf "SVNRQ" | tr R "\200" | q_to_nul >clear.badmagic &&
- test_must_fail test-svn-fe -d preimage clear.badmagic 5
-'
-
-test_expect_success 'reject truncated integer' '
- printf "SVNQ%s%s" "QQQQQ" "QQQQRRQ" |
- tr R "\200" |
- q_to_nul >clear.fullint &&
- printf "SVNQ%s%s" "QQQQQ" "QQQQRR" |
- tr RT "\201" |
- q_to_nul >clear.partialint &&
- test_must_fail test-svn-fe -d preimage clear.fullint 15 &&
- test-svn-fe -d preimage clear.fullint 16 &&
- test_must_fail test-svn-fe -d preimage clear.partialint 15
-'
-
-test_expect_success 'nonempty (but unused) preimage view' '
- printf "SVNQ%b" "Q\003QQQ" | q_to_nul >clear.readpreimage &&
- test-svn-fe -d preimage clear.readpreimage 9 >actual &&
- test_must_be_empty actual
-'
-
-test_expect_success 'preimage view: right endpoint cannot backtrack' '
- printf "SVNQ%b%b" "Q\003QQQ" "Q\002QQQ" |
- q_to_nul >clear.backtrack &&
- test_must_fail test-svn-fe -d preimage clear.backtrack 14
-'
-
-test_expect_success 'preimage view: left endpoint can advance' '
- printf "SVNQ%b%b" "Q\003QQQ" "\001\002QQQ" |
- q_to_nul >clear.preshrink &&
- printf "SVNQ%b%b" "Q\003QQQ" "\001\001QQQ" |
- q_to_nul >clear.shrinkbacktrack &&
- test-svn-fe -d preimage clear.preshrink 14 >actual &&
- test_must_fail test-svn-fe -d preimage clear.shrinkbacktrack 14 &&
- test_must_be_empty actual
-'
-
-test_expect_success 'preimage view: offsets compared by value' '
- printf "SVNQ%b%b" "\001\001QQQ" "\0200Q\003QQQ" |
- q_to_nul >clear.noisybacktrack &&
- printf "SVNQ%b%b" "\001\001QQQ" "\0200\001\002QQQ" |
- q_to_nul >clear.noisyadvance &&
- test_must_fail test-svn-fe -d preimage clear.noisybacktrack 15 &&
- test-svn-fe -d preimage clear.noisyadvance 15 &&
- test_must_be_empty actual
-'
-
-test_expect_success 'preimage view: reject truncated preimage' '
- printf "SVNQ%b" "\010QQQQ" | q_to_nul >clear.lateemptyread &&
- printf "SVNQ%b" "\010\001QQQ" | q_to_nul >clear.latenonemptyread &&
- printf "SVNQ%b" "\001\010QQQ" | q_to_nul >clear.longread &&
- test_must_fail test-svn-fe -d preimage clear.lateemptyread 9 &&
- test_must_fail test-svn-fe -d preimage clear.latenonemptyread 9 &&
- test_must_fail test-svn-fe -d preimage clear.longread 9
-'
-
-test_expect_success 'forbid unconsumed inline data' '
- printf "SVNQ%b%s%b%s" "QQQQ\003" "bar" "QQQQ\001" "x" |
- q_to_nul >inline.clear &&
- test_must_fail test-svn-fe -d preimage inline.clear 18 >actual
-'
-
-test_expect_success 'reject truncated inline data' '
- printf "SVNQ%b%s" "QQQQ\003" "b" | q_to_nul >inline.trunc &&
- test_must_fail test-svn-fe -d preimage inline.trunc 10
-'
-
-test_expect_success 'reject truncated inline data (after instruction section)' '
- printf "SVNQ%b%b%s" "QQ\001\001\003" "\0201" "b" | q_to_nul >insn.trunc &&
- test_must_fail test-svn-fe -d preimage insn.trunc 11
-'
-
-test_expect_success 'copyfrom_data' '
- echo hi >expect &&
- printf "SVNQ%b%b%b" "QQ\003\001\003" "\0203" "hi\n" | q_to_nul >copydat &&
- test-svn-fe -d preimage copydat 13 >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'multiple copyfrom_data' '
- echo hi >expect &&
- printf "SVNQ%b%b%b%b%b" "QQ\003\002\003" "\0201\0202" "hi\n" \
- "QQQ\002Q" "\0200Q" | q_to_nul >copy.multi &&
- len=$(wc -c <copy.multi) &&
- test-svn-fe -d preimage copy.multi $len >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'incomplete multiple insn' '
- printf "SVNQ%b%b%b" "QQ\003\002\003" "\0203\0200" "hi\n" |
- q_to_nul >copy.partial &&
- len=$(wc -c <copy.partial) &&
- test_must_fail test-svn-fe -d preimage copy.partial $len
-'
-
-test_expect_success 'catch attempt to copy missing data' '
- printf "SVNQ%b%b%s%b%s" "QQ\002\002\001" "\0201\0201" "X" \
- "QQQQ\002" "YZ" |
- q_to_nul >copy.incomplete &&
- len=$(wc -c <copy.incomplete) &&
- test_must_fail test-svn-fe -d preimage copy.incomplete $len
-'
-
-test_expect_success 'copyfrom target to repeat data' '
- printf foofoo >expect &&
- printf "SVNQ%b%b%s" "QQ\006\004\003" "\0203\0100\003Q" "foo" |
- q_to_nul >copytarget.repeat &&
- len=$(wc -c <copytarget.repeat) &&
- test-svn-fe -d preimage copytarget.repeat $len >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'copyfrom target out of order' '
- printf foooof >expect &&
- printf "SVNQ%b%b%s" \
- "QQ\006\007\003" "\0203\0101\002\0101\001\0101Q" "foo" |
- q_to_nul >copytarget.reverse &&
- len=$(wc -c <copytarget.reverse) &&
- test-svn-fe -d preimage copytarget.reverse $len >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'catch copyfrom future' '
- printf "SVNQ%b%b%s" "QQ\004\004\003" "\0202\0101\002\0201" "XYZ" |
- q_to_nul >copytarget.infuture &&
- len=$(wc -c <copytarget.infuture) &&
- test_must_fail test-svn-fe -d preimage copytarget.infuture $len
-'
-
-test_expect_success 'copy to sustain' '
- printf XYXYXYXYXYXZ >expect &&
- printf "SVNQ%b%b%s" "QQ\014\004\003" "\0202\0111Q\0201" "XYZ" |
- q_to_nul >copytarget.sustain &&
- len=$(wc -c <copytarget.sustain) &&
- test-svn-fe -d preimage copytarget.sustain $len >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'catch copy that overflows' '
- printf "SVNQ%b%b%s" "QQ\003\003\001" "\0201\0177Q" X |
- q_to_nul >copytarget.overflow &&
- len=$(wc -c <copytarget.overflow) &&
- test_must_fail test-svn-fe -d preimage copytarget.overflow $len
-'
-
-test_expect_success 'copyfrom source' '
- printf foo >expect &&
- printf "SVNQ%b%b" "Q\003\003\002Q" "\003Q" | q_to_nul >copysource.all &&
- test-svn-fe -d preimage copysource.all 11 >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'copy backwards' '
- printf oof >expect &&
- printf "SVNQ%b%b" "Q\003\003\006Q" "\001\002\001\001\001Q" |
- q_to_nul >copysource.rev &&
- test-svn-fe -d preimage copysource.rev 15 >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'offsets are relative to window' '
- printf fo >expect &&
- printf "SVNQ%b%b%b%b" "Q\003\001\002Q" "\001Q" \
- "\002\001\001\002Q" "\001Q" |
- q_to_nul >copysource.two &&
- test-svn-fe -d preimage copysource.two 18 >actual &&
- test_cmp expect actual
-'
-
-test_expect_success 'example from notes/svndiff' '
- printf aaaaccccdddddddd >expect &&
- printf aaaabbbbcccc >source &&
- printf "SVNQ%b%b%s" "Q\014\020\007\001" \
- "\004Q\004\010\0201\0107\010" d |
- q_to_nul >delta.example &&
- len=$(wc -c <delta.example) &&
- test-svn-fe -d source delta.example $len >actual &&
- test_cmp expect actual
-'
-
-test_done
diff --git a/t/t9020-remote-svn.sh b/t/t9020-remote-svn.sh
deleted file mode 100755
index 754c4a3284..0000000000
--- a/t/t9020-remote-svn.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/sh
-
-test_description='tests remote-svn'
-
-. ./test-lib.sh
-
-MARKSPATH=.git/info/fast-import/remote-svn
-
-if ! test_have_prereq PYTHON
-then
- skip_all='skipping remote-svn tests, python not available'
- test_done
-fi
-
-# Override svnrdump with our simulator
-PATH="$HOME:$PATH"
-export PATH PYTHON_PATH GIT_BUILD_DIR
-
-write_script "$HOME/svnrdump" <<\EOF
-exec "$PYTHON_PATH" "$GIT_BUILD_DIR/contrib/svn-fe/svnrdump_sim.py" "$@"
-EOF
-
-init_git () {
- rm -fr .git &&
- git init &&
- #git remote add svnsim testsvn::sim:///$TEST_DIRECTORY/t9020/example.svnrdump
- # let's reuse an existing dump file!?
- git remote add svnsim "testsvn::sim://$TEST_DIRECTORY/t9154/svn.dump"
- git remote add svnfile "testsvn::file://$TEST_DIRECTORY/t9154/svn.dump"
-}
-
-if test -e "$GIT_BUILD_DIR/git-remote-testsvn"
-then
- test_set_prereq REMOTE_SVN
-fi
-
-test_debug '
- git --version
- type git
- type svnrdump
-'
-
-test_expect_success REMOTE_SVN 'simple fetch' '
- init_git &&
- git fetch svnsim &&
- test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master &&
- cp .git/refs/remotes/svnsim/master master.good
-'
-
-test_debug '
- git show-ref -s refs/svn/svnsim/master
- git show-ref -s refs/remotes/svnsim/master
-'
-
-test_expect_success REMOTE_SVN 'repeated fetch, nothing shall change' '
- git fetch svnsim &&
- test_cmp master.good .git/refs/remotes/svnsim/master
-'
-
-test_expect_success REMOTE_SVN 'fetch from a file:// url gives the same result' '
- git fetch svnfile
-'
-
-test_expect_failure REMOTE_SVN 'the sha1 differ because the git-svn-id line in the commit msg contains the url' '
- test_cmp .git/refs/remotes/svnfile/master .git/refs/remotes/svnsim/master
-'
-
-test_expect_success REMOTE_SVN 'mark-file regeneration' '
- # filter out any other marks, that can not be regenerated. Only up to 3 digit revisions are allowed here
- grep ":[0-9]\{1,3\} " $MARKSPATH/svnsim.marks > $MARKSPATH/svnsim.marks.old &&
- rm $MARKSPATH/svnsim.marks &&
- git fetch svnsim &&
- test_cmp $MARKSPATH/svnsim.marks.old $MARKSPATH/svnsim.marks
-'
-
-test_expect_success REMOTE_SVN 'incremental imports must lead to the same head' '
- SVNRMAX=3 &&
- export SVNRMAX &&
- init_git &&
- git fetch svnsim &&
- test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master &&
- unset SVNRMAX &&
- git fetch svnsim &&
- test_cmp master.good .git/refs/remotes/svnsim/master
-'
-
-test_expect_success REMOTE_SVN 'respects configured default initial branch' '
- git -c init.defaultBranch=trunk remote add -f trunk \
- "testsvn::file://$TEST_DIRECTORY/t9154/svn.dump" &&
- git rev-parse --verify refs/remotes/trunk/trunk
-'
-
-test_debug 'git branch -a'
-
-test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 9f2d19ecc4..e4bb22034e 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -63,16 +63,16 @@ test_expect_success "$name" '
name='detect node change from file to directory #1'
-test_expect_success "$name" "
+test_expect_success "$name" '
mkdir dir/new_file &&
mv dir/file dir/new_file/file &&
mv dir/new_file dir/file &&
git update-index --remove dir/file &&
git update-index --add dir/file/file &&
- git commit -m '$name' &&
+ git commit -m "$name" &&
test_must_fail git svn set-tree --find-copies-harder --rmdir \
remotes/git-svn..mybranch
-"
+'
name='detect node change from directory to file #1'
@@ -200,8 +200,9 @@ GIT_SVN_ID=alt
export GIT_SVN_ID
test_expect_success "$name" \
'git svn init "$svnrepo" && git svn fetch &&
- git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a &&
- git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
+ git log --format="tree %T %s" remotes/git-svn |
+ awk "!seen[\$0]++ { print \$1, \$2 }" >a &&
+ git log --format="tree %T" alt >b &&
test_cmp a b'
name='check imported tree checksums expected tree checksums'
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index e151df81c0..308c1ef42c 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -92,7 +92,7 @@ test_expect_success 'A: create pack from stdin' '
EOF
reset refs/tags/to-be-deleted
- from 0000000000000000000000000000000000000000
+ from $ZERO_OID
tag nested
mark :6
@@ -102,7 +102,7 @@ test_expect_success 'A: create pack from stdin' '
EOF
reset refs/tags/nested
- from 0000000000000000000000000000000000000000
+ from $ZERO_OID
tag nested
mark :7
@@ -284,8 +284,9 @@ test_expect_success 'A: verify pack' '
'
test_expect_success 'A: verify diff' '
+ copy=$(git rev-parse --verify master:file2) &&
cat >expect <<-EOF &&
- :000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A copy-of-file2
+ :000000 100755 $ZERO_OID $copy A copy-of-file2
EOF
git diff-tree -M -r master verify--import-marks >actual &&
compare_diff_raw expect actual &&
@@ -364,7 +365,7 @@ test_expect_success 'B: fail on invalid blob sha1' '
COMMIT
from refs/heads/master
- M 755 0000000000000000000000000000000000000001 zero1
+ M 755 $(echo $ZERO_OID | sed -e "s/0$/1/") zero1
INPUT_END
@@ -528,6 +529,7 @@ test_expect_success 'B: fail on invalid committer (5)' '
test_expect_success 'C: incremental import create pack from stdin' '
newf=$(echo hi newf | git hash-object -w --stdin) &&
oldf=$(git rev-parse --verify master:file2) &&
+ thrf=$(git rev-parse --verify master:file3) &&
test_tick &&
cat >input <<-INPUT_END &&
commit refs/heads/branch
@@ -570,10 +572,11 @@ test_expect_success 'C: verify commit' '
'
test_expect_success 'C: validate rename result' '
+ zero=$ZERO_OID &&
cat >expect <<-EOF &&
- :000000 100755 0000000000000000000000000000000000000000 f1fb5da718392694d0076d677d6d0e364c79b0bc A file2/newf
- :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100 file2 file2/oldf
- :100644 000000 0d92e9f3374ae2947c23aa477cbc68ce598135f1 0000000000000000000000000000000000000000 D file3
+ :000000 100755 $zero $newf A file2/newf
+ :100644 100644 $oldf $oldf R100 file2 file2/oldf
+ :100644 000000 $thrf $zero D file3
EOF
git diff-tree -M -r master branch >actual &&
compare_diff_raw expect actual
@@ -614,9 +617,11 @@ test_expect_success 'D: verify pack' '
'
test_expect_success 'D: validate new files added' '
+ f5id=$(echo "$file5_data" | git hash-object --stdin) &&
+ f6id=$(echo "$file6_data" | git hash-object --stdin) &&
cat >expect <<-EOF &&
- :000000 100755 0000000000000000000000000000000000000000 e74b7d465e52746be2b4bae983670711e6e66657 A newdir/exec.sh
- :000000 100644 0000000000000000000000000000000000000000 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 A newdir/interesting
+ :000000 100755 $ZERO_OID $f6id A newdir/exec.sh
+ :000000 100644 $ZERO_OID $f5id A newdir/interesting
EOF
git diff-tree -M -r branch^ branch >actual &&
compare_diff_raw expect actual
@@ -779,12 +784,13 @@ test_expect_success 'H: verify pack' '
'
test_expect_success 'H: validate old files removed, new files added' '
+ f4id=$(git rev-parse HEAD:file4) &&
cat >expect <<-EOF &&
- :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file2/newf
- :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D file2/oldf
- :100755 000000 85df50785d62d3b05ab03d9cbf7e4a0b49449730 0000000000000000000000000000000000000000 D file4
- :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100 newdir/interesting h/e/l/lo
- :100755 000000 e74b7d465e52746be2b4bae983670711e6e66657 0000000000000000000000000000000000000000 D newdir/exec.sh
+ :100755 000000 $newf $zero D file2/newf
+ :100644 000000 $oldf $zero D file2/oldf
+ :100755 000000 $f4id $zero D file4
+ :100644 100644 $f5id $f5id R100 newdir/interesting h/e/l/lo
+ :100755 000000 $f6id $zero D newdir/exec.sh
EOF
git diff-tree -M -r H^ H >actual &&
compare_diff_raw expect actual
@@ -935,14 +941,15 @@ test_expect_success 'L: verify internal tree sorting' '
INPUT_END
cat >expect <<-EXPECT_END &&
- :100644 100644 4268632... 55d3a52... M b.
- :040000 040000 0ae5cac... 443c768... M b
- :100644 100644 4268632... 55d3a52... M ba
+ :100644 100644 M b.
+ :040000 040000 M b
+ :100644 100644 M ba
EXPECT_END
git fast-import <input &&
GIT_PRINT_SHA1_ELLIPSIS="yes" git diff-tree --abbrev --raw L^ L >output &&
- test_cmp expect output
+ cut -d" " -f1,2,5 output >actual &&
+ test_cmp expect actual
'
test_expect_success 'L: nested tree copy does not corrupt deltas' '
@@ -1004,7 +1011,7 @@ test_expect_success 'M: rename file in same subdirectory' '
INPUT_END
cat >expect <<-EOF &&
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 file2/newf file2/n.e.w.f
+ :100755 100755 $newf $newf R100 file2/newf file2/n.e.w.f
EOF
git fast-import <input &&
git diff-tree -M -r M1^ M1 >actual &&
@@ -1025,7 +1032,7 @@ test_expect_success 'M: rename file to new subdirectory' '
INPUT_END
cat >expect <<-EOF &&
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 file2/newf i/am/new/to/you
+ :100755 100755 $newf $newf R100 file2/newf i/am/new/to/you
EOF
git fast-import <input &&
git diff-tree -M -r M2^ M2 >actual &&
@@ -1046,7 +1053,7 @@ test_expect_success 'M: rename subdirectory to new subdirectory' '
INPUT_END
cat >expect <<-EOF &&
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 i/am/new/to/you other/sub/am/new/to/you
+ :100755 100755 $newf $newf R100 i/am/new/to/you other/sub/am/new/to/you
EOF
git fast-import <input &&
git diff-tree -M -r M3^ M3 >actual &&
@@ -1067,11 +1074,11 @@ test_expect_success 'M: rename root to subdirectory' '
INPUT_END
cat >expect <<-EOF &&
- :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100 file2/oldf sub/file2/oldf
- :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100 file4 sub/file4
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 i/am/new/to/you sub/i/am/new/to/you
- :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100 newdir/exec.sh sub/newdir/exec.sh
- :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100 newdir/interesting sub/newdir/interesting
+ :100644 100644 $oldf $oldf R100 file2/oldf sub/file2/oldf
+ :100755 100755 $f4id $f4id R100 file4 sub/file4
+ :100755 100755 $newf $newf R100 i/am/new/to/you sub/i/am/new/to/you
+ :100755 100755 $f6id $f6id R100 newdir/exec.sh sub/newdir/exec.sh
+ :100644 100644 $f5id $f5id R100 newdir/interesting sub/newdir/interesting
EOF
git fast-import <input &&
git diff-tree -M -r M4^ M4 >actual &&
@@ -1097,7 +1104,7 @@ test_expect_success 'N: copy file in same subdirectory' '
INPUT_END
cat >expect <<-EOF &&
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file2/n.e.w.f
+ :100755 100755 $newf $newf C100 file2/newf file2/n.e.w.f
EOF
git fast-import <input &&
git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
@@ -1129,9 +1136,9 @@ test_expect_success 'N: copy then modify subdirectory' '
INPUT_END
cat >expect <<-EOF &&
- :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
- :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ :100644 100644 $f5id $f5id C100 newdir/interesting file3/file5
+ :100755 100755 $newf $newf C100 file2/newf file3/newf
+ :100644 100644 $oldf $oldf C100 file2/oldf file3/oldf
EOF
git fast-import <input &&
git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
@@ -1162,9 +1169,9 @@ test_expect_success 'N: copy dirty subdirectory' '
'
test_expect_success 'N: copy directory by id' '
- cat >expect <<-\EOF &&
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
- :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ cat >expect <<-EOF &&
+ :100755 100755 $newf $newf C100 file2/newf file3/newf
+ :100644 100644 $oldf $oldf C100 file2/oldf file3/oldf
EOF
subdir=$(git rev-parse refs/heads/branch^0:file2) &&
cat >input <<-INPUT_END &&
@@ -1183,9 +1190,9 @@ test_expect_success 'N: copy directory by id' '
'
test_expect_success PIPE 'N: read and copy directory' '
- cat >expect <<-\EOF &&
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
- :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ cat >expect <<-EOF &&
+ :100755 100755 $newf $newf C100 file2/newf file3/newf
+ :100644 100644 $oldf $oldf C100 file2/oldf file3/oldf
EOF
git update-ref -d refs/heads/N4 &&
rm -f backflow &&
@@ -1254,9 +1261,9 @@ test_expect_success PIPE 'N: empty directory reads as missing' '
'
test_expect_success 'N: copy root directory by tree hash' '
- cat >expect <<-\EOF &&
- :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file3/newf
- :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D file3/oldf
+ cat >expect <<-EOF &&
+ :100755 000000 $newf $zero D file3/newf
+ :100644 000000 $oldf $zero D file3/oldf
EOF
root=$(git rev-parse refs/heads/branch^0^{tree}) &&
cat >input <<-INPUT_END &&
@@ -1275,12 +1282,12 @@ test_expect_success 'N: copy root directory by tree hash' '
'
test_expect_success 'N: copy root by path' '
- cat >expect <<-\EOF &&
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf oldroot/file2/newf
- :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf oldroot/file2/oldf
- :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 C100 file4 oldroot/file4
- :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 C100 newdir/exec.sh oldroot/newdir/exec.sh
- :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting oldroot/newdir/interesting
+ cat >expect <<-EOF &&
+ :100755 100755 $newf $newf C100 file2/newf oldroot/file2/newf
+ :100644 100644 $oldf $oldf C100 file2/oldf oldroot/file2/oldf
+ :100755 100755 $f4id $f4id C100 file4 oldroot/file4
+ :100755 100755 $f6id $f6id C100 newdir/exec.sh oldroot/newdir/exec.sh
+ :100644 100644 $f5id $f5id C100 newdir/interesting oldroot/newdir/interesting
EOF
cat >input <<-INPUT_END &&
commit refs/heads/N-copy-root-path
@@ -1340,10 +1347,10 @@ test_expect_success 'N: delete directory by copying' '
'
test_expect_success 'N: modify copied tree' '
- cat >expect <<-\EOF &&
- :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
- :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
- :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ cat >expect <<-EOF &&
+ :100644 100644 $f5id $f5id C100 newdir/interesting file3/file5
+ :100755 100755 $newf $newf C100 file2/newf file3/newf
+ :100644 100644 $oldf $oldf C100 file2/oldf file3/oldf
EOF
subdir=$(git rev-parse refs/heads/branch^0:file2) &&
cat >input <<-INPUT_END &&
@@ -2726,7 +2733,7 @@ test_expect_success 'R: corrupt lines do not mess marks file' '
rm -f io.marks &&
blob=$(echo hi | git hash-object --stdin) &&
cat >expect <<-EOF &&
- :3 0000000000000000000000000000000000000000
+ :3 $ZERO_OID
:1 $blob
:2 $blob
EOF
@@ -3077,7 +3084,7 @@ test_expect_success 'T: delete branch' '
git branch to-delete &&
git fast-import <<-EOF &&
reset refs/heads/to-delete
- from 0000000000000000000000000000000000000000
+ from $ZERO_OID
EOF
test_must_fail git rev-parse --verify refs/heads/to-delete
'
@@ -3117,6 +3124,9 @@ test_expect_success 'U: initialize for U tests' '
INPUT_END
+ f7id=$(echo "blob 1" | git hash-object --stdin) &&
+ f8id=$(echo "sleep well" | git hash-object --stdin) &&
+ f9id=$(echo "au revoir" | git hash-object --stdin) &&
git fast-import <input
'
@@ -3137,7 +3147,7 @@ test_expect_success 'U: filedelete file succeeds' '
test_expect_success 'U: validate file delete result' '
cat >expect <<-EOF &&
- :100644 000000 2907ebb4bf85d91bf0716bb3bd8a68ef48d6da76 0000000000000000000000000000000000000000 D good/night.txt
+ :100644 000000 $f8id $ZERO_OID D good/night.txt
EOF
git diff-tree -M -r U^1 U >actual &&
@@ -3162,7 +3172,7 @@ test_expect_success 'U: filedelete directory succeeds' '
test_expect_success 'U: validate directory delete result' '
cat >expect <<-EOF &&
- :100644 000000 69cb75792f55123d8389c156b0b41c2ff00ed507 0000000000000000000000000000000000000000 D good/bye.txt
+ :100644 000000 $f9id $ZERO_OID D good/bye.txt
EOF
git diff-tree -M -r U^1 U >actual &&
@@ -3187,7 +3197,7 @@ test_expect_success 'U: filedelete root succeeds' '
test_expect_success 'U: validate root delete result' '
cat >expect <<-EOF &&
- :100644 000000 c18147dc648481eeb65dc5e66628429a64843327 0000000000000000000000000000000000000000 D hello.c
+ :100644 000000 $f7id $ZERO_OID D hello.c
EOF
git diff-tree -M -r U^1 U >actual &&
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index ca223dca98..14c1baa739 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -470,12 +470,13 @@ test_expect_success 'add lots of commits and notes' '
'
test_expect_success 'verify that lots of notes trigger a fanout scheme' '
+ hexsz=$(test_oid hexsz) &&
# None of the entries in the top-level notes tree should be a full SHA1
git ls-tree --name-only refs/notes/many_notes |
while read path
do
- if test $(expr length "$path") -ge 40
+ if test $(expr length "$path") -ge $hexsz
then
return 1
fi
@@ -518,7 +519,7 @@ test_expect_success 'verify that importing a notes tree respects the fanout sche
git ls-tree --name-only refs/notes/other_notes |
while read path
do
- if test $(expr length "$path") -ge 40
+ if test $(expr length "$path") -ge $hexsz
then
return 1
fi
@@ -593,7 +594,7 @@ test_expect_success 'verify that changing notes respect existing fanout' '
git ls-tree --name-only refs/notes/many_notes |
while read path
do
- if test $(expr length "$path") -ge 40
+ if test $(expr length "$path") -ge $hexsz
then
return 1
fi
@@ -616,7 +617,7 @@ i=$(($num_commits - $remaining_notes))
for sha1 in $(git rev-list -n $i refs/heads/many_commits)
do
cat >>input <<INPUT_END
-N 0000000000000000000000000000000000000000 $sha1
+N $ZERO_OID $sha1
INPUT_END
done
@@ -646,7 +647,6 @@ test_expect_success 'remove lots of notes' '
'
test_expect_success 'verify that removing notes trigger fanout consolidation' '
-
# All entries in the top-level notes tree should be a full SHA1
git ls-tree --name-only -r refs/notes/many_notes |
while read path
@@ -656,7 +656,7 @@ test_expect_success 'verify that removing notes trigger fanout consolidation' '
test "$path" = "deadbeef" && continue
test "$path" = "de/adbeef" && continue
- if test $(expr length "$path") -ne 40
+ if test $(expr length "$path") -ne $hexsz
then
return 1
fi
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 690c90fb82..1372842559 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -132,12 +132,12 @@ test_expect_success 'reencoding iso-8859-7' '
sed "s/wer/i18n/" iso-8859-7.fi |
(cd new &&
git fast-import &&
- # The commit object, if not re-encoded, would be 240 bytes.
+ # The commit object, if not re-encoded, would be 200 bytes plus hash.
# Removing the "encoding iso-8859-7\n" header drops 20 bytes.
# Re-encoding the Pi character from \xF0 (\360) in iso-8859-7
# to \xCF\x80 (\317\200) in UTF-8 adds a byte. Check for
# the expected size.
- test 221 -eq "$(git cat-file -s i18n)" &&
+ test $(($(test_oid hexsz) + 181)) -eq "$(git cat-file -s i18n)" &&
# ...and for the expected translation of bytes.
git cat-file commit i18n >actual &&
grep $(printf "\317\200") actual &&
@@ -164,12 +164,12 @@ test_expect_success 'preserving iso-8859-7' '
sed "s/wer/i18n-no-recoding/" iso-8859-7.fi |
(cd new &&
git fast-import &&
- # The commit object, if not re-encoded, is 240 bytes.
+ # The commit object, if not re-encoded, is 200 bytes plus hash.
# Removing the "encoding iso-8859-7\n" header would drops 20
# bytes. Re-encoding the Pi character from \xF0 (\360) in
# iso-8859-7 to \xCF\x80 (\317\200) in UTF-8 adds a byte.
# Check for the expected size...
- test 240 -eq "$(git cat-file -s i18n-no-recoding)" &&
+ test $(($(test_oid hexsz) + 200)) -eq "$(git cat-file -s i18n-no-recoding)" &&
# ...as well as the expected byte.
git cat-file commit i18n-no-recoding >actual &&
grep $(printf "\360") actual &&
@@ -192,7 +192,7 @@ test_expect_success 'encoding preserved if reencoding fails' '
grep ^encoding actual &&
# Verify that the commit has the expected size; i.e.
# that no bytes were re-encoded to a different encoding.
- test 252 -eq "$(git cat-file -s i18n-invalid)" &&
+ test $(($(test_oid hexsz) + 212)) -eq "$(git cat-file -s i18n-invalid)" &&
# ...and check for the original special bytes
grep $(printf "\360") actual &&
grep $(printf "\377") actual)
@@ -694,7 +694,7 @@ test_expect_success 'delete ref because entire history excluded' '
git fast-export to-delete ^to-delete >actual &&
cat >expected <<-EOF &&
reset refs/heads/to-delete
- from 0000000000000000000000000000000000000000
+ from $ZERO_OID
EOF
test_cmp expected actual
@@ -704,7 +704,7 @@ test_expect_success 'delete refspec' '
git fast-export --refspec :refs/heads/to-delete >actual &&
cat >expected <<-EOF &&
reset refs/heads/to-delete
- from 0000000000000000000000000000000000000000
+ from $ZERO_OID
EOF
test_cmp expected actual
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index a5e5dca753..4a46f31c41 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -603,7 +603,7 @@ test_expect_success 'cvs server does not run with vanilla git-shell' '
cd cvswork &&
CVS_SERVER=$WORKDIR/remote-cvs &&
export CVS_SERVER &&
- test_must_fail cvs log merge
+ ! cvs log merge
)
'
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index 84787eee9a..c7a0dd84a4 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -167,10 +167,10 @@ test_expect_success 'adding files' '
test_expect_success 'updating' '
git pull gitcvs.git &&
- echo 'hi' > subdir/newfile.bin &&
- echo 'junk' > subdir/file.h &&
- echo 'hi' > subdir/newfile.c &&
- echo 'hello' >> binfile.bin &&
+ echo "hi" >subdir/newfile.bin &&
+ echo "junk" >subdir/file.h &&
+ echo "hi" >subdir/newfile.c &&
+ echo "hello" >>binfile.bin &&
git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin &&
git commit -q -m "Add and change some files" &&
git push gitcvs.git >/dev/null &&
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index cf31ace667..6436c91a3c 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -178,7 +178,7 @@ test_expect_success 'setup v1.2 on b1' '
mkdir cdir &&
echo "cdir/cfile" >cdir/cfile &&
git add -A cdir adir t3 t2 &&
- git commit -q -m 'v1.2' &&
+ git commit -q -m "v1.2" &&
git tag v1.2 &&
git push --tags gitcvs.git b1:b1
'
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 267ddc997d..b484e3e250 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -621,12 +621,22 @@ test_expect_success \
git config gitweb.snapshot "zip,tgz, tbz2" &&
gitweb_run "p=.git;a=tree"'
-cat >.git/config <<\EOF
-# testing noval and alternate separator
-[gitweb]
- blame
- snapshot = zip tgz
-EOF
+test_expect_success 'setup' '
+ version=$(git config core.repositoryformatversion) &&
+ algo=$(test_might_fail git config extensions.objectformat) &&
+ cat >.git/config <<-\EOF &&
+ # testing noval and alternate separator
+ [gitweb]
+ blame
+ snapshot = zip tgz
+ EOF
+ git config core.repositoryformatversion "$version" &&
+ if test -n "$algo"
+ then
+ git config extensions.objectformat "$algo"
+ fi
+'
+
test_expect_success \
'config override: tree view, features enabled in repo config (2)' \
'gitweb_run "p=.git;a=tree"'
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index 34cd01366f..e046f7db76 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -23,6 +23,8 @@ sub adjust_dirsep {
return $path;
}
+my $oid_re = qr/^[0-9a-fA-F]{40}(?:[0-9a-fA-F]{24})?$/;
+
BEGIN { use_ok('Git') }
# set up
@@ -59,15 +61,15 @@ ok($@, "config_bool: non-boolean values fail");
open STDERR, ">&", $tmpstderr or die "cannot restore STDERR";
# ident
-like($r->ident("aUthor"), qr/^A U Thor <author\@example.com> [0-9]+ \+0000$/,
+like($r->ident("aUthor"), qr/^A U Thor <author\@example.com> [0-9]+ [+-]\d{4}$/,
"ident scalar: author (type)");
-like($r->ident("cOmmitter"), qr/^C O Mitter <committer\@example.com> [0-9]+ \+0000$/,
+like($r->ident("cOmmitter"), qr/^C O Mitter <committer\@example.com> [0-9]+ [+-]\d{4}$/,
"ident scalar: committer (type)");
is($r->ident("invalid"), "invalid", "ident scalar: invalid ident string (no parsing)");
my ($name, $email, $time_tz) = $r->ident('author');
is_deeply([$name, $email], ["A U Thor", "author\@example.com"],
"ident array: author");
-like($time_tz, qr/[0-9]+ \+0000/, "ident array: author");
+like($time_tz, qr/[0-9]+ [+-]\d{4}/, "ident array: author");
is_deeply([$r->ident("Name <email> 123 +0000")], ["Name", "email", "123 +0000"],
"ident array: ident string");
is_deeply([$r->ident("invalid")], [], "ident array: invalid ident string");
@@ -93,7 +95,7 @@ is(Git::hash_object("blob", $tmpfile), $file1hash, "hash_object: roundtrip");
open TEMPFILE, ">$tmpfile" or die "Can't open $tmpfile: $!";
print TEMPFILE my $test_text = "test blob, to be inserted\n";
close TEMPFILE or die "Failed writing to $tmpfile: $!";
-like(our $newhash = $r->hash_and_insert_object($tmpfile), qr/[0-9a-fA-F]{40}/,
+like(our $newhash = $r->hash_and_insert_object($tmpfile), $oid_re,
"hash_and_insert_object: returns hash");
open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
is($r->cat_blob($newhash, \*TEMPFILE), length $test_text, "cat_blob: roundtrip size");
@@ -119,7 +121,7 @@ is($r2->wc_subdir, "directory2/", "wc_subdir initial (2)");
# commands in sub directory
my $last_commit = $r2->command_oneline(qw(rev-parse --verify HEAD));
-like($last_commit, qr/^[0-9a-fA-F]{40}$/, 'rev-parse returned hash');
+like($last_commit, $oid_re, 'rev-parse returned hash');
my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.');
isnt($last_commit, $dir_commit, 'log . does not show last commit');
diff --git a/t/t9832-unshelve.sh b/t/t9832-unshelve.sh
index e9276c48f4..7194fb2855 100755
--- a/t/t9832-unshelve.sh
+++ b/t/t9832-unshelve.sh
@@ -29,8 +29,11 @@ test_expect_success 'init depot' '
)
'
+# Create an initial clone, with a commit unrelated to the P4 change
+# on HEAD
test_expect_success 'initial clone' '
- git p4 clone --dest="$git" //depot/@all
+ git p4 clone --dest="$git" //depot/@all &&
+ test_commit -C "$git" "unrelated"
'
test_expect_success 'create shelved changelist' '
diff --git a/t/t9834-git-p4-file-dir-bug.sh b/t/t9834-git-p4-file-dir-bug.sh
index 031e1f8668..dac67e89d7 100755
--- a/t/t9834-git-p4-file-dir-bug.sh
+++ b/t/t9834-git-p4-file-dir-bug.sh
@@ -10,7 +10,7 @@ repository.'
test_expect_success 'start p4d' '
start_p4d &&
- test_might_fail p4 configure set submit.collision.check=0
+ { p4 configure set submit.collision.check=0 || :; }
'
test_expect_success 'init depot' '
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 3103be8a32..8d59b90348 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -798,6 +798,37 @@ list_contains () {
return 1
}
+# Returns success if the arguments indicate that a command should be
+# accepted by test_must_fail(). If the command is run with env, the env
+# and its corresponding variable settings will be stripped before we
+# test the command being run.
+test_must_fail_acceptable () {
+ if test "$1" = "env"
+ then
+ shift
+ while test $# -gt 0
+ do
+ case "$1" in
+ *?=*)
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+ fi
+
+ case "$1" in
+ git|__git*|test-tool|test_terminal)
+ return 0
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
# This is not among top-level (test_expect_success | test_expect_failure)
# but is a prefix that can be used in the test script, like:
#
@@ -817,6 +848,17 @@ list_contains () {
# Multiple signals can be specified as a comma separated list.
# Currently recognized signal names are: sigpipe, success.
# (Don't use 'success', use 'test_might_fail' instead.)
+#
+# Do not use this to run anything but "git" and other specific testable
+# commands (see test_must_fail_acceptable()). We are not in the
+# business of vetting system supplied commands -- in other words, this
+# is wrong:
+#
+# test_must_fail grep pattern output
+#
+# Instead use '!':
+#
+# ! grep pattern output
test_must_fail () {
case "$1" in
@@ -828,6 +870,11 @@ test_must_fail () {
_test_ok=
;;
esac
+ if ! test_must_fail_acceptable "$@"
+ then
+ echo >&7 "test_must_fail: only 'git' is allowed: $*"
+ return 1
+ fi
"$@" 2>&7
exit_code=$?
if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
@@ -905,7 +952,13 @@ test_expect_code () {
# - not all diff versions understand "-u"
test_cmp() {
- eval "$GIT_TEST_CMP" '"$@"'
+ test $# -eq 2 || BUG "test_cmp requires two arguments"
+ if ! eval "$GIT_TEST_CMP" '"$@"'
+ then
+ test "x$1" = x- || test -e "$1" || BUG "test_cmp '$1' missing"
+ test "x$2" = x- || test -e "$2" || BUG "test_cmp '$2' missing"
+ return 1
+ fi
}
# Check that the given config key has the expected value.
@@ -934,7 +987,13 @@ test_cmp_config() {
# test_cmp_bin - helper to compare binary files
test_cmp_bin() {
- cmp "$@"
+ test $# -eq 2 || BUG "test_cmp_bin requires two arguments"
+ if ! cmp "$@"
+ then
+ test "x$1" = x- || test -e "$1" || BUG "test_cmp_bin '$1' missing"
+ test "x$2" = x- || test -e "$2" || BUG "test_cmp_bin '$2' missing"
+ return 1
+ fi
}
# Use this instead of test_cmp to compare files that contain expected and
@@ -1417,9 +1476,7 @@ test_set_hash () {
# Detect the hash algorithm in use.
test_detect_hash () {
- # Currently we only support SHA-1, but in the future this function will
- # actually detect the algorithm in use.
- test_hash_algo='sha1'
+ test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
}
# Load common hash metadata and common placeholder object IDs for use with
@@ -1468,7 +1525,17 @@ test_oid_cache () {
# Look up a per-hash value based on a key ($1). The value must have been loaded
# by test_oid_init or test_oid_cache.
test_oid () {
- local var="test_oid_${test_hash_algo}_$1" &&
+ local algo="${test_hash_algo}" &&
+
+ case "$1" in
+ --hash=*)
+ algo="${1#--hash=}" &&
+ shift;;
+ *)
+ ;;
+ esac &&
+
+ local var="test_oid_${algo}_$1" &&
# If the variable is unset, we must be missing an entry for this
# key-hash pair, so exit with an error.
@@ -1561,3 +1628,36 @@ test_path_is_hidden () {
case "$("$SYSTEMROOT"/system32/attrib "$1")" in *H*?:*) return 0;; esac
return 1
}
+
+# Check that the given command was invoked as part of the
+# trace2-format trace on stdin.
+#
+# test_subcommand [!] <command> <args>... < <trace>
+#
+# For example, to look for an invocation of "git upload-pack
+# /path/to/repo"
+#
+# GIT_TRACE2_EVENT=event.log git fetch ... &&
+# test_subcommand git upload-pack "$PATH" <event.log
+#
+# If the first parameter passed is !, this instead checks that
+# the given command was not called.
+#
+test_subcommand () {
+ local negate=
+ if test "$1" = "!"
+ then
+ negate=t
+ shift
+ fi
+
+ local expr=$(printf '"%s",' "$@")
+ expr="${expr%,}"
+
+ if test -n "$negate"
+ then
+ ! grep "\[$expr\]"
+ else
+ grep "\[$expr\]"
+ fi
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 618a7c8d5b..ef31f40037 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -441,17 +441,23 @@ TEST_AUTHOR_LOCALNAME=author
TEST_AUTHOR_DOMAIN=example.com
GIT_AUTHOR_EMAIL=${TEST_AUTHOR_LOCALNAME}@${TEST_AUTHOR_DOMAIN}
GIT_AUTHOR_NAME='A U Thor'
+GIT_AUTHOR_DATE='1112354055 +0200'
TEST_COMMITTER_LOCALNAME=committer
TEST_COMMITTER_DOMAIN=example.com
GIT_COMMITTER_EMAIL=${TEST_COMMITTER_LOCALNAME}@${TEST_COMMITTER_DOMAIN}
GIT_COMMITTER_NAME='C O Mitter'
+GIT_COMMITTER_DATE='1112354055 +0200'
GIT_MERGE_VERBOSITY=5
GIT_MERGE_AUTOEDIT=no
export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT
export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
+export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
export EDITOR
+GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
+export GIT_DEFAULT_HASH
+
# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
GIT_TRACE_BARE=1
export GIT_TRACE_BARE
@@ -1686,7 +1692,11 @@ test_lazy_prereq CURL '
# which will not work with other hash algorithms and tests that work but don't
# test anything meaningful (e.g. special values which cause short collisions).
test_lazy_prereq SHA1 '
- test $(git hash-object /dev/null) = e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+ case "$GIT_DEFAULT_HASH" in
+ sha1) true ;;
+ "") test $(git hash-object /dev/null) = e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 ;;
+ *) false ;;
+ esac
'
test_lazy_prereq REBASE_P '
diff --git a/templates/hooks--pre-push.sample b/templates/hooks--pre-push.sample
index 6187dbf439..4ce688d32b 100755
--- a/templates/hooks--pre-push.sample
+++ b/templates/hooks--pre-push.sample
@@ -14,7 +14,7 @@
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
-# <local ref> <local sha1> <remote ref> <remote sha1>
+# <local ref> <local oid> <remote ref> <remote oid>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
@@ -22,27 +22,27 @@
remote="$1"
url="$2"
-z40=0000000000000000000000000000000000000000
+zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
-while read local_ref local_sha remote_ref remote_sha
+while read local_ref local_oid remote_ref remote_oid
do
- if [ "$local_sha" = $z40 ]
+ if test "$local_oid" = "$zero"
then
# Handle delete
:
else
- if [ "$remote_sha" = $z40 ]
+ if test "$remote_oid" = "$zero"
then
# New branch, examine all commits
- range="$local_sha"
+ range="$local_oid"
else
# Update to existing branch, examine new commits
- range="$remote_sha..$local_sha"
+ range="$remote_oid..$local_oid"
fi
# Check for WIP commit
- commit=`git rev-list -n 1 --grep '^WIP' "$range"`
- if [ -n "$commit" ]
+ commit=$(git rev-list -n 1 --grep '^WIP' "$range")
+ if test -n "$commit"
then
echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample
index 5014c4b31c..c4d426bc6e 100755
--- a/templates/hooks--update.sample
+++ b/templates/hooks--update.sample
@@ -60,7 +60,7 @@ esac
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
-zero="0000000000000000000000000000000000000000"
+zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 91c00567f4..42ed4db5d3 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -4,13 +4,13 @@
#include "sigchain.h"
#include "string-list.h"
#include "strbuf.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "quote.h"
#include "object-store.h"
struct tmp_objdir {
struct strbuf path;
- struct argv_array env;
+ struct strvec env;
};
/*
@@ -24,7 +24,7 @@ static struct tmp_objdir *the_tmp_objdir;
static void tmp_objdir_free(struct tmp_objdir *t)
{
strbuf_release(&t->path);
- argv_array_clear(&t->env);
+ strvec_clear(&t->env);
free(t);
}
@@ -79,7 +79,7 @@ static void remove_tmp_objdir_on_signal(int signo)
* separated by PATH_SEP (which is what separate values in
* GIT_ALTERNATE_OBJECT_DIRECTORIES).
*/
-static void env_append(struct argv_array *env, const char *key, const char *val)
+static void env_append(struct strvec *env, const char *key, const char *val)
{
struct strbuf quoted = STRBUF_INIT;
const char *old;
@@ -97,16 +97,16 @@ static void env_append(struct argv_array *env, const char *key, const char *val)
old = getenv(key);
if (!old)
- argv_array_pushf(env, "%s=%s", key, val);
+ strvec_pushf(env, "%s=%s", key, val);
else
- argv_array_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val);
+ strvec_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val);
strbuf_release(&quoted);
}
-static void env_replace(struct argv_array *env, const char *key, const char *val)
+static void env_replace(struct strvec *env, const char *key, const char *val)
{
- argv_array_pushf(env, "%s=%s", key, val);
+ strvec_pushf(env, "%s=%s", key, val);
}
static int setup_tmp_objdir(const char *root)
@@ -131,7 +131,7 @@ struct tmp_objdir *tmp_objdir_create(void)
t = xmalloc(sizeof(*t));
strbuf_init(&t->path, 0);
- argv_array_init(&t->env);
+ strvec_init(&t->env);
strbuf_addf(&t->path, "%s/incoming-XXXXXX", get_object_directory());
@@ -283,7 +283,7 @@ const char **tmp_objdir_env(const struct tmp_objdir *t)
{
if (!t)
return NULL;
- return t->env.argv;
+ return t->env.v;
}
void tmp_objdir_add_as_alternate(const struct tmp_objdir *t)
diff --git a/trailer.c b/trailer.c
index 0c414f2fed..68dabc2556 100644
--- a/trailer.c
+++ b/trailer.c
@@ -221,15 +221,13 @@ static char *apply_command(const char *command, const char *arg)
struct strbuf cmd = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
- const char *argv[] = {NULL, NULL};
char *result;
strbuf_addstr(&cmd, command);
if (arg)
strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
- argv[0] = cmd.buf;
- cp.argv = argv;
+ strvec_push(&cp.args, cmd.buf);
cp.env = local_repo_env;
cp.no_stdin = 1;
cp.use_shell = 1;
diff --git a/transport-helper.c b/transport-helper.c
index c6b753bfae..b573b6c730 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -9,7 +9,7 @@
#include "string-list.h"
#include "thread-utils.h"
#include "sigchain.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "refs.h"
#include "refspec.h"
#include "transport-internal.h"
@@ -128,17 +128,17 @@ static struct child_process *get_helper(struct transport *transport)
helper->in = -1;
helper->out = -1;
helper->err = 0;
- argv_array_pushf(&helper->args, "git-remote-%s", data->name);
- argv_array_push(&helper->args, transport->remote->name);
- argv_array_push(&helper->args, remove_ext_force(transport->url));
- helper->git_cmd = 0;
+ strvec_pushf(&helper->args, "remote-%s", data->name);
+ strvec_push(&helper->args, transport->remote->name);
+ strvec_push(&helper->args, remove_ext_force(transport->url));
+ helper->git_cmd = 1;
helper->silent_exec_failure = 1;
if (have_git_dir())
- argv_array_pushf(&helper->env_array, "%s=%s",
- GIT_DIR_ENVIRONMENT, get_git_dir());
+ strvec_pushf(&helper->env_array, "%s=%s",
+ GIT_DIR_ENVIRONMENT, get_git_dir());
- helper->trace2_child_class = helper->args.argv[0]; /* "remote-<name>" */
+ helper->trace2_child_class = helper->args.v[0]; /* "remote-<name>" */
code = start_command(helper);
if (code < 0 && errno == ENOENT)
@@ -439,13 +439,13 @@ static int get_importer(struct transport *transport, struct child_process *fasti
int cat_blob_fd, code;
child_process_init(fastimport);
fastimport->in = xdup(helper->out);
- argv_array_push(&fastimport->args, "fast-import");
- argv_array_push(&fastimport->args, "--allow-unsafe-features");
- argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
+ strvec_push(&fastimport->args, "fast-import");
+ strvec_push(&fastimport->args, "--allow-unsafe-features");
+ strvec_push(&fastimport->args, debug ? "--stats" : "--quiet");
if (data->bidi_import) {
cat_blob_fd = xdup(helper->in);
- argv_array_pushf(&fastimport->args, "--cat-blob-fd=%d", cat_blob_fd);
+ strvec_pushf(&fastimport->args, "--cat-blob-fd=%d", cat_blob_fd);
}
fastimport->git_cmd = 1;
@@ -466,17 +466,17 @@ static int get_exporter(struct transport *transport,
/* we need to duplicate helper->in because we want to use it after
* fastexport is done with it. */
fastexport->out = dup(helper->in);
- argv_array_push(&fastexport->args, "fast-export");
- argv_array_push(&fastexport->args, "--use-done-feature");
- argv_array_push(&fastexport->args, data->signed_tags ?
+ strvec_push(&fastexport->args, "fast-export");
+ strvec_push(&fastexport->args, "--use-done-feature");
+ strvec_push(&fastexport->args, data->signed_tags ?
"--signed-tags=verbatim" : "--signed-tags=warn-strip");
if (data->export_marks)
- argv_array_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks);
+ strvec_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks);
if (data->import_marks)
- argv_array_pushf(&fastexport->args, "--import-marks=%s", data->import_marks);
+ strvec_pushf(&fastexport->args, "--import-marks=%s", data->import_marks);
for (i = 0; i < revlist_args->nr; i++)
- argv_array_push(&fastexport->args, revlist_args->items[i].string);
+ strvec_push(&fastexport->args, revlist_args->items[i].string);
fastexport->git_cmd = 1;
return start_command(fastexport);
@@ -723,13 +723,61 @@ static int fetch(struct transport *transport,
return -1;
}
+struct push_update_ref_state {
+ struct ref *hint;
+ struct ref_push_report *report;
+ int new_report;
+};
+
static int push_update_ref_status(struct strbuf *buf,
- struct ref **ref,
+ struct push_update_ref_state *state,
struct ref *remote_refs)
{
char *refname, *msg;
int status, forced = 0;
+ if (starts_with(buf->buf, "option ")) {
+ struct object_id old_oid, new_oid;
+ const char *key, *val;
+ char *p;
+
+ if (!state->hint || !(state->report || state->new_report))
+ die(_("'option' without a matching 'ok/error' directive"));
+ if (state->new_report) {
+ if (!state->hint->report) {
+ state->hint->report = xcalloc(1, sizeof(struct ref_push_report));
+ state->report = state->hint->report;
+ } else {
+ state->report = state->hint->report;
+ while (state->report->next)
+ state->report = state->report->next;
+ state->report->next = xcalloc(1, sizeof(struct ref_push_report));
+ state->report = state->report->next;
+ }
+ state->new_report = 0;
+ }
+ key = buf->buf + 7;
+ p = strchr(key, ' ');
+ if (p)
+ *p++ = '\0';
+ val = p;
+ if (!strcmp(key, "refname"))
+ state->report->ref_name = xstrdup_or_null(val);
+ else if (!strcmp(key, "old-oid") && val &&
+ !parse_oid_hex(val, &old_oid, &val))
+ state->report->old_oid = oiddup(&old_oid);
+ else if (!strcmp(key, "new-oid") && val &&
+ !parse_oid_hex(val, &new_oid, &val))
+ state->report->new_oid = oiddup(&new_oid);
+ else if (!strcmp(key, "forced-update"))
+ state->report->forced_update = 1;
+ /* Not update remote namespace again. */
+ return 1;
+ }
+
+ state->report = NULL;
+ state->new_report = 0;
+
if (starts_with(buf->buf, "ok ")) {
status = REF_STATUS_OK;
refname = buf->buf + 3;
@@ -785,16 +833,16 @@ static int push_update_ref_status(struct strbuf *buf,
}
}
- if (*ref)
- *ref = find_ref_by_name(*ref, refname);
- if (!*ref)
- *ref = find_ref_by_name(remote_refs, refname);
- if (!*ref) {
+ if (state->hint)
+ state->hint = find_ref_by_name(state->hint, refname);
+ if (!state->hint)
+ state->hint = find_ref_by_name(remote_refs, refname);
+ if (!state->hint) {
warning(_("helper reported unexpected status of %s"), refname);
return 1;
}
- if ((*ref)->status != REF_STATUS_NONE) {
+ if (state->hint->status != REF_STATUS_NONE) {
/*
* Earlier, the ref was marked not to be pushed, so ignore the ref
* status reported by the remote helper if the latter is 'no match'.
@@ -803,9 +851,11 @@ static int push_update_ref_status(struct strbuf *buf,
return 1;
}
- (*ref)->status = status;
- (*ref)->forced_update |= forced;
- (*ref)->remote_status = msg;
+ if (status == REF_STATUS_OK)
+ state->new_report = 1;
+ state->hint->status = status;
+ state->hint->forced_update |= forced;
+ state->hint->remote_status = msg;
return !(status == REF_STATUS_OK);
}
@@ -813,37 +863,57 @@ static int push_update_refs_status(struct helper_data *data,
struct ref *remote_refs,
int flags)
{
+ struct ref *ref;
+ struct ref_push_report *report;
struct strbuf buf = STRBUF_INIT;
- struct ref *ref = remote_refs;
- int ret = 0;
+ struct push_update_ref_state state = { remote_refs, NULL, 0 };
for (;;) {
- char *private;
-
if (recvline(data, &buf)) {
- ret = 1;
- break;
+ strbuf_release(&buf);
+ return 1;
}
-
if (!buf.len)
break;
+ push_update_ref_status(&buf, &state, remote_refs);
+ }
+ strbuf_release(&buf);
- if (push_update_ref_status(&buf, &ref, remote_refs))
- continue;
+ if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)
+ return 0;
- if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)
- continue;
+ /* propagate back the update to the remote namespace */
+ for (ref = remote_refs; ref; ref = ref->next) {
+ char *private;
- /* propagate back the update to the remote namespace */
- private = apply_refspecs(&data->rs, ref->name);
- if (!private)
+ if (ref->status != REF_STATUS_OK)
continue;
- update_ref("update by helper", private, &ref->new_oid, NULL,
- 0, 0);
- free(private);
+
+ if (!ref->report) {
+ private = apply_refspecs(&data->rs, ref->name);
+ if (!private)
+ continue;
+ update_ref("update by helper", private, &(ref->new_oid),
+ NULL, 0, 0);
+ free(private);
+ } else {
+ for (report = ref->report; report; report = report->next) {
+ private = apply_refspecs(&data->rs,
+ report->ref_name
+ ? report->ref_name
+ : ref->name);
+ if (!private)
+ continue;
+ update_ref("update by helper", private,
+ report->new_oid
+ ? report->new_oid
+ : &(ref->new_oid),
+ NULL, 0, 0);
+ free(private);
+ }
+ }
}
- strbuf_release(&buf);
- return ret;
+ return 0;
}
static void set_common_push_options(struct transport *transport,
@@ -1082,7 +1152,7 @@ static int has_attribute(const char *attrs, const char *attr)
}
static struct ref *get_refs_list(struct transport *transport, int for_push,
- const struct argv_array *ref_prefixes)
+ const struct strvec *ref_prefixes)
{
get_helper(transport);
diff --git a/transport-internal.h b/transport-internal.h
index 1cde6258a7..27c9daffc4 100644
--- a/transport-internal.h
+++ b/transport-internal.h
@@ -3,7 +3,7 @@
struct ref;
struct transport;
-struct argv_array;
+struct strvec;
struct transport_vtable {
/**
@@ -30,7 +30,7 @@ struct transport_vtable {
* in the ref's old_sha1 field; otherwise it should be all 0.
**/
struct ref *(*get_refs_list)(struct transport *transport, int for_push,
- const struct argv_array *ref_prefixes);
+ const struct strvec *ref_prefixes);
/**
* Fetch the objects for the given refs. Note that this gets
diff --git a/transport.c b/transport.c
index b41386eccb..ffe2115845 100644
--- a/transport.c
+++ b/transport.c
@@ -127,7 +127,7 @@ struct bundle_transport_data {
static struct ref *get_refs_from_bundle(struct transport *transport,
int for_push,
- const struct argv_array *ref_prefixes)
+ const struct strvec *ref_prefixes)
{
struct bundle_transport_data *data = transport->data;
struct ref *result = NULL;
@@ -232,9 +232,6 @@ static int set_git_option(struct git_transport_options *opts,
} else if (!strcmp(name, TRANS_OPT_FROM_PROMISOR)) {
opts->from_promisor = !!value;
return 0;
- } else if (!strcmp(name, TRANS_OPT_NO_DEPENDENTS)) {
- opts->no_dependents = !!value;
- return 0;
} else if (!strcmp(name, TRANS_OPT_LIST_OBJECTS_FILTER)) {
list_objects_filter_die_if_populated(&opts->filter_options);
parse_list_objects_filter(&opts->filter_options, value);
@@ -283,7 +280,7 @@ static void die_if_server_options(struct transport *transport)
* remote refs.
*/
static struct ref *handshake(struct transport *transport, int for_push,
- const struct argv_array *ref_prefixes,
+ const struct strvec *ref_prefixes,
int must_list_refs)
{
struct git_transport_data *data = transport->data;
@@ -327,7 +324,7 @@ static struct ref *handshake(struct transport *transport, int for_push,
}
static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
- const struct argv_array *ref_prefixes)
+ const struct strvec *ref_prefixes)
{
return handshake(transport, for_push, ref_prefixes, 1);
}
@@ -359,7 +356,6 @@ static int fetch_refs_via_pack(struct transport *transport,
args.cloning = transport->cloning;
args.update_shallow = data->options.update_shallow;
args.from_promisor = data->options.from_promisor;
- args.no_dependents = data->options.no_dependents;
args.filter_options = data->options.filter_options;
args.stateless_rpc = transport->stateless_rpc;
args.server_options = transport->server_options;
@@ -436,37 +432,67 @@ int transport_refs_pushed(struct ref *ref)
return 0;
}
-void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
+static void update_one_tracking_ref(struct remote *remote, char *refname,
+ struct object_id *new_oid, int deletion,
+ int verbose)
{
struct refspec_item rs;
- if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
- return;
-
- rs.src = ref->name;
+ memset(&rs, 0, sizeof(rs));
+ rs.src = refname;
rs.dst = NULL;
if (!remote_find_tracking(remote, &rs)) {
if (verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
- if (ref->deletion) {
+ if (deletion)
delete_ref(NULL, rs.dst, NULL, 0);
- } else
- update_ref("update by push", rs.dst, &ref->new_oid,
+ else
+ update_ref("update by push", rs.dst, new_oid,
NULL, 0, 0);
free(rs.dst);
}
}
+void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)
+{
+ char *refname;
+ struct object_id *new_oid;
+ struct ref_push_report *report;
+
+ if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
+ return;
+
+ report = ref->report;
+ if (!report)
+ update_one_tracking_ref(remote, ref->name, &ref->new_oid,
+ ref->deletion, verbose);
+ else
+ for (; report; report = report->next) {
+ refname = report->ref_name ? (char *)report->ref_name : ref->name;
+ new_oid = report->new_oid ? report->new_oid : &ref->new_oid;
+ update_one_tracking_ref(remote, refname, new_oid,
+ is_null_oid(new_oid), verbose);
+ }
+}
+
static void print_ref_status(char flag, const char *summary,
struct ref *to, struct ref *from, const char *msg,
+ struct ref_push_report *report,
int porcelain, int summary_width)
{
+ const char *to_name;
+
+ if (report && report->ref_name)
+ to_name = report->ref_name;
+ else
+ to_name = to->name;
+
if (porcelain) {
if (from)
- fprintf(stdout, "%c\t%s:%s\t", flag, from->name, to->name);
+ fprintf(stdout, "%c\t%s:%s\t", flag, from->name, to_name);
else
- fprintf(stdout, "%c\t:%s\t", flag, to->name);
+ fprintf(stdout, "%c\t:%s\t", flag, to_name);
if (msg)
fprintf(stdout, "%s (%s)\n", summary, msg);
else
@@ -480,9 +506,11 @@ static void print_ref_status(char flag, const char *summary,
fprintf(stderr, " %s%c %-*s%s ", red, flag, summary_width,
summary, reset);
if (from)
- fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
+ fprintf(stderr, "%s -> %s",
+ prettify_refname(from->name),
+ prettify_refname(to_name));
else
- fputs(prettify_refname(to->name), stderr);
+ fputs(prettify_refname(to_name), stderr);
if (msg) {
fputs(" (", stderr);
fputs(msg, stderr);
@@ -492,24 +520,52 @@ static void print_ref_status(char flag, const char *summary,
}
}
-static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_width)
+static void print_ok_ref_status(struct ref *ref,
+ struct ref_push_report *report,
+ int porcelain, int summary_width)
{
+ struct object_id *old_oid;
+ struct object_id *new_oid;
+ const char *ref_name;
+ int forced_update;
+
+ if (report && report->old_oid)
+ old_oid = report->old_oid;
+ else
+ old_oid = &ref->old_oid;
+ if (report && report->new_oid)
+ new_oid = report->new_oid;
+ else
+ new_oid = &ref->new_oid;
+ if (report && report->forced_update)
+ forced_update = report->forced_update;
+ else
+ forced_update = ref->forced_update;
+ if (report && report->ref_name)
+ ref_name = report->ref_name;
+ else
+ ref_name = ref->name;
+
if (ref->deletion)
print_ref_status('-', "[deleted]", ref, NULL, NULL,
- porcelain, summary_width);
- else if (is_null_oid(&ref->old_oid))
+ report, porcelain, summary_width);
+ else if (is_null_oid(old_oid))
print_ref_status('*',
- (starts_with(ref->name, "refs/tags/") ? "[new tag]" :
- "[new branch]"),
- ref, ref->peer_ref, NULL, porcelain, summary_width);
+ (starts_with(ref_name, "refs/tags/")
+ ? "[new tag]"
+ : (starts_with(ref_name, "refs/heads/")
+ ? "[new branch]"
+ : "[new reference]")),
+ ref, ref->peer_ref, NULL,
+ report, porcelain, summary_width);
else {
struct strbuf quickref = STRBUF_INIT;
char type;
const char *msg;
- strbuf_add_unique_abbrev(&quickref, &ref->old_oid,
+ strbuf_add_unique_abbrev(&quickref, old_oid,
DEFAULT_ABBREV);
- if (ref->forced_update) {
+ if (forced_update) {
strbuf_addstr(&quickref, "...");
type = '+';
msg = "forced update";
@@ -518,16 +574,17 @@ static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_widt
type = ' ';
msg = NULL;
}
- strbuf_add_unique_abbrev(&quickref, &ref->new_oid,
+ strbuf_add_unique_abbrev(&quickref, new_oid,
DEFAULT_ABBREV);
print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg,
- porcelain, summary_width);
+ report, porcelain, summary_width);
strbuf_release(&quickref);
}
}
-static int print_one_push_status(struct ref *ref, const char *dest, int count,
+static int print_one_push_report(struct ref *ref, const char *dest, int count,
+ struct ref_push_report *report,
int porcelain, int summary_width)
{
if (!count) {
@@ -539,65 +596,89 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count,
switch(ref->status) {
case REF_STATUS_NONE:
print_ref_status('X', "[no match]", ref, NULL, NULL,
- porcelain, summary_width);
+ report, porcelain, summary_width);
break;
case REF_STATUS_REJECT_NODELETE:
print_ref_status('!', "[rejected]", ref, NULL,
"remote does not support deleting refs",
- porcelain, summary_width);
+ report, porcelain, summary_width);
break;
case REF_STATUS_UPTODATE:
print_ref_status('=', "[up to date]", ref,
- ref->peer_ref, NULL, porcelain, summary_width);
+ ref->peer_ref, NULL,
+ report, porcelain, summary_width);
break;
case REF_STATUS_REJECT_NONFASTFORWARD:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "non-fast-forward", porcelain, summary_width);
+ "non-fast-forward",
+ report, porcelain, summary_width);
break;
case REF_STATUS_REJECT_ALREADY_EXISTS:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "already exists", porcelain, summary_width);
+ "already exists",
+ report, porcelain, summary_width);
break;
case REF_STATUS_REJECT_FETCH_FIRST:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "fetch first", porcelain, summary_width);
+ "fetch first",
+ report, porcelain, summary_width);
break;
case REF_STATUS_REJECT_NEEDS_FORCE:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "needs force", porcelain, summary_width);
+ "needs force",
+ report, porcelain, summary_width);
break;
case REF_STATUS_REJECT_STALE:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "stale info", porcelain, summary_width);
+ "stale info",
+ report, porcelain, summary_width);
break;
case REF_STATUS_REJECT_SHALLOW:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"new shallow roots not allowed",
- porcelain, summary_width);
+ report, porcelain, summary_width);
break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
ref->deletion ? NULL : ref->peer_ref,
- ref->remote_status, porcelain, summary_width);
+ ref->remote_status,
+ report, porcelain, summary_width);
break;
case REF_STATUS_EXPECTING_REPORT:
print_ref_status('!', "[remote failure]", ref,
ref->deletion ? NULL : ref->peer_ref,
"remote failed to report status",
- porcelain, summary_width);
+ report, porcelain, summary_width);
break;
case REF_STATUS_ATOMIC_PUSH_FAILED:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "atomic push failed", porcelain, summary_width);
+ "atomic push failed",
+ report, porcelain, summary_width);
break;
case REF_STATUS_OK:
- print_ok_ref_status(ref, porcelain, summary_width);
+ print_ok_ref_status(ref, report, porcelain, summary_width);
break;
}
return 1;
}
+static int print_one_push_status(struct ref *ref, const char *dest, int count,
+ int porcelain, int summary_width)
+{
+ struct ref_push_report *report;
+ int n = 0;
+
+ if (!ref->report)
+ return print_one_push_report(ref, dest, count,
+ NULL, porcelain, summary_width);
+
+ for (report = ref->report; report; report = report->next)
+ print_one_push_report(ref, dest, count + n++,
+ report, porcelain, summary_width);
+ return n;
+}
+
static int measure_abbrev(const struct object_id *oid, int sofar)
{
char hex[GIT_MAX_HEXSZ + 1];
@@ -1153,7 +1234,7 @@ int transport_push(struct repository *r,
int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
int pretend = flags & TRANSPORT_PUSH_DRY_RUN;
int push_ret, ret, err;
- struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+ struct strvec ref_prefixes = STRVEC_INIT;
if (check_push_refs(local_refs, rs) < 0)
return -1;
@@ -1165,7 +1246,7 @@ int transport_push(struct repository *r,
&ref_prefixes);
trace2_region_leave("transport_push", "get_refs_list", r);
- argv_array_clear(&ref_prefixes);
+ strvec_clear(&ref_prefixes);
if (flags & TRANSPORT_PUSH_ALL)
match_flags |= MATCH_REFS_ALL;
@@ -1281,7 +1362,7 @@ int transport_push(struct repository *r,
}
const struct ref *transport_get_remote_refs(struct transport *transport,
- const struct argv_array *ref_prefixes)
+ const struct strvec *ref_prefixes)
{
if (!transport->got_remote_refs) {
transport->remote_refs =
diff --git a/transport.h b/transport.h
index b3c30133ea..ca409ea1e4 100644
--- a/transport.h
+++ b/transport.h
@@ -15,8 +15,9 @@ struct git_transport_options {
unsigned self_contained_and_connected : 1;
unsigned update_shallow : 1;
unsigned deepen_relative : 1;
+
+ /* see documentation of corresponding flag in fetch-pack.h */
unsigned from_promisor : 1;
- unsigned no_dependents : 1;
/*
* If this transport supports connect or stateless-connect,
@@ -201,12 +202,6 @@ void transport_check_allowed(const char *type);
/* Indicate that these objects are being fetched by a promisor */
#define TRANS_OPT_FROM_PROMISOR "from-promisor"
-/*
- * Indicate that only the objects wanted need to be fetched, not their
- * dependents
- */
-#define TRANS_OPT_NO_DEPENDENTS "no-dependents"
-
/* Filter objects for partial clone and fetch */
#define TRANS_OPT_LIST_OBJECTS_FILTER "filter"
@@ -243,7 +238,7 @@ int transport_push(struct repository *repo,
* ref_prefixes.
*/
const struct ref *transport_get_remote_refs(struct transport *transport,
- const struct argv_array *ref_prefixes);
+ const struct strvec *ref_prefixes);
/*
* Fetch the hash algorithm used by a remote.
diff --git a/tree-diff.c b/tree-diff.c
index f3d303c6e5..7cebbb327e 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -29,9 +29,9 @@ static struct combine_diff_path *ll_diff_tree_paths(
struct combine_diff_path *p, const struct object_id *oid,
const struct object_id **parents_oid, int nparent,
struct strbuf *base, struct diff_options *opt);
-static int ll_diff_tree_oid(const struct object_id *old_oid,
- const struct object_id *new_oid,
- struct strbuf *base, struct diff_options *opt);
+static void ll_diff_tree_oid(const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ struct strbuf *base, struct diff_options *opt);
/*
* Compare two tree entries, taking into account only path/S_ISDIR(mode),
@@ -434,7 +434,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
if (diff_can_quit_early(opt))
break;
- if (opt->max_changes && opt->num_changes > opt->max_changes)
+ if (opt->max_changes && diff_queued_diff.nr > opt->max_changes)
break;
if (opt->pathspec.nr) {
@@ -521,7 +521,6 @@ static struct combine_diff_path *ll_diff_tree_paths(
/* t↓ */
update_tree_entry(&t);
- opt->num_changes++;
}
/* t > p[imin] */
@@ -539,7 +538,6 @@ static struct combine_diff_path *ll_diff_tree_paths(
skip_emit_tp:
/* ∀ pi=p[imin] pi↓ */
update_tp_entries(tp, nparent);
- opt->num_changes++;
}
}
@@ -557,7 +555,6 @@ struct combine_diff_path *diff_tree_paths(
const struct object_id **parents_oid, int nparent,
struct strbuf *base, struct diff_options *opt)
{
- opt->num_changes = 0;
p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt);
/*
@@ -679,9 +676,9 @@ static void try_to_follow_renames(const struct object_id *old_oid,
q->nr = 1;
}
-static int ll_diff_tree_oid(const struct object_id *old_oid,
- const struct object_id *new_oid,
- struct strbuf *base, struct diff_options *opt)
+static void ll_diff_tree_oid(const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ struct strbuf *base, struct diff_options *opt)
{
struct combine_diff_path phead, *p;
pathchange_fn_t pathchange_old = opt->pathchange;
@@ -697,29 +694,27 @@ static int ll_diff_tree_oid(const struct object_id *old_oid,
}
opt->pathchange = pathchange_old;
- return 0;
}
-int diff_tree_oid(const struct object_id *old_oid,
- const struct object_id *new_oid,
- const char *base_str, struct diff_options *opt)
+void diff_tree_oid(const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ const char *base_str, struct diff_options *opt)
{
struct strbuf base;
- int retval;
strbuf_init(&base, PATH_MAX);
strbuf_addstr(&base, base_str);
- retval = ll_diff_tree_oid(old_oid, new_oid, &base, opt);
+ ll_diff_tree_oid(old_oid, new_oid, &base, opt);
if (!*base_str && opt->flags.follow_renames && diff_might_be_rename())
try_to_follow_renames(old_oid, new_oid, &base, opt);
strbuf_release(&base);
-
- return retval;
}
-int diff_root_tree_oid(const struct object_id *new_oid, const char *base, struct diff_options *opt)
+void diff_root_tree_oid(const struct object_id *new_oid,
+ const char *base,
+ struct diff_options *opt)
{
- return diff_tree_oid(NULL, new_oid, base, opt);
+ diff_tree_oid(NULL, new_oid, base, opt);
}
diff --git a/tree-walk.c b/tree-walk.c
index bb0ad34c54..0160294712 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -851,7 +851,14 @@ static int match_entry(const struct pathspec_item *item,
if (matchlen > pathlen) {
if (match[pathlen] != '/')
return 0;
- if (!S_ISDIR(entry->mode) && !S_ISGITLINK(entry->mode))
+ /*
+ * Reject non-directories as partial pathnames, except
+ * when match is a submodule with a trailing slash and
+ * nothing else (to handle 'submod/' and 'submod'
+ * uniformly).
+ */
+ if (!S_ISDIR(entry->mode) &&
+ (!S_ISGITLINK(entry->mode) || matchlen > pathlen + 1))
return 0;
}
diff --git a/unpack-trees.c b/unpack-trees.c
index 4be5fc3075..323280dd48 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,5 +1,5 @@
#include "cache.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "repository.h"
#include "config.h"
#include "dir.h"
@@ -106,7 +106,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
const char **msgs = opts->msgs;
const char *msg;
- argv_array_init(&opts->msgs_to_free);
+ strvec_init(&opts->msgs_to_free);
if (!strcmp(cmd, "checkout"))
msg = advice_commit_before_merge
@@ -124,7 +124,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
"Please commit your changes or stash them before you %s.")
: _("Your local changes to the following files would be overwritten by %s:\n%%s");
msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
- argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+ strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
msgs[ERROR_NOT_UPTODATE_DIR] =
_("Updating the following directories would lose untracked files in them:\n%s");
@@ -145,7 +145,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
"Please move or remove them before you %s.")
: _("The following untracked working tree files would be removed by %s:\n%%s");
msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] =
- argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+ strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
if (!strcmp(cmd, "checkout"))
msg = advice_commit_before_merge
@@ -163,7 +163,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
"Please move or remove them before you %s.")
: _("The following untracked working tree files would be overwritten by %s:\n%%s");
msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] =
- argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+ strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
/*
* Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@ -189,7 +189,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
void clear_unpack_trees_porcelain(struct unpack_trees_options *opts)
{
- argv_array_clear(&opts->msgs_to_free);
+ strvec_clear(&opts->msgs_to_free);
memset(opts->msgs, 0, sizeof(opts->msgs));
}
diff --git a/unpack-trees.h b/unpack-trees.h
index 9c2f08277e..2e87875b15 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -2,7 +2,7 @@
#define UNPACK_TREES_H
#include "cache.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "string-list.h"
#include "tree-walk.h"
@@ -70,7 +70,7 @@ struct unpack_trees_options {
struct pathspec *pathspec;
merge_fn_t fn;
const char *msgs[NB_UNPACK_TREES_WARNING_TYPES];
- struct argv_array msgs_to_free;
+ struct strvec msgs_to_free;
/*
* Store error messages in an array, each case
* corresponding to a error message type
diff --git a/upload-pack.c b/upload-pack.c
index 951a2b23aa..3b858eb457 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -18,7 +18,7 @@
#include "sigchain.h"
#include "version.h"
#include "string-list.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "prio-queue.h"
#include "protocol.h"
#include "quote.h"
@@ -88,6 +88,7 @@ struct upload_pack_data {
enum allow_uor allow_uor;
struct list_objects_filter_options filter_options;
+ struct string_list allowed_filters;
struct packet_writer writer;
@@ -103,6 +104,8 @@ struct upload_pack_data {
unsigned no_progress : 1;
unsigned use_include_tag : 1;
unsigned allow_filter : 1;
+ unsigned allow_filter_fallback : 1;
+ unsigned long tree_filter_max_depth;
unsigned done : 1; /* v2 only */
unsigned allow_ref_in_want : 1; /* v2 only */
@@ -120,6 +123,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
struct string_list deepen_not = STRING_LIST_INIT_DUP;
struct string_list uri_protocols = STRING_LIST_INIT_DUP;
struct object_array extra_edge_obj = OBJECT_ARRAY_INIT;
+ struct string_list allowed_filters = STRING_LIST_INIT_DUP;
memset(data, 0, sizeof(*data));
data->symref = symref;
@@ -131,6 +135,9 @@ static void upload_pack_data_init(struct upload_pack_data *data)
data->deepen_not = deepen_not;
data->uri_protocols = uri_protocols;
data->extra_edge_obj = extra_edge_obj;
+ data->allowed_filters = allowed_filters;
+ data->allow_filter_fallback = 1;
+ data->tree_filter_max_depth = ULONG_MAX;
packet_writer_init(&data->writer, 1);
data->keepalive = 5;
@@ -147,6 +154,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
string_list_clear(&data->deepen_not, 0);
object_array_clear(&data->extra_edge_obj);
list_objects_filter_release(&data->filter_options);
+ string_list_clear(&data->allowed_filters, 1);
free((char *)data->pack_objects_hook);
}
@@ -269,45 +277,44 @@ static void create_pack_file(struct upload_pack_data *pack_data,
if (!pack_data->pack_objects_hook)
pack_objects.git_cmd = 1;
else {
- argv_array_push(&pack_objects.args, pack_data->pack_objects_hook);
- argv_array_push(&pack_objects.args, "git");
+ strvec_push(&pack_objects.args, pack_data->pack_objects_hook);
+ strvec_push(&pack_objects.args, "git");
pack_objects.use_shell = 1;
}
if (pack_data->shallow_nr) {
- argv_array_push(&pack_objects.args, "--shallow-file");
- argv_array_push(&pack_objects.args, "");
+ strvec_push(&pack_objects.args, "--shallow-file");
+ strvec_push(&pack_objects.args, "");
}
- argv_array_push(&pack_objects.args, "pack-objects");
- argv_array_push(&pack_objects.args, "--revs");
+ strvec_push(&pack_objects.args, "pack-objects");
+ strvec_push(&pack_objects.args, "--revs");
if (pack_data->use_thin_pack)
- argv_array_push(&pack_objects.args, "--thin");
+ strvec_push(&pack_objects.args, "--thin");
- argv_array_push(&pack_objects.args, "--stdout");
+ strvec_push(&pack_objects.args, "--stdout");
if (pack_data->shallow_nr)
- argv_array_push(&pack_objects.args, "--shallow");
+ strvec_push(&pack_objects.args, "--shallow");
if (!pack_data->no_progress)
- argv_array_push(&pack_objects.args, "--progress");
+ strvec_push(&pack_objects.args, "--progress");
if (pack_data->use_ofs_delta)
- argv_array_push(&pack_objects.args, "--delta-base-offset");
+ strvec_push(&pack_objects.args, "--delta-base-offset");
if (pack_data->use_include_tag)
- argv_array_push(&pack_objects.args, "--include-tag");
+ strvec_push(&pack_objects.args, "--include-tag");
if (pack_data->filter_options.choice) {
const char *spec =
expand_list_objects_filter_spec(&pack_data->filter_options);
if (pack_objects.use_shell) {
struct strbuf buf = STRBUF_INIT;
sq_quote_buf(&buf, spec);
- argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf);
+ strvec_pushf(&pack_objects.args, "--filter=%s", buf.buf);
strbuf_release(&buf);
} else {
- argv_array_pushf(&pack_objects.args, "--filter=%s",
- spec);
+ strvec_pushf(&pack_objects.args, "--filter=%s", spec);
}
}
if (uri_protocols) {
for (i = 0; i < uri_protocols->nr; i++)
- argv_array_pushf(&pack_objects.args, "--uri-protocol=%s",
+ strvec_pushf(&pack_objects.args, "--uri-protocol=%s",
uri_protocols->items[i].string);
}
@@ -482,7 +489,8 @@ static int got_oid(struct upload_pack_data *data,
{
if (get_oid_hex(hex, oid))
die("git upload-pack: expected SHA1 object, got '%s'", hex);
- if (!has_object_file(oid))
+ if (!has_object_file_with_flags(oid,
+ OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
return -1;
return do_got_oid(data, oid);
}
@@ -595,9 +603,8 @@ static int do_reachable_revlist(struct child_process *cmd,
"rev-list", "--stdin", NULL,
};
struct object *o;
- char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */
+ FILE *cmd_in = NULL;
int i;
- const unsigned hexsz = the_hash_algo->hexsz;
cmd->argv = argv;
cmd->git_cmd = 1;
@@ -615,8 +622,8 @@ static int do_reachable_revlist(struct child_process *cmd,
if (start_command(cmd))
goto error;
- namebuf[0] = '^';
- namebuf[hexsz + 1] = '\n';
+ cmd_in = xfdopen(cmd->in, "w");
+
for (i = get_max_object_index(); 0 < i; ) {
o = get_indexed_object(--i);
if (!o)
@@ -625,11 +632,9 @@ static int do_reachable_revlist(struct child_process *cmd,
o->flags &= ~TMP_MARK;
if (!is_our_ref(o, allow_uor))
continue;
- memcpy(namebuf + 1, oid_to_hex(&o->oid), hexsz);
- if (write_in_full(cmd->in, namebuf, hexsz + 2) < 0)
+ if (fprintf(cmd_in, "^%s\n", oid_to_hex(&o->oid)) < 0)
goto error;
}
- namebuf[hexsz] = '\n';
for (i = 0; i < src->nr; i++) {
o = src->objects[i].item;
if (is_our_ref(o, allow_uor)) {
@@ -639,11 +644,12 @@ static int do_reachable_revlist(struct child_process *cmd,
}
if (reachable && o->type == OBJ_COMMIT)
o->flags |= TMP_MARK;
- memcpy(namebuf, oid_to_hex(&o->oid), hexsz);
- if (write_in_full(cmd->in, namebuf, hexsz + 1) < 0)
+ if (fprintf(cmd_in, "%s\n", oid_to_hex(&o->oid)) < 0)
goto error;
}
- close(cmd->in);
+ if (ferror(cmd_in) || fflush(cmd_in))
+ goto error;
+ fclose(cmd_in);
cmd->in = -1;
sigchain_pop(SIGPIPE);
@@ -652,8 +658,8 @@ static int do_reachable_revlist(struct child_process *cmd,
error:
sigchain_pop(SIGPIPE);
- if (cmd->in >= 0)
- close(cmd->in);
+ if (cmd_in)
+ fclose(cmd_in);
if (cmd->out >= 0)
close(cmd->out);
return -1;
@@ -731,7 +737,6 @@ static int has_unreachable(struct object_array *src, enum allow_uor allow_uor)
return 0;
error:
- sigchain_pop(SIGPIPE);
if (cmd.out >= 0)
close(cmd.out);
return 1;
@@ -880,26 +885,26 @@ static int send_shallow_list(struct upload_pack_data *data)
deepen(data, data->depth);
ret = 1;
} else if (data->deepen_rev_list) {
- struct argv_array av = ARGV_ARRAY_INIT;
+ struct strvec av = STRVEC_INIT;
int i;
- argv_array_push(&av, "rev-list");
+ strvec_push(&av, "rev-list");
if (data->deepen_since)
- argv_array_pushf(&av, "--max-age=%"PRItime, data->deepen_since);
+ strvec_pushf(&av, "--max-age=%"PRItime, data->deepen_since);
if (data->deepen_not.nr) {
- argv_array_push(&av, "--not");
+ strvec_push(&av, "--not");
for (i = 0; i < data->deepen_not.nr; i++) {
struct string_list_item *s = data->deepen_not.items + i;
- argv_array_push(&av, s->string);
+ strvec_push(&av, s->string);
}
- argv_array_push(&av, "--not");
+ strvec_push(&av, "--not");
}
for (i = 0; i < data->want_obj.nr; i++) {
struct object *o = data->want_obj.objects[i].item;
- argv_array_push(&av, oid_to_hex(&o->oid));
+ strvec_push(&av, oid_to_hex(&o->oid));
}
- deepen_by_rev_list(data, av.argc, av.argv);
- argv_array_clear(&av);
+ deepen_by_rev_list(data, av.nr, av.v);
+ strvec_clear(&av);
ret = 1;
} else {
if (data->shallows.nr > 0) {
@@ -983,6 +988,63 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
return 0;
}
+NORETURN __attribute__((format(printf,2,3)))
+static void send_err_and_die(struct upload_pack_data *data,
+ const char *fmt, ...)
+{
+ struct strbuf buf = STRBUF_INIT;
+ va_list ap;
+
+ va_start(ap, fmt);
+ strbuf_vaddf(&buf, fmt, ap);
+ va_end(ap);
+
+ packet_writer_error(&data->writer, "%s", buf.buf);
+ die("%s", buf.buf);
+}
+
+static void check_one_filter(struct upload_pack_data *data,
+ struct list_objects_filter_options *opts)
+{
+ const char *key = list_object_filter_config_name(opts->choice);
+ struct string_list_item *item = string_list_lookup(&data->allowed_filters,
+ key);
+ int allowed;
+
+ if (item)
+ allowed = (intptr_t)item->util;
+ else
+ allowed = data->allow_filter_fallback;
+
+ if (!allowed)
+ send_err_and_die(data, "filter '%s' not supported", key);
+
+ if (opts->choice == LOFC_TREE_DEPTH &&
+ opts->tree_exclude_depth > data->tree_filter_max_depth)
+ send_err_and_die(data,
+ "tree filter allows max depth %lu, but got %lu",
+ data->tree_filter_max_depth,
+ opts->tree_exclude_depth);
+}
+
+static void check_filter_recurse(struct upload_pack_data *data,
+ struct list_objects_filter_options *opts)
+{
+ size_t i;
+
+ check_one_filter(data, opts);
+ if (opts->choice != LOFC_COMBINE)
+ return;
+
+ for (i = 0; i < opts->sub_nr; i++)
+ check_filter_recurse(data, &opts->sub[i]);
+}
+
+static void die_if_using_banned_filter(struct upload_pack_data *data)
+{
+ check_filter_recurse(data, &data->filter_options);
+}
+
static void receive_needs(struct upload_pack_data *data,
struct packet_reader *reader)
{
@@ -1013,6 +1075,7 @@ static void receive_needs(struct upload_pack_data *data,
die("git upload-pack: filtering capability not negotiated");
list_objects_filter_die_if_populated(&data->filter_options);
parse_list_objects_filter(&data->filter_options, arg);
+ die_if_using_banned_filter(data);
continue;
}
@@ -1170,6 +1233,41 @@ static int find_symref(const char *refname, const struct object_id *oid,
return 0;
}
+static int parse_object_filter_config(const char *var, const char *value,
+ struct upload_pack_data *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *sub, *key;
+ size_t sub_len;
+
+ if (parse_config_key(var, "uploadpackfilter", &sub, &sub_len, &key))
+ return 0;
+
+ if (!sub) {
+ if (!strcmp(key, "allow"))
+ data->allow_filter_fallback = git_config_bool(var, value);
+ return 0;
+ }
+
+ strbuf_add(&buf, sub, sub_len);
+
+ if (!strcmp(key, "allow"))
+ string_list_insert(&data->allowed_filters, buf.buf)->util =
+ (void *)(intptr_t)git_config_bool(var, value);
+ else if (!strcmp(buf.buf, "tree") && !strcmp(key, "maxdepth")) {
+ if (!value) {
+ strbuf_release(&buf);
+ return config_error_nonbool(var);
+ }
+ string_list_insert(&data->allowed_filters, buf.buf)->util =
+ (void *)(intptr_t)1;
+ data->tree_filter_max_depth = git_config_ulong(var, value);
+ }
+
+ strbuf_release(&buf);
+ return 0;
+}
+
static int upload_pack_config(const char *var, const char *value, void *cb_data)
{
struct upload_pack_data *data = cb_data;
@@ -1209,6 +1307,8 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
return git_config_string(&data->pack_objects_hook, var, value);
}
+ parse_object_filter_config(var, value, data);
+
return parse_hide_refs_config(var, value, "uploadpack");
}
@@ -1389,6 +1489,7 @@ static void process_args(struct packet_reader *request,
if (data->allow_filter && skip_prefix(arg, "filter ", &p)) {
list_objects_filter_die_if_populated(&data->filter_options);
parse_list_objects_filter(&data->filter_options, p);
+ die_if_using_banned_filter(data);
continue;
}
@@ -1423,7 +1524,8 @@ static int process_haves(struct upload_pack_data *data, struct oid_array *common
for (i = 0; i < data->haves.nr; i++) {
const struct object_id *oid = &data->haves.oid[i];
- if (!has_object_file(oid))
+ if (!has_object_file_with_flags(oid,
+ OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
continue;
oid_array_append(common, oid);
@@ -1521,7 +1623,7 @@ enum fetch_state {
FETCH_DONE,
};
-int upload_pack_v2(struct repository *r, struct argv_array *keys,
+int upload_pack_v2(struct repository *r, struct strvec *keys,
struct packet_reader *request)
{
enum fetch_state state = FETCH_PROCESS_ARGS;
diff --git a/upload-pack.h b/upload-pack.h
index 4bafe16a22..27ddcdc6cb 100644
--- a/upload-pack.h
+++ b/upload-pack.h
@@ -11,9 +11,9 @@ struct upload_pack_options {
void upload_pack(struct upload_pack_options *options);
struct repository;
-struct argv_array;
+struct strvec;
struct packet_reader;
-int upload_pack_v2(struct repository *r, struct argv_array *keys,
+int upload_pack_v2(struct repository *r, struct strvec *keys,
struct packet_reader *request);
struct strbuf;
diff --git a/userdiff.c b/userdiff.c
index 1df884ef0b..fde02f225b 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -46,10 +46,14 @@ PATTERNS("elixir",
/* Not real operators, but should be grouped */
"|:?%[A-Za-z0-9_.]\\{\\}?"),
IPATTERN("fortran",
+ /* Don't match comment lines */
"!^([C*]|[ \t]*!)\n"
+ /* Don't match 'module procedure' lines */
"!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
+ /* Program, module, block data */
"^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA"
- "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$",
+ /* Subroutines and functions */
+ "|([^!'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$",
/* -- */
"[a-zA-Z][a-zA-Z0-9_]*"
"|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\."
diff --git a/vcs-svn/LICENSE b/vcs-svn/LICENSE
deleted file mode 100644
index eb91858b82..0000000000
--- a/vcs-svn/LICENSE
+++ /dev/null
@@ -1,32 +0,0 @@
-Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
-All rights reserved.
-
-Copyright (C) 2010 Jonathan Nieder <jrnieder@gmail.com>.
-
-Copyright (C) 2005 Stefan Hegny, hydrografix Consulting GmbH,
-Frankfurt/Main, Germany
-and others, see http://svn2cc.sarovar.org
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice(s), this list of conditions and the following disclaimer
- unmodified other than the allowable addition of one or more
- copyright notices.
-2. Redistributions in binary form must reproduce the above copyright
- notice(s), this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
deleted file mode 100644
index b5b8913cb0..0000000000
--- a/vcs-svn/fast_export.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "cache.h"
-#include "quote.h"
-#include "fast_export.h"
-#include "strbuf.h"
-#include "svndiff.h"
-#include "sliding_window.h"
-#include "line_buffer.h"
-
-#define MAX_GITSVN_LINE_LEN 4096
-
-static uint32_t first_commit_done;
-static struct line_buffer postimage = LINE_BUFFER_INIT;
-static struct line_buffer report_buffer = LINE_BUFFER_INIT;
-
-/* NEEDSWORK: move to fast_export_init() */
-static int init_postimage(void)
-{
- static int postimage_initialized;
- if (postimage_initialized)
- return 0;
- postimage_initialized = 1;
- return buffer_tmpfile_init(&postimage);
-}
-
-void fast_export_init(int fd)
-{
- first_commit_done = 0;
- if (buffer_fdinit(&report_buffer, fd))
- die_errno("cannot read from file descriptor %d", fd);
-}
-
-void fast_export_deinit(void)
-{
- if (buffer_deinit(&report_buffer))
- die_errno("error closing fast-import feedback stream");
-}
-
-void fast_export_delete(const char *path)
-{
- putchar('D');
- putchar(' ');
- quote_c_style(path, NULL, stdout, 0);
- putchar('\n');
-}
-
-static void fast_export_truncate(const char *path, uint32_t mode)
-{
- fast_export_modify(path, mode, "inline");
- printf("data 0\n\n");
-}
-
-void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
-{
- /* Mode must be 100644, 100755, 120000, or 160000. */
- if (!dataref) {
- fast_export_truncate(path, mode);
- return;
- }
- printf("M %06"PRIo32" %s ", mode, dataref);
- quote_c_style(path, NULL, stdout, 0);
- putchar('\n');
-}
-
-void fast_export_begin_note(uint32_t revision, const char *author,
- const char *log, timestamp_t timestamp, const char *note_ref)
-{
- static int firstnote = 1;
- size_t loglen = strlen(log);
- printf("commit %s\n", note_ref);
- printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
- printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
- fwrite(log, loglen, 1, stdout);
- if (firstnote) {
- if (revision > 1)
- printf("from %s^0", note_ref);
- firstnote = 0;
- }
- fputc('\n', stdout);
-}
-
-void fast_export_note(const char *committish, const char *dataref)
-{
- printf("N %s %s\n", dataref, committish);
-}
-
-static char gitsvnline[MAX_GITSVN_LINE_LEN];
-void fast_export_begin_commit(uint32_t revision, const char *author,
- const struct strbuf *log,
- const char *uuid, const char *url,
- timestamp_t timestamp, const char *local_ref)
-{
- static const struct strbuf empty = STRBUF_INIT;
- if (!log)
- log = &empty;
- if (*uuid && *url) {
- snprintf(gitsvnline, MAX_GITSVN_LINE_LEN,
- "\n\ngit-svn-id: %s@%"PRIu32" %s\n",
- url, revision, uuid);
- } else {
- *gitsvnline = '\0';
- }
- printf("commit %s\n", local_ref);
- printf("mark :%"PRIu32"\n", revision);
- printf("committer %s <%s@%s> %"PRItime" +0000\n",
- *author ? author : "nobody",
- *author ? author : "nobody",
- *uuid ? uuid : "local", timestamp);
- printf("data %"PRIuMAX"\n",
- (uintmax_t) (log->len + strlen(gitsvnline)));
- fwrite(log->buf, log->len, 1, stdout);
- printf("%s\n", gitsvnline);
- if (!first_commit_done) {
- if (revision > 1)
- printf("from :%"PRIu32"\n", revision - 1);
- first_commit_done = 1;
- }
-}
-
-void fast_export_end_commit(uint32_t revision)
-{
- printf("progress Imported commit %"PRIu32".\n\n", revision);
-}
-
-static void ls_from_rev(uint32_t rev, const char *path)
-{
- /* ls :5 path/to/old/file */
- printf("ls :%"PRIu32" ", rev);
- quote_c_style(path, NULL, stdout, 0);
- putchar('\n');
- fflush(stdout);
-}
-
-static void ls_from_active_commit(const char *path)
-{
- /* ls "path/to/file" */
- printf("ls \"");
- quote_c_style(path, NULL, stdout, 1);
- printf("\"\n");
- fflush(stdout);
-}
-
-static const char *get_response_line(void)
-{
- const char *line = buffer_read_line(&report_buffer);
- if (line)
- return line;
- if (buffer_ferror(&report_buffer))
- die_errno("error reading from fast-import");
- die("unexpected end of fast-import feedback");
-}
-
-static void die_short_read(struct line_buffer *input)
-{
- if (buffer_ferror(input))
- die_errno("error reading dump file");
- die("invalid dump: unexpected end of file");
-}
-
-static int parse_cat_response_line(const char *header, off_t *len)
-{
- uintmax_t n;
- const char *type;
- const char *end;
-
- if (ends_with(header, " missing"))
- return error("cat-blob reports missing blob: %s", header);
- type = strstr(header, " blob ");
- if (!type)
- return error("cat-blob header has wrong object type: %s", header);
- n = strtoumax(type + strlen(" blob "), (char **) &end, 10);
- if (end == type + strlen(" blob "))
- return error("cat-blob header does not contain length: %s", header);
- if (memchr(type + strlen(" blob "), '-', end - type - strlen(" blob ")))
- return error("cat-blob header contains negative length: %s", header);
- if (n == UINTMAX_MAX || n > maximum_signed_value_of_type(off_t))
- return error("blob too large for current definition of off_t");
- *len = n;
- if (*end)
- return error("cat-blob header contains garbage after length: %s", header);
- return 0;
-}
-
-static void check_preimage_overflow(off_t a, off_t b)
-{
- if (signed_add_overflows(a, b))
- die("blob too large for current definition of off_t");
-}
-
-static long apply_delta(off_t len, struct line_buffer *input,
- const char *old_data, uint32_t old_mode)
-{
- long ret;
- struct sliding_view preimage = SLIDING_VIEW_INIT(&report_buffer, 0);
- FILE *out;
-
- if (init_postimage() || !(out = buffer_tmpfile_rewind(&postimage)))
- die("cannot open temporary file for blob retrieval");
- if (old_data) {
- const char *response;
- printf("cat-blob %s\n", old_data);
- fflush(stdout);
- response = get_response_line();
- if (parse_cat_response_line(response, &preimage.max_off))
- die("invalid cat-blob response: %s", response);
- check_preimage_overflow(preimage.max_off, 1);
- }
- if (old_mode == S_IFLNK) {
- strbuf_addstr(&preimage.buf, "link ");
- check_preimage_overflow(preimage.max_off, strlen("link "));
- preimage.max_off += strlen("link ");
- check_preimage_overflow(preimage.max_off, 1);
- }
- if (svndiff0_apply(input, len, &preimage, out))
- die("cannot apply delta");
- if (old_data) {
- /* Read the remainder of preimage and trailing newline. */
- assert(!signed_add_overflows(preimage.max_off, 1));
- preimage.max_off++; /* room for newline */
- if (move_window(&preimage, preimage.max_off - 1, 1))
- die("cannot seek to end of input");
- if (preimage.buf.buf[0] != '\n')
- die("missing newline after cat-blob response");
- }
- ret = buffer_tmpfile_prepare_to_read(&postimage);
- if (ret < 0)
- die("cannot read temporary file for blob retrieval");
- strbuf_release(&preimage.buf);
- return ret;
-}
-
-void fast_export_buf_to_data(const struct strbuf *data)
-{
- printf("data %"PRIuMAX"\n", (uintmax_t)data->len);
- fwrite(data->buf, data->len, 1, stdout);
- fputc('\n', stdout);
-}
-
-void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input)
-{
- assert(len >= 0);
- if (mode == S_IFLNK) {
- /* svn symlink blobs start with "link " */
- if (len < 5)
- die("invalid dump: symlink too short for \"link\" prefix");
- len -= 5;
- if (buffer_skip_bytes(input, 5) != 5)
- die_short_read(input);
- }
- printf("data %"PRIuMAX"\n", (uintmax_t) len);
- if (buffer_copy_bytes(input, len) != len)
- die_short_read(input);
- fputc('\n', stdout);
-}
-
-static int parse_ls_response(const char *response, uint32_t *mode,
- struct strbuf *dataref)
-{
- const char *tab;
- const char *response_end;
-
- assert(response);
- response_end = response + strlen(response);
-
- if (*response == 'm') { /* Missing. */
- errno = ENOENT;
- return -1;
- }
-
- /* Mode. */
- if (response_end - response < (signed) strlen("100644") ||
- response[strlen("100644")] != ' ')
- die("invalid ls response: missing mode: %s", response);
- *mode = 0;
- for (; *response != ' '; response++) {
- char ch = *response;
- if (ch < '0' || ch > '7')
- die("invalid ls response: mode is not octal: %s", response);
- *mode *= 8;
- *mode += ch - '0';
- }
-
- /* ' blob ' or ' tree ' */
- if (response_end - response < (signed) strlen(" blob ") ||
- (response[1] != 'b' && response[1] != 't'))
- die("unexpected ls response: not a tree or blob: %s", response);
- response += strlen(" blob ");
-
- /* Dataref. */
- tab = memchr(response, '\t', response_end - response);
- if (!tab)
- die("invalid ls response: missing tab: %s", response);
- strbuf_add(dataref, response, tab - response);
- return 0;
-}
-
-int fast_export_ls_rev(uint32_t rev, const char *path,
- uint32_t *mode, struct strbuf *dataref)
-{
- ls_from_rev(rev, path);
- return parse_ls_response(get_response_line(), mode, dataref);
-}
-
-int fast_export_ls(const char *path, uint32_t *mode, struct strbuf *dataref)
-{
- ls_from_active_commit(path);
- return parse_ls_response(get_response_line(), mode, dataref);
-}
-
-const char *fast_export_read_path(const char *path, uint32_t *mode_out)
-{
- int err;
- static struct strbuf buf = STRBUF_INIT;
-
- strbuf_reset(&buf);
- err = fast_export_ls(path, mode_out, &buf);
- if (err) {
- if (errno != ENOENT)
- BUG("unexpected fast_export_ls error: %s",
- strerror(errno));
- /* Treat missing paths as directories. */
- *mode_out = S_IFDIR;
- return NULL;
- }
- return buf.buf;
-}
-
-void fast_export_copy(uint32_t revision, const char *src, const char *dst)
-{
- int err;
- uint32_t mode;
- static struct strbuf data = STRBUF_INIT;
-
- strbuf_reset(&data);
- err = fast_export_ls_rev(revision, src, &mode, &data);
- if (err) {
- if (errno != ENOENT)
- BUG("unexpected fast_export_ls_rev error: %s",
- strerror(errno));
- fast_export_delete(dst);
- return;
- }
- fast_export_modify(dst, mode, data.buf);
-}
-
-void fast_export_blob_delta(uint32_t mode,
- uint32_t old_mode, const char *old_data,
- off_t len, struct line_buffer *input)
-{
- long postimage_len;
-
- assert(len >= 0);
- postimage_len = apply_delta(len, input, old_data, old_mode);
- if (mode == S_IFLNK) {
- buffer_skip_bytes(&postimage, strlen("link "));
- postimage_len -= strlen("link ");
- }
- printf("data %ld\n", postimage_len);
- buffer_copy_bytes(&postimage, postimage_len);
- fputc('\n', stdout);
-}
diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
deleted file mode 100644
index e416caf8a4..0000000000
--- a/vcs-svn/line_buffer.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "git-compat-util.h"
-#include "line_buffer.h"
-#include "strbuf.h"
-
-#define COPY_BUFFER_LEN 4096
-
-int buffer_init(struct line_buffer *buf, const char *filename)
-{
- buf->infile = filename ? fopen(filename, "r") : stdin;
- if (!buf->infile)
- return -1;
- return 0;
-}
-
-int buffer_fdinit(struct line_buffer *buf, int fd)
-{
- buf->infile = fdopen(fd, "r");
- if (!buf->infile)
- return -1;
- return 0;
-}
-
-int buffer_tmpfile_init(struct line_buffer *buf)
-{
- buf->infile = tmpfile();
- if (!buf->infile)
- return -1;
- return 0;
-}
-
-int buffer_deinit(struct line_buffer *buf)
-{
- int err;
- if (buf->infile == stdin)
- return ferror(buf->infile);
- err = ferror(buf->infile);
- err |= fclose(buf->infile);
- return err;
-}
-
-FILE *buffer_tmpfile_rewind(struct line_buffer *buf)
-{
- rewind(buf->infile);
- return buf->infile;
-}
-
-long buffer_tmpfile_prepare_to_read(struct line_buffer *buf)
-{
- long pos = ftell(buf->infile);
- if (pos < 0)
- return error_errno("ftell error");
- if (fseek(buf->infile, 0, SEEK_SET))
- return error_errno("seek error");
- return pos;
-}
-
-int buffer_ferror(struct line_buffer *buf)
-{
- return ferror(buf->infile);
-}
-
-int buffer_read_char(struct line_buffer *buf)
-{
- return fgetc(buf->infile);
-}
-
-/* Read a line without trailing newline. */
-char *buffer_read_line(struct line_buffer *buf)
-{
- char *end;
- if (!fgets(buf->line_buffer, sizeof(buf->line_buffer), buf->infile))
- /* Error or data exhausted. */
- return NULL;
- end = buf->line_buffer + strlen(buf->line_buffer);
- if (end[-1] == '\n')
- end[-1] = '\0';
- else if (feof(buf->infile))
- ; /* No newline at end of file. That's fine. */
- else
- /*
- * Line was too long.
- * There is probably a saner way to deal with this,
- * but for now let's return an error.
- */
- return NULL;
- return buf->line_buffer;
-}
-
-size_t buffer_read_binary(struct line_buffer *buf,
- struct strbuf *sb, size_t size)
-{
- return strbuf_fread(sb, size, buf->infile);
-}
-
-off_t buffer_copy_bytes(struct line_buffer *buf, off_t nbytes)
-{
- char byte_buffer[COPY_BUFFER_LEN];
- off_t done = 0;
- while (done < nbytes && !feof(buf->infile) && !ferror(buf->infile)) {
- off_t len = nbytes - done;
- size_t in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
- in = fread(byte_buffer, 1, in, buf->infile);
- done += in;
- fwrite(byte_buffer, 1, in, stdout);
- if (ferror(stdout))
- return done + buffer_skip_bytes(buf, nbytes - done);
- }
- return done;
-}
-
-off_t buffer_skip_bytes(struct line_buffer *buf, off_t nbytes)
-{
- char byte_buffer[COPY_BUFFER_LEN];
- off_t done = 0;
- while (done < nbytes && !feof(buf->infile) && !ferror(buf->infile)) {
- off_t len = nbytes - done;
- size_t in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
- done += fread(byte_buffer, 1, in, buf->infile);
- }
- return done;
-}
diff --git a/vcs-svn/line_buffer.txt b/vcs-svn/line_buffer.txt
deleted file mode 100644
index 8e139eb22d..0000000000
--- a/vcs-svn/line_buffer.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-line_buffer API
-===============
-
-The line_buffer library provides a convenient interface for
-mostly-line-oriented input.
-
-Each line is not permitted to exceed 10000 bytes. The provided
-functions are not thread-safe or async-signal-safe, and like
-`fgets()`, they generally do not function correctly if interrupted
-by a signal without SA_RESTART set.
-
-Calling sequence
-----------------
-
-The calling program:
-
- - initializes a `struct line_buffer` to LINE_BUFFER_INIT
- - specifies a file to read with `buffer_init`
- - processes input with `buffer_read_line`, `buffer_skip_bytes`,
- and `buffer_copy_bytes`
- - closes the file with `buffer_deinit`, perhaps to start over and
- read another file.
-
-When finished, the caller can use `buffer_reset` to deallocate
-resources.
-
-Using temporary files
----------------------
-
-Temporary files provide a place to store data that should not outlive
-the calling program. A program
-
- - initializes a `struct line_buffer` to LINE_BUFFER_INIT
- - requests a temporary file with `buffer_tmpfile_init`
- - acquires an output handle by calling `buffer_tmpfile_rewind`
- - uses standard I/O functions like `fprintf` and `fwrite` to fill
- the temporary file
- - declares writing is over with `buffer_tmpfile_prepare_to_read`
- - can re-read what was written with `buffer_read_line`,
- `buffer_copy_bytes`, and so on
- - can reuse the temporary file by calling `buffer_tmpfile_rewind`
- again
- - removes the temporary file with `buffer_deinit`, perhaps to
- reuse the line_buffer for some other file.
-
-When finished, the calling program can use `buffer_reset` to deallocate
-resources.
-
-Functions
----------
-
-`buffer_init`, `buffer_fdinit`::
- Open the named file or file descriptor for input.
- buffer_init(buf, NULL) prepares to read from stdin.
- On failure, returns -1 (with errno indicating the nature
- of the failure).
-
-`buffer_deinit`::
- Stop reading from the current file (closing it unless
- it was stdin). Returns nonzero if `fclose` fails or
- the error indicator was set.
-
-`buffer_read_line`::
- Read a line and strip off the trailing newline.
- On failure or end of file, returns NULL.
-
-`buffer_copy_bytes`::
- Read `len` bytes of input and dump them to the standard output
- stream. Returns early for error or end of file.
-
-`buffer_skip_bytes`::
- Discards `len` bytes from the input stream (stopping early
- if necessary because of an error or eof). Return value is
- the number of bytes successfully read.
-
-`buffer_reset`::
- Deallocates non-static buffers.
diff --git a/vcs-svn/sliding_window.c b/vcs-svn/sliding_window.c
deleted file mode 100644
index 06d273c9e8..0000000000
--- a/vcs-svn/sliding_window.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "git-compat-util.h"
-#include "sliding_window.h"
-#include "line_buffer.h"
-#include "strbuf.h"
-
-static int input_error(struct line_buffer *file)
-{
- if (!buffer_ferror(file))
- return error("delta preimage ends early");
- return error_errno("cannot read delta preimage");
-}
-
-static int skip_or_whine(struct line_buffer *file, off_t gap)
-{
- if (buffer_skip_bytes(file, gap) != gap)
- return input_error(file);
- return 0;
-}
-
-static int read_to_fill_or_whine(struct line_buffer *file,
- struct strbuf *buf, size_t width)
-{
- buffer_read_binary(file, buf, width - buf->len);
- if (buf->len != width)
- return input_error(file);
- return 0;
-}
-
-static int check_offset_overflow(off_t offset, uintmax_t len)
-{
- if (len > maximum_signed_value_of_type(off_t))
- return error("unrepresentable length in delta: "
- "%"PRIuMAX" > OFF_MAX", len);
- if (signed_add_overflows(offset, (off_t) len))
- return error("unrepresentable offset in delta: "
- "%"PRIuMAX" + %"PRIuMAX" > OFF_MAX",
- (uintmax_t) offset, len);
- return 0;
-}
-
-int move_window(struct sliding_view *view, off_t off, size_t width)
-{
- off_t file_offset;
- assert(view);
- assert(view->width <= view->buf.len);
- assert(!check_offset_overflow(view->off, view->buf.len));
-
- if (check_offset_overflow(off, width))
- return -1;
- if (off < view->off || off + width < view->off + view->width)
- return error("invalid delta: window slides left");
- if (view->max_off >= 0 && view->max_off < off + (off_t) width)
- return error("delta preimage ends early");
-
- file_offset = view->off + view->buf.len;
- if (off < file_offset) {
- /* Move the overlapping region into place. */
- strbuf_remove(&view->buf, 0, off - view->off);
- } else {
- /* Seek ahead to skip the gap. */
- if (skip_or_whine(view->file, off - file_offset))
- return -1;
- strbuf_setlen(&view->buf, 0);
- }
-
- if (view->buf.len > width)
- ; /* Already read. */
- else if (read_to_fill_or_whine(view->file, &view->buf, width))
- return -1;
-
- view->off = off;
- view->width = width;
- return 0;
-}
diff --git a/vcs-svn/svndiff.c b/vcs-svn/svndiff.c
deleted file mode 100644
index 75c753162a..0000000000
--- a/vcs-svn/svndiff.c
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "git-compat-util.h"
-#include "sliding_window.h"
-#include "line_buffer.h"
-#include "svndiff.h"
-
-/*
- * svndiff0 applier
- *
- * See http://svn.apache.org/repos/asf/subversion/trunk/notes/svndiff.
- *
- * svndiff0 ::= 'SVN\0' window*
- * window ::= int int int int int instructions inline_data;
- * instructions ::= instruction*;
- * instruction ::= view_selector int int
- * | copyfrom_data int
- * | packed_view_selector int
- * | packed_copyfrom_data
- * ;
- * view_selector ::= copyfrom_source
- * | copyfrom_target
- * ;
- * copyfrom_source ::= # binary 00 000000;
- * copyfrom_target ::= # binary 01 000000;
- * copyfrom_data ::= # binary 10 000000;
- * packed_view_selector ::= # view_selector OR-ed with 6 bit value;
- * packed_copyfrom_data ::= # copyfrom_data OR-ed with 6 bit value;
- * int ::= highdigit* lowdigit;
- * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
- * lowdigit ::= # 7 bit value;
- */
-
-#define INSN_MASK 0xc0
-#define INSN_COPYFROM_SOURCE 0x00
-#define INSN_COPYFROM_TARGET 0x40
-#define INSN_COPYFROM_DATA 0x80
-#define OPERAND_MASK 0x3f
-
-#define VLI_CONTINUE 0x80
-#define VLI_DIGIT_MASK 0x7f
-#define VLI_BITS_PER_DIGIT 7
-
-struct window {
- struct sliding_view *in;
- struct strbuf out;
- struct strbuf instructions;
- struct strbuf data;
-};
-
-#define WINDOW_INIT(w) { (w), STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }
-
-static void window_release(struct window *ctx)
-{
- strbuf_release(&ctx->out);
- strbuf_release(&ctx->instructions);
- strbuf_release(&ctx->data);
-}
-
-static int write_strbuf(struct strbuf *sb, FILE *out)
-{
- if (fwrite(sb->buf, 1, sb->len, out) == sb->len) /* Success. */
- return 0;
- return error_errno("cannot write delta postimage");
-}
-
-static int error_short_read(struct line_buffer *input)
-{
- if (buffer_ferror(input))
- return error_errno("error reading delta");
- return error("invalid delta: unexpected end of file");
-}
-
-static int read_chunk(struct line_buffer *delta, off_t *delta_len,
- struct strbuf *buf, size_t len)
-{
- assert(*delta_len >= 0);
- strbuf_reset(buf);
- if (len > (uintmax_t) *delta_len ||
- buffer_read_binary(delta, buf, len) != len)
- return error_short_read(delta);
- *delta_len -= buf->len;
- return 0;
-}
-
-static int read_magic(struct line_buffer *in, off_t *len)
-{
- static const char magic[] = {'S', 'V', 'N', '\0'};
- struct strbuf sb = STRBUF_INIT;
-
- if (read_chunk(in, len, &sb, sizeof(magic))) {
- strbuf_release(&sb);
- return -1;
- }
- if (memcmp(sb.buf, magic, sizeof(magic))) {
- strbuf_release(&sb);
- return error("invalid delta: unrecognized file type");
- }
- strbuf_release(&sb);
- return 0;
-}
-
-static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
-{
- uintmax_t rv = 0;
- off_t sz;
- for (sz = *len; sz; sz--) {
- const int ch = buffer_read_char(in);
- if (ch == EOF)
- break;
-
- rv <<= VLI_BITS_PER_DIGIT;
- rv += (ch & VLI_DIGIT_MASK);
- if (ch & VLI_CONTINUE)
- continue;
-
- *result = rv;
- *len = sz - 1;
- return 0;
- }
- return error_short_read(in);
-}
-
-static int parse_int(const char **buf, size_t *result, const char *end)
-{
- size_t rv = 0;
- const char *pos;
- for (pos = *buf; pos != end; pos++) {
- unsigned char ch = *pos;
-
- rv <<= VLI_BITS_PER_DIGIT;
- rv += (ch & VLI_DIGIT_MASK);
- if (ch & VLI_CONTINUE)
- continue;
-
- *result = rv;
- *buf = pos + 1;
- return 0;
- }
- return error("invalid delta: unexpected end of instructions section");
-}
-
-static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
-{
- uintmax_t val;
- if (read_int(in, &val, len))
- return -1;
- if (val > maximum_signed_value_of_type(off_t))
- return error("unrepresentable offset in delta: %"PRIuMAX"", val);
- *result = val;
- return 0;
-}
-
-static int read_length(struct line_buffer *in, size_t *result, off_t *len)
-{
- uintmax_t val;
- if (read_int(in, &val, len))
- return -1;
- if (val > SIZE_MAX)
- return error("unrepresentable length in delta: %"PRIuMAX"", val);
- *result = val;
- return 0;
-}
-
-static int copyfrom_source(struct window *ctx, const char **instructions,
- size_t nbytes, const char *insns_end)
-{
- size_t offset;
- if (parse_int(instructions, &offset, insns_end))
- return -1;
- if (unsigned_add_overflows(offset, nbytes) ||
- offset + nbytes > ctx->in->width)
- return error("invalid delta: copies source data outside view");
- strbuf_add(&ctx->out, ctx->in->buf.buf + offset, nbytes);
- return 0;
-}
-
-static int copyfrom_target(struct window *ctx, const char **instructions,
- size_t nbytes, const char *instructions_end)
-{
- size_t offset;
- if (parse_int(instructions, &offset, instructions_end))
- return -1;
- if (offset >= ctx->out.len)
- return error("invalid delta: copies from the future");
- for (; nbytes > 0; nbytes--)
- strbuf_addch(&ctx->out, ctx->out.buf[offset++]);
- return 0;
-}
-
-static int copyfrom_data(struct window *ctx, size_t *data_pos, size_t nbytes)
-{
- const size_t pos = *data_pos;
- if (unsigned_add_overflows(pos, nbytes) ||
- pos + nbytes > ctx->data.len)
- return error("invalid delta: copies unavailable inline data");
- strbuf_add(&ctx->out, ctx->data.buf + pos, nbytes);
- *data_pos += nbytes;
- return 0;
-}
-
-static int parse_first_operand(const char **buf, size_t *out, const char *end)
-{
- size_t result = (unsigned char) *(*buf)++ & OPERAND_MASK;
- if (result) { /* immediate operand */
- *out = result;
- return 0;
- }
- return parse_int(buf, out, end);
-}
-
-static int execute_one_instruction(struct window *ctx,
- const char **instructions, size_t *data_pos)
-{
- unsigned int instruction;
- const char *insns_end = ctx->instructions.buf + ctx->instructions.len;
- size_t nbytes;
- assert(ctx);
- assert(instructions && *instructions);
- assert(data_pos);
-
- instruction = (unsigned char) **instructions;
- if (parse_first_operand(instructions, &nbytes, insns_end))
- return -1;
- switch (instruction & INSN_MASK) {
- case INSN_COPYFROM_SOURCE:
- return copyfrom_source(ctx, instructions, nbytes, insns_end);
- case INSN_COPYFROM_TARGET:
- return copyfrom_target(ctx, instructions, nbytes, insns_end);
- case INSN_COPYFROM_DATA:
- return copyfrom_data(ctx, data_pos, nbytes);
- default:
- return error("invalid delta: unrecognized instruction");
- }
-}
-
-static int apply_window_in_core(struct window *ctx)
-{
- const char *instructions;
- size_t data_pos = 0;
-
- /*
- * Fill ctx->out.buf using data from the source, target,
- * and inline data views.
- */
- for (instructions = ctx->instructions.buf;
- instructions != ctx->instructions.buf + ctx->instructions.len;
- )
- if (execute_one_instruction(ctx, &instructions, &data_pos))
- return -1;
- if (data_pos != ctx->data.len)
- return error("invalid delta: does not copy all inline data");
- return 0;
-}
-
-static int apply_one_window(struct line_buffer *delta, off_t *delta_len,
- struct sliding_view *preimage, FILE *out)
-{
- int rv = -1;
- struct window ctx = WINDOW_INIT(preimage);
- size_t out_len;
- size_t instructions_len;
- size_t data_len;
- assert(delta_len);
-
- /* "source view" offset and length already handled; */
- if (read_length(delta, &out_len, delta_len) ||
- read_length(delta, &instructions_len, delta_len) ||
- read_length(delta, &data_len, delta_len) ||
- read_chunk(delta, delta_len, &ctx.instructions, instructions_len) ||
- read_chunk(delta, delta_len, &ctx.data, data_len))
- goto error_out;
- strbuf_grow(&ctx.out, out_len);
- if (apply_window_in_core(&ctx))
- goto error_out;
- if (ctx.out.len != out_len) {
- rv = error("invalid delta: incorrect postimage length");
- goto error_out;
- }
- if (write_strbuf(&ctx.out, out))
- goto error_out;
- rv = 0;
-error_out:
- window_release(&ctx);
- return rv;
-}
-
-int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
- struct sliding_view *preimage, FILE *postimage)
-{
- assert(delta && preimage && postimage && delta_len >= 0);
-
- if (read_magic(delta, &delta_len))
- return -1;
- while (delta_len) { /* For each window: */
- off_t pre_off = -1;
- size_t pre_len;
-
- if (read_offset(delta, &pre_off, &delta_len) ||
- read_length(delta, &pre_len, &delta_len) ||
- move_window(preimage, pre_off, pre_len) ||
- apply_one_window(delta, &delta_len, preimage, postimage))
- return -1;
- }
- return 0;
-}
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
deleted file mode 100644
index 08d136b8cc..0000000000
--- a/vcs-svn/svndump.c
+++ /dev/null
@@ -1,540 +0,0 @@
-/*
- * Parse and rearrange a svnadmin dump.
- * Create the dump with:
- * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile
- *
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "cache.h"
-#include "fast_export.h"
-#include "line_buffer.h"
-#include "strbuf.h"
-#include "svndump.h"
-
-/*
- * Compare start of string to literal of equal length;
- * must be guarded by length test.
- */
-#define constcmp(s, ref) memcmp(s, ref, sizeof(ref) - 1)
-
-#define REPORT_FILENO 3
-
-#define NODEACT_REPLACE 4
-#define NODEACT_DELETE 3
-#define NODEACT_ADD 2
-#define NODEACT_CHANGE 1
-#define NODEACT_UNKNOWN 0
-
-/* States: */
-#define DUMP_CTX 0 /* dump metadata */
-#define REV_CTX 1 /* revision metadata */
-#define NODE_CTX 2 /* node metadata */
-#define INTERNODE_CTX 3 /* between nodes */
-
-#define DATE_RFC2822_LEN 31
-
-static struct line_buffer input = LINE_BUFFER_INIT;
-
-static struct {
- uint32_t action, srcRev, type;
- off_t prop_length, text_length;
- struct strbuf src, dst;
- uint32_t text_delta, prop_delta;
-} node_ctx;
-
-static struct {
- uint32_t revision;
- timestamp_t timestamp;
- struct strbuf log, author, note;
-} rev_ctx;
-
-static struct {
- uint32_t version;
- struct strbuf uuid, url;
-} dump_ctx;
-
-static void reset_node_ctx(char *fname)
-{
- node_ctx.type = 0;
- node_ctx.action = NODEACT_UNKNOWN;
- node_ctx.prop_length = -1;
- node_ctx.text_length = -1;
- strbuf_reset(&node_ctx.src);
- node_ctx.srcRev = 0;
- strbuf_reset(&node_ctx.dst);
- if (fname)
- strbuf_addstr(&node_ctx.dst, fname);
- node_ctx.text_delta = 0;
- node_ctx.prop_delta = 0;
-}
-
-static void reset_rev_ctx(uint32_t revision)
-{
- rev_ctx.revision = revision;
- rev_ctx.timestamp = 0;
- strbuf_reset(&rev_ctx.log);
- strbuf_reset(&rev_ctx.author);
- strbuf_reset(&rev_ctx.note);
-}
-
-static void reset_dump_ctx(const char *url)
-{
- strbuf_reset(&dump_ctx.url);
- if (url)
- strbuf_addstr(&dump_ctx.url, url);
- dump_ctx.version = 1;
- strbuf_reset(&dump_ctx.uuid);
-}
-
-static void handle_property(const struct strbuf *key_buf,
- struct strbuf *val,
- uint32_t *type_set)
-{
- const char *key = key_buf->buf;
- size_t keylen = key_buf->len;
-
- switch (keylen + 1) {
- case sizeof("svn:log"):
- if (constcmp(key, "svn:log"))
- break;
- if (!val)
- die("invalid dump: unsets svn:log");
- strbuf_swap(&rev_ctx.log, val);
- break;
- case sizeof("svn:author"):
- if (constcmp(key, "svn:author"))
- break;
- if (!val)
- strbuf_reset(&rev_ctx.author);
- else
- strbuf_swap(&rev_ctx.author, val);
- break;
- case sizeof("svn:date"):
- if (constcmp(key, "svn:date"))
- break;
- if (!val)
- die("invalid dump: unsets svn:date");
- if (parse_date_basic(val->buf, &rev_ctx.timestamp, NULL))
- warning("invalid timestamp: %s", val->buf);
- break;
- case sizeof("svn:executable"):
- case sizeof("svn:special"):
- if (keylen == strlen("svn:executable") &&
- constcmp(key, "svn:executable"))
- break;
- if (keylen == strlen("svn:special") &&
- constcmp(key, "svn:special"))
- break;
- if (*type_set) {
- if (!val)
- return;
- die("invalid dump: sets type twice");
- }
- if (!val) {
- node_ctx.type = S_IFREG | 0644;
- return;
- }
- *type_set = 1;
- node_ctx.type = keylen == strlen("svn:executable") ?
- (S_IFREG | 0755) :
- S_IFLNK;
- }
-}
-
-static void die_short_read(void)
-{
- if (buffer_ferror(&input))
- die_errno("error reading dump file");
- die("invalid dump: unexpected end of file");
-}
-
-static void read_props(void)
-{
- static struct strbuf key = STRBUF_INIT;
- static struct strbuf val = STRBUF_INIT;
- const char *t;
- /*
- * NEEDSWORK: to support simple mode changes like
- * K 11
- * svn:special
- * V 1
- * *
- * D 14
- * svn:executable
- * we keep track of whether a mode has been set and reset to
- * plain file only if not. We should be keeping track of the
- * symlink and executable bits separately instead.
- */
- uint32_t type_set = 0;
- while ((t = buffer_read_line(&input)) && strcmp(t, "PROPS-END")) {
- uint32_t len;
- const char type = t[0];
- int ch;
-
- if (!type || t[1] != ' ')
- die("invalid property line: %s", t);
- len = atoi(&t[2]);
- strbuf_reset(&val);
- buffer_read_binary(&input, &val, len);
- if (val.len < len)
- die_short_read();
-
- /* Discard trailing newline. */
- ch = buffer_read_char(&input);
- if (ch == EOF)
- die_short_read();
- if (ch != '\n')
- die("invalid dump: expected newline after %s", val.buf);
-
- switch (type) {
- case 'K':
- strbuf_swap(&key, &val);
- continue;
- case 'D':
- handle_property(&val, NULL, &type_set);
- continue;
- case 'V':
- handle_property(&key, &val, &type_set);
- strbuf_reset(&key);
- continue;
- default:
- die("invalid property line: %s", t);
- }
- }
-}
-
-static void handle_node(void)
-{
- const uint32_t type = node_ctx.type;
- const int have_props = node_ctx.prop_length != -1;
- const int have_text = node_ctx.text_length != -1;
- /*
- * Old text for this node:
- * NULL - directory or bug
- * empty_blob - empty
- * "<dataref>" - data retrievable from fast-import
- */
- static const char *const empty_blob = "::empty::";
- const char *old_data = NULL;
- uint32_t old_mode = S_IFREG | 0644;
-
- if (node_ctx.action == NODEACT_DELETE) {
- if (have_text || have_props || node_ctx.srcRev)
- die("invalid dump: deletion node has "
- "copyfrom info, text, or properties");
- fast_export_delete(node_ctx.dst.buf);
- return;
- }
- if (node_ctx.action == NODEACT_REPLACE) {
- fast_export_delete(node_ctx.dst.buf);
- node_ctx.action = NODEACT_ADD;
- }
- if (node_ctx.srcRev) {
- fast_export_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
- if (node_ctx.action == NODEACT_ADD)
- node_ctx.action = NODEACT_CHANGE;
- }
- if (have_text && type == S_IFDIR)
- die("invalid dump: directories cannot have text attached");
-
- /*
- * Find old content (old_data) and decide on the new mode.
- */
- if (node_ctx.action == NODEACT_CHANGE && !*node_ctx.dst.buf) {
- if (type != S_IFDIR)
- die("invalid dump: root of tree is not a regular file");
- old_data = NULL;
- } else if (node_ctx.action == NODEACT_CHANGE) {
- uint32_t mode;
- old_data = fast_export_read_path(node_ctx.dst.buf, &mode);
- if (mode == S_IFDIR && type != S_IFDIR)
- die("invalid dump: cannot modify a directory into a file");
- if (mode != S_IFDIR && type == S_IFDIR)
- die("invalid dump: cannot modify a file into a directory");
- node_ctx.type = mode;
- old_mode = mode;
- } else if (node_ctx.action == NODEACT_ADD) {
- if (type == S_IFDIR)
- old_data = NULL;
- else if (have_text)
- old_data = empty_blob;
- else
- die("invalid dump: adds node without text");
- } else {
- die("invalid dump: Node-path block lacks Node-action");
- }
-
- /*
- * Adjust mode to reflect properties.
- */
- if (have_props) {
- if (!node_ctx.prop_delta)
- node_ctx.type = type;
- if (node_ctx.prop_length)
- read_props();
- }
-
- /*
- * Save the result.
- */
- if (type == S_IFDIR) /* directories are not tracked. */
- return;
- assert(old_data);
- if (old_data == empty_blob)
- /* For the fast_export_* functions, NULL means empty. */
- old_data = NULL;
- if (!have_text) {
- fast_export_modify(node_ctx.dst.buf, node_ctx.type, old_data);
- return;
- }
- if (!node_ctx.text_delta) {
- fast_export_modify(node_ctx.dst.buf, node_ctx.type, "inline");
- fast_export_data(node_ctx.type, node_ctx.text_length, &input);
- return;
- }
- fast_export_modify(node_ctx.dst.buf, node_ctx.type, "inline");
- fast_export_blob_delta(node_ctx.type, old_mode, old_data,
- node_ctx.text_length, &input);
-}
-
-static void begin_revision(const char *remote_ref)
-{
- if (!rev_ctx.revision) /* revision 0 gets no git commit. */
- return;
- fast_export_begin_commit(rev_ctx.revision, rev_ctx.author.buf,
- &rev_ctx.log, dump_ctx.uuid.buf, dump_ctx.url.buf,
- rev_ctx.timestamp, remote_ref);
-}
-
-static void end_revision(const char *note_ref)
-{
- struct strbuf mark = STRBUF_INIT;
- if (rev_ctx.revision) {
- fast_export_end_commit(rev_ctx.revision);
- fast_export_begin_note(rev_ctx.revision, "remote-svn",
- "Note created by remote-svn.", rev_ctx.timestamp, note_ref);
- strbuf_addf(&mark, ":%"PRIu32, rev_ctx.revision);
- fast_export_note(mark.buf, "inline");
- fast_export_buf_to_data(&rev_ctx.note);
- strbuf_release(&mark);
- }
-}
-
-void svndump_read(const char *url, const char *local_ref, const char *notes_ref)
-{
- char *val;
- char *t;
- uint32_t active_ctx = DUMP_CTX;
- uint32_t len;
-
- reset_dump_ctx(url);
- while ((t = buffer_read_line(&input))) {
- val = strchr(t, ':');
- if (!val)
- continue;
- val++;
- if (*val != ' ')
- continue;
- val++;
-
- /* strlen(key) + 1 */
- switch (val - t - 1) {
- case sizeof("SVN-fs-dump-format-version"):
- if (constcmp(t, "SVN-fs-dump-format-version"))
- continue;
- dump_ctx.version = atoi(val);
- if (dump_ctx.version > 3)
- die("expected svn dump format version <= 3, found %"PRIu32,
- dump_ctx.version);
- break;
- case sizeof("UUID"):
- if (constcmp(t, "UUID"))
- continue;
- strbuf_reset(&dump_ctx.uuid);
- strbuf_addstr(&dump_ctx.uuid, val);
- break;
- case sizeof("Revision-number"):
- if (constcmp(t, "Revision-number"))
- continue;
- if (active_ctx == NODE_CTX)
- handle_node();
- if (active_ctx == REV_CTX)
- begin_revision(local_ref);
- if (active_ctx != DUMP_CTX)
- end_revision(notes_ref);
- active_ctx = REV_CTX;
- reset_rev_ctx(atoi(val));
- strbuf_addf(&rev_ctx.note, "%s\n", t);
- break;
- case sizeof("Node-path"):
- if (constcmp(t, "Node-"))
- continue;
- if (!constcmp(t + strlen("Node-"), "path")) {
- if (active_ctx == NODE_CTX)
- handle_node();
- if (active_ctx == REV_CTX)
- begin_revision(local_ref);
- active_ctx = NODE_CTX;
- reset_node_ctx(val);
- strbuf_addf(&rev_ctx.note, "%s\n", t);
- break;
- }
- if (constcmp(t + strlen("Node-"), "kind"))
- continue;
- strbuf_addf(&rev_ctx.note, "%s\n", t);
- if (!strcmp(val, "dir"))
- node_ctx.type = S_IFDIR;
- else if (!strcmp(val, "file"))
- node_ctx.type = S_IFREG | 0644;
- else
- fprintf(stderr, "Unknown node-kind: %s\n", val);
- break;
- case sizeof("Node-action"):
- if (constcmp(t, "Node-action"))
- continue;
- strbuf_addf(&rev_ctx.note, "%s\n", t);
- if (!strcmp(val, "delete")) {
- node_ctx.action = NODEACT_DELETE;
- } else if (!strcmp(val, "add")) {
- node_ctx.action = NODEACT_ADD;
- } else if (!strcmp(val, "change")) {
- node_ctx.action = NODEACT_CHANGE;
- } else if (!strcmp(val, "replace")) {
- node_ctx.action = NODEACT_REPLACE;
- } else {
- fprintf(stderr, "Unknown node-action: %s\n", val);
- node_ctx.action = NODEACT_UNKNOWN;
- }
- break;
- case sizeof("Node-copyfrom-path"):
- if (constcmp(t, "Node-copyfrom-path"))
- continue;
- strbuf_reset(&node_ctx.src);
- strbuf_addstr(&node_ctx.src, val);
- strbuf_addf(&rev_ctx.note, "%s\n", t);
- break;
- case sizeof("Node-copyfrom-rev"):
- if (constcmp(t, "Node-copyfrom-rev"))
- continue;
- node_ctx.srcRev = atoi(val);
- strbuf_addf(&rev_ctx.note, "%s\n", t);
- break;
- case sizeof("Text-content-length"):
- if (constcmp(t, "Text") && constcmp(t, "Prop"))
- continue;
- if (constcmp(t + 4, "-content-length"))
- continue;
- {
- char *end;
- uintmax_t len;
-
- len = strtoumax(val, &end, 10);
- if (!isdigit(*val) || *end)
- die("invalid dump: non-numeric length %s", val);
- if (len > maximum_signed_value_of_type(off_t))
- die("unrepresentable length in dump: %s", val);
-
- if (*t == 'T')
- node_ctx.text_length = (off_t) len;
- else
- node_ctx.prop_length = (off_t) len;
- break;
- }
- case sizeof("Text-delta"):
- if (!constcmp(t, "Text-delta")) {
- node_ctx.text_delta = !strcmp(val, "true");
- break;
- }
- if (constcmp(t, "Prop-delta"))
- continue;
- node_ctx.prop_delta = !strcmp(val, "true");
- break;
- case sizeof("Content-length"):
- if (constcmp(t, "Content-length"))
- continue;
- len = atoi(val);
- t = buffer_read_line(&input);
- if (!t)
- die_short_read();
- if (*t)
- die("invalid dump: expected blank line after content length header");
- if (active_ctx == REV_CTX) {
- read_props();
- } else if (active_ctx == NODE_CTX) {
- handle_node();
- active_ctx = INTERNODE_CTX;
- } else {
- fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len);
- if (buffer_skip_bytes(&input, len) != len)
- die_short_read();
- }
- }
- }
- if (buffer_ferror(&input))
- die_short_read();
- if (active_ctx == NODE_CTX)
- handle_node();
- if (active_ctx == REV_CTX)
- begin_revision(local_ref);
- if (active_ctx != DUMP_CTX)
- end_revision(notes_ref);
-}
-
-static void init(int report_fd)
-{
- fast_export_init(report_fd);
- strbuf_init(&dump_ctx.uuid, 4096);
- strbuf_init(&dump_ctx.url, 4096);
- strbuf_init(&rev_ctx.log, 4096);
- strbuf_init(&rev_ctx.author, 4096);
- strbuf_init(&rev_ctx.note, 4096);
- strbuf_init(&node_ctx.src, 4096);
- strbuf_init(&node_ctx.dst, 4096);
- reset_dump_ctx(NULL);
- reset_rev_ctx(0);
- reset_node_ctx(NULL);
- return;
-}
-
-int svndump_init(const char *filename)
-{
- if (buffer_init(&input, filename))
- return error_errno("cannot open %s", filename ? filename : "NULL");
- init(REPORT_FILENO);
- return 0;
-}
-
-int svndump_init_fd(int in_fd, int back_fd)
-{
- if(buffer_fdinit(&input, xdup(in_fd)))
- return error_errno("cannot open fd %d", in_fd);
- init(xdup(back_fd));
- return 0;
-}
-
-void svndump_deinit(void)
-{
- fast_export_deinit();
- reset_dump_ctx(NULL);
- reset_rev_ctx(0);
- reset_node_ctx(NULL);
- strbuf_release(&rev_ctx.log);
- strbuf_release(&rev_ctx.author);
- strbuf_release(&rev_ctx.note);
- strbuf_release(&node_ctx.src);
- strbuf_release(&node_ctx.dst);
- if (buffer_deinit(&input))
- fprintf(stderr, "Input error\n");
- if (ferror(stdout))
- fprintf(stderr, "Output error\n");
-}
-
-void svndump_reset(void)
-{
- strbuf_release(&dump_ctx.uuid);
- strbuf_release(&dump_ctx.url);
- strbuf_release(&rev_ctx.log);
- strbuf_release(&rev_ctx.author);
-}
diff --git a/worktree.c b/worktree.c
index cba2e54598..46a5fb8447 100644
--- a/worktree.c
+++ b/worktree.c
@@ -49,10 +49,8 @@ static struct worktree *get_main_worktree(void)
struct worktree *worktree = NULL;
struct strbuf worktree_path = STRBUF_INIT;
- strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
- if (!strbuf_strip_suffix(&worktree_path, "/.git/.") && /* in .git */
- !strbuf_strip_suffix(&worktree_path, "/.git")) /* in worktree */
- strbuf_strip_suffix(&worktree_path, "/."); /* in bare repo */
+ strbuf_add_real_path(&worktree_path, get_git_common_dir());
+ strbuf_strip_suffix(&worktree_path, "/.git");
worktree = xcalloc(1, sizeof(*worktree));
worktree->path = strbuf_detach(&worktree_path, NULL);
@@ -66,8 +64,6 @@ static struct worktree *get_main_worktree(void)
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository();
add_head_info(worktree);
-
- strbuf_release(&worktree_path);
return worktree;
}
@@ -84,16 +80,8 @@ static struct worktree *get_linked_worktree(const char *id)
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
/* invalid gitdir file */
goto done;
-
strbuf_rtrim(&worktree_path);
- if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
- strbuf_reset(&worktree_path);
- strbuf_add_absolute_path(&worktree_path, ".");
- strbuf_strip_suffix(&worktree_path, "/.");
- }
-
- strbuf_reset(&path);
- strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
+ strbuf_strip_suffix(&worktree_path, "/.git");
worktree = xcalloc(1, sizeof(*worktree));
worktree->path = strbuf_detach(&worktree_path, NULL);
@@ -583,3 +571,138 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
free_worktrees(worktrees);
return ret;
}
+
+/*
+ * Repair worktree's /path/to/worktree/.git file if missing, corrupt, or not
+ * pointing at <repo>/worktrees/<id>.
+ */
+static void repair_gitfile(struct worktree *wt,
+ worktree_repair_fn fn, void *cb_data)
+{
+ struct strbuf dotgit = STRBUF_INIT;
+ struct strbuf repo = STRBUF_INIT;
+ char *backlink;
+ const char *repair = NULL;
+ int err;
+
+ /* missing worktree can't be repaired */
+ if (!file_exists(wt->path))
+ return;
+
+ if (!is_directory(wt->path)) {
+ fn(1, wt->path, _("not a directory"), cb_data);
+ return;
+ }
+
+ strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
+ strbuf_addf(&dotgit, "%s/.git", wt->path);
+ backlink = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
+
+ if (err == READ_GITFILE_ERR_NOT_A_FILE)
+ fn(1, wt->path, _(".git is not a file"), cb_data);
+ else if (err)
+ repair = _(".git file broken");
+ else if (fspathcmp(backlink, repo.buf))
+ repair = _(".git file incorrect");
+
+ if (repair) {
+ fn(0, wt->path, repair, cb_data);
+ write_file(dotgit.buf, "gitdir: %s", repo.buf);
+ }
+
+ free(backlink);
+ strbuf_release(&repo);
+ strbuf_release(&dotgit);
+}
+
+static void repair_noop(int iserr, const char *path, const char *msg,
+ void *cb_data)
+{
+ /* nothing */
+}
+
+void repair_worktrees(worktree_repair_fn fn, void *cb_data)
+{
+ struct worktree **worktrees = get_worktrees();
+ struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
+
+ if (!fn)
+ fn = repair_noop;
+ for (; *wt; wt++)
+ repair_gitfile(*wt, fn, cb_data);
+ free_worktrees(worktrees);
+}
+
+static int is_main_worktree_path(const char *path)
+{
+ struct strbuf target = STRBUF_INIT;
+ struct strbuf maindir = STRBUF_INIT;
+ int cmp;
+
+ strbuf_add_real_path(&target, path);
+ strbuf_strip_suffix(&target, "/.git");
+ strbuf_add_real_path(&maindir, get_git_common_dir());
+ strbuf_strip_suffix(&maindir, "/.git");
+ cmp = fspathcmp(maindir.buf, target.buf);
+
+ strbuf_release(&maindir);
+ strbuf_release(&target);
+ return !cmp;
+}
+
+/*
+ * Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
+ * the worktree's path.
+ */
+void repair_worktree_at_path(const char *path,
+ worktree_repair_fn fn, void *cb_data)
+{
+ struct strbuf dotgit = STRBUF_INIT;
+ struct strbuf realdotgit = STRBUF_INIT;
+ struct strbuf gitdir = STRBUF_INIT;
+ struct strbuf olddotgit = STRBUF_INIT;
+ char *backlink = NULL;
+ const char *repair = NULL;
+ int err;
+
+ if (!fn)
+ fn = repair_noop;
+
+ if (is_main_worktree_path(path))
+ goto done;
+
+ strbuf_addf(&dotgit, "%s/.git", path);
+ if (!strbuf_realpath(&realdotgit, dotgit.buf, 0)) {
+ fn(1, path, _("not a valid path"), cb_data);
+ goto done;
+ }
+
+ backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
+ if (err == READ_GITFILE_ERR_NOT_A_FILE) {
+ fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
+ goto done;
+ } else if (err) {
+ fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
+ goto done;
+ }
+
+ strbuf_addf(&gitdir, "%s/gitdir", backlink);
+ if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
+ repair = _("gitdir unreadable");
+ else {
+ strbuf_rtrim(&olddotgit);
+ if (fspathcmp(olddotgit.buf, realdotgit.buf))
+ repair = _("gitdir incorrect");
+ }
+
+ if (repair) {
+ fn(0, gitdir.buf, repair, cb_data);
+ write_file(gitdir.buf, "%s", realdotgit.buf);
+ }
+done:
+ free(backlink);
+ strbuf_release(&olddotgit);
+ strbuf_release(&gitdir);
+ strbuf_release(&realdotgit);
+ strbuf_release(&dotgit);
+}
diff --git a/worktree.h b/worktree.h
index 516744c433..ff7b62e434 100644
--- a/worktree.h
+++ b/worktree.h
@@ -89,6 +89,29 @@ int validate_worktree(const struct worktree *wt,
void update_worktree_location(struct worktree *wt,
const char *path_);
+typedef void (* worktree_repair_fn)(int iserr, const char *path,
+ const char *msg, void *cb_data);
+
+/*
+ * Visit each registered linked worktree and repair corruptions. For each
+ * repair made or error encountered while attempting a repair, the callback
+ * function, if non-NULL, is called with the path of the worktree and a
+ * description of the repair or error, along with the callback user-data.
+ */
+void repair_worktrees(worktree_repair_fn, void *cb_data);
+
+/*
+ * Repair administrative files corresponding to the worktree at the given path.
+ * The worktree's .git file pointing at the repository must be intact for the
+ * repair to succeed. Useful for re-associating an orphaned worktree with the
+ * repository if the worktree has been moved manually (without using "git
+ * worktree move"). For each repair made or error encountered while attempting
+ * a repair, the callback function, if non-NULL, is called with the path of the
+ * worktree and a description of the repair or error, along with the callback
+ * user-data.
+ */
+void repair_worktree_at_path(const char *, worktree_repair_fn, void *cb_data);
+
/*
* Free up the memory for worktree(s)
*/
diff --git a/wrapper.c b/wrapper.c
index 4ff4a9c3db..bcda41e374 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -117,10 +117,13 @@ void *xrealloc(void *ptr, size_t size)
{
void *ret;
+ if (!size) {
+ free(ptr);
+ return xmalloc(0);
+ }
+
memory_limit_check(size, 0);
ret = realloc(ptr, size);
- if (!ret && !size)
- ret = realloc(ptr, 1);
if (!ret)
die("Out of memory, realloc failed");
return ret;
diff --git a/wt-status.c b/wt-status.c
index c560cbe860..160d14970a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -8,7 +8,7 @@
#include "diffcore.h"
#include "quote.h"
#include "run-command.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "remote.h"
#include "refs.h"
#include "submodule.h"
@@ -259,8 +259,6 @@ static void wt_longstatus_print_trailer(struct wt_status *s)
status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
}
-#define quote_path quote_path_relative
-
static const char *wt_status_unmerged_status_string(int stagemask)
{
switch (stagemask) {
@@ -338,7 +336,7 @@ static void wt_longstatus_print_unmerged_data(struct wt_status *s,
memset(padding, ' ', label_width);
}
- one = quote_path(it->string, s->prefix, &onebuf);
+ one = quote_path(it->string, s->prefix, &onebuf, 0);
status_printf(s, color(WT_STATUS_HEADER, s), "\t");
how = wt_status_unmerged_status_string(d->stagemask);
@@ -404,8 +402,8 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
if (d->rename_status == status)
one_name = d->rename_source;
- one = quote_path(one_name, s->prefix, &onebuf);
- two = quote_path(two_name, s->prefix, &twobuf);
+ one = quote_path(one_name, s->prefix, &onebuf, 0);
+ two = quote_path(two_name, s->prefix, &twobuf, 0);
status_printf(s, color(WT_STATUS_HEADER, s), "\t");
what = wt_status_diff_status_string(status);
@@ -703,7 +701,7 @@ static void wt_status_collect_untracked(struct wt_status *s)
if (!s->show_untracked_files)
return;
- memset(&dir, 0, sizeof(dir));
+ dir_init(&dir);
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
dir.flags |=
DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
@@ -724,19 +722,15 @@ static void wt_status_collect_untracked(struct wt_status *s)
struct dir_entry *ent = dir.entries[i];
if (index_name_is_other(istate, ent->name, ent->len))
string_list_insert(&s->untracked, ent->name);
- free(ent);
}
for (i = 0; i < dir.ignored_nr; i++) {
struct dir_entry *ent = dir.ignored[i];
if (index_name_is_other(istate, ent->name, ent->len))
string_list_insert(&s->ignored, ent->name);
- free(ent);
}
- free(dir.entries);
- free(dir.ignored);
- clear_directory(&dir);
+ dir_clear(&dir);
if (advice_status_u_option)
s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
@@ -913,17 +907,16 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom
struct strbuf summary = STRBUF_INIT;
char *summary_content;
- argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s",
- s->index_file);
+ strvec_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s", s->index_file);
- argv_array_push(&sm_summary.args, "submodule");
- argv_array_push(&sm_summary.args, "summary");
- argv_array_push(&sm_summary.args, uncommitted ? "--files" : "--cached");
- argv_array_push(&sm_summary.args, "--for-status");
- argv_array_push(&sm_summary.args, "--summary-limit");
- argv_array_pushf(&sm_summary.args, "%d", s->submodule_summary);
+ strvec_push(&sm_summary.args, "submodule");
+ strvec_push(&sm_summary.args, "summary");
+ strvec_push(&sm_summary.args, uncommitted ? "--files" : "--cached");
+ strvec_push(&sm_summary.args, "--for-status");
+ strvec_push(&sm_summary.args, "--summary-limit");
+ strvec_pushf(&sm_summary.args, "%d", s->submodule_summary);
if (!uncommitted)
- argv_array_push(&sm_summary.args, s->amend ? "HEAD^" : "HEAD");
+ strvec_push(&sm_summary.args, s->amend ? "HEAD^" : "HEAD");
sm_summary.git_cmd = 1;
sm_summary.no_stdin = 1;
@@ -971,7 +964,7 @@ static void wt_longstatus_print_other(struct wt_status *s,
struct string_list_item *it;
const char *path;
it = &(l->items[i]);
- path = quote_path(it->string, s->prefix, &buf);
+ path = quote_path(it->string, s->prefix, &buf, 0);
if (column_active(s->colopts)) {
string_list_append(&output, path);
continue;
@@ -1574,7 +1567,7 @@ static void wt_status_get_detached_from(struct repository *r,
return;
}
- if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref) == 1 &&
+ if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref, 1) == 1 &&
/* sha1 is a commit? match without further lookup */
(oideq(&cb.noid, &oid) ||
/* perhaps sha1 is a tag, try to dereference to a commit */
@@ -1673,13 +1666,13 @@ void wt_status_get_state(struct repository *r,
state->merge_in_progress = 1;
} else if (wt_status_check_rebase(NULL, state)) {
; /* all set */
- } else if (!stat(git_path_cherry_pick_head(r), &st) &&
- !get_oid("CHERRY_PICK_HEAD", &oid)) {
+ } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
+ !get_oid("CHERRY_PICK_HEAD", &oid)) {
state->cherry_pick_in_progress = 1;
oidcpy(&state->cherry_pick_head_oid, &oid);
}
wt_status_check_bisect(NULL, state);
- if (!stat(git_path_revert_head(r), &st) &&
+ if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
!get_oid("REVERT_HEAD", &oid)) {
state->revert_in_progress = 1;
oidcpy(&state->revert_head_oid, &oid);
@@ -1855,7 +1848,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it,
} else {
struct strbuf onebuf = STRBUF_INIT;
const char *one;
- one = quote_path(it->string, s->prefix, &onebuf);
+ one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
printf(" %s\n", one);
strbuf_release(&onebuf);
}
@@ -1884,21 +1877,12 @@ static void wt_shortstatus_status(struct string_list_item *it,
const char *one;
if (d->rename_source) {
- one = quote_path(d->rename_source, s->prefix, &onebuf);
- if (*one != '"' && strchr(one, ' ') != NULL) {
- putchar('"');
- strbuf_addch(&onebuf, '"');
- one = onebuf.buf;
- }
+ one = quote_path(d->rename_source, s->prefix, &onebuf,
+ QUOTE_PATH_QUOTE_SP);
printf("%s -> ", one);
strbuf_release(&onebuf);
}
- one = quote_path(it->string, s->prefix, &onebuf);
- if (*one != '"' && strchr(one, ' ') != NULL) {
- putchar('"');
- strbuf_addch(&onebuf, '"');
- one = onebuf.buf;
- }
+ one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
printf("%s\n", one);
strbuf_release(&onebuf);
}
@@ -1912,7 +1896,7 @@ static void wt_shortstatus_other(struct string_list_item *it,
} else {
struct strbuf onebuf = STRBUF_INIT;
const char *one;
- one = quote_path(it->string, s->prefix, &onebuf);
+ one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
printf(" %s\n", one);
strbuf_release(&onebuf);
@@ -2035,7 +2019,7 @@ static void wt_porcelain_print(struct wt_status *s)
* [# branch.upstream <upstream><eol>
* [# branch.ab +<ahead> -<behind><eol>]]
*
- * <commit> ::= the current commit hash or the the literal
+ * <commit> ::= the current commit hash or the literal
* "(initial)" to indicate an initialized repo
* with no commits.
*
@@ -2229,9 +2213,9 @@ static void wt_porcelain_v2_print_changed_entry(
*/
sep_char = '\t';
eol_char = '\n';
- path = quote_path(it->string, s->prefix, &buf);
+ path = quote_path(it->string, s->prefix, &buf, 0);
if (d->rename_source)
- path_from = quote_path(d->rename_source, s->prefix, &buf_from);
+ path_from = quote_path(d->rename_source, s->prefix, &buf_from, 0);
}
if (path_from)
@@ -2317,7 +2301,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
if (s->null_termination)
path_index = it->string;
else
- path_index = quote_path(it->string, s->prefix, &buf_index);
+ path_index = quote_path(it->string, s->prefix, &buf_index, 0);
fprintf(s->fp, "%c %s %s %06o %06o %06o %06o %s %s %s %s%c",
unmerged_prefix, key, submodule_token,
@@ -2350,7 +2334,7 @@ static void wt_porcelain_v2_print_other(
path = it->string;
eol_char = '\0';
} else {
- path = quote_path(it->string, s->prefix, &buf);
+ path = quote_path(it->string, s->prefix, &buf, 0);
eol_char = '\n';
}