summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--.mailmap13
-rw-r--r--Documentation/Makefile45
-rw-r--r--Documentation/RelNotes-1.5.0.4.txt2
-rw-r--r--Documentation/RelNotes-1.5.0.5.txt2
-rw-r--r--Documentation/RelNotes-1.5.0.6.txt1
-rw-r--r--Documentation/RelNotes-1.5.1.3.txt1
-rw-r--r--Documentation/RelNotes-1.5.3.txt322
-rw-r--r--Documentation/SubmittingPatches22
-rw-r--r--Documentation/asciidoc.conf2
-rw-r--r--Documentation/blame-options.txt20
-rwxr-xr-xDocumentation/cmd-list.perl23
-rw-r--r--Documentation/config.txt96
-rw-r--r--Documentation/core-intro.txt2
-rw-r--r--Documentation/core-tutorial.txt46
-rw-r--r--Documentation/diff-format.txt28
-rw-r--r--Documentation/diff-options.txt28
-rw-r--r--Documentation/diffcore.txt3
-rw-r--r--Documentation/docbook-xsl.css572
-rw-r--r--Documentation/docbook.xsl5
-rw-r--r--Documentation/fetch-options.txt1
-rwxr-xr-xDocumentation/fix-texi.perl15
-rw-r--r--Documentation/git-add.txt42
-rw-r--r--Documentation/git-am.txt13
-rw-r--r--Documentation/git-apply.txt1
-rw-r--r--Documentation/git-applymbox.txt98
-rw-r--r--Documentation/git-applypatch.txt53
-rw-r--r--Documentation/git-archimport.txt45
-rw-r--r--Documentation/git-bisect.txt3
-rw-r--r--Documentation/git-blame.txt7
-rw-r--r--Documentation/git-branch.txt17
-rw-r--r--Documentation/git-cat-file.txt1
-rw-r--r--Documentation/git-check-attr.txt1
-rw-r--r--Documentation/git-checkout-index.txt1
-rw-r--r--Documentation/git-checkout.txt7
-rw-r--r--Documentation/git-cherry-pick.txt1
-rw-r--r--Documentation/git-cherry.txt1
-rw-r--r--Documentation/git-citool.txt32
-rw-r--r--Documentation/git-clone.txt27
-rw-r--r--Documentation/git-commit-tree.txt5
-rw-r--r--Documentation/git-commit.txt63
-rw-r--r--Documentation/git-config.txt55
-rw-r--r--Documentation/git-convert-objects.txt1
-rw-r--r--Documentation/git-count-objects.txt1
-rw-r--r--Documentation/git-cvsexportcommit.txt20
-rw-r--r--Documentation/git-cvsimport.txt36
-rw-r--r--Documentation/git-cvsserver.txt43
-rw-r--r--Documentation/git-daemon.txt7
-rw-r--r--Documentation/git-describe.txt8
-rw-r--r--Documentation/git-diff-files.txt3
-rw-r--r--Documentation/git-diff-index.txt3
-rw-r--r--Documentation/git-diff-tree.txt1
-rw-r--r--Documentation/git-diff.txt30
-rw-r--r--Documentation/git-fast-import.txt59
-rw-r--r--Documentation/git-fetch.txt2
-rw-r--r--Documentation/git-filter-branch.txt278
-rw-r--r--Documentation/git-fmt-merge-msg.txt1
-rw-r--r--Documentation/git-format-patch.txt20
-rw-r--r--Documentation/git-fsck.txt12
-rw-r--r--Documentation/git-gc.txt16
-rw-r--r--Documentation/git-get-tar-commit-id.txt1
-rw-r--r--Documentation/git-grep.txt1
-rw-r--r--Documentation/git-gui.txt115
-rw-r--r--Documentation/git-hash-object.txt3
-rw-r--r--Documentation/git-http-fetch.txt1
-rw-r--r--Documentation/git-http-push.txt2
-rw-r--r--Documentation/git-index-pack.txt1
-rw-r--r--Documentation/git-init-db.txt3
-rw-r--r--Documentation/git-init.txt7
-rw-r--r--Documentation/git-instaweb.txt1
-rw-r--r--Documentation/git-local-fetch.txt3
-rw-r--r--Documentation/git-log.txt26
-rw-r--r--Documentation/git-ls-files.txt1
-rw-r--r--Documentation/git-ls-remote.txt1
-rw-r--r--Documentation/git-ls-tree.txt15
-rw-r--r--Documentation/git-mailinfo.txt3
-rw-r--r--Documentation/git-mailsplit.txt14
-rw-r--r--Documentation/git-merge-base.txt1
-rw-r--r--Documentation/git-merge-index.txt3
-rw-r--r--Documentation/git-merge-one-file.txt1
-rw-r--r--Documentation/git-merge-tree.txt1
-rw-r--r--Documentation/git-merge.txt27
-rw-r--r--Documentation/git-mergetool.txt3
-rw-r--r--Documentation/git-mktag.txt15
-rw-r--r--Documentation/git-mktree.txt1
-rw-r--r--Documentation/git-mv.txt1
-rw-r--r--Documentation/git-name-rev.txt8
-rw-r--r--Documentation/git-pack-objects.txt36
-rw-r--r--Documentation/git-pack-redundant.txt3
-rw-r--r--Documentation/git-patch-id.txt1
-rw-r--r--Documentation/git-peek-remote.txt1
-rw-r--r--Documentation/git-prune-packed.txt1
-rw-r--r--Documentation/git-prune.txt1
-rw-r--r--Documentation/git-pull.txt3
-rw-r--r--Documentation/git-push.txt30
-rw-r--r--Documentation/git-quiltimport.txt1
-rw-r--r--Documentation/git-read-tree.txt9
-rw-r--r--Documentation/git-rebase.txt107
-rw-r--r--Documentation/git-receive-pack.txt10
-rw-r--r--Documentation/git-reflog.txt14
-rw-r--r--Documentation/git-relink.txt1
-rw-r--r--Documentation/git-remote.txt1
-rw-r--r--Documentation/git-repack.txt17
-rw-r--r--Documentation/git-request-pull.txt1
-rw-r--r--Documentation/git-rerere.txt4
-rw-r--r--Documentation/git-reset.txt16
-rw-r--r--Documentation/git-rev-list.txt48
-rw-r--r--Documentation/git-rev-parse.txt14
-rw-r--r--Documentation/git-revert.txt1
-rw-r--r--Documentation/git-rm.txt4
-rw-r--r--Documentation/git-runstatus.txt1
-rw-r--r--Documentation/git-send-email.txt30
-rw-r--r--Documentation/git-sh-setup.txt1
-rw-r--r--Documentation/git-shell.txt1
-rw-r--r--Documentation/git-shortlog.txt1
-rw-r--r--Documentation/git-show-index.txt1
-rw-r--r--Documentation/git-show.txt1
-rw-r--r--Documentation/git-ssh-fetch.txt3
-rw-r--r--Documentation/git-ssh-upload.txt3
-rw-r--r--Documentation/git-stash.txt165
-rw-r--r--Documentation/git-status.txt8
-rw-r--r--Documentation/git-stripspace.txt6
-rw-r--r--Documentation/git-submodule.txt77
-rw-r--r--Documentation/git-svn.txt27
-rw-r--r--Documentation/git-svnimport.txt1
-rw-r--r--Documentation/git-tag.txt21
-rw-r--r--Documentation/git-tar-tree.txt1
-rw-r--r--Documentation/git-unpack-file.txt1
-rw-r--r--Documentation/git-unpack-objects.txt1
-rw-r--r--Documentation/git-update-index.txt9
-rw-r--r--Documentation/git-update-ref.txt5
-rw-r--r--Documentation/git-update-server-info.txt1
-rw-r--r--Documentation/git-var.txt1
-rw-r--r--Documentation/git-verify-pack.txt1
-rw-r--r--Documentation/git-verify-tag.txt5
-rw-r--r--Documentation/git-whatchanged.txt1
-rw-r--r--Documentation/git-write-tree.txt1
-rw-r--r--Documentation/git.txt60
-rw-r--r--Documentation/gitattributes.txt57
-rw-r--r--Documentation/gitignore.txt22
-rw-r--r--Documentation/gitk.txt1
-rw-r--r--Documentation/gitmodules.txt62
-rw-r--r--Documentation/hooks.txt13
-rw-r--r--Documentation/howto/rebase-and-edit.txt20
-rw-r--r--Documentation/howto/rebase-from-internal-branch.txt12
-rw-r--r--Documentation/howto/rebuild-from-update-hook.txt1
-rw-r--r--Documentation/howto/revert-branch-rebase.txt2
-rw-r--r--Documentation/howto/separating-topic-branches.txt13
-rw-r--r--Documentation/howto/use-git-daemon.txt1
-rwxr-xr-xDocumentation/install-doc-quick.sh18
-rw-r--r--Documentation/merge-options.txt5
-rw-r--r--Documentation/pretty-formats.txt3
-rw-r--r--Documentation/pretty-options.txt14
-rw-r--r--Documentation/pull-fetch-param.txt2
-rw-r--r--Documentation/repository-layout.txt1
-rw-r--r--Documentation/technical/pack-format.txt8
-rw-r--r--Documentation/tutorial.txt2
-rw-r--r--Documentation/urls-remotes.txt55
-rw-r--r--Documentation/urls.txt68
-rw-r--r--Documentation/user-manual.txt124
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--INSTALL34
-rw-r--r--Makefile192
l---------RelNotes2
-rw-r--r--archive-tar.c4
-rw-r--r--archive-zip.c2
-rw-r--r--arm/sha1.c4
-rw-r--r--arm/sha1_arm.S1
-rw-r--r--attr.c120
-rw-r--r--builtin-add.c97
-rw-r--r--builtin-annotate.c1
-rw-r--r--builtin-apply.c48
-rw-r--r--builtin-archive.c2
-rw-r--r--builtin-blame.c19
-rw-r--r--builtin-branch.c198
-rw-r--r--builtin-bundle.c98
-rw-r--r--builtin-check-attr.c5
-rw-r--r--builtin-checkout-index.c1
-rw-r--r--builtin-config.c84
-rw-r--r--builtin-count-objects.c2
-rw-r--r--builtin-describe.c14
-rw-r--r--builtin-diff-index.c2
-rw-r--r--builtin-diff.c15
-rw-r--r--builtin-fetch--tool.c3
-rw-r--r--builtin-fmt-merge-msg.c1
-rw-r--r--builtin-for-each-ref.c3
-rw-r--r--builtin-fsck.c90
-rw-r--r--builtin-gc.c37
-rw-r--r--builtin-init-db.c31
-rw-r--r--builtin-log.c128
-rw-r--r--builtin-ls-files.c10
-rw-r--r--builtin-ls-tree.c35
-rw-r--r--builtin-mailinfo.c68
-rw-r--r--builtin-mailsplit.c142
-rw-r--r--builtin-merge-base.c1
-rw-r--r--builtin-merge-file.c13
-rw-r--r--builtin-name-rev.c24
-rw-r--r--builtin-pack-objects.c798
-rw-r--r--builtin-pack-refs.c69
-rw-r--r--builtin-push.c324
-rw-r--r--builtin-read-tree.c33
-rw-r--r--builtin-reflog.c2
-rw-r--r--builtin-rerere.c56
-rw-r--r--builtin-rev-list.c24
-rw-r--r--builtin-rev-parse.c17
-rw-r--r--builtin-revert.c13
-rw-r--r--builtin-rm.c32
-rw-r--r--builtin-runstatus.c1
-rw-r--r--builtin-shortlog.c1
-rw-r--r--builtin-show-branch.c23
-rw-r--r--builtin-show-ref.c1
-rw-r--r--builtin-stripspace.c93
-rw-r--r--builtin-symbolic-ref.c2
-rw-r--r--builtin-tag.c460
-rw-r--r--builtin-update-index.c14
-rw-r--r--builtin-update-ref.c13
-rw-r--r--builtin-verify-tag.c110
-rw-r--r--builtin-write-tree.c2
-rw-r--r--builtin.h6
-rw-r--r--cache-tree.c4
-rw-r--r--cache.h80
-rw-r--r--combine-diff.c3
-rw-r--r--commit.c437
-rw-r--r--commit.h6
-rw-r--r--compat/mmap.c1
-rw-r--r--config.c125
-rw-r--r--config.mak.in1
-rw-r--r--configure.ac14
-rw-r--r--connect.c323
-rw-r--r--contrib/README1
-rw-r--r--contrib/blameview/README1
-rwxr-xr-xcontrib/completion/git-completion.bash12
-rw-r--r--contrib/emacs/Makefile5
-rw-r--r--contrib/emacs/git.el257
-rw-r--r--contrib/emacs/vc-git.el65
-rwxr-xr-xcontrib/examples/git-gc.sh8
-rwxr-xr-xcontrib/examples/git-resolve.sh34
-rwxr-xr-xcontrib/examples/git-tag.sh (renamed from git-tag.sh)96
-rwxr-xr-xcontrib/examples/git-verify-tag.sh (renamed from git-verify-tag.sh)14
-rwxr-xr-xcontrib/fast-import/git-p41621
-rw-r--r--contrib/fast-import/git-p4.bat1
-rw-r--r--contrib/fast-import/git-p4.txt159
-rwxr-xr-xcontrib/gitview/gitview34
-rw-r--r--contrib/hooks/post-receive-email2
-rw-r--r--contrib/hooks/update-paranoid179
-rw-r--r--contrib/p4import/README1
-rw-r--r--contrib/p4import/git-p4import.py (renamed from git-p4import.py)1
-rw-r--r--contrib/p4import/git-p4import.txt (renamed from Documentation/git-p4import.txt)1
-rw-r--r--contrib/patches/docbook-xsl-manpages-charmap.patch21
-rw-r--r--contrib/remotes2config.sh6
-rwxr-xr-xcontrib/stats/git-common-hash26
-rwxr-xr-xcontrib/stats/mailmap.pl38
-rwxr-xr-xcontrib/stats/packinfo.pl212
-rwxr-xr-xcontrib/workdir/git-new-workdir17
-rw-r--r--convert-objects.c2
-rw-r--r--copy.c1
-rw-r--r--csum-file.c63
-rw-r--r--csum-file.h2
-rw-r--r--ctype.c1
-rw-r--r--daemon.c32
-rw-r--r--date.c46
-rw-r--r--delta.h7
-rw-r--r--diff-delta.c139
-rw-r--r--diff-lib.c12
-rw-r--r--diff.c289
-rw-r--r--diff.h5
-rw-r--r--diffcore-break.c3
-rw-r--r--diffcore-delta.c39
-rw-r--r--diffcore-pickaxe.c2
-rw-r--r--diffcore-rename.c37
-rw-r--r--diffcore.h8
-rw-r--r--dir.c96
-rw-r--r--dir.h12
-rw-r--r--entry.c25
-rw-r--r--environment.c51
-rw-r--r--fast-import.c174
-rw-r--r--fetch-pack.c4
-rw-r--r--fetch.c6
-rwxr-xr-xfixup-builtins16
-rwxr-xr-xgenerate-cmdlist.sh2
-rwxr-xr-xgit-add--interactive.perl19
-rwxr-xr-xgit-am.sh62
-rwxr-xr-xgit-applymbox.sh121
-rwxr-xr-xgit-applypatch.sh212
-rwxr-xr-xgit-archimport.perl175
-rwxr-xr-xgit-bisect.sh34
-rwxr-xr-xgit-checkout.sh53
-rwxr-xr-xgit-clean.sh4
-rwxr-xr-xgit-clone.sh158
-rwxr-xr-xgit-commit.sh173
-rw-r--r--git-compat-util.h26
-rwxr-xr-xgit-cvsexportcommit.perl15
-rwxr-xr-xgit-cvsimport.perl77
-rwxr-xr-xgit-cvsserver.perl168
-rwxr-xr-xgit-fetch.sh46
-rwxr-xr-xgit-filter-branch.sh390
-rwxr-xr-xgit-gui/GIT-VERSION-GEN2
-rw-r--r--git-gui/Makefile42
-rwxr-xr-xgit-gui/git-gui.sh935
-rw-r--r--git-gui/lib/blame.tcl111
-rw-r--r--git-gui/lib/branch.tcl562
-rw-r--r--git-gui/lib/branch_checkout.tcl89
-rw-r--r--git-gui/lib/branch_create.tcl220
-rw-r--r--git-gui/lib/branch_delete.tcl149
-rw-r--r--git-gui/lib/branch_rename.tcl128
-rw-r--r--git-gui/lib/browser.tcl88
-rw-r--r--git-gui/lib/checkout_op.tcl588
-rw-r--r--git-gui/lib/choose_rev.tcl627
-rw-r--r--git-gui/lib/class.tcl38
-rw-r--r--git-gui/lib/commit.tcl84
-rw-r--r--git-gui/lib/console.tcl69
-rw-r--r--git-gui/lib/database.tcl29
-rw-r--r--git-gui/lib/diff.tcl27
-rw-r--r--git-gui/lib/encoding.tcl276
-rw-r--r--git-gui/lib/error.tcl7
-rw-r--r--git-gui/lib/index.tcl50
-rw-r--r--git-gui/lib/merge.tcl285
-rw-r--r--git-gui/lib/option.tcl19
-rw-r--r--git-gui/lib/remote.tcl106
-rw-r--r--git-gui/lib/remote_branch_delete.tcl347
-rw-r--r--git-gui/lib/shortcut.tcl51
-rw-r--r--git-gui/lib/status_bar.tcl96
-rw-r--r--git-gui/lib/transport.tcl20
-rwxr-xr-xgit-instaweb.sh8
-rwxr-xr-xgit-lost-found.sh4
-rwxr-xr-xgit-ls-remote.sh4
-rwxr-xr-xgit-merge-octopus.sh14
-rwxr-xr-xgit-merge-one-file.sh30
-rwxr-xr-xgit-merge-ours.sh2
-rwxr-xr-xgit-merge-resolve.sh6
-rwxr-xr-xgit-merge-stupid.sh10
-rwxr-xr-xgit-merge.sh92
-rwxr-xr-xgit-mergetool.sh72
-rwxr-xr-xgit-parse-remote.sh24
-rwxr-xr-xgit-pull.sh19
-rwxr-xr-xgit-quiltimport.sh17
-rwxr-xr-xgit-rebase--interactive.sh483
-rwxr-xr-xgit-rebase.sh69
-rwxr-xr-xgit-remote.perl15
-rwxr-xr-xgit-repack.sh32
-rwxr-xr-xgit-request-pull.sh4
-rwxr-xr-xgit-reset.sh16
-rwxr-xr-xgit-send-email.perl148
-rwxr-xr-xgit-sh-setup.sh68
-rwxr-xr-xgit-stash.sh215
-rwxr-xr-xgit-submodule.sh322
-rwxr-xr-xgit-svn.perl272
-rwxr-xr-xgit-svnimport.perl32
-rw-r--r--git.c162
-rw-r--r--git.spec.in46
-rwxr-xr-xgitk3764
-rw-r--r--gitweb/README1
-rw-r--r--gitweb/gitweb.css11
-rwxr-xr-xgitweb/gitweb.perl852
-rw-r--r--help.c2
-rw-r--r--http-fetch.c2
-rw-r--r--http-push.c3
-rw-r--r--http.c2
-rw-r--r--ident.c27
-rw-r--r--imap-send.c2
-rw-r--r--index-pack.c213
-rw-r--r--interpolate.c46
-rw-r--r--interpolate.h6
-rw-r--r--list-objects.c2
-rw-r--r--local-fetch.c8
-rw-r--r--lockfile.c112
-rw-r--r--log-tree.c41
-rw-r--r--mailmap.c1
-rw-r--r--match-trees.c1
-rw-r--r--merge-file.c1
-rw-r--r--merge-index.c2
-rw-r--r--merge-recursive.c73
-rw-r--r--merge-tree.c2
-rw-r--r--mktag.c11
-rw-r--r--mozilla-sha1/sha1.c19
-rw-r--r--mozilla-sha1/sha1.h18
-rw-r--r--object-refs.c2
-rw-r--r--object.h2
-rw-r--r--pack-check.c75
-rw-r--r--pack-redundant.c7
-rw-r--r--pack-write.c142
-rw-r--r--pack.h14
-rw-r--r--pager.c5
-rw-r--r--patch-id.c2
-rw-r--r--path-list.c15
-rw-r--r--path-list.h2
-rw-r--r--path.c91
-rw-r--r--peek-remote.c2
-rw-r--r--perl/Makefile1
-rw-r--r--pkt-line.c2
-rw-r--r--ppc/sha1.c2
-rw-r--r--progress.c6
-rw-r--r--progress.h1
-rw-r--r--quote.c19
-rw-r--r--quote.h2
-rw-r--r--reachable.c4
-rw-r--r--read-cache.c83
-rw-r--r--receive-pack.c2
-rw-r--r--reflog-walk.c7
-rw-r--r--reflog-walk.h2
-rw-r--r--refs.c114
-rw-r--r--refs.h3
-rw-r--r--remote.c633
-rw-r--r--remote.h51
-rw-r--r--revision.c92
-rw-r--r--revision.h4
-rw-r--r--rsh.h2
-rw-r--r--run-command.c45
-rw-r--r--run-command.h9
-rw-r--r--send-pack.c59
-rw-r--r--server-info.c2
-rw-r--r--setup.c127
-rw-r--r--sha1_file.c149
-rw-r--r--sha1_name.c13
-rw-r--r--shallow.c1
-rw-r--r--ssh-upload.c10
-rw-r--r--strbuf.c1
-rw-r--r--t/Makefile4
-rw-r--r--t/lib-git-svn.sh34
-rw-r--r--t/lib-read-tree-m-3way.sh60
-rwxr-xr-xt/t0000-basic.sh122
-rwxr-xr-xt/t0020-crlf.sh81
-rwxr-xr-xt/t0022-crlf-rename.sh33
-rwxr-xr-xt/t0030-stripspace.sh400
-rwxr-xr-xt/t1000-read-tree-m-3way.sh172
-rwxr-xr-xt/t1001-read-tree-m-2way.sh176
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh174
-rwxr-xr-xt/t1003-read-tree-prefix.sh10
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh2
-rwxr-xr-xt/t1020-subdirectory.sh36
-rwxr-xr-xt/t1100-commit-tree-options.sh10
-rwxr-xr-xt/t1200-tutorial.sh25
-rwxr-xr-xt/t1300-repo-config.sh228
-rwxr-xr-xt/t1301-shared-repo.sh27
-rwxr-xr-xt/t1400-update-ref.sh74
-rwxr-xr-xt/t1420-lost-found.sh35
-rwxr-xr-xt/t1500-rev-parse.sh79
-rwxr-xr-xt/t1501-worktree.sh106
-rwxr-xr-xt/t2000-checkout-cache-clash.sh20
-rwxr-xr-xt/t2001-checkout-cache-clash.sh35
-rwxr-xr-xt/t2002-checkout-cache-u.sh26
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh18
-rwxr-xr-xt/t2004-checkout-cache-temp.sh48
-rwxr-xr-xt/t2005-checkout-index-symlinks.sh10
-rwxr-xr-xt/t2050-git-dir-relative.sh55
-rwxr-xr-xt/t2100-update-cache-badpath.sh12
-rwxr-xr-xt/t2101-update-index-reupdate.sh28
-rwxr-xr-xt/t2102-update-index-symlinks.sh14
-rwxr-xr-xt/t2200-add-update.sh4
-rwxr-xr-xt/t3000-ls-files-others.sh18
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh16
-rwxr-xr-xt/t3002-ls-files-dashpath.sh24
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh20
-rwxr-xr-xt/t3020-ls-files-error-unmatch.sh14
-rwxr-xr-xt/t3030-merge-recursive.sh1
-rwxr-xr-xt/t3100-ls-tree-restrict.sh32
-rwxr-xr-xt/t3101-ls-tree-dirname.sh24
-rwxr-xr-xt/t3200-branch.sh175
-rwxr-xr-xt/t3210-pack-refs.sh86
-rwxr-xr-xt/t3300-funny-names.sh76
-rwxr-xr-xt/t3400-rebase.sh45
-rwxr-xr-xt/t3401-rebase-partial.sh18
-rwxr-xr-xt/t3403-rebase-skip.sh3
-rwxr-xr-xt/t3404-rebase-interactive.sh279
-rwxr-xr-xt/t3405-rebase-malformed.sh48
-rwxr-xr-xt/t3500-cherry.sh22
-rwxr-xr-xt/t3600-rm.sh82
-rwxr-xr-xt/t3700-add.sh115
-rwxr-xr-xt/t3800-mktag.sh4
-rwxr-xr-xt/t3900-i18n-commit.sh32
-rwxr-xr-xt/t3901-i18n-patch.sh34
-rwxr-xr-xt/t3902-quoted.sh132
-rwxr-xr-xt/t3903-stash.sh76
-rwxr-xr-xt/t4000-diff-format.sh8
-rwxr-xr-xt/t4001-diff-rename.sh23
-rwxr-xr-xt/t4002-diff-basic.sh70
-rwxr-xr-xt/t4003-diff-rename-1.sh18
-rwxr-xr-xt/t4004-diff-rename-symlink.sh8
-rwxr-xr-xt/t4005-diff-rename-2.sh16
-rwxr-xr-xt/t4006-diff-mode.sh17
-rwxr-xr-xt/t4007-rename-3.sh16
-rwxr-xr-xt/t4008-diff-break-rewrite.sh38
-rwxr-xr-xt/t4009-diff-rename-4.sh16
-rwxr-xr-xt/t4010-diff-pathspec.sh16
-rwxr-xr-xt/t4011-diff-symlink.sh20
-rwxr-xr-xt/t4012-diff-binary.sh26
-rwxr-xr-xt/t4013-diff-various.sh2
-rw-r--r--t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX2
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master^^60
-rwxr-xr-xt/t4015-diff-whitespace.sh16
-rw-r--r--t/t4018-diff-funcname.sh60
-rwxr-xr-xt/t4020-diff-external.sh12
-rwxr-xr-xt/t4100-apply-stat.sh17
-rwxr-xr-xt/t4101-apply-nonl.sh4
-rwxr-xr-xt/t4102-apply-rename.sh10
-rwxr-xr-xt/t4103-apply-binary.sh52
-rwxr-xr-xt/t4104-apply-boundary.sh2
-rwxr-xr-xt/t4109-apply-multifrag.sh14
-rwxr-xr-xt/t4110-apply-scan.sh7
-rwxr-xr-xt/t4112-apply-renames.sh24
-rwxr-xr-xt/t4113-apply-ending.sh10
-rwxr-xr-xt/t4114-apply-typechange.sh2
-rwxr-xr-xt/t4115-apply-symlink.sh2
-rwxr-xr-xt/t4116-apply-reverse.sh2
-rwxr-xr-xt/t4117-apply-reject.sh2
-rwxr-xr-xt/t4118-apply-empty-context.sh3
-rwxr-xr-xt/t4119-apply-config.sh4
-rwxr-xr-xt/t4120-apply-popt.sh2
-rwxr-xr-xt/t4121-apply-diffs.sh3
-rwxr-xr-xt/t4122-apply-symlink-inside.sh1
-rwxr-xr-xt/t4200-rerere.sh69
-rwxr-xr-xt/t4201-shortlog.sh2
-rwxr-xr-xt/t5000-tar-tree.sh57
-rwxr-xr-xt/t5100-mailinfo.sh6
-rwxr-xr-xt/t5300-pack-object.sh72
-rwxr-xr-xt/t5301-sliding-window.sh40
-rwxr-xr-xt/t5302-pack-index.sh56
-rwxr-xr-xt/t5400-send-pack.sh38
-rwxr-xr-xt/t5401-update-hooks.sh26
-rwxr-xr-xt/t5500-fetch-pack.sh32
-rwxr-xr-xt/t5510-fetch.sh8
-rwxr-xr-xt/t5516-fetch-push.sh247
-rwxr-xr-xt/t5520-pull.sh1
-rwxr-xr-xt/t5700-clone-reference.sh2
-rwxr-xr-xt/t5701-clone-local.sh17
-rwxr-xr-xt/t5710-info-alternate.sh1
-rwxr-xr-xt/t6000lib.sh16
-rwxr-xr-xt/t6002-rev-list-bisect.sh46
-rwxr-xr-xt/t6003-rev-list-topo-order.sh76
-rwxr-xr-xt/t6004-rev-list-path-optim.sh8
-rwxr-xr-xt/t6005-rev-list-count.sh44
-rwxr-xr-xt/t6006-rev-list-format.sh10
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh43
-rwxr-xr-xt/t6010-merge-base.sh24
-rwxr-xr-xt/t6021-merge-criss-cross.sh2
-rwxr-xr-xt/t6023-merge-file.sh16
-rwxr-xr-xt/t6024-recursive-merge.sh8
-rwxr-xr-xt/t6025-merge-symlinks.sh14
-rwxr-xr-xt/t6027-merge-binary.sh67
-rwxr-xr-xt/t6030-bisect-porcelain.sh1
-rwxr-xr-xt/t6101-rev-parse-parents.sh39
-rwxr-xr-xt/t6120-describe.sh22
-rwxr-xr-xt/t7001-mv.sh32
-rwxr-xr-xt/t7003-filter-branch.sh162
-rwxr-xr-xt/t7004-tag.sh1002
-rw-r--r--t/t7004/pubring.gpgbin0 -> 1164 bytes
-rw-r--r--t/t7004/random_seedbin0 -> 600 bytes
-rw-r--r--t/t7004/secring.gpgbin0 -> 1237 bytes
-rw-r--r--t/t7004/trustdb.gpgbin0 -> 1280 bytes
-rwxr-xr-xt/t7005-editor.sh91
-rwxr-xr-xt/t7101-reset.sh10
-rwxr-xr-xt/t7300-clean.sh6
-rwxr-xr-xt/t7400-submodule-basic.sh178
-rwxr-xr-xt/t7500-commit.sh96
-rwxr-xr-xt/t7500/add-comments4
-rwxr-xr-xt/t7500/add-content3
-rwxr-xr-xt/t7500/add-signed-off3
-rw-r--r--t/t7501-commit.sh134
-rwxr-xr-xt/t8001-annotate.sh2
-rwxr-xr-xt/t8002-blame.sh2
-rwxr-xr-xt/t9100-git-svn-basic.sh12
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh30
-rwxr-xr-xt/t9107-git-svn-migrate.sh27
-rwxr-xr-xt/t9110-git-svn-use-svm-props.sh20
-rwxr-xr-xt/t9111-git-svn-use-svnsync-props.sh20
-rw-r--r--t/t9111/svnsync.dump2
-rwxr-xr-xt/t9113-git-svn-dcommit-new-file.sh40
-rwxr-xr-xt/t9114-git-svn-dcommit-merge.sh89
-rwxr-xr-xt/t9115-git-svn-dcommit-funky-renames.sh54
-rw-r--r--t/t9115/funky-names.dump103
-rwxr-xr-xt/t9116-git-svn-log.sh48
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh64
-rwxr-xr-xt/t9300-fast-import.sh251
-rwxr-xr-xt/t9400-git-cvsserver-server.sh284
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh552
-rw-r--r--t/test-lib.sh4
-rw-r--r--templates/Makefile5
-rw-r--r--templates/hooks--commit-msg1
-rw-r--r--templates/hooks--post-receive1
-rw-r--r--templates/hooks--pre-applypatch1
-rw-r--r--templates/hooks--pre-commit1
-rw-r--r--test-absolute-path.c11
-rw-r--r--tree-diff.c78
-rw-r--r--tree-walk.c1
-rw-r--r--tree-walk.h2
-rw-r--r--tree.c73
-rw-r--r--unpack-file.c4
-rw-r--r--unpack-trees.c162
-rw-r--r--unpack-trees.h13
-rw-r--r--upload-pack.c4
-rw-r--r--var.c4
-rw-r--r--write_or_die.c40
-rw-r--r--wt-status.c2
-rw-r--r--xdiff-interface.c69
-rw-r--r--xdiff-interface.h2
-rw-r--r--xdiff/xdiff.h5
-rw-r--r--xdiff/xdiffi.c1
-rw-r--r--xdiff/xdiffi.h1
-rw-r--r--xdiff/xemit.c42
-rw-r--r--xdiff/xemit.h1
-rw-r--r--xdiff/xinclude.h1
-rw-r--r--xdiff/xmacros.h1
-rw-r--r--xdiff/xprepare.c1
-rw-r--r--xdiff/xprepare.h1
-rw-r--r--xdiff/xtypes.h1
-rw-r--r--xdiff/xutils.c1
-rw-r--r--xdiff/xutils.h1
607 files changed, 27873 insertions, 9524 deletions
diff --git a/.gitignore b/.gitignore
index b7546a5591..63c918c667 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,8 +7,6 @@ git-add--interactive
git-am
git-annotate
git-apply
-git-applymbox
-git-applypatch
git-archimport
git-archive
git-bisect
@@ -42,6 +40,7 @@ git-fast-import
git-fetch
git-fetch--tool
git-fetch-pack
+git-filter-branch
git-findtags
git-fmt-merge-msg
git-for-each-ref
@@ -97,6 +96,7 @@ git-push
git-quiltimport
git-read-tree
git-rebase
+git-rebase--interactive
git-receive-pack
git-reflog
git-relink
@@ -124,8 +124,10 @@ git-ssh-fetch
git-ssh-pull
git-ssh-push
git-ssh-upload
+git-stash
git-status
git-stripspace
+git-submodule
git-svn
git-svnimport
git-symbolic-ref
@@ -146,19 +148,21 @@ git-write-tree
git-core-*/?*
gitk-wish
gitweb/gitweb.cgi
+test-absolute-path
test-chmtime
test-date
test-delta
test-dump-cache-tree
test-genrandom
test-match-trees
+test-sha1
common-cmds.h
*.tar.gz
*.dsc
*.deb
git.spec
*.exe
-*.[ao]
+*.[aos]
*.py[co]
config.mak
autom4te.cache
diff --git a/.mailmap b/.mailmap
index 4e0615e9be..5529b198e8 100644
--- a/.mailmap
+++ b/.mailmap
@@ -7,6 +7,8 @@
Aneesh Kumar K.V <aneesh.kumar@gmail.com>
Chris Shoemaker <c.shoemaker@cox.net>
+Dana L. How <danahow@gmail.com>
+Dana L. How <how@deathvalley.cswitch.com>
Daniel Barkalow <barkalow@iabervon.org>
David KÃ¥gedal <davidk@lysator.liu.se>
Fredrik Kuivinen <freku045@student.liu.se>
@@ -17,13 +19,16 @@ Horst H. von Brand <vonbrand@inf.utfsm.cl>
Joachim Berdal Haga <cjhaga@fys.uio.no>
Jon Loeliger <jdl@freescale.com>
Jon Seymour <jon@blackcubes.dyndns.org>
+Junio C Hamano <junio@twinsun.com>
Karl Hasselström <kha@treskal.com>
Kent Engstrom <kent@lysator.liu.se>
-Lars Doelle <lars.doelle@on-line.de>
Lars Doelle <lars.doelle@on-line ! de>
+Lars Doelle <lars.doelle@on-line.de>
Lukas Sandström <lukass@etek.chalmers.se>
Martin Langhoff <martin@catalyst.net.nz>
+Michael Coleman <tutufan@gmail.com>
Michele Ballabio <barra_cuda@katamail.com>
+Nanako Shiraishi <nanako3@bluebottle.com>
Nguyá»…n Thái Ngá»c Duy <pclouds@gmail.com>
Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
René Scharfe <rene.scharfe@lsrfire.ath.cx>
@@ -34,12 +39,12 @@ Sean Estabrooks <seanlkml@sympatico.ca>
Shawn O. Pearce <spearce@spearce.org>
Theodore Ts'o <tytso@mit.edu>
Tony Luck <tony.luck@intel.com>
-Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
Uwe Kleine-König <Uwe_Zeisberger@digi.com>
-Uwe Kleine-König <uzeisberger@io.fsforth.de>
Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
+Uwe Kleine-König <uzeisberger@io.fsforth.de>
+Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
Ville Skyttä <scop@xemacs.org>
+William Pursell <bill.pursell@gmail.com>
YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
anonymous <linux@horizon.com>
anonymous <linux@horizon.net>
-Dana L. How <how@deathvalley.cswitch.com>
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 4edf788c3a..fbefe9a45b 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -2,7 +2,7 @@ MAN1_TXT= \
$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
$(wildcard git-*.txt)) \
gitk.txt
-MAN5_TXT=gitattributes.txt gitignore.txt
+MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt
MAN7_TXT=git.txt
DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT))
@@ -29,7 +29,7 @@ DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
prefix?=$(HOME)
bindir?=$(prefix)/bin
-mandir?=$(prefix)/man
+mandir?=$(prefix)/share/man
man1dir=$(mandir)/man1
man5dir=$(mandir)/man5
man7dir=$(mandir)/man7
@@ -41,8 +41,14 @@ ifdef ASCIIDOC8
ASCIIDOC_EXTRA += -a asciidoc7compatible
endif
INSTALL?=install
+RM ?= rm -f
DOC_REF = origin/man
+infodir?=$(prefix)/share/info
+MAKEINFO=makeinfo
+INSTALL_INFO=install-info
+DOCBOOK2X_TEXI=docbook2x-texi
+
-include ../config.mak.autogen
-include ../config.mak
@@ -66,6 +72,8 @@ man1: $(DOC_MAN1)
man5: $(DOC_MAN5)
man7: $(DOC_MAN7)
+info: git.info
+
install: man
$(INSTALL) -d -m755 $(DESTDIR)$(man1dir)
$(INSTALL) -d -m755 $(DESTDIR)$(man5dir)
@@ -74,6 +82,14 @@ install: man
$(INSTALL) -m644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
+install-info: info
+ $(INSTALL) -d -m755 $(DESTDIR)$(infodir)
+ $(INSTALL) -m644 git.info $(DESTDIR)$(infodir)
+ if test -r $(DESTDIR)$(infodir)/dir; then \
+ $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\
+ else \
+ echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
+ fi
../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
$(MAKE) -C ../ GIT-VERSION-FILE
@@ -84,7 +100,7 @@ install: man
# Determine "include::" file references in asciidoc files.
#
doc.dep : $(wildcard *.txt) build-docdep.perl
- rm -f $@+ $@
+ $(RM) $@+ $@
perl ./build-docdep.perl >$@+
mv $@+ $@
@@ -103,26 +119,28 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
$(cmds_txt): cmd-list.made
cmd-list.made: cmd-list.perl $(MAN1_TXT)
+ $(RM) $@
perl ./cmd-list.perl
date >$@
git.7 git.html: git.txt core-intro.txt
clean:
- rm -f *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 howto-index.txt howto/*.html doc.dep
- rm -f $(cmds_txt) *.made
+ $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 *.texi *.texi+ howto-index.txt howto/*.html doc.dep
+ $(RM) $(cmds_txt) *.made
%.html : %.txt
- rm -f $@+ $@
+ $(RM) $@+ $@
$(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
mv $@+ $@
%.1 %.5 %.7 : %.xml
+ $(RM) $@
xmlto -m callouts.xsl man $<
%.xml : %.txt
- rm -f $@+ $@
+ $(RM) $@+ $@
$(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $<
mv $@+ $@
@@ -130,14 +148,21 @@ clean:
user-manual.xml: user-manual.txt user-manual.conf
$(ASCIIDOC) -b docbook -d book $<
-XSLT = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
+XSLT = docbook.xsl
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
+git.info: user-manual.xml
+ $(RM) $@ $*.texi $*.texi+
+ $(DOCBOOK2X_TEXI) user-manual.xml --to-stdout >$*.texi+
+ perl fix-texi.perl <$*.texi+ >$*.texi
+ $(MAKEINFO) --no-split $*.texi
+ $(RM) $*.texi $*.texi+
+
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
- rm -f $@+ $@
+ $(RM) $@+ $@
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+
mv $@+ $@
@@ -147,7 +172,7 @@ $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
WEBDOC_DEST = /pub/software/scm/git/docs
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
- rm -f $@+ $@
+ $(RM) $@+ $@
sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+
mv $@+ $@
diff --git a/Documentation/RelNotes-1.5.0.4.txt b/Documentation/RelNotes-1.5.0.4.txt
index b727a8d1e5..feefa5dfd4 100644
--- a/Documentation/RelNotes-1.5.0.4.txt
+++ b/Documentation/RelNotes-1.5.0.4.txt
@@ -20,5 +20,3 @@ Fixes since v1.5.0.3
* Documentation updates
* User manual updates
-
-
diff --git a/Documentation/RelNotes-1.5.0.5.txt b/Documentation/RelNotes-1.5.0.5.txt
index aa86149d4f..eeec3d73d0 100644
--- a/Documentation/RelNotes-1.5.0.5.txt
+++ b/Documentation/RelNotes-1.5.0.5.txt
@@ -24,5 +24,3 @@ Fixes since v1.5.0.3
* Documentation updates
* User manual updates
-
-
diff --git a/Documentation/RelNotes-1.5.0.6.txt b/Documentation/RelNotes-1.5.0.6.txt
index e15447ffdb..c02015ad5f 100644
--- a/Documentation/RelNotes-1.5.0.6.txt
+++ b/Documentation/RelNotes-1.5.0.6.txt
@@ -19,4 +19,3 @@ Fixes since v1.5.0.5
- user-manual has better cross references.
- gitweb installation/deployment procedure is now documented.
-
diff --git a/Documentation/RelNotes-1.5.1.3.txt b/Documentation/RelNotes-1.5.1.3.txt
index 2ddeabd029..876408b65a 100644
--- a/Documentation/RelNotes-1.5.1.3.txt
+++ b/Documentation/RelNotes-1.5.1.3.txt
@@ -43,4 +43,3 @@ Fixes since v1.5.1.2
description was given by the caller.
Also contains various documentation updates.
-
diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt
new file mode 100644
index 0000000000..9c36e8baeb
--- /dev/null
+++ b/Documentation/RelNotes-1.5.3.txt
@@ -0,0 +1,322 @@
+GIT v1.5.3 Release Notes
+========================
+
+Updates since v1.5.2
+--------------------
+
+* The commit walkers other than http are officially deprecated,
+ but still supported for now.
+
+* The submodule support has Porcelain layer.
+
+ Note that the current submodule support is minimal and this is
+ deliberately so. A design decision we made is that operations
+ at the supermodule level do not recurse into submodules by
+ default. The expectation is that later we would add a
+ mechanism to tell git which submodules the user is interested
+ in, and this information might be used to determine the
+ recursive behaviour of certain commands (e.g. "git checkout"
+ and "git diff"), but currently we haven't agreed on what that
+ mechanism should look like. Therefore, if you use submodules,
+ you would probably need "git submodule update" on the
+ submodules you care about after running a "git checkout" at
+ the supermodule level.
+
+* There are a handful pack-objects changes to help you cope better
+ with repositories with pathologically large blobs in them.
+
+* For people who need to import from Perforce, a front-end for
+ fast-import is in contrib/fast-import/.
+
+* Comes with git-gui 0.8.0.
+
+* Comes with updated gitk.
+
+* New commands and options.
+
+ - "git log --date=<format>" can use more formats: iso8601, rfc2822.
+
+ - The hunk header output from "git diff" family can be customized
+ with the attributes mechanism. See gitattributes(5) for details.
+
+ - "git stash" allows you to quickly save away your work in
+ progress and replay it later on an updated state.
+
+ - "git rebase" learned an "interactive" mode that let you
+ pick and reorder which commits to rebuild.
+
+ - "git fsck" can save its findings in $GIT_DIR/lost-found, without a
+ separate invocation of "git lost-found" command. The blobs stored by
+ lost-found are stored in plain format to allow you to grep in them.
+
+ - $GIT_WORK_TREE environment variable can be used together with
+ $GIT_DIR to work in a subdirectory of a working tree that is
+ not located at "$GIT_DIR/..".
+
+ - Giving "--file=<file>" option to "git config" is the same as
+ running the command with GIT_CONFIG=<file> environment.
+
+ - "git log" learned a new option "--follow", to follow
+ renaming history of a single file.
+
+ - "git filter-branch" lets you rewrite the revision history of
+ specified branches. You can specify a number of filters to
+ modify the commits, files and trees.
+
+ - "git cvsserver" learned new options (--base-path, --export-all,
+ --strict-paths) inspired by "git daemon".
+
+ - "git daemon --base-path-relaxed" can help migrating a repository URL
+ that did not use to use --base-path to use --base-path.
+
+ - "git commit" can use "-t templatefile" option and commit.template
+ configuration variable to prime the commit message given to you in the
+ editor.
+
+ - "git submodule" command helps you manage the projects from
+ the superproject that contain them.
+
+ - In addition to core.compression configuration option,
+ core.loosecompression and pack.compression options can
+ independently tweak zlib compression levels used for loose
+ and packed objects.
+
+ - "git ls-tree -l" shows size of blobs pointed at by the
+ tree entries, similar to "/bin/ls -l".
+
+ - "git rev-list" learned --regexp-ignore-case and
+ --extended-regexp options to tweak its matching logic used
+ for --grep fitering.
+
+ - "git describe --contains" is a handier way to call more
+ obscure command "git name-rev --tags".
+
+ - "git gc --aggressive" tells the command to spend more cycles
+ to optimize the repository harder.
+
+ - "git repack" learned a "window-memory" limit which
+ dynamically reduces the window size to stay within the
+ specified memory usage.
+
+ - "git repack" can be told to split resulting packs to avoid
+ exceeding limit specified with "--max-pack-size".
+
+ - "git fsck" gained --verbose option. This is really really
+ verbose but it might help you identify exact commit that is
+ corrupt in your repository.
+
+ - "git format-patch" learned --numbered-files option. This
+ may be useful for MH users.
+
+ - "git format-patch" learned format.subjectprefix configuration
+ variable, which serves the same purpose as "--subject-prefix"
+ option.
+
+ - "git tag -n -l" shows tag annotations while listing tags.
+
+ - "git cvsimport" can optionally use the separate-remote layout.
+
+ - "git blame" can be told to see through commits that change
+ whitespaces and indentation levels with "-w" option.
+
+ - "git send-email" can be told not to thread the messages when
+ sending out more than one patches.
+
+ - "git config" learned NUL terminated output format via -z to
+ help scripts.
+
+ - "git add" learned "--refresh <paths>..." option to selectively refresh
+ the cached stat information.
+
+ - "git init -q" makes the command quieter.
+
+* Updated behavior of existing commands.
+
+ - "gitweb" can offer multiple snapshot formats.
+
+ ***NOTE*** Unfortunately, this changes the format of the
+ $feature{snapshot}{default} entry in the per-site
+ configuration file 'gitweb_config.perl'. It used to be a
+ three-element tuple that describe a single format; with the
+ new configuration item format, you only have to say the name
+ of the format ('tgz', 'tbz2' or 'zip'). Please update the
+ your configuration file accordingly.
+
+ - "git clone" uses -l (hardlink files under .git) by default when
+ cloning locally.
+
+ - "git bundle create" can now create a bundle without negative refs,
+ i.e. "everything since the beginning up to certain points".
+
+ - "git diff" (but not the plumbing level "git diff-tree") now
+ recursively descends into trees by default.
+
+ - "git diff" does not show differences that come only from
+ stat-dirtiness in the form of "diff --git" header anymore. When
+ generating a textual diff, it shows a warning message at the end.
+
+ - The editor to use with many interactive commands can be
+ overridden with GIT_EDITOR environment variable, or if it
+ does not exist, with core.editor configuration variable. As
+ before, if you have neither, environment variables VISUAL
+ and EDITOR are consulted in this order, and then finally we
+ fall back on "vi".
+
+ - "git rm --cached" does not complain when removing a newly
+ added file from the index anymore.
+
+ - Options to "git log" to affect how --grep/--author options look for
+ given strings now have shorter abbreviations. -i is for ignore case,
+ and -E is for extended regexp.
+
+ - "git log" learned --log-size to show the number of bytes in
+ the log message part of the output to help qgit.
+
+ - "git svn dcommit" retains local merge information.
+
+ - "git svnimport" allows an empty string to be specified as the
+ trunk/ directory. This is necessary to suck data from a SVN
+ repository that doe not have trunk/ branches/ and tags/ organization
+ at all.
+
+ - "git config" to set values also honors type flags like --bool
+ and --int.
+
+ - core.quotepath configuration can be used to make textual git
+ output to emit most of the characters in the path literally.
+
+ - "git mergetool" chooses its backend more wisely, taking
+ notice of its environment such as use of X, Gnome/KDE, etc.
+
+ - "gitweb" shows merge commits a lot nicer than before. The
+ default view uses more compact --cc format, while the UI
+ allows to choose normal diff with any parent.
+
+ - snapshot files "gitweb" creates from a repository at
+ $path/$project/.git are more useful. We use $project part
+ in the filename, which we used to discard.
+
+ - "git cvsimport" creates lightweight tags; there is no
+ interesting information we can record in an annotated tag,
+ and the handcrafted ones the old code created was not
+ properly formed anyway.
+
+ - "git push" pretends that you immediately fetched back from
+ the remote by updating corresponding remote tracking
+ branches if you have any.
+
+ - The diffstat given after a merge (or a pull) honors the
+ color.diff configuration.
+
+ - "git commit --amend" is now compatible with various message source
+ options such as -m/-C/-c/-F.
+
+ - "git apply --whitespace=strip" removes blank lines added at
+ the end of the file.
+
+ - "git fetch" over git native protocols with "-v" option shows
+ connection status, and the IP address of the other end, to
+ help diagnosing problems.
+
+ - We used to have core.legacyheaders configuration, when
+ set to false, allowed git to write loose objects in a format
+ that mimicks the format used by objects stored in packs. It
+ turns out that this was not so useful. Although we will
+ continue to read objects written in that format, we do not
+ honor that configuration anymore and create loose objects in
+ the legacy/traditional format.
+
+ - "--find-copies-harder" option to diff family can now be
+ spelled as "-C -C" for brevity.
+
+ - "git mailsplit" (hence "git am") can read from Maildir
+ formatted mailboxes.
+
+ - "git cvsserver" does not barf upon seeing "cvs login"
+ request.
+
+ - "pack-objects" honors "delta" attribute set in
+ .gitattributes. It does not attempt to deltify blobs that
+ come from paths with delta attribute set to false.
+
+ - "new-workdir" script (in contrib) can now be used with a
+ bare repository.
+
+ - "git mergetool" learned to use gvimdiff.
+
+ - "gitview" (in contrib) has a better blame interface.
+
+ - "git log" and friends did not handle a commit log message
+ that is larger than 16kB; they do now.
+
+ - "--pretty=oneline" output format for "git log" and friends
+ deals with "malformed" commit log messages that have more
+ than one lines in the first paragraph better. We used to
+ show the first line, cutting the title at mid-sentence; we
+ concatenate them into a single line and treat the result as
+ "oneline".
+
+ - "git p4import" has been demoted to contrib status. For
+ a superior option, checkout the "git p4" front end to
+ "git fast-import" (also in contrib). The man page and p4
+ rpm have been removed as well.
+
+ - "git mailinfo" (hence "am") now tries to see if the message
+ is in utf-8 first, instead of assuming iso-8859-1, if
+ incoming e-mail does not say what encoding it is in.
+
+* Builds
+
+ - old-style function definitions (most notably, a function
+ without parameter defined with "func()", not "func(void)")
+ have been eradicated.
+
+ - "git tag" and "git verify-tag" have been rewritten in C.
+
+* Performance Tweaks
+
+ - "git pack-objects" avoids re-deltification cost by caching
+ small enough delta results it creates while looking for the
+ best delta candidates.
+
+ - "git pack-objects" learned a new heuristcs to prefer delta
+ that is shallower in depth over the smallest delta
+ possible. This improves both overall packfile access
+ performance and packfile density.
+
+ - diff-delta code that is used for packing has been improved
+ to work better on big files.
+
+ - when there are more than one pack files in the repository,
+ the runtime used to try finding an object always from the
+ newest packfile; it now tries the same packfile as we found
+ the object requested the last time, which exploits the
+ locality of references.
+
+ - verifying pack contents done by "git fsck --full" got boost
+ by carefully choosing the order to verify objects in them.
+
+ - "git read-tree -m" to read into an already populated index
+ has been optimized vastly. The effect of this can be seen
+ when switching branches that have differences in only a
+ handful paths.
+
+ - "git commit paths..." has also been optimized.
+
+
+Fixes since v1.5.2
+------------------
+
+All of the fixes in v1.5.2 maintenance series are included in
+this release, unless otherwise noted.
+
+* Bugfixes
+
+ - "gitweb" had trouble handling non UTF-8 text with older
+ Encode.pm Perl module.
+
+--
+exec >/var/tmp/1
+O=v1.5.3-rc4
+echo O=`git describe refs/heads/master`
+git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index b94d9a8166..01354c2bb5 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -14,6 +14,8 @@ Checklist (and a short version for the impatient):
commit message (or just use the option "-s" when
committing) to confirm that you agree to the Developer's
Certificate of Origin
+ - make sure that you have tests for the bug you are fixing
+ - make sure that the test suite passes after your commit
Patch:
@@ -33,6 +35,8 @@ Checklist (and a short version for the impatient):
- if you change, add, or remove a command line option or
make some other user interface change, the associated
documentation should be updated as well.
+ - if your name is not writable in ASCII, make sure that
+ you send off a message in the correct encoding.
Long version:
@@ -239,7 +243,7 @@ One test you could do yourself if your MUA is set up correctly is:
$ git fetch http://kernel.org/pub/scm/git/git.git master:test-apply
$ git checkout test-apply
$ git reset --hard
- $ git applymbox a.patch
+ $ git am a.patch
If it does not apply correctly, there can be various reasons.
@@ -247,7 +251,7 @@ If it does not apply correctly, there can be various reasons.
does not have much to do with your MUA. Please rebase the
patch appropriately.
-* Your MUA corrupted your patch; applymbox would complain that
+* Your MUA corrupted your patch; "am" would complain that
the patch does not apply. Look at .dotest/ subdirectory and
see what 'patch' file contains and check for the common
corruption patterns mentioned above.
@@ -292,15 +296,15 @@ diff --git a/pico/pico.c b/pico/pico.c
--- a/pico/pico.c
+++ b/pico/pico.c
@@ -219,7 +219,9 @@ PICO *pm;
- switch(pico_all_done){ /* prepare for/handle final events */
- case COMP_EXIT : /* already confirmed */
- packheader();
+ switch(pico_all_done){ /* prepare for/handle final events */
+ case COMP_EXIT : /* already confirmed */
+ packheader();
+#if 0
- stripwhitespace();
+ stripwhitespace();
+#endif
- c |= COMP_EXIT;
- break;
-
+ c |= COMP_EXIT;
+ break;
+
(Daniel Barkalow)
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index a0d8459e21..af5b1558a6 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -61,5 +61,3 @@ ifdef::backend-xhtml11[]
[gitlink-inlinemacro]
<a href="{target}.html">{target}{0?({0})}</a>
endif::backend-xhtml11[]
-
-
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index a46bf6ce70..17379f0576 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -64,11 +64,11 @@ of lines before or after the line given by <start>.
assigns blame to the lines that were moved down (i.e. A)
to the child commit. With this option, both groups of lines
are blamed on the parent.
-
- <num> is optional but it is the lower bound on the number of
- alphanumeric characters that git must detect as moving
- within a file for it to associate those lines with the parent
- commit.
++
+<num> is optional but it is the lower bound on the number of
+alphanumeric characters that git must detect as moving
+within a file for it to associate those lines with the parent
+commit.
-C|<num>|::
In addition to `-M`, detect lines copied from other
@@ -77,11 +77,11 @@ of lines before or after the line given by <start>.
around across files. When this option is given twice,
the command looks for copies from all other files in the
parent for the commit that creates the file in addition.
-
- <num> is optional but it is the lower bound on the number of
- alphanumeric characters that git must detect as moving
- between files for it to associate those lines with the parent
- commit.
++
+<num> is optional but it is the lower bound on the number of
+alphanumeric characters that git must detect as moving
+between files for it to associate those lines with the parent
+commit.
-h, --help::
Show help message.
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 443802a9a3..4ee76eaf99 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -68,12 +68,12 @@ for my $cat (qw(ancillaryinterrogators
}
}
+# The following list is sorted with "sort -d" to make it easier
+# to find entry in the resulting git.html manual page.
__DATA__
git-add mainporcelain
git-am mainporcelain
git-annotate ancillaryinterrogators
-git-applymbox ancillaryinterrogators
-git-applypatch purehelpers
git-apply plumbingmanipulators
git-archimport foreignscminterface
git-archive mainporcelain
@@ -82,16 +82,18 @@ git-blame ancillaryinterrogators
git-branch mainporcelain
git-bundle mainporcelain
git-cat-file plumbinginterrogators
-git-checkout-index plumbingmanipulators
-git-checkout mainporcelain
git-check-attr purehelpers
+git-checkout mainporcelain
+git-checkout-index plumbingmanipulators
git-check-ref-format purehelpers
git-cherry ancillaryinterrogators
git-cherry-pick mainporcelain
+git-citool mainporcelain
git-clean mainporcelain
git-clone mainporcelain
git-commit mainporcelain
git-commit-tree plumbingmanipulators
+git-config ancillarymanipulators
git-convert-objects ancillarymanipulators
git-count-objects ancillaryinterrogators
git-cvsexportcommit foreignscminterface
@@ -99,13 +101,14 @@ git-cvsimport foreignscminterface
git-cvsserver foreignscminterface
git-daemon synchingrepositories
git-describe mainporcelain
+git-diff mainporcelain
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
-git-diff mainporcelain
git-diff-tree plumbinginterrogators
git-fast-import ancillarymanipulators
git-fetch mainporcelain
git-fetch-pack synchingrepositories
+git-filter-branch ancillarymanipulators
git-fmt-merge-msg purehelpers
git-for-each-ref plumbinginterrogators
git-format-patch mainporcelain
@@ -113,6 +116,7 @@ git-fsck ancillaryinterrogators
git-gc mainporcelain
git-get-tar-commit-id ancillaryinterrogators
git-grep mainporcelain
+git-gui mainporcelain
git-hash-object plumbingmanipulators
git-http-fetch synchelpers
git-http-push synchelpers
@@ -129,13 +133,13 @@ git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
git-mailinfo purehelpers
git-mailsplit purehelpers
+git-merge mainporcelain
git-merge-base plumbinginterrogators
git-merge-file plumbingmanipulators
git-merge-index plumbingmanipulators
-git-merge mainporcelain
git-merge-one-file purehelpers
-git-merge-tree ancillaryinterrogators
git-mergetool ancillarymanipulators
+git-merge-tree ancillaryinterrogators
git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
git-mv mainporcelain
@@ -156,9 +160,8 @@ git-rebase mainporcelain
git-receive-pack synchelpers
git-reflog ancillarymanipulators
git-relink ancillarymanipulators
-git-repack ancillarymanipulators
-git-config ancillarymanipulators
git-remote ancillarymanipulators
+git-repack ancillarymanipulators
git-request-pull foreignscminterface
git-rerere ancillaryinterrogators
git-reset mainporcelain
@@ -178,8 +181,10 @@ git-show-ref plumbinginterrogators
git-sh-setup purehelpers
git-ssh-fetch synchingrepositories
git-ssh-upload synchingrepositories
+git-stash mainporcelain
git-status mainporcelain
git-stripspace purehelpers
+git-submodule mainporcelain
git-svn foreignscminterface
git-svnimport foreignscminterface
git-symbolic-ref plumbingmanipulators
diff --git a/Documentation/config.txt b/Documentation/config.txt
index a75f7f7664..de9e72b562 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -101,7 +101,7 @@ Example
# Proxy settings
[core]
- gitProxy="ssh" for "ssh://kernel.org/"
+ gitProxy="ssh" for "kernel.org"
gitProxy=default-proxy ; for the rest
Variables
@@ -117,6 +117,18 @@ core.fileMode::
the working copy are ignored; useful on broken filesystems like FAT.
See gitlink:git-update-index[1]. True by default.
+core.quotepath::
+ The commands that output paths (e.g. `ls-files`,
+ `diff`), when not given the `-z` option, will quote
+ "unusual" characters in the pathname by enclosing the
+ pathname in a double-quote pair and with backslashes the
+ same way strings in C source code are quoted. If this
+ variable is set to false, the bytes higher than 0x80 are
+ not quoted but output as verbatim. Note that double
+ quote, backslash and control characters are always
+ quoted without `-z` regardless of the setting of this
+ variable.
+
core.autocrlf::
If true, makes git convert `CRLF` at the end of lines in text files to
`LF` when reading from the filesystem, and convert in reverse when
@@ -172,6 +184,13 @@ repository that ends in "/.git" is assumed to be not bare (bare =
false), while all other repositories are assumed to be bare (bare
= true).
+core.worktree::
+ Set the path to the working tree. The value will not be
+ used in combination with repositories found automatically in
+ a .git directory (i.e. $GIT_DIR is not set).
+ This can be overriden by the GIT_WORK_TREE environment
+ variable and the '--work-tree' command line option.
+
core.logAllRefUpdates::
Updates to a ref <ref> is logged to the file
"$GIT_DIR/logs/<ref>", by appending the new and old
@@ -204,23 +223,16 @@ core.warnAmbiguousRefs::
and might match multiple refs in the .git/refs/ tree. True by default.
core.compression::
+ An integer -1..9, indicating a default compression level.
+ -1 is the zlib default. 0 means no compression,
+ and 1..9 are various speed/size tradeoffs, 9 being slowest.
+
+core.loosecompression::
An integer -1..9, indicating the compression level for objects that
- are not in a pack file. -1 is the zlib and git default. 0 means no
+ are not in a pack file. -1 is the zlib default. 0 means no
compression, and 1..9 are various speed/size tradeoffs, 9 being
- slowest.
-
-core.legacyheaders::
- A boolean which
- changes the format of loose objects so that they are more
- efficient to pack and to send out of the repository over git
- native protocol, since v1.4.2. However, loose objects
- written in the new format cannot be read by git older than
- that version; people fetching from your repository using
- older versions of git over dumb transports (e.g. http)
- will also be affected.
-+
-To let git use the new loose object format, you have to
-set core.legacyheaders to false.
+ slowest. If not set, defaults to core.compression. If that is
+ not set, defaults to 0 (best speed).
core.packedGitWindowSize::
Number of bytes of a pack file to map into memory in a
@@ -269,6 +281,18 @@ core.excludesfile::
of files which are not meant to be tracked. See
gitlink:gitignore[5].
+core.editor::
+ Commands such as `commit` and `tag` that lets you edit
+ messages by lauching an editor uses the value of this
+ variable when it is set, and the environment variable
+ `GIT_EDITOR` is not set. The order of preference is
+ `GIT_EDITOR` environment, `core.editor`, `VISUAL` and
+ `EDITOR` environment variables and then finally `vi`.
+
+core.pager::
+ The command that git will use to paginate output. Can be overridden
+ with the `GIT_PAGER` environment variable.
+
alias.*::
Command aliases for the gitlink:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation
@@ -369,6 +393,9 @@ color.status.<slot>::
or `untracked` (files which are not tracked by git). The values of
these variables may be specified as in color.branch.<slot>.
+commit.template::
+ Specify a file to use as the template for new commit messages.
+
diff.renameLimit::
The number of files to consider when performing the copy/rename
detection; equivalent to the git diff option '-l'.
@@ -397,6 +424,11 @@ format.suffix::
`.patch`. Use this variable to change that suffix (make sure to
include the dot if you want it).
+gc.aggressiveWindow::
+ The window size parameter used in the delta compression
+ algorithm used by 'git gc --aggressive'. This defaults
+ to 10.
+
gc.packrefs::
`git gc` does not run `git pack-refs` in a bare repository by
default so that older dumb-transport clients can still fetch
@@ -427,6 +459,11 @@ gc.rerereunresolved::
kept for this many days when `git rerere gc` is run.
The default is 15 days. See gitlink:git-rerere[1].
+rerere.enabled::
+ Activate recording of resolved conflicts, so that identical
+ conflict hunks can be resolved automatically, should they
+ be encountered again. See gitlink:git-rerere[1].
+
gitcvs.enabled::
Whether the cvs server interface is enabled for this repository.
See gitlink:git-cvsserver[1].
@@ -533,7 +570,7 @@ merge.summary::
merge.tool::
Controls which merge resolution program is used by
gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff",
- "meld", "xxdiff", "emerge", "vimdiff", and "opendiff"
+ "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
merge.verbosity::
Controls the amount of output shown by the recursive merge
@@ -541,6 +578,7 @@ merge.verbosity::
message if conflicts were detected. Level 1 outputs only
conflicts, 2 outputs conflicts and file changes. Level 5 and
above outputs debugging information. The default is level 2.
+ Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
merge.<driver>.name::
Defines a human readable name for a custom low-level
@@ -563,6 +601,28 @@ pack.depth::
The maximum delta depth used by gitlink:git-pack-objects[1] when no
maximum depth is given on the command line. Defaults to 50.
+pack.windowMemory::
+ The window memory size limit used by gitlink:git-pack-objects[1]
+ when no limit is given on the command line. The value can be
+ suffixed with "k", "m", or "g". Defaults to 0, meaning no
+ limit.
+
+pack.compression::
+ An integer -1..9, indicating the compression level for objects
+ in a pack file. -1 is the zlib default. 0 means no
+ compression, and 1..9 are various speed/size tradeoffs, 9 being
+ slowest. If not set, defaults to core.compression. If that is
+ not set, defaults to -1.
+
+pack.deltaCacheSize::
+ The maxium memory in bytes used for caching deltas in
+ gitlink:git-pack-objects[1].
+ A value of 0 means no limit. Defaults to 0.
+
+pack.deltaCacheLimit::
+ The maxium size of a delta, that is cached in
+ gitlink:git-pack-objects[1]. Defaults to 1000.
+
pull.octopus::
The default merge strategy to use when pulling multiple branches
at once.
@@ -668,5 +728,3 @@ receive.denyNonFastForwards::
transfer.unpackLimit::
When `fetch.unpackLimit` or `receive.unpackLimit` are
not set, the value of this variable is used instead.
-
-
diff --git a/Documentation/core-intro.txt b/Documentation/core-intro.txt
index eea44d9d56..f3cc2238c7 100644
--- a/Documentation/core-intro.txt
+++ b/Documentation/core-intro.txt
@@ -528,7 +528,7 @@ paths that have been trivially merged.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sadly, many merges aren't trivial. If there are files that have
-been added.moved or removed, or if both branches have modified the
+been added, moved or removed, or if both branches have modified the
same file, you will be left with an index tree that contains "merge
entries" in it. Such an index tree can 'NOT' be written out to a tree
object, and you will have to resolve any such merge clashes using
diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
index 6b9b9ad7d1..4fb6f4143c 100644
--- a/Documentation/core-tutorial.txt
+++ b/Documentation/core-tutorial.txt
@@ -9,11 +9,11 @@ repository, mainly because being hands-on and using explicit examples is
often the best way of explaining what is going on.
In normal life, most people wouldn't use the "core" git programs
-directly, but rather script around them to make them more palatable.
+directly, but rather script around them to make them more palatable.
Understanding the core git stuff may help some people get those scripts
done, though, and it may also be instructive in helping people
understand what it is that the higher-level helper scripts are actually
-doing.
+doing.
The core git is often called "plumbing", with the prettier user
interfaces on top of it called "porcelain". You may not want to use the
@@ -41,7 +41,7 @@ Creating a new git repository couldn't be easier: all git repositories start
out empty, and the only thing you need to do is find yourself a
subdirectory that you want to use as a working tree - either an empty
one for a totally new project, or an existing working tree that you want
-to import into git.
+to import into git.
For our first example, we're going to start a totally new repository from
scratch, with no pre-existing files, and we'll call it `git-tutorial`.
@@ -169,7 +169,7 @@ $ ls .git/objects/??/*
and see two files:
----------------
-.git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238
+.git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238
.git/objects/f2/4c74a2e500f5ee1332c86b94199f52b1d1d962
----------------
@@ -220,7 +220,7 @@ you have not actually really "checked in" your files into git so far,
you've only *told* git about them.
However, since git knows about them, you can now start using some of the
-most basic git commands to manipulate the files or look at their status.
+most basic git commands to manipulate the files or look at their status.
In particular, let's not even check in the two files into git yet, we'll
start off by adding another line to `hello` first:
@@ -350,7 +350,7 @@ Making a change
Remember how we did the `git-update-index` on file `hello` and then we
changed `hello` afterward, and could compare the new state of `hello` with the
-state we saved in the index file?
+state we saved in the index file?
Further, remember how I said that `git-write-tree` writes the contents
of the *index* file to the tree, and thus what we just committed was in
@@ -370,7 +370,7 @@ file and the working tree, `git-diff-index` shows the differences
between a committed *tree* and either the index file or the working
tree. In other words, `git-diff-index` wants a tree to be diffed
against, and before we did the commit, we couldn't do that, because we
-didn't have anything to diff against.
+didn't have anything to diff against.
But now we can do
@@ -379,7 +379,7 @@ $ git-diff-index -p HEAD
----------------
(where `-p` has the same meaning as it did in `git-diff-files`), and it
-will show us the same difference, but for a totally different reason.
+will show us the same difference, but for a totally different reason.
Now we're comparing the working tree not against the index file,
but against the tree we just wrote. It just so happens that those two
are obviously the same, so we get the same result.
@@ -398,7 +398,7 @@ working tree, but when given the `\--cached` flag, it is told to
instead compare against just the index cache contents, and ignore the
current working tree state entirely. Since we just wrote the index
file to HEAD, doing `git-diff-index \--cached -p HEAD` should thus return
-an empty set of differences, and that's exactly what it does.
+an empty set of differences, and that's exactly what it does.
[NOTE]
================
@@ -549,7 +549,7 @@ $ git-whatchanged -p --root
----------------
and you will see exactly what has changed in the repository over its
-short history.
+short history.
[NOTE]
The `\--root` flag is a flag to `git-diff-tree` to tell it to
@@ -637,7 +637,7 @@ So the mental model of "the git information is always tied directly to
the working tree that it describes" may not be technically 100%
accurate, but it's a good model for all normal use.
-This has two implications:
+This has two implications:
- if you grow bored with the tutorial repository you created (or you've
made a mistake and want to start all over), you can just do simple
@@ -705,7 +705,7 @@ Many (most?) public remote repositories will not contain any of
the checked out files or even an index file, and will *only* contain the
actual core git files. Such a repository usually doesn't even have the
`.git` subdirectory, but has all the git files directly in the
-repository.
+repository.
To create your own local live copy of such a "raw" git repository, you'd
first create your own subdirectory for the project, and then copy the
@@ -718,7 +718,7 @@ $ cd my-git
$ rsync -rL rsync://rsync.kernel.org/pub/scm/git/git.git/ .git
----------------
-followed by
+followed by
----------------
$ git-read-tree HEAD
@@ -738,7 +738,7 @@ up-to-date (so that you don't have to refresh it afterward), and the
`-a` flag means "check out all files" (if you have a stale copy or an
older version of a checked out tree you may also need to add the `-f`
flag first, to tell git-checkout-index to *force* overwriting of any old
-files).
+files).
Again, this can all be simplified with
@@ -751,7 +751,7 @@ $ git checkout
which will end up doing all of the above for you.
You have now successfully copied somebody else's (mine) remote
-repository, and checked it out.
+repository, and checked it out.
Creating a new branch
@@ -760,14 +760,14 @@ Creating a new branch
Branches in git are really nothing more than pointers into the git
object database from within the `.git/refs/` subdirectory, and as we
already discussed, the `HEAD` branch is nothing but a symlink to one of
-these object pointers.
+these object pointers.
You can at any time create a new branch by just picking an arbitrary
point in the project history, and just writing the SHA1 name of that
object into a file under `.git/refs/heads/`. You can use any filename you
want (and indeed, subdirectories), but the convention is that the
"normal" branch is called `master`. That's just a convention, though,
-and nothing enforces it.
+and nothing enforces it.
To show that as an example, let's go back to the git-tutorial repository we
used earlier, and create a branch in it. You do that by simply just
@@ -778,7 +778,7 @@ $ git checkout -b mybranch
------------
will create a new branch based at the current `HEAD` position, and switch
-to it.
+to it.
[NOTE]
================================================
@@ -825,7 +825,7 @@ checking it out and switching to it. If so, just use the command
$ git branch <branchname> [startingpoint]
------------
-which will simply _create_ the branch, but will not do anything further.
+which will simply _create_ the branch, but will not do anything further.
You can then later -- once you decide that you want to actually develop
on that branch -- switch to that branch with a regular `git checkout`
with the branchname as the argument.
@@ -884,7 +884,7 @@ $ gitk --all
will show you graphically both of your branches (that's what the `\--all`
means: normally it will just show you your current `HEAD`) and their
histories. You can also see exactly how they came to be from a common
-source.
+source.
Anyway, let's exit `gitk` (`^Q` or the File menu), and decide that we want
to merge the work we did on the `mybranch` branch into the `master`
@@ -905,8 +905,8 @@ of it as it can automatically (which in this case is just merge the `example`
file, which had no differences in the `mybranch` branch), and say:
----------------
- Auto-merging hello
- CONFLICT (content): Merge conflict in hello
+ Auto-merging hello
+ CONFLICT (content): Merge conflict in hello
Automatic merge failed; fix up by hand
----------------
@@ -1387,7 +1387,7 @@ repository. Kernel.org mirror network takes care of the
propagation to other publicly visible machines:
------------
-$ git push master.kernel.org:/pub/scm/git/git.git/
+$ git push master.kernel.org:/pub/scm/git/git.git/
------------
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index e38a1f1405..001503205b 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -1,7 +1,7 @@
The output format from "git-diff-index", "git-diff-tree" and
"git-diff-files" are very similar.
-These commands all compare two sets of things; what is
+These commands all compare two sets of things; what is
compared differs:
git-diff-index <tree-ish>::
@@ -126,6 +126,13 @@ the file that rename/copy produces, respectively.
If there is need for such substitution then the whole
pathname is put in double quotes.
+The similarity index is the percentage of unchanged lines, and
+the dissimilarity index is the percentage of changed lines. It
+is a rounded down integer, followed by a percent sign. The
+similarity index value of 100% is thus reserved for two equal
+files, while 100% dissimilarity means that no line from the old
+file made it into the new one.
+
combined diff format
--------------------
@@ -139,28 +146,28 @@ index fabadb8,cc95eb0..4866510
--- a/describe.c
+++ b/describe.c
@@@ -98,20 -98,12 +98,20 @@@
- return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
+ return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
}
-
+
- static void describe(char *arg)
-static void describe(struct commit *cmit, int last_one)
++static void describe(char *arg, int last_one)
{
+ unsigned char sha1[20];
+ struct commit *cmit;
- struct commit_list *list;
- static int initialized = 0;
- struct commit_name *n;
-
+ struct commit_list *list;
+ static int initialized = 0;
+ struct commit_name *n;
+
+ if (get_sha1(arg, sha1) < 0)
+ usage(describe_usage);
+ cmit = lookup_commit_reference(sha1);
+ if (!cmit)
+ usage(describe_usage);
+
- if (!initialized) {
- initialized = 1;
- for_each_ref(get_name);
+ if (!initialized) {
+ initialized = 1;
+ for_each_ref(get_name);
------------
1. It is preceded with a "git diff" header, that looks like
@@ -233,4 +240,3 @@ parents). When shown by `git diff-files -c`, it compares the
two unresolved merge parents with the working tree file
(i.e. file1 is stage 2 aka "our version", file2 is stage 3 aka
"their version").
-
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 1689c74817..228ccaf10a 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -4,6 +4,13 @@
-u::
Synonym for "-p".
+-U<n>::
+ Shorthand for "--unified=<n>".
+
+--unified=<n>::
+ Generate diffs with <n> lines of context instead of
+ the usual three. Implies "-p".
+
--raw::
Generate the raw format.
@@ -36,7 +43,9 @@
Synonym for "-p --stat".
-z::
- \0 line termination on output
+ NUL-line termination on output. This affects the --raw
+ output field terminator. Also output from commands such
+ as "git-log" will be delimited with NUL between commits.
--name-only::
Show only names of changed files.
@@ -86,7 +95,7 @@
Detect renames.
-C::
- Detect copies as well as renames.
+ Detect copies as well as renames. See also `--find-copies-harder`.
--diff-filter=[ACDMRTUXB*]::
Select only files that are Added (`A`), Copied (`C`),
@@ -100,12 +109,13 @@
that matches other criteria, nothing is selected.
--find-copies-harder::
- For performance reasons, by default, -C option finds copies only
- if the original file of the copy was modified in the same
+ For performance reasons, by default, `-C` option finds copies only
+ if the original file of the copy was modified in the same
changeset. This flag makes the command
inspect unmodified files as candidates for the source of
copy. This is a very expensive operation for large
- projects, so use it with caution.
+ projects, so use it with caution. Giving more than one
+ `-C` option has the same effect.
-l<num>::
-M and -C options require O(n^2) processing time where n
@@ -167,5 +177,13 @@
--quiet::
Disable all output of the program. Implies --exit-code.
+--ext-diff::
+ Allow an external diff helper to be executed. If you set an
+ external diff driver with gitlink:gitattributes(5), you need
+ to use this option with gitlink:git-log(1) and friends.
+
+--no-ext-diff::
+ Disallow external diff drivers.
+
For more detailed explanation on these common options, see also
link:diffcore.html[diffcore documentation].
diff --git a/Documentation/diffcore.txt b/Documentation/diffcore.txt
index 34cd306bb1..c6a983a5d5 100644
--- a/Documentation/diffcore.txt
+++ b/Documentation/diffcore.txt
@@ -71,7 +71,7 @@ The first transformation in the chain is diffcore-pathspec, and
is controlled by giving the pathname parameters to the
git-diff-* commands on the command line. The pathspec is used
to limit the world diff operates in. It removes the filepairs
-outside the specified set of pathnames. E.g. If the input set
+outside the specified set of pathnames. E.g. If the input set
of filepairs included:
------------------------------------------------
@@ -269,4 +269,3 @@ Documentation
*.c
t
------------------------------------------------
-
diff --git a/Documentation/docbook-xsl.css b/Documentation/docbook-xsl.css
index 8821e305dd..b878b385c6 100644
--- a/Documentation/docbook-xsl.css
+++ b/Documentation/docbook-xsl.css
@@ -1,286 +1,286 @@
-/*
- CSS stylesheet for XHTML produced by DocBook XSL stylesheets.
- Tested with XSL stylesheets 1.61.2, 1.67.2
-*/
-
-span.strong {
- font-weight: bold;
-}
-
-body blockquote {
- margin-top: .75em;
- line-height: 1.5;
- margin-bottom: .75em;
-}
-
-html body {
- margin: 1em 5% 1em 5%;
- line-height: 1.2;
-}
-
-body div {
- margin: 0;
-}
-
-h1, h2, h3, h4, h5, h6,
-div.toc p b,
-div.list-of-figures p b,
-div.list-of-tables p b,
-div.abstract p.title
-{
- color: #527bbd;
- font-family: tahoma, verdana, sans-serif;
-}
-
-div.toc p:first-child,
-div.list-of-figures p:first-child,
-div.list-of-tables p:first-child,
-div.example p.title
-{
- margin-bottom: 0.2em;
-}
-
-body h1 {
- margin: .0em 0 0 -4%;
- line-height: 1.3;
- border-bottom: 2px solid silver;
-}
-
-body h2 {
- margin: 0.5em 0 0 -4%;
- line-height: 1.3;
- border-bottom: 2px solid silver;
-}
-
-body h3 {
- margin: .8em 0 0 -3%;
- line-height: 1.3;
-}
-
-body h4 {
- margin: .8em 0 0 -3%;
- line-height: 1.3;
-}
-
-body h5 {
- margin: .8em 0 0 -2%;
- line-height: 1.3;
-}
-
-body h6 {
- margin: .8em 0 0 -1%;
- line-height: 1.3;
-}
-
-body hr {
- border: none; /* Broken on IE6 */
-}
-div.footnotes hr {
- border: 1px solid silver;
-}
-
-div.navheader th, div.navheader td, div.navfooter td {
- font-family: sans-serif;
- font-size: 0.9em;
- font-weight: bold;
- color: #527bbd;
-}
-div.navheader img, div.navfooter img {
- border-style: none;
-}
-div.navheader a, div.navfooter a {
- font-weight: normal;
-}
-div.navfooter hr {
- border: 1px solid silver;
-}
-
-body td {
- line-height: 1.2
-}
-
-body th {
- line-height: 1.2;
-}
-
-ol {
- line-height: 1.2;
-}
-
-ul, body dir, body menu {
- line-height: 1.2;
-}
-
-html {
- margin: 0;
- padding: 0;
-}
-
-body h1, body h2, body h3, body h4, body h5, body h6 {
- margin-left: 0
-}
-
-body pre {
- margin: 0.5em 10% 0.5em 1em;
- line-height: 1.0;
- color: navy;
-}
-
-tt.literal, code.literal {
- color: navy;
-}
-
-div.literallayout p {
- padding: 0em;
- margin: 0em;
-}
-
-div.literallayout {
- font-family: monospace;
-# margin: 0.5em 10% 0.5em 1em;
- margin: 0em;
- color: navy;
- border: 1px solid silver;
- background: #f4f4f4;
- padding: 0.5em;
-}
-
-.programlisting, .screen {
- border: 1px solid silver;
- background: #f4f4f4;
- margin: 0.5em 10% 0.5em 0;
- padding: 0.5em 1em;
-}
-
-div.sidebar {
- background: #ffffee;
- margin: 1.0em 10% 0.5em 0;
- padding: 0.5em 1em;
- border: 1px solid silver;
-}
-div.sidebar * { padding: 0; }
-div.sidebar div { margin: 0; }
-div.sidebar p.title {
- font-family: sans-serif;
- margin-top: 0.5em;
- margin-bottom: 0.2em;
-}
-
-div.bibliomixed {
- margin: 0.5em 5% 0.5em 1em;
-}
-
-div.glossary dt {
- font-weight: bold;
-}
-div.glossary dd p {
- margin-top: 0.2em;
-}
-
-dl {
- margin: .8em 0;
- line-height: 1.2;
-}
-
-dt {
- margin-top: 0.5em;
-}
-
-dt span.term {
- font-style: italic;
-}
-
-div.variablelist dd p {
- margin-top: 0;
-}
-
-div.itemizedlist li, div.orderedlist li {
- margin-left: -0.8em;
- margin-top: 0.5em;
-}
-
-ul, ol {
- list-style-position: outside;
-}
-
-div.sidebar ul, div.sidebar ol {
- margin-left: 2.8em;
-}
-
-div.itemizedlist p.title,
-div.orderedlist p.title,
-div.variablelist p.title
-{
- margin-bottom: -0.8em;
-}
-
-div.revhistory table {
- border-collapse: collapse;
- border: none;
-}
-div.revhistory th {
- border: none;
- color: #527bbd;
- font-family: tahoma, verdana, sans-serif;
-}
-div.revhistory td {
- border: 1px solid silver;
-}
-
-/* Keep TOC and index lines close together. */
-div.toc dl, div.toc dt,
-div.list-of-figures dl, div.list-of-figures dt,
-div.list-of-tables dl, div.list-of-tables dt,
-div.indexdiv dl, div.indexdiv dt
-{
- line-height: normal;
- margin-top: 0;
- margin-bottom: 0;
-}
-
-/*
- Table styling does not work because of overriding attributes in
- generated HTML.
-*/
-div.table table,
-div.informaltable table
-{
- margin-left: 0;
- margin-right: 5%;
- margin-bottom: 0.8em;
-}
-div.informaltable table
-{
- margin-top: 0.4em
-}
-div.table thead,
-div.table tfoot,
-div.table tbody,
-div.informaltable thead,
-div.informaltable tfoot,
-div.informaltable tbody
-{
- /* No effect in IE6. */
- border-top: 2px solid #527bbd;
- border-bottom: 2px solid #527bbd;
-}
-div.table thead, div.table tfoot,
-div.informaltable thead, div.informaltable tfoot
-{
- font-weight: bold;
-}
-
-div.mediaobject img {
- border: 1px solid silver;
- margin-bottom: 0.8em;
-}
-div.figure p.title,
-div.table p.title
-{
- margin-top: 1em;
- margin-bottom: 0.4em;
-}
-
-@media print {
- div.navheader, div.navfooter { display: none; }
-}
+/*
+ CSS stylesheet for XHTML produced by DocBook XSL stylesheets.
+ Tested with XSL stylesheets 1.61.2, 1.67.2
+*/
+
+span.strong {
+ font-weight: bold;
+}
+
+body blockquote {
+ margin-top: .75em;
+ line-height: 1.5;
+ margin-bottom: .75em;
+}
+
+html body {
+ margin: 1em 5% 1em 5%;
+ line-height: 1.2;
+}
+
+body div {
+ margin: 0;
+}
+
+h1, h2, h3, h4, h5, h6,
+div.toc p b,
+div.list-of-figures p b,
+div.list-of-tables p b,
+div.abstract p.title
+{
+ color: #527bbd;
+ font-family: tahoma, verdana, sans-serif;
+}
+
+div.toc p:first-child,
+div.list-of-figures p:first-child,
+div.list-of-tables p:first-child,
+div.example p.title
+{
+ margin-bottom: 0.2em;
+}
+
+body h1 {
+ margin: .0em 0 0 -4%;
+ line-height: 1.3;
+ border-bottom: 2px solid silver;
+}
+
+body h2 {
+ margin: 0.5em 0 0 -4%;
+ line-height: 1.3;
+ border-bottom: 2px solid silver;
+}
+
+body h3 {
+ margin: .8em 0 0 -3%;
+ line-height: 1.3;
+}
+
+body h4 {
+ margin: .8em 0 0 -3%;
+ line-height: 1.3;
+}
+
+body h5 {
+ margin: .8em 0 0 -2%;
+ line-height: 1.3;
+}
+
+body h6 {
+ margin: .8em 0 0 -1%;
+ line-height: 1.3;
+}
+
+body hr {
+ border: none; /* Broken on IE6 */
+}
+div.footnotes hr {
+ border: 1px solid silver;
+}
+
+div.navheader th, div.navheader td, div.navfooter td {
+ font-family: sans-serif;
+ font-size: 0.9em;
+ font-weight: bold;
+ color: #527bbd;
+}
+div.navheader img, div.navfooter img {
+ border-style: none;
+}
+div.navheader a, div.navfooter a {
+ font-weight: normal;
+}
+div.navfooter hr {
+ border: 1px solid silver;
+}
+
+body td {
+ line-height: 1.2
+}
+
+body th {
+ line-height: 1.2;
+}
+
+ol {
+ line-height: 1.2;
+}
+
+ul, body dir, body menu {
+ line-height: 1.2;
+}
+
+html {
+ margin: 0;
+ padding: 0;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6 {
+ margin-left: 0
+}
+
+body pre {
+ margin: 0.5em 10% 0.5em 1em;
+ line-height: 1.0;
+ color: navy;
+}
+
+tt.literal, code.literal {
+ color: navy;
+}
+
+div.literallayout p {
+ padding: 0em;
+ margin: 0em;
+}
+
+div.literallayout {
+ font-family: monospace;
+# margin: 0.5em 10% 0.5em 1em;
+ margin: 0em;
+ color: navy;
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+.programlisting, .screen {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ margin: 0.5em 10% 0.5em 0;
+ padding: 0.5em 1em;
+}
+
+div.sidebar {
+ background: #ffffee;
+ margin: 1.0em 10% 0.5em 0;
+ padding: 0.5em 1em;
+ border: 1px solid silver;
+}
+div.sidebar * { padding: 0; }
+div.sidebar div { margin: 0; }
+div.sidebar p.title {
+ font-family: sans-serif;
+ margin-top: 0.5em;
+ margin-bottom: 0.2em;
+}
+
+div.bibliomixed {
+ margin: 0.5em 5% 0.5em 1em;
+}
+
+div.glossary dt {
+ font-weight: bold;
+}
+div.glossary dd p {
+ margin-top: 0.2em;
+}
+
+dl {
+ margin: .8em 0;
+ line-height: 1.2;
+}
+
+dt {
+ margin-top: 0.5em;
+}
+
+dt span.term {
+ font-style: italic;
+}
+
+div.variablelist dd p {
+ margin-top: 0;
+}
+
+div.itemizedlist li, div.orderedlist li {
+ margin-left: -0.8em;
+ margin-top: 0.5em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+
+div.sidebar ul, div.sidebar ol {
+ margin-left: 2.8em;
+}
+
+div.itemizedlist p.title,
+div.orderedlist p.title,
+div.variablelist p.title
+{
+ margin-bottom: -0.8em;
+}
+
+div.revhistory table {
+ border-collapse: collapse;
+ border: none;
+}
+div.revhistory th {
+ border: none;
+ color: #527bbd;
+ font-family: tahoma, verdana, sans-serif;
+}
+div.revhistory td {
+ border: 1px solid silver;
+}
+
+/* Keep TOC and index lines close together. */
+div.toc dl, div.toc dt,
+div.list-of-figures dl, div.list-of-figures dt,
+div.list-of-tables dl, div.list-of-tables dt,
+div.indexdiv dl, div.indexdiv dt
+{
+ line-height: normal;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+/*
+ Table styling does not work because of overriding attributes in
+ generated HTML.
+*/
+div.table table,
+div.informaltable table
+{
+ margin-left: 0;
+ margin-right: 5%;
+ margin-bottom: 0.8em;
+}
+div.informaltable table
+{
+ margin-top: 0.4em
+}
+div.table thead,
+div.table tfoot,
+div.table tbody,
+div.informaltable thead,
+div.informaltable tfoot,
+div.informaltable tbody
+{
+ /* No effect in IE6. */
+ border-top: 2px solid #527bbd;
+ border-bottom: 2px solid #527bbd;
+}
+div.table thead, div.table tfoot,
+div.informaltable thead, div.informaltable tfoot
+{
+ font-weight: bold;
+}
+
+div.mediaobject img {
+ border: 1px solid silver;
+ margin-bottom: 0.8em;
+}
+div.figure p.title,
+div.table p.title
+{
+ margin-top: 1em;
+ margin-bottom: 0.4em;
+}
+
+@media print {
+ div.navheader, div.navfooter { display: none; }
+}
diff --git a/Documentation/docbook.xsl b/Documentation/docbook.xsl
new file mode 100644
index 0000000000..9a6912c641
--- /dev/null
+++ b/Documentation/docbook.xsl
@@ -0,0 +1,5 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'>
+ <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
+ <xsl:output method="html" encoding="UTF-8" indent="no" />
+</xsl:stylesheet>
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index bdc7332c7b..da034223f3 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -52,4 +52,3 @@
Deepen the history of a 'shallow' repository created by
`git clone` with `--depth=<depth>` option (see gitlink:git-clone[1])
by the specified number of commits.
-
diff --git a/Documentation/fix-texi.perl b/Documentation/fix-texi.perl
new file mode 100755
index 0000000000..ff7d78f620
--- /dev/null
+++ b/Documentation/fix-texi.perl
@@ -0,0 +1,15 @@
+#!/usr/bin/perl -w
+
+while (<>) {
+ if (/^\@setfilename/) {
+ $_ = "\@setfilename git.info\n";
+ } elsif (/^\@direntry/) {
+ print '@dircategory Development
+@direntry
+* Git: (git). A fast distributed revision control system
+@end direntry
+'; }
+ unless (/^\@direntry/../^\@end direntry/) {
+ print;
+ }
+}
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index a0c9f68580..dee38f8250 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -3,32 +3,35 @@ git-add(1)
NAME
----
-git-add - Add file contents to the changeset to be committed next
+git-add - Add file contents to the index
SYNOPSIS
--------
-'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--] <file>...
+'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <file>...
DESCRIPTION
-----------
-All the changed file contents to be committed together in a single set
-of changes must be "added" with the 'add' command before using the
-'commit' command. This is not only for adding new files. Even modified
-files must be added to the set of changes about to be committed.
+This command adds the current content of new or modified files to the
+index, thus staging that content for inclusion in the next commit.
-This command can be performed multiple times before a commit. The added
-content corresponds to the state of specified file(s) at the time the
-'add' command is used. This means the 'commit' command will not consider
-subsequent changes to already added content if it is not added again before
-the commit.
+The "index" holds a snapshot of the content of the working tree, and it
+is this snapshot that is taken as the contents of the next commit. Thus
+after making any changes to the working directory, and before running
+the commit command, you must use the 'add' command to add any new or
+modified files to the index.
-The 'git status' command can be used to obtain a summary of what is included
-for the next commit.
+This command can be performed multiple times before a commit. It only
+adds the content of the specified file(s) at the time the add command is
+run; if you want subsequent changes included in the next commit, then
+you must run 'git add' again to add the new content to the index.
-This command can be used to add ignored files with `-f` (force)
-option, but they have to be
-explicitly and exactly specified from the command line. File globbing
-and recursive behaviour do not add ignored files.
+The 'git status' command can be used to obtain a summary of which
+files have changes that are staged for the next commit.
+
+The 'add' command can be used to add ignored files with `-f` (force)
+option, but they have to be explicitly and exactly specified from the
+command line. File globbing and recursive behaviour do not add ignored
+files.
Please see gitlink:git-commit[1] for alternative ways to add content to a
commit.
@@ -63,6 +66,10 @@ OPTIONS
command line. If no paths are specified, all tracked files are
updated.
+\--refresh::
+ Don't add the file(s), but only refresh their stat()
+ information in the index.
+
\--::
This option can be used to separate command-line options from
the list of files, (useful when filenames might be mistaken
@@ -228,4 +235,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index ba79773f79..e4a6b3a6f0 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -12,7 +12,7 @@ SYNOPSIS
'git-am' [--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
[--3way] [--interactive] [--binary]
[--whitespace=<option>] [-C<n>] [-p<n>]
- <mbox>...
+ <mbox>|<Maildir>...
'git-am' [--skip | --resolved]
DESCRIPTION
@@ -23,9 +23,10 @@ current branch.
OPTIONS
-------
-<mbox>...::
+<mbox>|<Maildir>...::
The list of mailbox files to read patches from. If you do not
- supply this argument, reads from the standard input.
+ supply this argument, reads from the standard input. If you supply
+ directories, they'll be treated as Maildirs.
-s, --signoff::
Add `Signed-off-by:` line to the commit message, using
@@ -126,8 +127,7 @@ is terminated before the first occurrence of such a line.
When initially invoking it, you give it names of the mailboxes
to crunch. Upon seeing the first patch that does not apply, it
-aborts in the middle, just like 'git-applymbox' does. You can
-recover from this in one of two ways:
+aborts in the middle,. You can recover from this in one of two ways:
. skip the current patch by re-running the command with '--skip'
option.
@@ -144,7 +144,7 @@ names.
SEE ALSO
--------
-gitlink:git-applymbox[1], gitlink:git-applypatch[1], gitlink:git-apply[1].
+gitlink:git-apply[1].
Author
@@ -158,4 +158,3 @@ Documentation by Petr Baudis, Junio C Hamano and the git-list <git@vger.kernel.o
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 3bd2c995da..f03f661652 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -183,4 +183,3 @@ Documentation by Junio C Hamano
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-applymbox.txt b/Documentation/git-applymbox.txt
deleted file mode 100644
index ea919ba5d7..0000000000
--- a/Documentation/git-applymbox.txt
+++ /dev/null
@@ -1,98 +0,0 @@
-git-applymbox(1)
-================
-
-NAME
-----
-git-applymbox - Apply a series of patches in a mailbox
-
-
-SYNOPSIS
---------
-'git-applymbox' [-u] [-k] [-q] [-m] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
-
-DESCRIPTION
------------
-Splits mail messages in a mailbox into commit log message,
-authorship information and patches, and applies them to the
-current branch.
-
-
-OPTIONS
--------
--q::
- Apply patches interactively. The user will be given
- opportunity to edit the log message and the patch before
- attempting to apply it.
-
--k::
- Usually the program 'cleans up' the Subject: header line
- to extract the title line for the commit log message,
- among which (1) remove 'Re:' or 're:', (2) leading
- whitespaces, (3) '[' up to ']', typically '[PATCH]', and
- then prepends "[PATCH] ". This flag forbids this
- munging, and is most useful when used to read back 'git
- format-patch -k' output.
-
--m::
- Patches are applied with `git-apply` command, and unless
- it cleanly applies without fuzz, the processing fails.
- With this flag, if a tree that the patch applies cleanly
- is found in a repository, the patch is applied to the
- tree and then a 3-way merge between the resulting tree
- and the current tree.
-
--u::
- Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
- The proposed commit log message taken from the e-mail
- are re-coded into UTF-8 encoding (configuration variable
- `i18n.commitencoding` can be used to specify project's
- preferred encoding if it is not UTF-8). This used to be
- optional but now it is the default.
-+
-Note that the patch is always used as-is without charset
-conversion, even with this flag.
-
--n::
- Pass `-n` flag to `git-mailinfo` (see
- gitlink:git-mailinfo[1]).
-
--c .dotest/<num>::
- When the patch contained in an e-mail does not cleanly
- apply, the command exits with an error message. The
- patch and extracted message are found in .dotest/, and
- you could re-run 'git applymbox' with '-c .dotest/<num>'
- flag to restart the process after inspecting and fixing
- them.
-
-<mbox>::
- The name of the file that contains the e-mail messages
- with patches. This file should be in the UNIX mailbox
- format. See 'SubmittingPatches' document to learn about
- the formatting convention for e-mail submission.
-
-<signoff>::
- The name of the file that contains your "Signed-off-by"
- line. See 'SubmittingPatches' document to learn what
- "Signed-off-by" line means. You can also just say
- 'yes', 'true', 'me', or 'please' to use an automatically
- generated "Signed-off-by" line based on your committer
- identity.
-
-
-SEE ALSO
---------
-gitlink:git-am[1], gitlink:git-applypatch[1].
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-applypatch.txt b/Documentation/git-applypatch.txt
deleted file mode 100644
index 451434a757..0000000000
--- a/Documentation/git-applypatch.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-git-applypatch(1)
-=================
-
-NAME
-----
-git-applypatch - Apply one patch extracted from an e-mail
-
-
-SYNOPSIS
---------
-'git-applypatch' <msg> <patch> <info> [<signoff>]
-
-DESCRIPTION
------------
-This is usually not what an end user wants to run directly. See
-gitlink:git-am[1] instead.
-
-Takes three files <msg>, <patch>, and <info> prepared from an
-e-mail message by 'git-mailinfo', and creates a commit. It is
-usually not necessary to use this command directly.
-
-This command can run `applypatch-msg`, `pre-applypatch`, and
-`post-applypatch` hooks. See link:hooks.html[hooks] for more
-information.
-
-
-OPTIONS
--------
-<msg>::
- Commit log message (sans the first line, which comes
- from e-mail Subject stored in <info>).
-
-<patch>::
- The patch to apply.
-
-<info>::
- Author and subject information extracted from e-mail,
- used on "author" line and as the first line of the
- commit log message.
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-archimport.txt b/Documentation/git-archimport.txt
index 82cb41d279..7091b8d61c 100644
--- a/Documentation/git-archimport.txt
+++ b/Documentation/git-archimport.txt
@@ -17,26 +17,26 @@ DESCRIPTION
Imports a project from one or more Arch repositories. It will follow branches
and repositories within the namespaces defined by the <archive/branch>
parameters supplied. If it cannot find the remote branch a merge comes from
-it will just import it as a regular commit. If it can find it, it will mark it
-as a merge whenever possible (see discussion below).
+it will just import it as a regular commit. If it can find it, it will mark it
+as a merge whenever possible (see discussion below).
-The script expects you to provide the key roots where it can start the import
-from an 'initial import' or 'tag' type of Arch commit. It will follow and
-import new branches within the provided roots.
+The script expects you to provide the key roots where it can start the import
+from an 'initial import' or 'tag' type of Arch commit. It will follow and
+import new branches within the provided roots.
-It expects to be dealing with one project only. If it sees
-branches that have different roots, it will refuse to run. In that case,
-edit your <archive/branch> parameters to define clearly the scope of the
-import.
+It expects to be dealing with one project only. If it sees
+branches that have different roots, it will refuse to run. In that case,
+edit your <archive/branch> parameters to define clearly the scope of the
+import.
-`git-archimport` uses `tla` extensively in the background to access the
+`git-archimport` uses `tla` extensively in the background to access the
Arch repository.
Make sure you have a recent version of `tla` available in the path. `tla` must
-know about the repositories you pass to `git-archimport`.
+know about the repositories you pass to `git-archimport`.
-For the initial import `git-archimport` expects to find itself in an empty
-directory. To follow the development of a project that uses Arch, rerun
-`git-archimport` with the same parameters as the initial import to perform
+For the initial import `git-archimport` expects to find itself in an empty
+directory. To follow the development of a project that uses Arch, rerun
+`git-archimport` with the same parameters as the initial import to perform
incremental imports.
While git-archimport will try to create sensible branch names for the
@@ -54,15 +54,15 @@ convert Arch repositories that had been rotated periodically.
MERGES
------
-Patch merge data from Arch is used to mark merges in git as well. git
+Patch merge data from Arch is used to mark merges in git as well. git
does not care much about tracking patches, and only considers a merge when a
branch incorporates all the commits since the point they forked. The end result
-is that git will have a good idea of how far branches have diverged. So the
+is that git will have a good idea of how far branches have diverged. So the
import process does lose some patch-trading metadata.
-Fortunately, when you try and merge branches imported from Arch,
-git will find a good merge base, and it has a good chance of identifying
-patches that have been traded out-of-sequence between the branches.
+Fortunately, when you try and merge branches imported from Arch,
+git will find a good merge base, and it has a good chance of identifying
+patches that have been traded out-of-sequence between the branches.
OPTIONS
-------
@@ -71,10 +71,10 @@ OPTIONS
Display usage.
-v::
- Verbose output.
+ Verbose output.
-T::
- Many tags. Will create a tag for every commit, reflecting the commit
+ Many tags. Will create a tag for every commit, reflecting the commit
name in the Arch repository.
-f::
@@ -104,7 +104,7 @@ OPTIONS
<archive/branch>::
- Archive/branch identifier in a format that `tla log` understands.
+ Archive/branch identifier in a format that `tla log` understands.
Author
@@ -118,4 +118,3 @@ Documentation by Junio C Hamano, Martin Langhoff and the git-list <git@vger.kern
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 5f68ee1584..1072fb87d1 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -8,7 +8,7 @@ git-bisect - Find the change that introduced a bug by binary search
SYNOPSIS
--------
-'git bisect' <subcommand> <options>
+'git bisect' <subcommand> <options>
DESCRIPTION
-----------
@@ -200,4 +200,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 44678b0c36..66f1203701 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -8,7 +8,7 @@ git-blame - Show what revision and author last modified each line of a file
SYNOPSIS
--------
[verse]
-'git-blame' [-c] [-b] [--root] [-s] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m]
+'git-blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [--incremental] [-L n,m]
[-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
[<rev> | --contents <file>] [--] <file>
@@ -63,6 +63,11 @@ include::blame-options.txt[]
-s::
Suppress author name and timestamp from the output.
+-w::
+ Ignore whitespace when comparing parent's version and
+ child's to find where the lines came from.
+
+
THE PORCELAIN FORMAT
--------------------
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 8dc5171f5e..33bc31b0d4 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -41,7 +41,7 @@ to happen.
With a `-d` or `-D` option, `<branchname>` will be deleted. You may
specify more than one branch for deletion. If the branch currently
-has a ref log then the ref log will also be deleted. Use -r together with -d
+has a reflog then the reflog will also be deleted. Use -r together with -d
to delete remote-tracking branches.
@@ -54,9 +54,9 @@ OPTIONS
Delete a branch irrespective of its index status.
-l::
- Create the branch's ref log. This activates recording of
- all changes to made the branch ref, enabling use of date
- based sha1 expressions such as "<branchname>@{yesterday}".
+ Create the branch's reflog. This activates recording of
+ all changes made to the branch ref, enabling use of date
+ based sha1 expressions such as "<branchname>@\{yesterday}".
-f::
Force the creation of a new branch even if it means deleting
@@ -130,12 +130,12 @@ Delete unneeded branch::
------------
$ git clone git://git.kernel.org/.../git.git my.git
$ cd my.git
-$ git branch -d -r todo html man <1>
-$ git branch -D test <2>
+$ git branch -d -r origin/todo origin/html origin/man <1>
+$ git branch -D test <2>
------------
+
-<1> delete remote-tracking branches "todo", "html", "man"
-<2> delete "test" branch even if the "master" branch does not have all
+<1> Delete remote-tracking branches "todo", "html", "man"
+<2> Delete "test" branch even if the "master" branch does not have all
commits from test branch.
@@ -158,4 +158,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 075c0d05ef..afa095c795 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -71,4 +71,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
index ceb51959b1..856d2af2ed 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -34,4 +34,3 @@ Documentation by James Bowes.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt
index 6dd6db04bb..b1a8ce110c 100644
--- a/Documentation/git-checkout-index.txt
+++ b/Documentation/git-checkout-index.txt
@@ -182,4 +182,3 @@ Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 918d8ee720..818b720b91 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -62,9 +62,9 @@ OPTIONS
configuration variable.
-l::
- Create the new branch's ref log. This activates recording of
- all changes to made the branch ref, enabling use of date
- based sha1 expressions such as "<branchname>@{yesterday}".
+ Create the new branch's reflog. This activates recording of
+ all changes made to the branch ref, enabling use of date
+ based sha1 expressions such as "<branchname>@\{yesterday}".
-m::
If you have local modifications to one or more files that
@@ -215,4 +215,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 68bba98260..47b1e8c2fc 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -68,4 +68,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt
index 8c7d9670d3..e6943822cd 100644
--- a/Documentation/git-cherry.txt
+++ b/Documentation/git-cherry.txt
@@ -67,4 +67,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-citool.txt b/Documentation/git-citool.txt
new file mode 100644
index 0000000000..5217ab2234
--- /dev/null
+++ b/Documentation/git-citool.txt
@@ -0,0 +1,32 @@
+git-citool(1)
+=============
+
+NAME
+----
+git-citool - Graphical alternative to git-commit
+
+SYNOPSIS
+--------
+'git citool'
+
+DESCRIPTION
+-----------
+A Tcl/Tk based graphical interface to review modified files, stage
+them into the index, enter a commit message and record the new
+commit onto the current branch. This interface is an alternative
+to the less interactive gitlink:git-commit[1] program.
+
+git-citool is actually a standard alias for 'git gui citool'.
+See gitlink:git-gui[1] for more details.
+
+Author
+------
+Written by Shawn O. Pearce <spearce@spearce.org>.
+
+Documentation
+--------------
+Documentation by Shawn O. Pearce <spearce@spearce.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index ac938d39df..227f092e26 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,7 +9,8 @@ git-clone - Clone a repository into a new directory
SYNOPSIS
--------
[verse]
-'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
+'git-clone' [--template=<template_directory>]
+ [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
[--depth <depth>] <repository> [<directory>]
@@ -40,8 +41,19 @@ OPTIONS
this flag bypasses normal "git aware" transport
mechanism and clones the repository by making a copy of
HEAD and everything under objects and refs directories.
- The files under .git/objects/ directory are hardlinked
- to save space when possible.
+ The files under `.git/objects/` directory are hardlinked
+ to save space when possible. This is now the default when
+ the source repository is specified with `/path/to/repo`
+ syntax, so it essentially is a no-op option. To force
+ copying instead of hardlinking (which may be desirable
+ if you are trying to make a back-up of your repository),
+ but still avoid the usual "git aware" transport
+ mechanism, `--no-hardlinks` can be used.
+
+--no-hardlinks::
+ Optimize the cloning process from a repository on a
+ local filesystem by copying files under `.git/objects`
+ directory.
--shared::
-s::
@@ -64,6 +76,7 @@ OPTIONS
Operate quietly. This flag is passed to "rsync" and
"git-fetch-pack" commands when given.
+--no-checkout::
-n::
No checkout of HEAD is performed after the clone is complete.
@@ -106,8 +119,9 @@ OPTIONS
as patches.
<repository>::
- The (possibly remote) repository to clone from. It can
- be any URL git-fetch supports.
+ The (possibly remote) repository to clone from. See the
+ <<URLS,URLS>> section below for more information on specifying
+ repositories.
<directory>::
The name of a new directory to clone into. The "humanish"
@@ -116,6 +130,8 @@ OPTIONS
for "host.xz:foo/.git"). Cloning into an existing directory
is not allowed.
+include::urls.txt[]
+
Examples
--------
@@ -175,4 +191,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt
index 504a3aa1b4..5870c2ce47 100644
--- a/Documentation/git-commit-tree.txt
+++ b/Documentation/git-commit-tree.txt
@@ -40,7 +40,7 @@ OPTIONS
-p <parent commit>::
Each '-p' indicates the id of a parent commit object.
-
+
Commit Information
------------------
@@ -72,7 +72,7 @@ GIT_AUTHOR_EMAIL:
name = "Your Name"
email = "your@email.address.xz"
-A commit comment is read from stdin (max 999 chars). If a changelog
+A commit comment is read from stdin. If a changelog
entry is not provided via "<" redirection, "git-commit-tree" will just wait
for one to be entered and terminated with ^D.
@@ -107,4 +107,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 53a7bb0895..e54fb12103 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -8,33 +8,34 @@ git-commit - Record changes to the repository
SYNOPSIS
--------
[verse]
-'git-commit' [-a | --interactive] [-s] [-v]
+'git-commit' [-a | --interactive] [-s] [-v] [-u]
[(-c | -C) <commit> | -F <file> | -m <msg> | --amend]
[--no-verify] [-e] [--author <author>]
[--] [[-i | -o ]<file>...]
DESCRIPTION
-----------
-Use 'git commit' when you want to record your changes into the repository
-along with a log message describing what the commit is about. All changes
-to be committed must be explicitly identified using one of the following
-methods:
+Use 'git commit' to store the current contents of the index in a new
+commit along with a log message describing the changes you have made.
+
+The content to be added can be specified in several ways:
1. by using gitlink:git-add[1] to incrementally "add" changes to the
- next commit before using the 'commit' command (Note: even modified
+ index before using the 'commit' command (Note: even modified
files must be "added");
-2. by using gitlink:git-rm[1] to identify content removal for the next
- commit, again before using the 'commit' command;
+2. by using gitlink:git-rm[1] to remove files from the working tree
+ and the index, again before using the 'commit' command;
-3. by directly listing files containing changes to be committed as arguments
- to the 'commit' command, in which cases only those files alone will be
- considered for the commit;
+3. by listing files as arguments to the 'commit' command, in which
+ case the commit will ignore changes staged in the index, and instead
+ record the current content of the listed files;
-4. by using the -a switch with the 'commit' command to automatically "add"
- changes from all known files i.e. files that have already been committed
- before, and to automatically "rm" files that have been
- removed from the working tree, and perform the actual commit.
+4. by using the -a switch with the 'commit' command to automatically
+ "add" changes from all known files (i.e. all files that are already
+ listed in the index) and to automatically "rm" files in the index
+ that have been removed from the working tree, and then perform the
+ actual commit;
5. by using the --interactive switch with the 'commit' command to decide one
by one which files should be part of the commit, before finalizing the
@@ -71,9 +72,16 @@ OPTIONS
Override the author name used in the commit. Use
`A U Thor <author@example.com>` format.
--m <msg>::
+-m <msg>|--message=<msg>::
Use the given <msg> as the commit message.
+-t <file>|--template=<file>::
+ Use the contents of the given file as the initial version
+ of the commit message. The editor is invoked and you can
+ make subsequent changes. If a message is specified using
+ the `-m` or `-F` options, this option has no effect. This
+ overrides the `commit.template` configuration variable.
+
-s|--signoff::
Add Signed-off-by line at the end of the commit message.
@@ -115,6 +123,19 @@ but can be used to amend a merge commit.
as well. This is usually not what you want unless you
are concluding a conflicted merge.
+-u|--untracked-files::
+ Show all untracked files, also those in uninteresting
+ directories, in the "Untracked files:" section of commit
+ message template. Without this option only its name and
+ a trailing slash are displayed for each untracked
+ directory.
+
+-v|--verbose::
+ Show unified diff between the HEAD commit and what
+ would be committed at the bottom of the commit message
+ template. Note that this diff output doesn't have its
+ lines prefixed with '#'.
+
-q|--quiet::
Suppress commit summary message.
@@ -231,10 +252,12 @@ on the Subject: line and the rest of the commit in the body.
include::i18n.txt[]
-ENVIRONMENT VARIABLES
----------------------
-The command specified by either the VISUAL or EDITOR environment
-variables is used to edit the commit log message.
+ENVIRONMENT AND CONFIGURATION VARIABLES
+---------------------------------------
+The editor used to edit the commit log message will be chosen from the
+GIT_EDITOR environment variable, the core.editor configuration variable, the
+VISUAL environment variable, or the EDITOR environment variable (in that
+order).
HOOKS
-----
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 387d7bc841..c3dffffe32 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,17 +9,17 @@ git-config - Get and set repository or global options
SYNOPSIS
--------
[verse]
-'git-config' [--system | --global] name [value [value_regex]]
-'git-config' [--system | --global] --add name value
-'git-config' [--system | --global] --replace-all name [value [value_regex]]
-'git-config' [--system | --global] [type] --get name [value_regex]
-'git-config' [--system | --global] [type] --get-all name [value_regex]
-'git-config' [--system | --global] [type] --get-regexp name_regex [value_regex]
-'git-config' [--system | --global] --unset name [value_regex]
-'git-config' [--system | --global] --unset-all name [value_regex]
-'git-config' [--system | --global] --rename-section old_name new_name
-'git-config' [--system | --global] --remove-section name
-'git-config' [--system | --global] -l | --list
+'git-config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
+'git-config' [<file-option>] [type] --add name value
+'git-config' [<file-option>] [type] --replace-all name [value [value_regex]]
+'git-config' [<file-option>] [type] [-z|--null] --get name [value_regex]
+'git-config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
+'git-config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
+'git-config' [<file-option>] --unset name [value_regex]
+'git-config' [<file-option>] --unset-all name [value_regex]
+'git-config' [<file-option>] --rename-section old_name new_name
+'git-config' [<file-option>] --remove-section name
+'git-config' [<file-option>] [-z|--null] -l | --list
DESCRIPTION
-----------
@@ -37,14 +37,19 @@ prepend a single exclamation mark in front (see also <<EXAMPLES>>).
The type specifier can be either '--int' or '--bool', which will make
'git-config' ensure that the variable(s) are of the given type and
convert the value to the canonical form (simple decimal number for int,
-a "true" or "false" string for bool). Type specifiers currently only
-take effect for reading operations. If no type specifier is passed,
+a "true" or "false" string for bool). If no type specifier is passed,
no checks or transformations are performed on the value.
+The file-option can be one of '--system', '--global' or '--file'
+which specify where the values will be read from or written to.
+The default is to assume the config file of the current repository,
+.git/config unless defined otherwise with GIT_DIR and GIT_CONFIG
+(see <<FILES>>).
+
This command will fail if:
-. The .git/config file is invalid,
-. Can not write to .git/config,
+. The config file is invalid,
+. Can not write to the config file,
. no section was provided,
. the section or key is invalid,
. you try to unset an option which does not exist,
@@ -94,6 +99,9 @@ rather than from all available files.
+
See also <<FILES>>.
+-f config-file, --file config-file::
+ Use the given config file instead of the one specified by GIT_CONFIG.
+
--remove-section::
Remove the given section from the configuration file.
@@ -118,13 +126,21 @@ See also <<FILES>>.
in the config file will cause the value to be multiplied
by 1024, 1048576, or 1073741824 prior to output.
+-z, --null::
+ For all options that output values and/or keys, always
+ end values with with the null character (instead of a
+ newline). Use newline instead as a delimiter between
+ key and value. This allows for secure parsing of the
+ output without getting confused e.g. by values that
+ contain line breaks.
+
[[FILES]]
FILES
-----
-There are three files where git-config will search for configuration
-options:
+If not set explicitely with '--file', there are three files where
+git-config will search for configuration options:
.git/config::
Repository specific configuration file. (The filename is
@@ -198,9 +214,7 @@ Given a .git/config like this:
; Proxy settings
[core]
- gitproxy="ssh" for "ssh://kernel.org/"
gitproxy="proxy-command" for kernel.org
- gitproxy="myprotocol-command" for "my://"
gitproxy=default-proxy ; for all the rest
you can set the filemode to true with
@@ -275,7 +289,7 @@ To actually match only values with an exclamation mark, you have to
To add a new proxy, without altering any of the existing ones, use
------------
-% git config core.gitproxy '"proxy" for example.com'
+% git config core.gitproxy '"proxy-command" for example.com'
------------
@@ -293,4 +307,3 @@ Documentation by Johannes Schindelin, Petr Baudis and the git-list <git@vger.ker
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-convert-objects.txt b/Documentation/git-convert-objects.txt
index b1220c06e1..9718abf86d 100644
--- a/Documentation/git-convert-objects.txt
+++ b/Documentation/git-convert-objects.txt
@@ -26,4 +26,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt
index 91c8c92c76..81614111a4 100644
--- a/Documentation/git-count-objects.txt
+++ b/Documentation/git-count-objects.txt
@@ -35,4 +35,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
index f3590dee04..6c423e3a2f 100644
--- a/Documentation/git-cvsexportcommit.txt
+++ b/Documentation/git-cvsexportcommit.txt
@@ -8,25 +8,25 @@ git-cvsexportcommit - Export a single commit to a CVS checkout
SYNOPSIS
--------
-'git-cvsexportcommit' [-h] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
DESCRIPTION
-----------
Exports a commit from GIT to a CVS checkout, making it easier
-to merge patches from a git repository into a CVS repository.
+to merge patches from a git repository into a CVS repository.
-Execute it from the root of the CVS working copy. GIT_DIR must be defined.
+Execute it from the root of the CVS working copy. GIT_DIR must be defined.
See examples below.
-It does its best to do the safe thing, it will check that the files are
-unchanged and up to date in the CVS checkout, and it will not autocommit
+It does its best to do the safe thing, it will check that the files are
+unchanged and up to date in the CVS checkout, and it will not autocommit
by default.
Supports file additions, removals, and commits that affect binary files.
If the commit is a merge commit, you must tell git-cvsexportcommit what parent
-should the changeset be done against.
+should the changeset be done against.
OPTIONS
-------
@@ -55,9 +55,12 @@ OPTIONS
Force the parent commit, even if it is not a direct parent.
-m::
- Prepend the commit message with the provided prefix.
+ Prepend the commit message with the provided prefix.
Useful for patch series and the like.
+-u::
+ Update affected files from cvs repository before attempting export.
+
-v::
Verbose.
@@ -70,7 +73,7 @@ Merge one patch into CVS::
$ export GIT_DIR=~/project/.git
$ cd ~/project_cvs_checkout
$ git-cvsexportcommit -v <commit-sha1>
-$ cvs commit -F .mgs <files>
+$ cvs commit -F .mgs <files>
------------
Merge pending patches into CVS automatically -- only if you really know what you are doing::
@@ -92,4 +95,3 @@ Documentation by Martin Langhoff <martin@catalyst.net.nz>
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index e0be856546..fdd7ec7edd 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -13,7 +13,7 @@ SYNOPSIS
[-A <author-conv-file>] [-p <options-for-cvsps>] [-P <file>]
[-C <git_repository>] [-z <fuzz>] [-i] [-k] [-u] [-s <subst>]
[-a] [-m] [-M <regex>] [-S <regex>] [-L <commitlimit>]
- [<CVS_module>]
+ [-r <remote>] [<CVS_module>]
DESCRIPTION
@@ -25,10 +25,12 @@ Splitting the CVS log into patch sets is done by 'cvsps'.
At least version 2.1 is required.
You should *never* do any work of your own on the branches that are
-created by git-cvsimport. The initial import will create and populate a
+created by git-cvsimport. By default initial import will create and populate a
"master" branch from the CVS repository's main branch which you're free
to work with; after that, you need to 'git merge' incremental imports, or
-any CVS branches, yourself.
+any CVS branches, yourself. It is advisable to specify a named remote via
+-r to separate and protect the incoming branches.
+
OPTIONS
-------
@@ -37,7 +39,7 @@ OPTIONS
-d <CVSROOT>::
The root of the CVS archive. May be local (a simple path) or remote;
- currently, only the :local:, :ext: and :pserver: access methods
+ currently, only the :local:, :ext: and :pserver: access methods
are supported. If not given, git-cvsimport will try to read it
from `CVS/Root`. If no such file exists, it checks for the
`CVSROOT` environment variable.
@@ -51,10 +53,19 @@ OPTIONS
The git repository to import to. If the directory doesn't
exist, it will be created. Default is the current directory.
+-r <remote>::
+ The git remote to import this CVS repository into.
+ Moves all CVS branches into remotes/<remote>/<branch>
+ akin to the git-clone --use-separate-remote option.
+
-o <branch-for-HEAD>::
- The 'HEAD' branch from CVS is imported to the 'origin' branch within
- the git repository, as 'HEAD' already has a special meaning for git.
- Use this option if you want to import into a different branch.
+ When no remote is specified (via -r) the 'HEAD' branch
+ from CVS is imported to the 'origin' branch within the git
+ repository, as 'HEAD' already has a special meaning for git.
+ When a remote is specified the 'HEAD' branch is named
+ remotes/<remote>/master mirroring git-clone behaviour.
+ Use this option if you want to import into a different
+ branch.
+
Use '-o master' for continuing an import that was initially done by
the old cvs2git tool.
@@ -67,7 +78,7 @@ the old cvs2git tool.
-k::
Kill keywords: will extract files with '-kk' from the CVS archive
to avoid noisy changesets. Highly recommended, but off by default
- to preserve compatibility with early imported trees.
+ to preserve compatibility with early imported trees.
-u::
Convert underscores in tag and branch names to dots.
@@ -89,15 +100,15 @@ If you need to pass multiple options, separate them with a comma.
Instead of calling cvsps, read the provided cvsps output file. Useful
for debugging or when cvsps is being handled outside cvsimport.
--m::
+-m::
Attempt to detect merges based on the commit message. This option
- will enable default regexes that try to capture the name source
- branch name from the commit message.
+ will enable default regexes that try to capture the name source
+ branch name from the commit message.
-M <regex>::
Attempt to detect merges based on the commit message with a custom
regex. It can be used with '-m' to also see the default regexes.
- You must escape forward slashes.
+ You must escape forward slashes.
-S <regex>::
Skip paths matching the regex.
@@ -156,4 +167,3 @@ Documentation by Matthias Urlichs <smurf@smurf.noris.de>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index e5005f02f9..60d0bcf0f3 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -7,10 +7,53 @@ git-cvsserver - A CVS server emulator for git
SYNOPSIS
--------
+
+SSH:
+
[verse]
export CVS_SERVER=git-cvsserver
'cvs' -d :ext:user@server/path/repo.git co <HEAD_name>
+pserver (/etc/inetd.conf):
+
+[verse]
+cvspserver stream tcp nowait nobody /usr/bin/git-cvsserver git-cvsserver pserver
+
+Usage:
+
+[verse]
+'git-cvsserver' [options] [pserver|server] [<directory> ...]
+
+OPTIONS
+-------
+
+All these options obviously only make sense if enforced by the server side.
+They have been implemented to resemble the gitlink:git-daemon[1] options as
+closely as possible.
+
+--base-path <path>::
+Prepend 'path' to requested CVSROOT
+
+--strict-paths::
+Don't allow recursing into subdirectories
+
+--export-all::
+Don't check for `gitcvs.enabled` in config. You also have to specify a list
+of allowed directories (see below) if you want to use this option.
+
+--version, -V::
+Print version information and exit
+
+--help, -h, -H::
+Print usage information and exit
+
+<directory>::
+You can specify a list of allowed directories. If no directories
+are given, all are allowed. This is an additional restriction, gitcvs
+access still needs to be enabled by the `gitcvs.enabled` config option
+unless '--export-all' was given, too.
+
+
DESCRIPTION
-----------
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 9ddab71203..f902161c08 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -54,6 +54,12 @@ OPTIONS
'git://example.com/hello.git', `git-daemon` will interpret the path
as '/srv/git/hello.git'.
+--base-path-relaxed::
+ If --base-path is enabled and repo lookup fails, with this option
+ `git-daemon` will attempt to lookup without prefixing the base path.
+ This is useful for switching to --base-path usage, while still
+ allowing the old paths.
+
--interpolated-path=pathtemplate::
To support virtual hosting, an interpolated path template can be
used to dynamically construct alternate paths. The template
@@ -235,4 +241,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 47a583d3a6..ac23e28f27 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -8,7 +8,7 @@ git-describe - Show the most recent tag that is reachable from a commit
SYNOPSIS
--------
-'git-describe' [--all] [--tags] [--abbrev=<n>] <committish>...
+'git-describe' [--all] [--tags] [--contains] [--abbrev=<n>] <committish>...
DESCRIPTION
-----------
@@ -31,6 +31,11 @@ OPTIONS
Instead of using only the annotated tags, use any tag
found in `.git/refs/tags`.
+--contains::
+ Instead of finding the tag that predates the commit, find
+ the tag that comes after the commit, and thus contains it.
+ Automatically implies --tags.
+
--abbrev=<n>::
Instead of using the default 8 hexadecimal digits as the
abbreviated object name, use <n> digits.
@@ -119,4 +124,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index 2e1e29ef5a..d8a0a86022 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -26,7 +26,7 @@ include::diff-options.txt[]
branch" respectively. With these options, diffs for
merged entries are not shown.
+
-The default is to diff against our branch (-2) and the
+The default is to diff against our branch (-2) and the
cleanly resolved paths. The option -0 can be given to
omit diff output for unmerged entries and just show "Unmerged".
@@ -58,4 +58,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-diff-index.txt b/Documentation/git-diff-index.txt
index 2df581c2c9..7bd262cefd 100644
--- a/Documentation/git-diff-index.txt
+++ b/Documentation/git-diff-index.txt
@@ -75,7 +75,7 @@ actually doing a "git-write-tree" and comparing that. Except this one is much
nicer for the case where you just want to check where you are.
So doing a "git-diff-index --cached" is basically very useful when you are
-asking yourself "what have I already marked for being committed, and
+asking yourself "what have I already marked for being committed, and
what's the difference to a previous tree".
Non-cached Mode
@@ -130,4 +130,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 6e660e2d08..6b3f74efe7 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -166,4 +166,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 044cee9b42..b36e705dd0 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -76,10 +76,10 @@ $ git diff --cached <2>
$ git diff HEAD <3>
------------
+
-<1> changes in the working tree not yet staged for the next commit.
-<2> changes between the index and your last commit; what you
+<1> Changes in the working tree not yet staged for the next commit.
+<2> Changes between the index and your last commit; what you
would be committing if you run "git commit" without "-a" option.
-<3> changes in the working tree since your last commit; what you
+<3> Changes in the working tree since your last commit; what you
would be committing if you run "git commit -a"
Comparing with arbitrary commits::
@@ -90,30 +90,27 @@ $ git diff HEAD -- ./test <2>
$ git diff HEAD^ HEAD <3>
------------
+
-<1> instead of using the tip of the current branch, compare with the
+<1> Instead of using the tip of the current branch, compare with the
tip of "test" branch.
-<2> instead of comparing with the tip of "test" branch, compare with
+<2> Instead of comparing with the tip of "test" branch, compare with
the tip of the current branch, but limit the comparison to the
file "test".
-<3> compare the version before the last commit and the last commit.
+<3> Compare the version before the last commit and the last commit.
Limiting the diff output::
+
------------
$ git diff --diff-filter=MRC <1>
-$ git diff --name-status -r <2>
+$ git diff --name-status <2>
$ git diff arch/i386 include/asm-i386 <3>
------------
+
-<1> show only modification, rename and copy, but not addition
+<1> Show only modification, rename and copy, but not addition
nor deletion.
-<2> show only names and the nature of change, but not actual
-diff output. --name-status disables usual patch generation
-which in turn also disables recursive behavior, so without -r
-you would only see the directory name if there is a change in a
-file in a subdirectory.
-<3> limit diff output to named subtrees.
+<2> Show only names and the nature of change, but not actual
+diff output.
+<3> Limit diff output to named subtrees.
Munging the diff output::
+
@@ -122,9 +119,9 @@ $ git diff --find-copies-harder -B -C <1>
$ git diff -R <2>
------------
+
-<1> spend extra cycles to find renames, copies and complete
+<1> Spend extra cycles to find renames, copies and complete
rewrites (very expensive).
-<2> output diff in reverse.
+<2> Output diff in reverse.
Author
@@ -138,4 +135,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index 87e70fe12d..30ee98d17f 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -302,7 +302,7 @@ change to the project.
data
('from' SP <committish> LF)?
('merge' SP <committish> LF)?
- (filemodify | filedelete | filedeleteall)*
+ (filemodify | filedelete | filecopy | filerename | filedeleteall)*
LF
....
@@ -325,11 +325,13 @@ commit message use a 0 length data. Commit messages are free-form
and are not interpreted by Git. Currently they must be encoded in
UTF-8, as fast-import does not permit other encodings to be specified.
-Zero or more `filemodify`, `filedelete` and `filedeleteall` commands
+Zero or more `filemodify`, `filedelete`, `filecopy`, `filerename`
+and `filedeleteall` commands
may be included to update the contents of the branch prior to
creating the commit. These commands may be supplied in any order.
However it is recommended that a `filedeleteall` command preceed
-all `filemodify` commands in the same commit, as `filedeleteall`
+all `filemodify`, `filecopy` and `filerename` commands in the same
+commit, as `filedeleteall`
wipes the branch clean (see below).
`author`
@@ -495,6 +497,56 @@ here `<path>` is the complete path of the file or subdirectory to
be removed from the branch.
See `filemodify` above for a detailed description of `<path>`.
+`filecopy`
+^^^^^^^^^^^^
+Recursively copies an existing file or subdirectory to a different
+location within the branch. The existing file or directory must
+exist. If the destination exists it will be completely replaced
+by the content copied from the source.
+
+....
+ 'C' SP <path> SP <path> LF
+....
+
+here the first `<path>` is the source location and the second
+`<path>` is the destination. See `filemodify` above for a detailed
+description of what `<path>` may look like. To use a source path
+that contains SP the path must be quoted.
+
+A `filecopy` command takes effect immediately. Once the source
+location has been copied to the destination any future commands
+applied to the source location will not impact the destination of
+the copy.
+
+`filerename`
+^^^^^^^^^^^^
+Renames an existing file or subdirectory to a different location
+within the branch. The existing file or directory must exist. If
+the destination exists it will be replaced by the source directory.
+
+....
+ 'R' SP <path> SP <path> LF
+....
+
+here the first `<path>` is the source location and the second
+`<path>` is the destination. See `filemodify` above for a detailed
+description of what `<path>` may look like. To use a source path
+that contains SP the path must be quoted.
+
+A `filerename` command takes effect immediately. Once the source
+location has been renamed to the destination any future commands
+applied to the source location will create new files there and not
+impact the destination of the rename.
+
+Note that a `filerename` is the same as a `filecopy` followed by a
+`filedelete` of the source location. There is a slight performance
+advantage to using `filerename`, but the advantage is so small
+that it is never worth trying to convert a delete/add pair in
+source material into a rename for fast-import. This `filerename`
+command is provided just to simplify frontends that already have
+rename information and don't want bother with decomposing it into a
+`filecopy` followed by a `filedelete`.
+
`filedeleteall`
^^^^^^^^^^^^^^^
Included in a `commit` command to remove all files (and also all
@@ -910,4 +962,3 @@ Documentation by Shawn O. Pearce <spearce@spearce.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index 5fbeab76b7..9003473596 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -35,7 +35,7 @@ include::fetch-options.txt[]
include::pull-fetch-param.txt[]
-include::urls.txt[]
+include::urls-remotes.txt[]
SEE ALSO
--------
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
new file mode 100644
index 0000000000..915258f410
--- /dev/null
+++ b/Documentation/git-filter-branch.txt
@@ -0,0 +1,278 @@
+git-filter-branch(1)
+====================
+
+NAME
+----
+git-filter-branch - Rewrite branches
+
+SYNOPSIS
+--------
+[verse]
+'git-filter-branch' [--env-filter <command>] [--tree-filter <command>]
+ [--index-filter <command>] [--parent-filter <command>]
+ [--msg-filter <command>] [--commit-filter <command>]
+ [--tag-name-filter <command>] [--subdirectory-filter <directory>]
+ [-d <directory>] [-f | --force] [<rev-list options>...]
+
+DESCRIPTION
+-----------
+Lets you rewrite git revision history by creating a new branch from
+your current branch, applying custom filters on each revision.
+Those filters can modify each tree (e.g. removing a file or running
+a perl rewrite on all files) or information about each commit.
+Otherwise, all information (including original commit times or merge
+information) will be preserved.
+
+The command takes the new branch name as a mandatory argument and
+the filters as optional arguments. If you specify no filters, the
+commits will be recommitted without any changes, which would normally
+have no effect. Nevertheless, this may be useful in the future for
+compensating for some git bugs or such, therefore such a usage is
+permitted.
+
+*WARNING*! The rewritten history will have different object names for all
+the objects and will not converge with the original branch. You will not
+be able to easily push and distribute the rewritten branch on top of the
+original branch. Please do not use this command if you do not know the
+full implications, and avoid using it anyway, if a simple single commit
+would suffice to fix your problem.
+
+Always verify that the rewritten version is correct: The original refs,
+if different from the rewritten ones, will be stored in the namespace
+'refs/original/'.
+
+Note that since this operation is extensively I/O expensive, it might
+be a good idea to redirect the temporary directory off-disk, e.g. on
+tmpfs. Reportedly the speedup is very noticeable.
+
+
+Filters
+~~~~~~~
+
+The filters are applied in the order as listed below. The <command>
+argument is always evaluated in shell using the 'eval' command (with the
+notable exception of the commit filter, for technical reasons).
+Prior to that, the $GIT_COMMIT environment variable will be set to contain
+the id of the commit being rewritten. Also, GIT_AUTHOR_NAME,
+GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL,
+and GIT_COMMITTER_DATE are set according to the current commit.
+
+A 'map' function is available that takes an "original sha1 id" argument
+and outputs a "rewritten sha1 id" if the commit has been already
+rewritten, and "original sha1 id" otherwise; the 'map' function can
+return several ids on separate lines if your commit filter emitted
+multiple commits.
+
+
+OPTIONS
+-------
+
+--env-filter <command>::
+ This is the filter for modifying the environment in which
+ the commit will be performed. Specifically, you might want
+ to rewrite the author/committer name/email/time environment
+ variables (see gitlink:git-commit[1] for details). Do not forget
+ to re-export the variables.
+
+--tree-filter <command>::
+ This is the filter for rewriting the tree and its contents.
+ The argument is evaluated in shell with the working
+ directory set to the root of the checked out tree. The new tree
+ is then used as-is (new files are auto-added, disappeared files
+ are auto-removed - neither .gitignore files nor any other ignore
+ rules *HAVE ANY EFFECT*!).
+
+--index-filter <command>::
+ This is the filter for rewriting the index. It is similar to the
+ tree filter but does not check out the tree, which makes it much
+ faster. For hairy cases, see gitlink:git-update-index[1].
+
+--parent-filter <command>::
+ This is the filter for rewriting the commit's parent list.
+ It will receive the parent string on stdin and shall output
+ the new parent string on stdout. The parent string is in
+ a format accepted by gitlink:git-commit-tree[1]: empty for
+ the initial commit, "-p parent" for a normal commit and
+ "-p parent1 -p parent2 -p parent3 ..." for a merge commit.
+
+--msg-filter <command>::
+ This is the filter for rewriting the commit messages.
+ The argument is evaluated in the shell with the original
+ commit message on standard input; its standard output is
+ used as the new commit message.
+
+--commit-filter <command>::
+ This is the filter for performing the commit.
+ If this filter is specified, it will be called instead of the
+ gitlink:git-commit-tree[1] command, with arguments of the form
+ "<TREE_ID> [-p <PARENT_COMMIT_ID>]..." and the log message on
+ stdin. The commit id is expected on stdout.
++
+As a special extension, the commit filter may emit multiple
+commit ids; in that case, ancestors of the original commit will
+have all of them as parents.
+
+--tag-name-filter <command>::
+ This is the filter for rewriting tag names. When passed,
+ it will be called for every tag ref that points to a rewritten
+ object (or to a tag object which points to a rewritten object).
+ The original tag name is passed via standard input, and the new
+ tag name is expected on standard output.
++
+The original tags are not deleted, but can be overwritten;
+use "--tag-name-filter=cat" to simply update the tags. In this
+case, be very careful and make sure you have the old tags
+backed up in case the conversion has run afoul.
++
+Note that there is currently no support for proper rewriting of
+tag objects; in layman terms, if the tag has a message or signature
+attached, the rewritten tag won't have it. Sorry. (It is by
+definition impossible to preserve signatures at any rate.)
+
+--subdirectory-filter <directory>::
+ Only look at the history which touches the given subdirectory.
+ The result will contain that directory (and only that) as its
+ project root.
+
+-d <directory>::
+ Use this option to set the path to the temporary directory used for
+ rewriting. When applying a tree filter, the command needs to
+ temporary checkout the tree to some directory, which may consume
+ considerable space in case of large projects. By default it
+ does this in the '.git-rewrite/' directory but you can override
+ that choice by this parameter.
+
+-f\|--force::
+ `git filter-branch` refuses to start with an existing temporary
+ directory or when there are already refs starting with
+ 'refs/original/', unless forced.
+
+<rev-list-options>::
+ When options are given after the new branch name, they will
+ be passed to gitlink:git-rev-list[1]. Only commits in the resulting
+ output will be filtered, although the filtered commits can still
+ reference parents which are outside of that set.
+
+
+Examples
+--------
+
+Suppose you want to remove a file (containing confidential information
+or copyright violation) from all commits:
+
+-------------------------------------------------------
+git filter-branch --tree-filter 'rm filename' HEAD
+-------------------------------------------------------
+
+A significantly faster version:
+
+--------------------------------------------------------------------------
+git filter-branch --index-filter 'git update-index --remove filename' HEAD
+--------------------------------------------------------------------------
+
+Now, you will get the rewritten history saved in the branch 'newbranch'
+(your current branch is left untouched).
+
+To set a commit (which typically is at the tip of another
+history) to be the parent of the current initial commit, in
+order to paste the other history behind the current history:
+
+-------------------------------------------------------------------
+git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' HEAD
+-------------------------------------------------------------------
+
+(if the parent string is empty - therefore we are dealing with the
+initial commit - add graftcommit as a parent). Note that this assumes
+history with a single root (that is, no merge without common ancestors
+happened). If this is not the case, use:
+
+--------------------------------------------------------------------------
+git filter-branch --parent-filter \
+ 'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' HEAD
+--------------------------------------------------------------------------
+
+or even simpler:
+
+-----------------------------------------------
+echo "$commit-id $graft-id" >> .git/info/grafts
+git filter-branch $graft-id..HEAD
+-----------------------------------------------
+
+To remove commits authored by "Darl McBribe" from the history:
+
+------------------------------------------------------------------------------
+git filter-branch --commit-filter '
+ if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ];
+ then
+ shift;
+ while [ -n "$1" ];
+ do
+ shift;
+ echo "$1";
+ shift;
+ done;
+ else
+ git commit-tree "$@";
+ fi' HEAD
+------------------------------------------------------------------------------
+
+The shift magic first throws away the tree id and then the -p
+parameters. Note that this handles merges properly! In case Darl
+committed a merge between P1 and P2, it will be propagated properly
+and all children of the merge will become merge commits with P1,P2
+as their parents instead of the merge commit.
+
+To restrict rewriting to only part of the history, specify a revision
+range in addition to the new branch name. The new branch name will
+point to the top-most revision that a 'git rev-list' of this range
+will print.
+
+Note that the changes introduced by the commits, and not reverted by
+subsequent commits, will still be in the rewritten branch. If you want
+to throw out _changes_ together with the commits, you should use the
+interactive mode of gitlink:git-rebase[1].
+
+Consider this history:
+
+------------------
+ D--E--F--G--H
+ / /
+A--B-----C
+------------------
+
+To rewrite only commits D,E,F,G,H, but leave A, B and C alone, use:
+
+--------------------------------
+git filter-branch ... C..H
+--------------------------------
+
+To rewrite commits E,F,G,H, use one of these:
+
+----------------------------------------
+git filter-branch ... C..H --not D
+git filter-branch ... D..H --not C
+----------------------------------------
+
+To move the whole tree into a subdirectory, or remove it from there:
+
+---------------------------------------------------------------
+git filter-branch --index-filter \
+ 'git ls-files -s | sed "s-\t-&newsubdir/-" |
+ GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
+ git update-index --index-info &&
+ mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
+---------------------------------------------------------------
+
+
+Author
+------
+Written by Petr "Pasky" Baudis <pasky@suse.cz>,
+and the git list <git@vger.kernel.org>
+
+Documentation
+--------------
+Documentation by Petr Baudis and the git list.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt
index 4913c2552f..6affc5bb4d 100644
--- a/Documentation/git-fmt-merge-msg.txt
+++ b/Documentation/git-fmt-merge-msg.txt
@@ -60,4 +60,3 @@ Documentation by Petr Baudis, Junio C Hamano and the git-list <git@vger.kernel.o
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 0ca58a7eb3..6cbcf937bc 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -11,7 +11,8 @@ SYNOPSIS
[verse]
'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread]
[--attach[=<boundary>] | --inline[=<boundary>]]
- [-s | --signoff] [<common diff options>] [--start-number <n>]
+ [-s | --signoff] [<common diff options>]
+ [--start-number <n>] [--numbered-files]
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
[--ignore-if-in-upstream]
[--subject-prefix=Subject-Prefix]
@@ -30,9 +31,11 @@ gitlink:git-rev-parse[1].
The output of this command is convenient for e-mail submission or
for use with gitlink:git-am[1].
-Each output file is numbered sequentially from 1, and uses the
+By default, each output file is numbered sequentially from 1, and uses the
first line of the commit message (massaged for pathname safety) as
-the filename. The names of the output files are printed to standard
+the filename. With the --numbered-files option, the output file names
+will only be numbers, without the first line of the commit appended.
+The names of the output files are printed to standard
output, unless the --stdout option is specified.
If -o is specified, output files are created in <dir>. Otherwise
@@ -63,6 +66,11 @@ include::diff-options.txt[]
--start-number <n>::
Start numbering the patches at <n> instead of 1.
+--numbered-files::
+ Output file names will be a simple number sequence
+ without the default first line of the commit appended.
+ Mutually exclusive with the --stdout option.
+
-k|--keep-subject::
Do not strip/add '[PATCH]' from the first line of the
commit log message.
@@ -121,12 +129,13 @@ not add any suffix.
CONFIGURATION
-------------
You can specify extra mail header lines to be added to each
-message in the repository configuration. Also you can specify
-the default suffix different from the built-in one:
+message in the repository configuration. You can also specify
+new defaults for the subject prefix and file suffix.
------------
[format]
headers = "Organization: git-foo\n"
+ subjectprefix = CHANGE
suffix = .txt
------------
@@ -173,4 +182,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt
index 8c68cf0372..45c0bee50a 100644
--- a/Documentation/git-fsck.txt
+++ b/Documentation/git-fsck.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git-fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
- [--full] [--strict] [<object>*]
+ [--full] [--strict] [--verbose] [--lost-found] [<object>*]
DESCRIPTION
-----------
@@ -61,6 +61,15 @@ index file and all SHA1 references in .git/refs/* as heads.
objects that triggers this check, but it is recommended
to check new projects with this flag.
+--verbose::
+ Be chatty.
+
+--lost-found::
+ Write dangling objects into .git/lost-found/commit/ or
+ .git/lost-found/other/, depending on type. If the object is
+ a blob, the contents are written into the file, rather than
+ its object name.
+
It tests SHA1 and general object sanity, and it does full tracking of
the resulting reachability and everything else. It prints out any
corruption it finds (missing or bad objects), and if you use the
@@ -142,4 +151,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index bc1658434a..c7742ca963 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
SYNOPSIS
--------
-'git-gc' [--prune]
+'git-gc' [--prune] [--aggressive]
DESCRIPTION
-----------
@@ -35,6 +35,13 @@ OPTIONS
repository at the same time (e.g. never use this option
in a cron script).
+--aggressive::
+ Usually 'git-gc' runs very quickly while providing good disk
+ space utilization and performance. This option will cause
+ git-gc to more aggressively optimize the repository at the expense
+ of taking much more time. The effects of this optimization are
+ persistent, so this option only needs to be used occasionally; every
+ few hundred changesets or so.
Configuration
-------------
@@ -67,6 +74,13 @@ The optional configuration variable 'gc.packrefs' determines if
is not run in bare repositories by default, to allow older dumb-transport
clients fetch from the repository, but this will change in the future.
+The optional configuration variable 'gc.aggressiveWindow' controls how
+much time is spent optimizing the delta compression of the objects in
+the repository when the --aggressive option is specified. The larger
+the value, the more time is spent optimizing the delta compression. See
+the documentation for the --window' option in gitlink:git-repack[1] for
+more details. This defaults to 10.
+
See Also
--------
gitlink:git-prune[1]
diff --git a/Documentation/git-get-tar-commit-id.txt b/Documentation/git-get-tar-commit-id.txt
index 48805b651c..9b5f86fc30 100644
--- a/Documentation/git-get-tar-commit-id.txt
+++ b/Documentation/git-get-tar-commit-id.txt
@@ -34,4 +34,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index c5a5dad1ce..97faaa1d3a 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -144,4 +144,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt
new file mode 100644
index 0000000000..bd613b2fcf
--- /dev/null
+++ b/Documentation/git-gui.txt
@@ -0,0 +1,115 @@
+git-gui(1)
+==========
+
+NAME
+----
+git-gui - A portable graphical interface to Git
+
+SYNOPSIS
+--------
+'git gui' [<command>] [arguments]
+
+DESCRIPTION
+-----------
+A Tcl/Tk based graphical user interface to Git. git-gui focuses
+on allowing users to make changes to their repository by making
+new commits, amending existing ones, creating branches, performing
+local merges, and fetching/pushing to remote repositories.
+
+Unlike gitlink:gitk[1], git-gui focuses on commit generation
+and single file annotation, and does not show project history.
+It does however supply menu actions to start a gitk session from
+within git-gui.
+
+git-gui is known to work on all popular UNIX systems, Mac OS X,
+and Windows (under both Cygwin and MSYS). To the extent possible
+OS specific user interface guidelines are followed, making git-gui
+a fairly native interface for users.
+
+COMMANDS
+--------
+blame::
+ Start a blame viewer on the specified file on the given
+ version (or working directory if not specified).
+
+browser::
+ Start a tree browser showing all files in the specified
+ commit (or 'HEAD' by default). Files selected through the
+ browser are opened in the blame viewer.
+
+citool::
+ Start git-gui and arrange to make exactly one commit before
+ exiting and returning to the shell. The interface is limited
+ to only commit actions, slightly reducing the application's
+ startup time and simplifying the menubar.
+
+version::
+ Display the currently running version of git-gui.
+
+
+Examples
+--------
+git gui blame Makefile::
+
+ Show the contents of the file 'Makefile' in the current
+ working directory, and provide annotations for both the
+ original author of each line, and who moved the line to its
+ current location. The uncommitted file is annotated, and
+ uncommitted changes (if any) are explicitly attributed to
+ 'Not Yet Committed'.
+
+git gui blame v0.99.8 Makefile::
+
+ Show the contents of 'Makefile' in revision 'v0.99.8'
+ and provide annotations for each line. Unlike the above
+ example the file is read from the object database and not
+ the working directory.
+
+git gui citool::
+
+ Make one commit and return to the shell when it is complete.
+
+git citool::
+
+ Same as 'git gui citool' (above).
+
+git gui browser maint::
+
+ Show a browser for the tree of the 'maint' branch. Files
+ selected in the browser can be viewed with the internal
+ blame viewer.
+
+See Also
+--------
+'gitk(1)'::
+ The git repository browser. Shows branches, commit history
+ and file differences. gitk is the utility started by
+ git-gui's Repository Visualize actions.
+
+Other
+-----
+git-gui is actually maintained as an independent project, but stable
+versions are distributed as part of the Git suite for the convience
+of end users.
+
+A git-gui development repository can be obtained from:
+
+ git clone git://repo.or.cz/git-gui.git
+
+or
+
+ git clone http://repo.or.cz/r/git-gui.git
+
+or browsed online at http://repo.or.cz/w/git-gui.git/[].
+
+Author
+------
+Written by Shawn O. Pearce <spearce@spearce.org>.
+
+Documentation
+--------------
+Documentation by Shawn O. Pearce <spearce@spearce.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt
index 5edc36f060..616f196d81 100644
--- a/Documentation/git-hash-object.txt
+++ b/Documentation/git-hash-object.txt
@@ -18,7 +18,7 @@ work tree), and optionally writes the resulting object into the
object database. Reports its object ID to its standard output.
This is used by "git-cvsimport" to update the index
without modifying files in the work tree. When <type> is not
-specified, it defaults to "blob".
+specified, it defaults to "blob".
OPTIONS
-------
@@ -43,4 +43,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index 4deabc376c..45e48453a1 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -54,4 +54,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt
index a15cf5b2a3..9afb860381 100644
--- a/Documentation/git-http-push.txt
+++ b/Documentation/git-http-push.txt
@@ -52,7 +52,7 @@ Specifying the Refs
A '<ref>' specification can be either a single pattern, or a pair
of such patterns separated by a colon ":" (this means that a ref name
-cannot have a colon in it). A single pattern '<name>' is just a
+cannot have a colon in it). A single pattern '<name>' is just a
shorthand for '<name>:<name>'.
Each pattern pair consists of the source side (before the colon)
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 226926964e..a8a7f6f04b 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -98,4 +98,3 @@ Documentation by Sergey Vlasov
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index 5412135d76..d4e01cb325 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository
SYNOPSIS
--------
-'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]]
+'git-init-db' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]]
DESCRIPTION
@@ -16,4 +16,3 @@ DESCRIPTION
This is a synonym for gitlink:git-init[1]. Please refer to the
documentation of that command.
-
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index 1b64d3ab03..07484a4fd0 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one
SYNOPSIS
--------
-'git-init' [--template=<template_directory>] [--shared[=<permissions>]]
+'git-init' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]]
OPTIONS
@@ -16,6 +16,10 @@ OPTIONS
--
+-q, \--quiet::
+
+Only print error and warning messages, all other output will be suppressed.
+
--template=<template_directory>::
Provide the directory from which templates will be used. The default template
@@ -108,4 +112,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
index 9df0ab2d76..cec60ee780 100644
--- a/Documentation/git-instaweb.txt
+++ b/Documentation/git-instaweb.txt
@@ -82,4 +82,3 @@ Documentation by Eric Wong <normalperson@yhbt.net>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt
index 51389ef37d..141b76768c 100644
--- a/Documentation/git-local-fetch.txt
+++ b/Documentation/git-local-fetch.txt
@@ -14,6 +14,8 @@ SYNOPSIS
DESCRIPTION
-----------
+THIS COMMAND IS DEPRECATED.
+
Duplicates another git repository on a local system.
OPTIONS
@@ -62,4 +64,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 0f353f6558..5a90f65b5e 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -52,7 +52,24 @@ include::pretty-options.txt[]
See also gitlink:git-reflog[1].
--decorate::
- Print out the ref names of any commits that are shown.
+ Print out the ref names of any commits that are shown.
+
+--full-diff::
+ Without this flag, "git log -p <paths>..." shows commits that
+ touch the specified paths, and diffs about the same specified
+ paths. With this, the full diff is shown for commits that touch
+ the specified paths; this means that "<paths>..." limits only
+ commits, and doesn't limit diff for those commits.
+
+--follow::
+ Continue listing the history of a file beyond renames.
+
+--log-size::
+ Before the log message print out its size in bytes. Intended
+ mainly for porcelain tools consumption. If git is unable to
+ produce a valid value size is set to zero.
+ Note that only message is considered, if also a diff is shown
+ its size is not included.
<paths>...::
Show only commits that affect the specified paths.
@@ -84,6 +101,12 @@ git log -r --name-status release..test::
in the "release" branch, along with the list of paths
each commit modifies.
+git log --follow builtin-rev-list.c::
+
+ Shows the commits that changed builtin-rev-list.c, including
+ those commits that occurred before the file was given its
+ present name.
+
Discussion
----------
@@ -101,4 +124,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index a78a9ff1b8..997594549f 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -180,4 +180,3 @@ Documentation by David Greaves, Junio C Hamano, Josh Triplett, and the git-list
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index c254005ca3..93e9a60330 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -70,4 +70,3 @@ Written by Junio C Hamano <junkio@cox.net>
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
index 7899394081..7b78599673 100644
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -9,7 +9,7 @@ git-ls-tree - List the contents of a tree object
SYNOPSIS
--------
[verse]
-'git-ls-tree' [-d] [-r] [-t] [-z]
+'git-ls-tree' [-d] [-r] [-t] [-l] [-z]
[--name-only] [--name-status] [--full-name] [--abbrev=[<n>]]
<tree-ish> [paths...]
@@ -36,6 +36,10 @@ OPTIONS
Show tree entries even when going to recurse them. Has no effect
if '-r' was not passed. '-d' implies '-t'.
+-l::
+--long::
+ Show object size of blob (file) entries.
+
-z::
\0 line termination on output.
@@ -65,6 +69,14 @@ Output Format
When the `-z` option is not used, TAB, LF, and backslash characters
in pathnames are represented as `\t`, `\n`, and `\\`, respectively.
+When the `-l` option is used, format changes to
+
+ <mode> SP <type> SP <object> SP <object size> TAB <file>
+
+Object size identified by <object> is given in bytes, and right-justified
+with minimum width of 7 characters. Object size is given only for blobs
+(file) entries; for other entries `-` character is used in place of size.
+
Author
------
@@ -80,4 +92,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index 8eadcebfcf..64aa6a1ea6 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -16,7 +16,7 @@ DESCRIPTION
Reading a single e-mail message from the standard input, and
writes the commit log message in <msg> file, and the patches in
<patch> file. The author name, e-mail and e-mail subject are
-written out to the standard output to be used by git-applypatch
+written out to the standard output to be used by git-am
to create a commit. It is usually not necessary to use this
command directly. See gitlink:git-am[1] instead.
@@ -67,4 +67,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt
index c11d6a530f..c4f4cabbdc 100644
--- a/Documentation/git-mailsplit.txt
+++ b/Documentation/git-mailsplit.txt
@@ -7,12 +7,15 @@ git-mailsplit - Simple UNIX mbox splitter program
SYNOPSIS
--------
-'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>...]
+'git-mailsplit' [-b] [-f<nn>] [-d<prec>] -o<directory> [--] [<mbox>|<Maildir>...]
DESCRIPTION
-----------
-Splits a mbox file into a list of files: "0001" "0002" .. in the specified
-directory so you can process them further from there.
+Splits a mbox file or a Maildir into a list of files: "0001" "0002" .. in the
+specified directory so you can process them further from there.
+
+IMPORTANT: Maildir splitting relies upon filenames being sorted to output
+patches in the correct order.
OPTIONS
-------
@@ -20,6 +23,10 @@ OPTIONS
Mbox file to split. If not given, the mbox is read from
the standard input.
+<Maildir>::
+ Root of the Maildir to split. This directory should contain the cur, tmp
+ and new subdirectories.
+
<directory>::
Directory in which to place the individual messages.
@@ -49,4 +56,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 3190aed108..6b71880ec4 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -40,4 +40,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index b8ee1ff2b0..17e9f10c65 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -59,7 +59,7 @@ Examples:
This is modified MM in the branch B. # merge2
This is modified MM in the branch B. # current contents
-or
+or
torvalds@ppc970:~/merge-test> git-merge-index cat AA MM
cat: : No such file or directory
@@ -85,4 +85,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-merge-one-file.txt b/Documentation/git-merge-one-file.txt
index f80ab3b8c4..f35d0e1b45 100644
--- a/Documentation/git-merge-one-file.txt
+++ b/Documentation/git-merge-one-file.txt
@@ -27,4 +27,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index 35fb4fb713..6892fdac3d 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -34,4 +34,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 9c08efa53a..144bc16ff2 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -9,7 +9,7 @@ git-merge - Join two or more development histories together
SYNOPSIS
--------
[verse]
-'git-merge' [-n] [--no-commit] [--squash] [-s <strategy>]...
+'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]...
[-m <msg>] <remote> <remote>...
DESCRIPTION
@@ -43,6 +43,21 @@ If you tried a merge which resulted in a complex conflicts and
would want to start over, you can recover with
gitlink:git-reset[1].
+CONFIGURATION
+-------------
+
+merge.summary::
+ Whether to include summaries of merged commits in newly
+ created merge commit. False by default.
+
+merge.verbosity::
+ Controls the amount of output shown by the recursive merge
+ strategy. Level 0 outputs nothing except a final error
+ message if conflicts were detected. Level 1 outputs only
+ conflicts, 2 outputs conflicts and file changes. Level 5 and
+ above outputs debugging information. The default is level 2.
+ Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable.
+
HOW MERGE WORKS
---------------
@@ -92,11 +107,11 @@ pull after you are done and ready.
When things cleanly merge, these things happen:
-1. the results are updated both in the index file and in your
- working tree,
-2. index file is written out as a tree,
-3. the tree gets committed, and
-4. the `HEAD` pointer gets advanced.
+1. The results are updated both in the index file and in your
+ working tree;
+2. Index file is written out as a tree;
+3. The tree gets committed; and
+4. The `HEAD` pointer gets advanced.
Because of 2., we require that the original state of the index
file to match exactly the current `HEAD` commit; otherwise we
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index add01e855a..6c32c6d18e 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -25,7 +25,7 @@ OPTIONS
-t or --tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
- kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, and opendiff
+ kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, and opendiff
+
If a merge resolution program is not specified, 'git mergetool'
will use the configuration variable merge.tool. If the
@@ -43,4 +43,3 @@ Documentation by Theodore Y Ts'o.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
index 2860a3d1ba..ea7a75234a 100644
--- a/Documentation/git-mktag.txt
+++ b/Documentation/git-mktag.txt
@@ -19,18 +19,18 @@ The output is the new tag's <object> identifier.
Tag Format
----------
-A tag signature file has a very simple fixed format: three lines of
+A tag signature file has a very simple fixed format: four lines of
object <sha1>
type <typename>
tag <tagname>
+ tagger <tagger>
-followed by some 'optional' free-form signature that git itself
-doesn't care about, but that can be verified with gpg or similar.
-
-The size of the full object is artificially limited to 8kB. (Just
-because I'm a lazy bastard, and if you can't fit a signature in that
-size, you're doing something wrong)
+followed by some 'optional' free-form message (some tags created
+by older git may not have `tagger` line). The message, when
+exists, is separated by a blank line from the header. The
+message part may contain a signature that git itself doesn't
+care about, but that can be verified with gpg.
Author
@@ -44,4 +44,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt
index 5f9ee603b7..638abc7d0f 100644
--- a/Documentation/git-mktree.txt
+++ b/Documentation/git-mktree.txt
@@ -32,4 +32,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index 6756b76bb1..2c9cf743c7 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -51,4 +51,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index d6c8bf800f..91eede120e 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -34,6 +34,13 @@ OPTIONS
Read from stdin, append "(<rev_name>)" to all sha1's of nameable
commits, and pass to stdout
+--name-only::
+ Instead of printing both the SHA-1 and the name, print only
+ the name. If given with --tags the usual tag prefix of
+ "tags/" is also ommitted from the name, matching the output
+ of gitlink::git-describe[1] more closely. This option
+ cannot be combined with --stdin.
+
EXAMPLE
-------
@@ -69,4 +76,3 @@ Documentation by Johannes Schindelin.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index bd3ee456e3..6f17cff24a 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -85,6 +85,22 @@ base-name::
times to get to the necessary object.
The default value for --window is 10 and --depth is 50.
+--window-memory=[N]::
+ This option provides an additional limit on top of `--window`;
+ the window size will dynamically scale down so as to not take
+ up more than N bytes in memory. This is useful in
+ repositories with a mix of large and small objects to not run
+ out of memory with a large window, but still be able to take
+ advantage of the large window for the smaller objects. The
+ size can be suffixed with "k", "m", or "g".
+ `--window-memory=0` makes memory usage unlimited, which is the
+ default.
+
+--max-pack-size=<n>::
+ Maximum size of each output packfile, expressed in MiB.
+ If specified, multiple packfiles may be created.
+ The default is unlimited.
+
--incremental::
This flag causes an object already in a pack ignored
even if it appears in the standard input.
@@ -127,6 +143,25 @@ base-name::
This flag tells the command not to reuse existing deltas
but compute them from scratch.
+--no-reuse-object::
+ This flag tells the command not to reuse existing object data at all,
+ including non deltified object, forcing recompression of everything.
+ This implies --no-reuse-delta. Useful only in the obscure case where
+ wholesale enforcement of a different compression level on the
+ packed data is desired.
+
+--compression=[N]::
+ Specifies compression level for newly-compressed data in the
+ generated pack. If not specified, pack compression level is
+ determined first by pack.compression, then by core.compression,
+ and defaults to -1, the zlib default, if neither is set.
+ Data copied from loose objects will be recompressed
+ if core.legacyheaders was true when they were created or if
+ the loose compression level (see core.loosecompression and
+ core.compression) is now a different value than the pack
+ compression level. Add --no-reuse-object if you want to force
+ a uniform compression level on all data no matter the source.
+
--delta-base-offset::
A packed archive can express base object of a delta as
either 20-byte object name or as an offset in the
@@ -161,4 +196,3 @@ gitlink:git-prune-packed[1]
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt
index 94bbea0db2..f2ceebac4b 100644
--- a/Documentation/git-pack-redundant.txt
+++ b/Documentation/git-pack-redundant.txt
@@ -17,7 +17,7 @@ are redundant. The output is suitable for piping to
'xargs rm' if you are in the root of the repository.
git-pack-redundant accepts a list of objects on standard input. Any objects
-given will be ignored when checking which packs are required. This makes the
+given will be ignored when checking which packs are required. This makes the
following command useful when wanting to remove packs which contain unreachable
objects.
@@ -55,4 +55,3 @@ gitlink:git-prune-packed[1]
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-patch-id.txt b/Documentation/git-patch-id.txt
index a7e9fd021a..ad528a9224 100644
--- a/Documentation/git-patch-id.txt
+++ b/Documentation/git-patch-id.txt
@@ -40,4 +40,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt
index 74f37bd904..abc171266a 100644
--- a/Documentation/git-peek-remote.txt
+++ b/Documentation/git-peek-remote.txt
@@ -52,4 +52,3 @@ Documentation by Junio C Hamano.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index 310033e460..3800edb7bb 100644
--- a/Documentation/git-prune-packed.txt
+++ b/Documentation/git-prune-packed.txt
@@ -50,4 +50,3 @@ gitlink:git-repack[1]
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index b8166a210f..0ace233d18 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -58,4 +58,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 94478ed94d..e1eb2c1d00 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -29,7 +29,7 @@ include::fetch-options.txt[]
include::pull-fetch-param.txt[]
-include::urls.txt[]
+include::urls-remotes.txt[]
include::merge-strategies.txt[]
@@ -165,4 +165,3 @@ Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index e9ad10672a..0dd9caf867 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -53,9 +53,8 @@ side are updated.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+
-A parameter <ref> without a colon is equivalent to
-<ref>`:`<ref>, hence updates <ref> in the destination from <ref>
-in the source.
+A parameter <ref> without a colon pushes the <ref> from the source
+repository to the destination repository under the same name.
+
Pushing an empty <src> allows you to delete the <dst> ref from
the remote repository.
@@ -80,7 +79,7 @@ the remote repository.
-f, \--force::
Usually, the command refuses to update a remote ref that is
- not a descendant of the local ref used to overwrite it.
+ not an ancestor of the local ref used to overwrite it.
This flag disables the check. This can cause the
remote repository to lose commits; use it with care.
@@ -96,7 +95,27 @@ the remote repository.
-v::
Run verbosely.
-include::urls.txt[]
+include::urls-remotes.txt[]
+
+
+Examples
+--------
+
+git push origin master::
+ Find a ref that matches `master` in the source repository
+ (most likely, it would find `refs/heads/master`), and update
+ the same ref (e.g. `refs/heads/master`) in `origin` repository
+ with it.
+
+git push origin :experimental::
+ Find a ref that matches `experimental` in the `origin` repository
+ (e.g. `refs/heads/experimental`), and delete it.
+
+git push origin master:satellite/master::
+ Find a ref that matches `master` in the source repository
+ (most likely, it would find `refs/heads/master`), and update
+ the ref that matches `satellite/master` (most likely, it would
+ be `refs/remotes/satellite/master`) in `origin` repository with it.
Author
------
@@ -110,4 +129,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt
index 296937a416..1c3ef4c593 100644
--- a/Documentation/git-quiltimport.txt
+++ b/Documentation/git-quiltimport.txt
@@ -58,4 +58,3 @@ Documentation by Eric Biederman <ebiederm@lnxi.com>
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index acb57447a8..74c5478ba1 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -8,7 +8,7 @@ git-read-tree - Reads tree information into the index
SYNOPSIS
--------
-'git-read-tree' (<tree-ish> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+'git-read-tree' (<tree-ish> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
DESCRIPTION
@@ -50,6 +50,12 @@ OPTIONS
trees that are not directly related to the current
working tree status into a temporary index file.
+--trivial::
+ Restrict three-way merge by `git-read-tree` to happen
+ only if there is no file-level merging required, instead
+ of resolving merge for trivial cases and leaving
+ conflicting files unresolved in the index.
+
--aggressive::
Usually a three-way merge by `git-read-tree` resolves
the merge for really trivial cases and leaves other
@@ -356,4 +362,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 753b275a0f..96907d4863 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -8,7 +8,8 @@ git-rebase - Forward-port local commits to the updated upstream head
SYNOPSIS
--------
[verse]
-'git-rebase' [-v] [--merge] [-C<n>] [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [-i | --interactive] [-v | --verbose] [--merge] [-C<n>]
+ [-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
DESCRIPTION
@@ -208,6 +209,14 @@ OPTIONS
context exist they all must match. By default no context is
ever ignored.
+-i, \--interactive::
+ Make a list of the commits which are about to be rebased. Let the
+ user edit that list before rebasing.
+
+-p, \--preserve-merges::
+ Instead of ignoring merges, try to recreate them. This option
+ only works in interactive mode.
+
include::merge-strategies.txt[]
NOTES
@@ -226,9 +235,100 @@ pre-rebase hook script for an example.
You must be in the top directory of your project to start (or continue)
a rebase. Upon completion, <branch> will be the current branch.
-Author
+INTERACTIVE MODE
+----------------
+
+Rebasing interactively means that you have a chance to edit the commits
+which are rebased. You can reorder the commits, and you can
+remove them (weeding out bad or otherwise unwanted patches).
+
+The interactive mode is meant for this type of workflow:
+
+1. have a wonderful idea
+2. hack on the code
+3. prepare a series for submission
+4. submit
+
+where point 2. consists of several instances of
+
+a. regular use
+ 1. finish something worthy of a commit
+ 2. commit
+b. independent fixup
+ 1. realize that something does not work
+ 2. fix that
+ 3. commit it
+
+Sometimes the thing fixed in b.2. cannot be amended to the not-quite
+perfect commit it fixes, because that commit is buried deeply in a
+patch series. That is exactly what interactive rebase is for: use it
+after plenty of "a"s and "b"s, by rearranging and editing
+commits, and squashing multiple commits into one.
+
+Start it with the last commit you want to retain as-is:
+
+ git rebase -i <after-this-commit>
+
+An editor will be fired up with all the commits in your current branch
+(ignoring merge commits), which come after the given commit. You can
+reorder the commits in this list to your heart's content, and you can
+remove them. The list looks more or less like this:
+
+-------------------------------------------
+pick deadbee The oneline of this commit
+pick fa1afe1 The oneline of the next commit
+...
+-------------------------------------------
+
+The oneline descriptions are purely for your pleasure; `git-rebase` will
+not look at them but at the commit names ("deadbee" and "fa1afe1" in this
+example), so do not delete or edit the names.
+
+By replacing the command "pick" with the command "edit", you can tell
+`git-rebase` to stop after applying that commit, so that you can edit
+the files and/or the commit message, amend the commit, and continue
+rebasing.
+
+If you want to fold two or more commits into one, replace the command
+"pick" with "squash" for the second and subsequent commit. If the
+commits had different authors, it will attribute the squashed commit to
+the author of the last commit.
+
+In both cases, or when a "pick" does not succeed (because of merge
+errors), the loop will stop to let you fix things, and you can continue
+the loop with `git rebase --continue`.
+
+For example, if you want to reorder the last 5 commits, such that what
+was HEAD~4 becomes the new HEAD. To achieve that, you would call
+`git-rebase` like this:
+
+----------------------
+$ git rebase -i HEAD~5
+----------------------
+
+And move the first patch to the end of the list.
+
+You might want to preserve merges, if you have a history like this:
+
+------------------
+ X
+ \
+ A---M---B
+ /
+---o---O---P---Q
+------------------
+
+Suppose you want to rebase the side branch starting at "A" to "Q". Make
+sure that the current HEAD is "B", and call
+
+-----------------------------
+$ git rebase -i -p --onto Q O
+-----------------------------
+
+Authors
------
-Written by Junio C Hamano <junkio@cox.net>
+Written by Junio C Hamano <junkio@cox.net> and
+Johannes E. Schindelin <johannes.schindelin@gmx.de>
Documentation
--------------
@@ -237,4 +337,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index 6914aa59c3..4ef1840472 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -48,8 +48,8 @@ standard input of the hook will be one line per ref to be updated:
The refname value is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 values before
each refname are the object names for the refname before and after
-the update. Refs to be created will have sha1-old equal to 0{40},
-while refs to be deleted will have sha1-new equal to 0{40}, otherwise
+the update. Refs to be created will have sha1-old equal to 0\{40},
+while refs to be deleted will have sha1-new equal to 0\{40}, otherwise
sha1-old and sha1-new should be valid objects in the repository.
This hook is called before any refname is updated and before any
@@ -71,7 +71,7 @@ The refname parameter is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 arguments are
the object names for the refname before and after the update.
Note that the hook is called before the refname is updated,
-so either sha1-old is 0{40} (meaning there is no such ref yet),
+so either sha1-old is 0\{40} (meaning there is no such ref yet),
or it should match what is recorded in refname.
The hook should exit with non-zero status if it wants to disallow
@@ -96,8 +96,8 @@ The refname value is relative to $GIT_DIR; e.g. for the master
head this is "refs/heads/master". The two sha1 values before
each refname are the object names for the refname before and after
the update. Refs that were created will have sha1-old equal to
-0{40}, while refs that were deleted will have sha1-new equal to
-0{40}, otherwise sha1-old and sha1-new should be valid objects in
+0\{40}, while refs that were deleted will have sha1-new equal to
+0\{40}, otherwise sha1-old and sha1-new should be valid objects in
the repository.
Using this hook, it is easy to generate mails describing the updates
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index 1e343bcdcd..89bc9c51ea 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -39,6 +39,19 @@ the current branch. It is basically an alias for 'git log -g --abbrev-commit
OPTIONS
-------
+--stale-fix::
+ This revamps the logic -- the definition of "broken commit"
+ becomes: a commit that is not reachable from any of the refs and
+ there is a missing object among the commit, tree, or blob
+ objects reachable from it that is not reachable from any of the
+ refs.
++
+This computation involves traversing all the reachable objects, i.e. it
+has the same cost as 'git prune'. Fortunately, once this is run, we
+should not have to ever worry about missing objects, because the current
+prune and pack-objects know about reflogs and protect objects referred by
+them.
+
--expire=<time>::
Entries older than this time are pruned. Without the
option it is taken from configuration `gc.reflogExpire`,
@@ -65,4 +78,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-relink.txt b/Documentation/git-relink.txt
index aca60120c8..fe631bb3dd 100644
--- a/Documentation/git-relink.txt
+++ b/Documentation/git-relink.txt
@@ -34,4 +34,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index b35c65ba3b..61a6022ce8 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -135,4 +135,3 @@ Documentation by J. Bruce Fields and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index c57013b953..5283ef84a9 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -68,6 +68,22 @@ OPTIONS
to be applied that many times to get to the necessary object.
The default value for --window is 10 and --depth is 50.
+--window-memory=[N]::
+ This option provides an additional limit on top of `--window`;
+ the window size will dynamically scale down so as to not take
+ up more than N bytes in memory. This is useful in
+ repositories with a mix of large and small objects to not run
+ out of memory with a large window, but still be able to take
+ advantage of the large window for the smaller objects. The
+ size can be suffixed with "k", "m", or "g".
+ `--window-memory=0` makes memory usage unlimited, which is the
+ default.
+
+--max-pack-size=<n>::
+ Maximum size of each output packfile, expressed in MiB.
+ If specified, multiple packfiles may be created.
+ The default is unlimited.
+
Configuration
-------------
@@ -99,4 +115,3 @@ gitlink:git-prune-packed[1]
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-request-pull.txt b/Documentation/git-request-pull.txt
index 478a5fd6b7..087eeb7cc2 100644
--- a/Documentation/git-request-pull.txt
+++ b/Documentation/git-request-pull.txt
@@ -37,4 +37,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index 7ff9b05e68..c4d4263238 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -23,7 +23,7 @@ initial manual merge, and later by noticing the same automerge
results and applying the previously recorded hand resolution.
[NOTE]
-You need to create `$GIT_DIR/rr-cache` directory to enable this
+You need to set the config variable rerere.enabled to enable this
command.
@@ -171,7 +171,7 @@ records it if it is a new conflict, or reuses the earlier hand
resolve when it is not. `git-commit` also invokes `git-rerere`
when recording a merge result. What this means is that you do
not have to do anything special yourself (Note: you still have
-to create `$GIT_DIR/rr-cache` directory to enable this command).
+to set the config variable rerere.enabled to enable this command).
In our example, when you did the test merge, the manual
resolution is recorded, and it will be reused when you do the
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 19c5b9bbda..15e3aca9a1 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -63,7 +63,7 @@ $ git commit -a -c ORIG_HEAD <3>
<1> This is most often done when you remembered what you
just committed is incomplete, or you misspelled your commit
message, or both. Leaves working tree as it was before "reset".
-<2> make corrections to working tree files.
+<2> Make corrections to working tree files.
<3> "reset" copies the old head to .git/ORIG_HEAD; redo the
commit by starting with its log message. If you do not need to
edit the message further, you can give -C option instead.
@@ -106,17 +106,17 @@ $ git reset <3>
$ git pull git://info.example.com/ nitfol <4>
------------
+
-<1> you are happily working on something, and find the changes
+<1> You are happily working on something, and find the changes
in these files are in good order. You do not want to see them
when you run "git diff", because you plan to work on other files
and changes with these files are distracting.
-<2> somebody asks you to pull, and the changes sounds worthy of merging.
-<3> however, you already dirtied the index (i.e. your index does
+<2> Somebody asks you to pull, and the changes sounds worthy of merging.
+<3> However, you already dirtied the index (i.e. your index does
not match the HEAD commit). But you know the pull you are going
to make does not affect frotz.c nor filfre.c, so you revert the
index changes for these two files. Your changes in working tree
remain there.
-<4> then you can pull and merge, leaving frotz.c and filfre.c
+<4> Then you can pull and merge, leaving frotz.c and filfre.c
changes still in the working tree.
Undo a merge or pull::
@@ -133,15 +133,15 @@ Fast forward
$ git reset --hard ORIG_HEAD <4>
------------
+
-<1> try to update from the upstream resulted in a lot of
+<1> Try to update from the upstream resulted in a lot of
conflicts; you were not ready to spend a lot of time merging
right now, so you decide to do that later.
<2> "pull" has not made merge commit, so "git reset --hard"
which is a synonym for "git reset --hard HEAD" clears the mess
from the index file and the working tree.
-<3> merge a topic branch into the current branch, which resulted
+<3> Merge a topic branch into the current branch, which resulted
in a fast forward.
-<4> but you decided that the topic branch is not ready for public
+<4> But you decided that the topic branch is not ready for public
consumption yet. "pull" or "merge" always leaves the original
tip of the current branch in ORIG_HEAD, so resetting hard to it
brings your index file and the working tree back to that state,
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index c3c2043d18..1c1978140f 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -16,16 +16,20 @@ SYNOPSIS
[ \--sparse ]
[ \--no-merges ]
[ \--remove-empty ]
+ [ \--full-history ]
[ \--not ]
[ \--all ]
[ \--stdin ]
[ \--topo-order ]
[ \--parents ]
+ [ \--timestamp ]
[ \--left-right ]
[ \--cherry-pick ]
[ \--encoding[=<encoding>] ]
[ \--(author|committer|grep)=<pattern> ]
- [ \--date={local|relative|default} ]
+ [ \--regexp-ignore-case | \-i ]
+ [ \--extended-regexp | \-E ]
+ [ \--date={local|relative|default|iso|rfc|short} ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
[ \--pretty | \--header ]
[ \--bisect ]
@@ -33,6 +37,7 @@ SYNOPSIS
[ \--merge ]
[ \--reverse ]
[ \--walk-reflogs ]
+ [ \--no-walk ] [ \--do-walk ]
<commit>... [ \-- <paths>... ]
DESCRIPTION
@@ -93,7 +98,7 @@ include::pretty-options.txt[]
Synonym for `--date=relative`.
---date={relative,local,default}::
+--date={relative,local,default,iso,rfc}::
Only takes effect for dates shown in human-readable format, such
as when using "--pretty".
@@ -103,6 +108,13 @@ e.g. "2 hours ago".
+
`--date=local` shows timestamps in user's local timezone.
+
+`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
++
+`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
+format, often found in E-mail messages.
++
+`--date=short` shows only date but not time, in `YYYY-MM-DD` fomat.
++
`--date=default` shows timestamps in the original timezone
(either committer's or author's).
@@ -115,6 +127,9 @@ e.g. "2 hours ago".
Print the parents of the commit.
+--timestamp::
+ Print the raw commit timestamp.
+
--left-right::
Mark which side of a symmetric diff a commit is reachable from.
@@ -214,10 +229,27 @@ limiting may be applied.
Limit the commits output to ones with log message that
matches the specified pattern (regular expression).
+-i, --regexp-ignore-case::
+
+ Match the regexp limiting patterns without regard to letters case.
+
+-E, --extended-regexp::
+
+ Consider the limiting patterns to be extended regular expressions
+ instead of the default basic regular expressions.
+
--remove-empty::
Stop when a given path disappears from the tree.
+--full-history::
+
+ Show also parts of history irrelevant to current state of a given
+ path. This turns off history simplification, which removed merges
+ which didn't change anything at all at some child. It will still actually
+ simplify away merges that didn't change anything at all into either
+ child.
+
--no-merges::
Do not print commits with more than one parent.
@@ -261,9 +293,9 @@ excluded from the output.
+
With '\--pretty' format other than oneline (for obvious reasons),
this causes the output to have two extra lines of information
-taken from the reflog. By default, 'commit@{Nth}' notation is
+taken from the reflog. By default, 'commit@\{Nth}' notation is
used in the output. When the starting commit is specified as
-'commit@{now}', output also uses 'commit@{timestamp}' notation
+'commit@{now}', output also uses 'commit@\{timestamp}' notation
instead. Under '\--pretty=oneline', the commit message is
prefixed with this information on the same line.
@@ -367,6 +399,14 @@ These options are mostly targeted for packing of git repositories.
Only useful with '--objects'; print the object IDs that are not
in packs.
+--no-walk::
+
+ Only show the given revs, but do not traverse their ancestors.
+
+--do-walk::
+
+ Overrides a previous --no-walk.
+
include::pretty-formats.txt[]
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 7757abe621..4b4d229e60 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -89,6 +89,17 @@ OPTIONS
--git-dir::
Show `$GIT_DIR` if defined else show the path to the .git directory.
+--is-inside-git-dir::
+ When the current working directory is below the repository
+ directory print "true", otherwise "false".
+
+--is-inside-work-tree::
+ When the current working directory is inside the work tree of the
+ repository print "true", otherwise "false".
+
+--is-bare-repository::
+ When the repository is bare print "true", otherwise "false".
+
--short, --short=number::
Instead of outputting the full SHA1 values of object names try to
abbreviate them to a shorter unique name. When no length is specified
@@ -213,7 +224,7 @@ left-to-right.
G H I J
\ / \ /
D E F
- \ | / \
+ \ | / \
\ | / |
\|/ |
B C
@@ -286,4 +297,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 8081bbaffa..69db498447 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -56,4 +56,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index a65f24a0f6..be61a82164 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -14,7 +14,8 @@ DESCRIPTION
Remove files from the working tree and from the index. The
files have to be identical to the tip of the branch, and no
updates to its contents must have been placed in the staging
-area (aka index).
+area (aka index). When --cached is given, the staged content has to
+match either the tip of the branch *or* the file on disk.
OPTIONS
@@ -95,4 +96,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-runstatus.txt b/Documentation/git-runstatus.txt
index 8bb52f4687..dee5d0da9d 100644
--- a/Documentation/git-runstatus.txt
+++ b/Documentation/git-runstatus.txt
@@ -66,4 +66,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 7ae39fd5a2..d243ed1e3a 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -44,8 +44,8 @@ The --cc option must be repeated for each user you want on the cc list.
value; if that is unspecified, default to --chain-reply-to.
--compose::
- Use $EDITOR to edit an introductory message for the
- patch series.
+ Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
+ introductory message for the patch series.
--from::
Specify the sender of the emails. This will default to
@@ -59,9 +59,11 @@ The --cc option must be repeated for each user you want on the cc list.
Only necessary if --compose is also set. If --compose
is not set, this will be prompted for.
---no-signed-off-by-cc::
- Do not add emails found in Signed-off-by: or Cc: lines to the
- cc list.
+--signed-off-by-cc, --no-signed-off-by-cc::
+ If this is set, add emails found in Signed-off-by: or Cc: lines to the
+ cc list.
+ Default is the value of 'sendemail.signedoffbycc' configuration value;
+ if that is unspecified, default to --signed-off-by-cc.
--quiet::
Make git-send-email less verbose. One line per email should be
@@ -78,13 +80,22 @@ The --cc option must be repeated for each user you want on the cc list.
`localhost` otherwise.
--subject::
- Specify the initial subject of the email thread.
+ Specify the initial subject of the email thread.
Only necessary if --compose is also set. If --compose
is not set, this will be prompted for.
---suppress-from::
- Do not add the From: address to the cc: list, if it shows up in a From:
- line.
+--suppress-from, --no-suppress-from::
+ If this is set, do not add the From: address to the cc: list, if it
+ shows up in a From: line.
+ Default is the value of 'sendemail.suppressfrom' configuration value;
+ if that is unspecified, default to --no-supress-from.
+
+--thread, --no-thread::
+ If this is set, the In-Reply-To header will be set on each email sent.
+ If disabled with "--no-thread", no emails will have the In-Reply-To
+ header set.
+ Default is the value of the 'sendemail.thread' configuration value;
+ if that is unspecified, default to --thread.
--dry-run::
Do everything except actually send the emails.
@@ -137,4 +148,3 @@ Documentation by Ryan Anderson
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-sh-setup.txt b/Documentation/git-sh-setup.txt
index 2b2abebd60..1ea1faa1b5 100644
--- a/Documentation/git-sh-setup.txt
+++ b/Documentation/git-sh-setup.txt
@@ -69,4 +69,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-shell.txt b/Documentation/git-shell.txt
index 228b9f14f3..48f2d57b7b 100644
--- a/Documentation/git-shell.txt
+++ b/Documentation/git-shell.txt
@@ -32,4 +32,3 @@ Documentation by Petr Baudis and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index 15cc6f77c1..2220ef6ea8 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -56,4 +56,3 @@ Documentation by Junio C Hamano.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-show-index.txt b/Documentation/git-show-index.txt
index be09b62beb..764d99356b 100644
--- a/Documentation/git-show-index.txt
+++ b/Documentation/git-show-index.txt
@@ -32,4 +32,3 @@ Documentation by Junio C Hamano
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index 34c5caf2d0..a42e121150 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -84,4 +84,3 @@ This manual page is a stub. You can help the git documentation by expanding it.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-ssh-fetch.txt b/Documentation/git-ssh-fetch.txt
index 192b1f15a9..8d3e2ffb2c 100644
--- a/Documentation/git-ssh-fetch.txt
+++ b/Documentation/git-ssh-fetch.txt
@@ -13,6 +13,8 @@ SYNOPSIS
DESCRIPTION
-----------
+THIS COMMAND IS DEPRECATED.
+
Pulls from a remote repository over ssh connection, invoking
git-ssh-upload on the other end. It functions identically to
git-ssh-upload, aside from which end you run it on.
@@ -48,4 +50,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-ssh-upload.txt b/Documentation/git-ssh-upload.txt
index a9b7e9f974..5e2ca8dccf 100644
--- a/Documentation/git-ssh-upload.txt
+++ b/Documentation/git-ssh-upload.txt
@@ -12,6 +12,8 @@ SYNOPSIS
DESCRIPTION
-----------
+THIS COMMAND IS DEPRECATED.
+
Pushes from a remote repository over ssh connection, invoking
git-ssh-fetch on the other end. It functions identically to
git-ssh-fetch, aside from which end you run it on.
@@ -44,4 +46,3 @@ Documentation by Daniel Barkalow
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
new file mode 100644
index 0000000000..05f40cff6c
--- /dev/null
+++ b/Documentation/git-stash.txt
@@ -0,0 +1,165 @@
+git-stash(1)
+============
+
+NAME
+----
+git-stash - Stash the changes in a dirty working directory away
+
+SYNOPSIS
+--------
+[verse]
+'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
+'git-stash' [save] [message...]
+
+DESCRIPTION
+-----------
+
+Use 'git-stash' when you want to record the current state of the
+working directory and the index, but want to go back to a clean
+working directory. The command saves your local modifications away
+and reverts the working directory to match the `HEAD` commit.
+
+The modifications stashed away by this command can be listed with
+`git-stash list`, inspected with `git-stash show`, and restored
+(potentially on top of a different commit) with `git-stash apply`.
+Calling git-stash without any arguments is equivalent to `git-stash
+save`. A stash is by default listed as "WIP on 'branchname' ...", but
+you can give a more descriptive message on the command line when
+you create one.
+
+The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
+stashes are found in the reflog of this reference and can be named using
+the usual reflog syntax (e.g. `stash@\{0}` is the most recently
+created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}`
+is also possible).
+
+OPTIONS
+-------
+
+save::
+
+ Save your local modifications to a new 'stash', and run `git-reset
+ --hard` to revert them. This is the default action when no
+ subcommand is given.
+
+list::
+
+ List the stashes that you currently have. Each 'stash' is listed
+ with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is
+ the one before, etc.), the name of the branch that was current when the
+ stash was made, and a short description of the commit the stash was
+ based on.
++
+----------------------------------------------------------------
+stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
+stash@{1}: On master: 9cc0589... Add git-stash
+----------------------------------------------------------------
+
+show [<stash>]::
+
+ Show the changes recorded in the stash as a diff between the the
+ stashed state and its original parent. When no `<stash>` is given,
+ shows the latest one. By default, the command shows the diffstat, but
+ it will accept any format known to `git-diff` (e.g., `git-stash show
+ -p stash@\{1}` to view the second most recent stash in patch form).
+
+apply [<stash>]::
+
+ Restore the changes recorded in the stash on top of the current
+ working tree state. When no `<stash>` is given, applies the latest
+ one. The working directory must match the index.
++
+This operation can fail with conflicts; you need to resolve them
+by hand in the working tree.
+
+clear::
+ Remove all the stashed states. Note that those states will then
+ be subject to pruning, and may be difficult or impossible to recover.
+
+
+DISCUSSION
+----------
+
+A stash is represented as a commit whose tree records the state of the
+working directory, and its first parent is the commit at `HEAD` when
+the stash was created. The tree of the second parent records the
+state of the index when the stash is made, and it is made a child of
+the `HEAD` commit. The ancestry graph looks like this:
+
+ .----W
+ / /
+ -----H----I
+
+where `H` is the `HEAD` commit, `I` is a commit that records the state
+of the index, and `W` is a commit that records the state of the working
+tree.
+
+
+EXAMPLES
+--------
+
+Pulling into a dirty tree::
+
+When you are in the middle of something, you learn that there are
+upstream changes that are possibly relevant to what you are
+doing. When your local changes do not conflict with the changes in
+the upstream, a simple `git pull` will let you move forward.
++
+However, there are cases in which your local changes do conflict with
+the upstream changes, and `git pull` refuses to overwrite your
+changes. In such a case, you can stash your changes away,
+perform a pull, and then unstash, like this:
++
+----------------------------------------------------------------
+$ git pull
+...
+file foobar not up to date, cannot merge.
+$ git stash
+$ git pull
+$ git stash apply
+----------------------------------------------------------------
+
+Interrupted workflow::
+
+When you are in the middle of something, your boss comes in and
+demands that you fix something immediately. Traditionally, you would
+make a commit to a temporary branch to store your changes away, and
+return to your original branch to make the emergency fix, like this:
++
+----------------------------------------------------------------
+... hack hack hack ...
+$ git checkout -b my_wip
+$ git commit -a -m "WIP"
+$ git checkout master
+$ edit emergency fix
+$ git commit -a -m "Fix in a hurry"
+$ git checkout my_wip
+$ git reset --soft HEAD^
+... continue hacking ...
+----------------------------------------------------------------
++
+You can use `git-stash` to simplify the above, like this:
++
+----------------------------------------------------------------
+... hack hack hack ...
+$ git stash
+$ edit emergency fix
+$ git commit -a -m "Fix in a hurry"
+$ git stash apply
+... continue hacking ...
+----------------------------------------------------------------
+
+SEE ALSO
+--------
+gitlink:git-checkout[1],
+gitlink:git-commit[1],
+gitlink:git-reflog[1],
+gitlink:git-reset[1]
+
+AUTHOR
+------
+Written by Nanako Shiraishi <nanako3@bluebottle.com>
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 1fd1af102a..8fd0fc6236 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -27,6 +27,13 @@ The command takes the same set of options as `git-commit`; it
shows what would be committed if the same options are given to
`git-commit`.
+If any paths have been touched in the working tree (that is,
+their modification times have changed) but their contents and
+permissions are identical to those in the index file, the command
+updates the index file. Running `git-status` can thus speed up
+subsequent operations such as `git-diff` if the working tree
+contains many paths that have been touched but not modified.
+
OUTPUT
------
@@ -58,4 +65,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt
index 3a03dd0410..5212358306 100644
--- a/Documentation/git-stripspace.txt
+++ b/Documentation/git-stripspace.txt
@@ -8,7 +8,7 @@ git-stripspace - Filter out empty lines
SYNOPSIS
--------
-'git-stripspace' < <stream>
+'git-stripspace' [-s | --strip-comments] < <stream>
DESCRIPTION
-----------
@@ -16,6 +16,9 @@ Remove multiple empty lines, and empty lines at beginning and end.
OPTIONS
-------
+-s\|--strip-comments::
+ In addition to empty lines, also strip lines starting with '#'.
+
<stream>::
Byte stream to act on.
@@ -30,4 +33,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
new file mode 100644
index 0000000000..2c48936fcd
--- /dev/null
+++ b/Documentation/git-submodule.txt
@@ -0,0 +1,77 @@
+git-submodule(1)
+================
+
+NAME
+----
+git-submodule - Initialize, update or inspect submodules
+
+
+SYNOPSIS
+--------
+[verse]
+'git-submodule' [--quiet] [-b branch] add <repository> [<path>]
+'git-submodule' [--quiet] [--cached] [status|init|update] [--] [<path>...]
+
+
+COMMANDS
+--------
+add::
+ Add the given repository as a submodule at the given path
+ to the changeset to be committed next. In particular, the
+ repository is cloned at the specified path, added to the
+ changeset and registered in .gitmodules. If no path is
+ specified, the path is deduced from the repository specification.
+
+status::
+ Show the status of the submodules. This will print the SHA-1 of the
+ currently checked out commit for each submodule, along with the
+ submodule path and the output of gitlink:git-describe[1] for the
+ SHA-1. Each SHA-1 will be prefixed with `-` if the submodule is not
+ initialized and `+` if the currently checked out submodule commit
+ does not match the SHA-1 found in the index of the containing
+ repository. This command is the default command for git-submodule.
+
+init::
+ Initialize the submodules, i.e. register in .git/config each submodule
+ name and url found in .gitmodules. The key used in .git/config is
+ `submodule.$name.url`. This command does not alter existing information
+ in .git/config.
+
+update::
+ Update the registered submodules, i.e. clone missing submodules and
+ checkout the commit specified in the index of the containing repository.
+ This will make the submodules HEAD be detached.
+
+
+OPTIONS
+-------
+-q, --quiet::
+ Only print error messages.
+
+-b, --branch::
+ Branch of repository to add as submodule.
+
+--cached::
+ Display the SHA-1 stored in the index, not the SHA-1 of the currently
+ checked out submodule commit. This option is only valid for the
+ status command.
+
+<path>::
+ Path to submodule(s). When specified this will restrict the command
+ to only operate on the submodules found at the specified paths.
+
+FILES
+-----
+When initializing submodules, a .gitmodules file in the top-level directory
+of the containing repository is used to find the url of each submodule.
+This file should be formatted in the same way as $GIR_DIR/config. The key
+to each submodule url is "submodule.$name.url".
+
+
+AUTHOR
+------
+Written by Lars Hjemli <hjemli@gmail.com>
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index c0d7d9597b..816340b944 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -66,9 +66,10 @@ COMMANDS
to the names of remotes if trunk/branches/tags are
specified. The prefix does not automatically include a
trailing slash, so be sure you include one in the
- argument if that is what you want. This is useful if
- you wish to track multiple projects that share a common
- repository.
+ argument if that is what you want. If --branches/-b is
+ specified, the prefix must include a trailing slash.
+ Setting a prefix is useful if you wish to track multiple
+ projects that share a common repository.
'fetch'::
Fetch unfetched revisions from the Subversion remote we are
@@ -434,6 +435,26 @@ Tracking and contributing to an entire Subversion-managed project
# of dcommit/rebase/show-ignore should be the same as above.
------------------------------------------------------------------------
+The initial 'git-svn clone' can be quite time-consuming
+(especially for large Subversion repositories). If multiple
+people (or one person with multiple machines) want to use
+git-svn to interact with the same Subversion repository, you can
+do the initial 'git-svn clone' to a repository on a server and
+have each person clone that repository with 'git clone':
+
+------------------------------------------------------------------------
+# Do the initial import on a server
+ ssh server "cd /pub && git-svn clone http://svn.foo.org/project
+# Clone locally
+ git clone server:/pub/project
+# Tell git-svn which branch contains the Subversion commits
+ git update-ref refs/remotes/git-svn origin/master
+# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
+ git-svn init http://svn.foo.org/project
+# Pull the latest changes from Subversion
+ git-svn rebase
+------------------------------------------------------------------------
+
REBASE VS. PULL/MERGE
---------------------
diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index bdae7d87dc..e97d15e8f2 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -174,4 +174,3 @@ Documentation by Matthias Urlichs <smurf@smurf.noris.de>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 4e3e02756c..119117f0bd 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -11,8 +11,8 @@ SYNOPSIS
[verse]
'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <name> [<head>]
'git-tag' -d <name>...
-'git-tag' -l [<pattern>]
-'git-tag' -v <name>
+'git-tag' [-n [<num>]] -l [<pattern>]
+'git-tag' -v <name>...
DESCRIPTION
-----------
@@ -23,7 +23,7 @@ Unless `-f` is given, the tag must not yet exist in
If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
creates a 'tag' object, and requires the tag message. Unless
-`-m <msg>` is given, an editor is started for the user to type
+`-m <msg>` or `-F <file>` is given, an editor is started for the user to type
in the tag message.
Otherwise just the SHA1 object name of the commit object is
@@ -38,8 +38,8 @@ GnuPG key for signing.
`-v <tag>` verifies the gpg signature of the tag.
-`-l <pattern>` lists tags that match the given pattern (or all
-if no pattern is given).
+`-l <pattern>` lists tags with names that match the given pattern
+(or all if no pattern is given).
OPTIONS
-------
@@ -59,10 +59,17 @@ OPTIONS
Delete existing tags with the given names.
-v::
- Verify the gpg signature of given the tag
+ Verify the gpg signature of the given tag names.
+
+-n <num>::
+ <num> specifies how many lines from the annotation, if any,
+ are printed when using -l.
+ The default is not to print any annotation lines.
+ If no number is given to `-n`, only the first line is printed.
-l <pattern>::
- List tags that match the given pattern (or all if no pattern is given).
+ List tags with names that match the given pattern (or all if no pattern is given).
+ Typing "git tag" without arguments, also lists all tags.
-m <msg>::
Use the given tag message (instead of prompting)
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
index 7bde73b1b8..2d01d9666f 100644
--- a/Documentation/git-tar-tree.txt
+++ b/Documentation/git-tar-tree.txt
@@ -90,4 +90,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-unpack-file.txt b/Documentation/git-unpack-file.txt
index 213dc8196b..20bb6a7800 100644
--- a/Documentation/git-unpack-file.txt
+++ b/Documentation/git-unpack-file.txt
@@ -33,4 +33,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt
index b1b3ec9772..d529a43f55 100644
--- a/Documentation/git-unpack-objects.txt
+++ b/Documentation/git-unpack-objects.txt
@@ -52,4 +52,3 @@ Documentation by Junio C Hamano
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 6cfbd9a842..0a1953803e 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -56,7 +56,7 @@ OPTIONS
--unmerged::
If --refresh finds unmerged changes in the index, the default
- behavior is to error out. This option makes git-update-index
+ behavior is to error out. This option makes git-update-index
continue anyway.
--ignore-missing::
@@ -64,12 +64,12 @@ OPTIONS
--cacheinfo <mode> <object> <path>::
Directly insert the specified info into the index.
-
+
--index-info::
Read index information from stdin.
--chmod=(+|-)x::
- Set the execute permissions on the updated files.
+ Set the execute permissions on the updated files.
--assume-unchanged, --no-assume-unchanged::
When these flags are specified, the object name recorded
@@ -126,7 +126,7 @@ OPTIONS
<file>::
Files to act on.
Note that files beginning with '.' are discarded. This includes
- `./file` and `dir/./file`. If you don't want this, then use
+ `./file` and `dir/./file`. If you don't want this, then use
cleaner names.
The same applies to directories ending '/' and paths with '//'
@@ -324,4 +324,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 9424feab32..f222616591 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -7,7 +7,7 @@ git-update-ref - Update the object name stored in a ref safely
SYNOPSIS
--------
-'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>])
+'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | [--no-deref] <ref> <newvalue> [<oldvalue>])
DESCRIPTION
-----------
@@ -36,6 +36,9 @@ them and update them as a regular file (i.e. it will allow the
filesystem to follow them, but will overwrite such a symlink to
somewhere else with a regular filename).
+If --no-deref is given, <ref> itself is overwritten, rather than
+the result of following the symbolic pointers.
+
In general, using
git-update-ref HEAD "$head"
diff --git a/Documentation/git-update-server-info.txt b/Documentation/git-update-server-info.txt
index 88a03c7c5e..e7e82a31ea 100644
--- a/Documentation/git-update-server-info.txt
+++ b/Documentation/git-update-server-info.txt
@@ -55,4 +55,3 @@ Documentation by Junio C Hamano.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index 9b0de1c111..813942368b 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -62,4 +62,3 @@ Documentation by Eric Biederman and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index 7a6132b016..f4c540f39b 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -51,4 +51,3 @@ Documentation by Junio C Hamano
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-verify-tag.txt b/Documentation/git-verify-tag.txt
index 0f9bdb58dc..ac7fb19154 100644
--- a/Documentation/git-verify-tag.txt
+++ b/Documentation/git-verify-tag.txt
@@ -3,11 +3,11 @@ git-verify-tag(1)
NAME
----
-git-verify-tag - Check the GPG signature of tag
+git-verify-tag - Check the GPG signature of tags
SYNOPSIS
--------
-'git-verify-tag' <tag>
+'git-verify-tag' <tag>...
DESCRIPTION
-----------
@@ -29,4 +29,3 @@ Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index 399bff3bbc..607df48f09 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -78,4 +78,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git-write-tree.txt b/Documentation/git-write-tree.txt
index 96d5e07b11..cb8d6aadeb 100644
--- a/Documentation/git-write-tree.txt
+++ b/Documentation/git-write-tree.txt
@@ -47,4 +47,3 @@ Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 98860af045..18f8b6a0a1 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
- [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
+ [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
+ [--help] COMMAND [ARGS]
DESCRIPTION
-----------
@@ -41,9 +42,13 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.5.2/git.html[documentation for release 1.5.2]
+* link:v1.5.2.4/git.html[documentation for release 1.5.2.4]
* release notes for
+ link:RelNotes-1.5.2.4.txt[1.5.2.4],
+ link:RelNotes-1.5.2.3.txt[1.5.2.3],
+ link:RelNotes-1.5.2.2.txt[1.5.2.2],
+ link:RelNotes-1.5.2.1.txt[1.5.2.1],
link:RelNotes-1.5.2.txt[1.5.2].
* link:v1.5.1.6/git.html[documentation for release 1.5.1.6]
@@ -101,6 +106,14 @@ OPTIONS
Set the path to the repository. This can also be controlled by
setting the GIT_DIR environment variable.
+--work-tree=<path>::
+ Set the path to the working tree. The value will not be
+ used in combination with repositories found automatically in
+ a .git directory (i.e. $GIT_DIR is not set).
+ This can also be controlled by setting the GIT_WORK_TREE
+ environment variable and the core.worktree configuration
+ variable.
+
--bare::
Same as --git-dir=`pwd`.
@@ -345,6 +358,13 @@ git so take care if using Cogito etc.
specifies a path to use instead of the default `.git`
for the base of the repository.
+'GIT_WORK_TREE'::
+ Set the path to the working tree. The value will not be
+ used in combination with repositories found automatically in
+ a .git directory (i.e. $GIT_DIR is not set).
+ This can also be controlled by the '--work-tree' command line
+ option and the core.worktree configuration variable.
+
git Commits
~~~~~~~~~~~
'GIT_AUTHOR_NAME'::
@@ -391,8 +411,41 @@ parameter, <path>.
other
~~~~~
+'GIT_MERGE_VERBOSITY'::
+ A number controlling the amount of output shown by
+ the recursive merge strategy. Overrides merge.verbosity.
+ See gitlink:git-merge[1]
+
'GIT_PAGER'::
- This environment variable overrides `$PAGER`.
+ This environment variable overrides `$PAGER`. If it is set
+ to an empty string or to the value "cat", git will not launch
+ a pager.
+
+'GIT_SSH'::
+ If this environment variable is set then gitlink:git-fetch[1]
+ and gitlink:git-push[1] will use this command instead
+ of `ssh` when they need to connect to a remote system.
+ The 'GIT_SSH' command will be given exactly two arguments:
+ the 'username@host' (or just 'host') from the URL and the
+ shell command to execute on that remote system.
++
+To pass options to the program that you want to list in GIT_SSH
+you will need to wrap the program and options into a shell script,
+then set GIT_SSH to refer to the shell script.
++
+Usually it is easier to configure any desired options through your
+personal `.ssh/config` file. Please consult your ssh documentation
+for further details.
+
+'GIT_FLUSH'::
+ If this environment variable is set to "1", then commands such
+ as git-blame (in incremental mode), git-rev-list, git-log,
+ git-whatchanged, etc., will force a flush of the output stream
+ after each commit-oriented record have been flushed. If this
+ variable is set to "0", the output of these commands will be done
+ using completely buffered I/O. If this environment variable is
+ not set, git will choose buffered or record-oriented flushing
+ based on whether stdout appears to be redirected to a file or not.
'GIT_TRACE'::
If this variable is set to "1", "2" or "true" (comparison
@@ -428,4 +481,3 @@ contributors on the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index d3ac9c7181..8b90a5b980 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -72,8 +72,8 @@ EFFECTS
-------
Certain operations by git can be influenced by assigning
-particular attributes to a path. Currently, three operations
-are attributes-aware.
+particular attributes to a path. Currently, the following
+operations are attributes-aware.
Checking-out and checking-in
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -199,7 +199,9 @@ Generating diff text
~~~~~~~~~~~~~~~~~~~~
The attribute `diff` affects if `git diff` generates textual
-patch for the path or just says `Binary files differ`.
+patch for the path or just says `Binary files differ`. It also
+can affect what line is shown on the hunk header `@@ -k,l +n,m @@`
+line.
Set::
@@ -224,7 +226,8 @@ String::
Diff is shown using the specified custom diff driver.
The driver program is given its input using the same
calling convention as used for GIT_EXTERNAL_DIFF
- program.
+ program. This name is also used for custom hunk header
+ selection.
Defining a custom diff driver
@@ -249,6 +252,50 @@ parameters, just like `GIT_EXTERNAL_DIFF` program is called.
See gitlink:git[7] for details.
+Defining a custom hunk-header
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Each group of changes (called "hunk") in the textual diff output
+is prefixed with a line of the form:
+
+ @@ -k,l +n,m @@ TEXT
+
+The text is called 'hunk header', and by default a line that
+begins with an alphabet, an underscore or a dollar sign is used,
+which matches what GNU `diff -p` output uses. This default
+selection however is not suited for some contents, and you can
+use customized pattern to make a selection.
+
+First in .gitattributes, you would assign the `diff` attribute
+for paths.
+
+------------------------
+*.tex diff=tex
+------------------------
+
+Then, you would define "diff.tex.funcname" configuration to
+specify a regular expression that matches a line that you would
+want to appear as the hunk header, like this:
+
+------------------------
+[diff "tex"]
+ funcname = "^\\(\\\\\\(sub\\)*section{.*\\)$"
+------------------------
+
+Note. A single level of backslashes are eaten by the
+configuration file parser, so you would need to double the
+backslashes; the pattern above picks a line that begins with a
+backslash, and zero or more occurences of `sub` followed by
+`section` followed by open brace, to the end of line.
+
+There are a few built-in patterns to make this easier, and `tex`
+is one of them, so you do not have to write the above in your
+configuration file (you still need to enable this with the
+attribute mechanism, via `.gitattributes`). Another built-in
+pattern is defined for `java` that defines a pattern suitable
+for program text in Java language.
+
+
Performing a three-way merge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -358,7 +405,7 @@ the attributes given to path `t/abc` are computed as follows:
and `bar` attributes should be given to this path, so it
leaves `foo` and `bar` unset. Attribute `baz` is set.
-3. Finally it examines `$GIT_DIR/info/gitattributes`. This file
+3. Finally it examines `$GIT_DIR/info/attributes`. This file
is used to override the in-tree settings. The first line is
a match, and `foo` is set, `bar` is reverted to unspecified
state, and `baz` is unset.
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index ea79d74b88..9c83095693 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -18,21 +18,26 @@ pattern.
When deciding whether to ignore a path, git normally checks
`gitignore` patterns from multiple sources, with the following
-order of precedence:
+order of precedence, from highest to lowest (within one level of
+precedence, the last matching pattern decides the outcome):
- * Patterns read from the file specified by the configuration
- variable 'core.excludesfile'.
-
- * Patterns read from `$GIT_DIR/info/exclude`.
+ * Patterns read from the command line for those commands that support
+ them.
* Patterns read from a `.gitignore` file in the same directory
- as the path, or in any parent directory, ordered from the
- deepest such file to a file in the root of the repository.
+ as the path, or in any parent directory, with patterns in the
+ higher level files (up to the root) being overriden by those in
+ lower level files down to the directory containing the file.
These patterns match relative to the location of the
`.gitignore` file. A project normally includes such
`.gitignore` files in its repository, containing patterns for
files generated as part of the project build.
+ * Patterns read from `$GIT_DIR/info/exclude`.
+
+ * Patterns read from the file specified by the configuration
+ variable 'core.excludesfile'.
+
The underlying git plumbing tools, such as
gitlink:git-ls-files[1] and gitlink:git-read-tree[1], read
`gitignore` patterns specified by command-line options, or from
@@ -49,7 +54,8 @@ Patterns have the following format:
- An optional prefix '!' which negates the pattern; any
matching file excluded by a previous pattern will become
- included again.
+ included again. If a negated pattern matches, this will
+ override lower precedence patterns sources.
- If the pattern does not contain a slash '/', git treats it as
a shell glob pattern and checks for a match against the
diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt
index 48c5894736..e9f82b97b9 100644
--- a/Documentation/gitk.txt
+++ b/Documentation/gitk.txt
@@ -99,4 +99,3 @@ Documentation by Junio C Hamano, Jonas Fonseca, and the git-list
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
new file mode 100644
index 0000000000..035294e208
--- /dev/null
+++ b/Documentation/gitmodules.txt
@@ -0,0 +1,62 @@
+gitmodules(5)
+=============
+
+NAME
+----
+gitmodules - defining submodule properties
+
+SYNOPSIS
+--------
+gitmodules
+
+
+DESCRIPTION
+-----------
+
+The `.gitmodules` file, located in the top-level directory of a git
+working tree, is a text file with a syntax matching the requirements
+of gitlink:git-config[1].
+
+The file contains one subsection per submodule, and the subsection value
+is the name of the submodule. Each submodule section also contains the
+following required keys:
+
+submodule.<name>.path::
+ Defines the path, relative to the top-level directory of the git
+ working tree, where the submodule is expected to be checked out.
+ The path name must not end with a `/`. All submodule paths must
+ be unique within the .gitmodules file.
+
+submodule.<name>.url::
+ Defines an url from where the submodule repository can be cloned.
+
+
+EXAMPLES
+--------
+
+Consider the following .gitmodules file:
+
+ [submodule "libfoo"]
+ path = include/foo
+ url = git://foo.com/git/lib.git
+
+ [submodule "libbar"]
+ path = include/bar
+ url = git://bar.com/git/lib.git
+
+
+This defines two submodules, `libfoo` and `libbar`. These are expected to
+be checked out in the paths 'include/foo' and 'include/bar', and for both
+submodules an url is specified which can be used for cloning the submodules.
+
+SEE ALSO
+--------
+gitlink:git-submodule[1] gitlink:git-config[1]
+
+DOCUMENTATION
+-------------
+Documentation by Lars Hjemli <hjemli@gmail.com>
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt
index aabb9750fd..6836477ca8 100644
--- a/Documentation/hooks.txt
+++ b/Documentation/hooks.txt
@@ -12,11 +12,10 @@ This document describes the currently defined hooks.
applypatch-msg
--------------
-This hook is invoked by `git-applypatch` script, which is
-typically invoked by `git-applymbox`. It takes a single
+This hook is invoked by `git-am` script. It takes a single
parameter, the name of the file that holds the proposed commit
log message. Exiting with non-zero status causes
-`git-applypatch` to abort before applying the patch.
+`git-am` to abort before applying the patch.
The hook is allowed to edit the message file in place, and can
be used to normalize the message into some project standard
@@ -29,8 +28,7 @@ The default 'applypatch-msg' hook, when enabled, runs the
pre-applypatch
--------------
-This hook is invoked by `git-applypatch` script, which is
-typically invoked by `git-applymbox`. It takes no parameter,
+This hook is invoked by `git-am`. It takes no parameter,
and is invoked after the patch is applied, but before a commit
is made. Exiting with non-zero status causes the working tree
after application of the patch not committed.
@@ -44,12 +42,11 @@ The default 'pre-applypatch' hook, when enabled, runs the
post-applypatch
---------------
-This hook is invoked by `git-applypatch` script, which is
-typically invoked by `git-applymbox`. It takes no parameter,
+This hook is invoked by `git-am`. It takes no parameter,
and is invoked after the patch is applied and a commit is made.
This hook is meant primarily for notification, and cannot affect
-the outcome of `git-applypatch`.
+the outcome of `git-am`.
pre-commit
----------
diff --git a/Documentation/howto/rebase-and-edit.txt b/Documentation/howto/rebase-and-edit.txt
index 646c55cc69..554909fe08 100644
--- a/Documentation/howto/rebase-and-edit.txt
+++ b/Documentation/howto/rebase-and-edit.txt
@@ -9,16 +9,16 @@ Abstract: In this article, Linus demonstrates how a broken commit
On Sat, 13 Aug 2005, Linus Torvalds wrote:
-> That's correct. Same things apply: you can move a patch over, and create a
-> new one with a modified comment, but basically the _old_ commit will be
+> That's correct. Same things apply: you can move a patch over, and create a
+> new one with a modified comment, but basically the _old_ commit will be
> immutable.
Let me clarify.
You can entirely _drop_ old branches, so commits may be immutable, but
-nothing forces you to keep them. Of course, when you drop a commit, you'll
-always end up dropping all the commits that depended on it, and if you
-actually got somebody else to pull that commit you can't drop it from
+nothing forces you to keep them. Of course, when you drop a commit, you'll
+always end up dropping all the commits that depended on it, and if you
+actually got somebody else to pull that commit you can't drop it from
_their_ repository, but undoing things is not impossible.
For example, let's say that you've made a mess of things: you've committed
@@ -29,7 +29,7 @@ want to save "b" and "c". What you can do is
# for reference
git branch broken
- # Reset the main branch to three parents back: this
+ # Reset the main branch to three parents back: this
# effectively undoes the three top commits
git reset HEAD^^^
git checkout -f
@@ -59,7 +59,7 @@ Finally, check out the end result again:
to see that everything looks sensible.
-And then, you can just remove the broken branch if you decide you really
+And then, you can just remove the broken branch if you decide you really
don't want it:
# remove 'broken' branch
@@ -68,8 +68,8 @@ don't want it:
# Prune old objects if you're really really sure
git prune
-And yeah, I'm sure there are other ways of doing this. And as usual, the
-above is totally untested, and I just wrote it down in this email, so if
+And yeah, I'm sure there are other ways of doing this. And as usual, the
+above is totally untested, and I just wrote it down in this email, so if
I've done something wrong, you'll have to figure it out on your own ;)
Linus
@@ -77,5 +77,3 @@ I've done something wrong, you'll have to figure it out on your own ;)
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
-
-
diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt
index 3b3a5c2e69..7a76045eb7 100644
--- a/Documentation/howto/rebase-from-internal-branch.txt
+++ b/Documentation/howto/rebase-from-internal-branch.txt
@@ -14,10 +14,10 @@ Petr Baudis <pasky@suse.cz> writes:
> Dear diary, on Sun, Aug 14, 2005 at 09:57:13AM CEST, I got a letter
> where Junio C Hamano <junkio@cox.net> told me that...
>> Linus Torvalds <torvalds@osdl.org> writes:
->>
->> > Junio, maybe you want to talk about how you move patches from your "pu"
+>>
+>> > Junio, maybe you want to talk about how you move patches from your "pu"
>> > branch to the real branches.
->>
+>>
> Actually, wouldn't this be also precisely for what StGIT is intended to?
Exactly my feeling. I was sort of waiting for Catalin to speak
@@ -118,7 +118,7 @@ up your changes, along with other changes.
where *your "master" head
upstream --> #1 --> #2 --> #3
- used \
+ used \
to be \--> #A --> #2' --> #3' --> #B --> #C
*upstream head
@@ -133,7 +133,7 @@ You fetch from upstream, but not merge.
$ git fetch upstream
This leaves the updated upstream head in .git/FETCH_HEAD but
-does not touch your .git/HEAD nor .git/refs/heads/master.
+does not touch your .git/HEAD nor .git/refs/heads/master.
You run "git rebase" now.
$ git rebase FETCH_HEAD master
@@ -161,5 +161,3 @@ the #1' commit.
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
-
-
diff --git a/Documentation/howto/rebuild-from-update-hook.txt b/Documentation/howto/rebuild-from-update-hook.txt
index 02621b54a0..8d55dfbfae 100644
--- a/Documentation/howto/rebuild-from-update-hook.txt
+++ b/Documentation/howto/rebuild-from-update-hook.txt
@@ -84,4 +84,3 @@ There are four things worth mentioning:
- This is still crude and does not protect against simultaneous
make invocations stomping on each other. I would need to add
some locking mechanism for this.
-
diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt
index d88ec23a97..865a666324 100644
--- a/Documentation/howto/revert-branch-rebase.txt
+++ b/Documentation/howto/revert-branch-rebase.txt
@@ -146,7 +146,7 @@ Everything is in the good order. I do not need the temporary branch
nor tag anymore, so remove them:
------------------------------------------------
-$ rm -f .git/refs/tags/pu-anchor
+$ rm -f .git/refs/tags/pu-anchor
$ git branch -d revert-c99
------------------------------------------------
diff --git a/Documentation/howto/separating-topic-branches.txt b/Documentation/howto/separating-topic-branches.txt
index 090e2c9b01..0d73b31224 100644
--- a/Documentation/howto/separating-topic-branches.txt
+++ b/Documentation/howto/separating-topic-branches.txt
@@ -12,7 +12,7 @@ up with a history like this:
"master"
o---o
- \ "topic"
+ \ "topic"
o---o---o---o---o---o
At this point, "topic" contains something I know I want, but it
@@ -29,11 +29,11 @@ start building on top of "master":
$ git checkout -b topicA master
... pick and apply pieces from P.diff to build
... commits on topicA branch.
-
+
o---o---o
/ "topicA"
o---o"master"
- \ "topic"
+ \ "topic"
o---o---o---o---o---o
Before doing each commit on "topicA" HEAD, I run "diff HEAD"
@@ -59,7 +59,7 @@ other topic:
/o---o---o
|/ "topicA"
o---o"master"
- \ "topic"
+ \ "topic"
o---o---o---o---o---o
After I am done, I'd try a pretend-merge between "topicA" and
@@ -73,7 +73,7 @@ After I am done, I'd try a pretend-merge between "topicA" and
/o---o---o----------'
|/ "topicA"
o---o"master"
- \ "topic"
+ \ "topic"
o---o---o---o---o---o
The last diff better not to show anything other than cleanups
@@ -84,8 +84,7 @@ for crufts. Then I can finally clean things up:
"topicB"
o---o---o---o---o
- /
+ /
/o---o---o
|/ "topicA"
o---o"master"
-
diff --git a/Documentation/howto/use-git-daemon.txt b/Documentation/howto/use-git-daemon.txt
index 1a1eb246bf..4e2f75cb61 100644
--- a/Documentation/howto/use-git-daemon.txt
+++ b/Documentation/howto/use-git-daemon.txt
@@ -49,4 +49,3 @@ Now, test your daemon with
$ git ls-remote git://127.0.0.1/rule-the-world.git
If this does not work, find out why, and submit a patch to this document.
-
diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh
index a64054948a..5433cf8ced 100755
--- a/Documentation/install-doc-quick.sh
+++ b/Documentation/install-doc-quick.sh
@@ -7,10 +7,10 @@ mandir="$2"
SUBDIRECTORY_OK=t
USAGE='<refname> <target directory>'
. git-sh-setup
-export GIT_DIR
+cd_to_toplevel
test -z "$mandir" && usage
-if ! git-rev-parse --verify "$head^0" >/dev/null; then
+if ! git rev-parse --verify "$head^0" >/dev/null; then
echo >&2 "head: $head does not exist in the current repository"
usage
fi
@@ -18,14 +18,14 @@ fi
GIT_INDEX_FILE=`pwd`/.quick-doc.index
export GIT_INDEX_FILE
rm -f "$GIT_INDEX_FILE"
-git-read-tree $head
-git-checkout-index -a -f --prefix="$mandir"/
+trap 'rm -f "$GIT_INDEX_FILE"' 0
+
+git read-tree $head
+git checkout-index -a -f --prefix="$mandir"/
if test -n "$GZ"; then
- cd "$mandir"
- for i in `git-ls-tree -r --name-only $head`
- do
- gzip < $i > $i.gz && rm $i
- done
+ git ls-tree -r --name-only $head |
+ xargs printf "$mandir/%s\n" |
+ xargs gzip -f
fi
rm -f "$GIT_INDEX_FILE"
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 182cef54be..d64c259bb3 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -1,3 +1,7 @@
+--summary::
+ Show a diffstat at the end of the merge. The diffstat is also
+ controlled by the configuration option merge.diffstat.
+
-n, \--no-summary::
Do not show diffstat at the end of the merge.
@@ -21,4 +25,3 @@
If there is no `-s` option, a built-in list of strategies
is used instead (`git-merge-recursive` when merging a single
head, `git-merge-octopus` otherwise).
-
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index d922e8e86c..0193c3ce58 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -106,12 +106,14 @@ The placeholders are:
- '%aD': author date, RFC2822 style
- '%ar': author date, relative
- '%at': author date, UNIX timestamp
+- '%ai': author date, ISO 8601 format
- '%cn': committer name
- '%ce': committer email
- '%cd': committer date
- '%cD': committer date, RFC2822 style
- '%cr': committer date, relative
- '%ct': committer date, UNIX timestamp
+- '%ci': committer date, ISO 8601 format
- '%e': encoding
- '%s': subject
- '%b': body
@@ -121,4 +123,3 @@ The placeholders are:
- '%Creset': reset color
- '%m': left, right or boundary mark
- '%n': newline
-
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 7d515be0fd..973d8dd733 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -1,9 +1,18 @@
--pretty[='<format>']::
- Pretty print the contents of the commit logs in a given format,
+ Pretty-print the contents of the commit logs in a given format,
where '<format>' can be one of 'oneline', 'short', 'medium',
'full', 'fuller', 'email', 'raw' and 'format:<string>'.
- When left out the format default to 'medium'.
+ When omitted, the format defaults to 'medium'.
+
+--abbrev-commit::
+ Instead of showing the full 40-byte hexadecimal commit object
+ name, show only handful hexdigits prefix. Non default number of
+ digits can be specified with "--abbrev=<n>" (which also modifies
+ diff output, if it is displayed).
++
+This should make "--pretty=oneline" a whole lot more readable for
+people using 80-column terminals.
--encoding[=<encoding>]::
The commit objects record the encoding used for the log message
@@ -11,4 +20,3 @@
command to re-code the commit log message in the encoding
preferred by the user. For non plumbing commands this
defaults to UTF-8.
-
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index 8d4e950abc..b6eb7fc618 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -58,7 +58,7 @@ is often useful.
+
Some short-cut notations are also supported.
+
-* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
+* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
it requests fetching everything up to the given tag.
* A parameter <ref> without a colon is equivalent to
<ref>: when pulling/fetching, so it merges <ref> into the current
diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt
index 15221b5320..4c92e375fe 100644
--- a/Documentation/repository-layout.txt
+++ b/Documentation/repository-layout.txt
@@ -177,4 +177,3 @@ shallow::
This is similar to `info/grafts` but is internally used
and maintained by shallow clone mechanism. See `--depth`
option to gitlink:git-clone[1] and gitlink:git-fetch[1].
-
diff --git a/Documentation/technical/pack-format.txt b/Documentation/technical/pack-format.txt
index 9ce3c473ae..e5b31c81fa 100644
--- a/Documentation/technical/pack-format.txt
+++ b/Documentation/technical/pack-format.txt
@@ -80,7 +80,7 @@ Pack Idx file:
+--------------------------------+ |
main | offset | |
index | object name 00XXXXXXXXXXXXXXXX | |
-table +--------------------------------+ |
+table +--------------------------------+ |
| offset | |
| object name 00XXXXXXXXXXXXXXXX | |
+--------------------------------+ |
@@ -97,14 +97,14 @@ trailer | | packfile checksum |
| +--------------------------------+
| | idxfile checksum |
| +--------------------------------+
- .-------.
+ .-------.
|
Pack file entry: <+
packed object header:
1-byte size extension bit (MSB)
type (next 3 bit)
- size0 (lower 4-bit)
+ size0 (lower 4-bit)
n-byte sizeN (as long as MSB is set, each 7-bit)
size0..sizeN form 4+7+7+..+7 bit integer, size0
is the least significant part, and sizeN is the
@@ -114,5 +114,5 @@ Pack file entry: <+
is the size before compression).
If it is DELTA, then
20-byte base object name SHA1 (the size above is the
- size of the delta data that follows).
+ size of the delta data that follows).
delta data, deflated.
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 965ec00a6b..bd9fbee99a 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -354,7 +354,7 @@ used for pulls:
-------------------------------------
$ git config --get remote.origin.url
-/home/bob/myrepo
+/home/alice/project
-------------------------------------
(The complete configuration created by git-clone is visible using
diff --git a/Documentation/urls-remotes.txt b/Documentation/urls-remotes.txt
new file mode 100644
index 0000000000..5dd1f836c6
--- /dev/null
+++ b/Documentation/urls-remotes.txt
@@ -0,0 +1,55 @@
+include::urls.txt[]
+
+REMOTES
+-------
+
+In addition to the above, as a short-hand, the name of a
+file in `$GIT_DIR/remotes` directory can be given; the
+named file should be in the following format:
+
+------------
+ URL: one of the above URL format
+ Push: <refspec>
+ Pull: <refspec>
+
+------------
+
+Then such a short-hand is specified in place of
+<repository> without <refspec> parameters on the command
+line, <refspec> specified on `Push:` lines or `Pull:`
+lines are used for `git-push` and `git-fetch`/`git-pull`,
+respectively. Multiple `Push:` and `Pull:` lines may
+be specified for additional branch mappings.
+
+Or, equivalently, in the `$GIT_DIR/config` (note the use
+of `fetch` instead of `Pull:`):
+
+------------
+ [remote "<remote>"]
+ url = <url>
+ push = <refspec>
+ fetch = <refspec>
+
+------------
+
+The name of a file in `$GIT_DIR/branches` directory can be
+specified as an older notation short-hand; the named
+file should contain a single line, a URL in one of the
+above formats, optionally followed by a hash `#` and the
+name of remote head (URL fragment notation).
+`$GIT_DIR/branches/<remote>` file that stores a <url>
+without the fragment is equivalent to have this in the
+corresponding file in the `$GIT_DIR/remotes/` directory.
+
+------------
+ URL: <url>
+ Pull: refs/heads/master:<remote>
+
+------------
+
+while having `<url>#<head>` is equivalent to
+
+------------
+ URL: <url>
+ Pull: refs/heads/<head>:<remote>
+------------
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 745f9677d0..b38145faff 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -15,11 +15,11 @@ to name the remote repository:
- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
===============================================================
-SSH is the default transport protocol. You can optionally specify
-which user to log-in as, and an alternate, scp-like syntax is also
-supported. Both syntaxes support username expansion,
-as does the native git protocol. The following three are
-identical to the last three above, respectively:
+SSH is the default transport protocol over the network. You can
+optionally specify which user to log-in as, and an alternate,
+scp-like syntax is also supported. Both syntaxes support
+username expansion, as does the native git protocol. The following
+three are identical to the last three above, respectively:
===============================================================
- {startsb}user@{endsb}host.xz:/path/to/repo.git/
@@ -27,62 +27,12 @@ identical to the last three above, respectively:
- {startsb}user@{endsb}host.xz:path/to/repo.git
===============================================================
-To sync with a local directory, use:
+To sync with a local directory, you can use:
===============================================================
- /path/to/repo.git/
+- file:///path/to/repo.git/
===============================================================
-REMOTES
--------
-
-In addition to the above, as a short-hand, the name of a
-file in `$GIT_DIR/remotes` directory can be given; the
-named file should be in the following format:
-
-------------
- URL: one of the above URL format
- Push: <refspec>
- Pull: <refspec>
-
-------------
-
-Then such a short-hand is specified in place of
-<repository> without <refspec> parameters on the command
-line, <refspec> specified on `Push:` lines or `Pull:`
-lines are used for `git-push` and `git-fetch`/`git-pull`,
-respectively. Multiple `Push:` and `Pull:` lines may
-be specified for additional branch mappings.
-
-Or, equivalently, in the `$GIT_DIR/config` (note the use
-of `fetch` instead of `Pull:`):
-
-------------
- [remote "<remote>"]
- url = <url>
- push = <refspec>
- fetch = <refspec>
-
-------------
-
-The name of a file in `$GIT_DIR/branches` directory can be
-specified as an older notation short-hand; the named
-file should contain a single line, a URL in one of the
-above formats, optionally followed by a hash `#` and the
-name of remote head (URL fragment notation).
-`$GIT_DIR/branches/<remote>` file that stores a <url>
-without the fragment is equivalent to have this in the
-corresponding file in the `$GIT_DIR/remotes/` directory.
-
-------------
- URL: <url>
- Pull: refs/heads/master:<remote>
-
-------------
-
-while having `<url>#<head>` is equivalent to
-
-------------
- URL: <url>
- Pull: refs/heads/<head>:<remote>
-------------
+They are mostly equivalent, except when cloning. See
+gitlink:git-clone[1] for details.
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index d6caff4248..f89952ad84 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1,4 +1,4 @@
-Git User's Manual (for version 1.5.1 or newer)
+Git User's Manual (for version 1.5.3 or newer)
______________________________________________
@@ -154,11 +154,11 @@ Author: Jamal Hadi Salim <hadi@cyberus.ca>
Date: Sat Dec 2 22:22:25 2006 -0800
[XFRM]: Fix aevent structuring to be more complete.
-
+
aevents can not uniquely identify an SA. We break the ABI with this
patch, but consensus is that since it is not yet utilized by any
(known) application then it is fine (better do it now than later).
-
+
Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>
@@ -167,7 +167,7 @@ index 8be626f..d7aac9d 100644
--- a/Documentation/networking/xfrm_sync.txt
+++ b/Documentation/networking/xfrm_sync.txt
@@ -47,10 +47,13 @@ aevent_id structure looks like:
-
+
struct xfrm_aevent_id {
struct xfrm_usersa_id sa_id;
+ xfrm_address_t saddr;
@@ -449,7 +449,7 @@ Exploring git history
Git is best thought of as a tool for storing the history of a
collection of files. It does this by storing compressed snapshots of
-the contents of a file heirarchy, together with "commits" which show
+the contents of a file hierarchy, together with "commits" which show
the relationships between these snapshots.
Git provides extremely flexible and fast tools for exploring the
@@ -1057,7 +1057,7 @@ $ git show
-------------------------------------------------
As a special shortcut,
-
+
-------------------------------------------------
$ git commit -a
-------------------------------------------------
@@ -1070,7 +1070,7 @@ about to commit:
-------------------------------------------------
$ git diff --cached # difference between HEAD and the index; what
- # would be commited if you ran "commit" now.
+ # would be committed if you ran "commit" now.
$ git diff # difference between the index file and your
# working directory; changes that would not
# be included if you ran "commit" now.
@@ -1079,6 +1079,11 @@ $ git diff HEAD # difference between HEAD and working tree; what
$ git status # a brief per-file summary of the above.
-------------------------------------------------
+You can also use gitlink:git-gui[1] to create commits, view changes in
+the index and the working tree files, and individually select diff hunks
+for inclusion in the index (by right-clicking on the diff hunk and
+choosing "Stage Hunk For Commit").
+
[[creating-good-commit-messages]]
Creating good commit messages
-----------------------------
@@ -1257,7 +1262,7 @@ index 802992c,2b60207..0000000
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
-------------------------------------------------
-Recall that the commit which will be commited after we resolve this
+Recall that the commit which will be committed after we resolve this
conflict will have two parents instead of the usual one: one parent
will be HEAD, the tip of the current branch; the other will be the
tip of the other branch, which is stored temporarily in MERGE_HEAD.
@@ -1351,7 +1356,7 @@ away, you can always return to the pre-merge state with
$ git reset --hard HEAD
-------------------------------------------------
-Or, if you've already commited the merge that you want to throw away,
+Or, if you've already committed the merge that you want to throw away,
-------------------------------------------------
$ git reset --hard ORIG_HEAD
@@ -1484,6 +1489,38 @@ $ git show HEAD^:path/to/file
which will display the given version of the file.
+[[interrupted-work]]
+Temporarily setting aside work in progress
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+While you are in the middle of working on something complicated, you
+find an unrelated but obvious and trivial bug. You would like to fix it
+before continuing. You can use gitlink:git-stash[1] to save the current
+state of your work, and after fixing the bug (or, optionally after doing
+so on a different branch and then coming back), unstash the
+work-in-progress changes.
+
+------------------------------------------------
+$ git stash "work in progress for foo feature"
+------------------------------------------------
+
+This command will save your changes away to the `stash`, and
+reset your working tree and the index to match the tip of your
+current branch. Then you can make your fix as usual.
+
+------------------------------------------------
+... edit and test ...
+$ git commit -a -m "blorpl: typofix"
+------------------------------------------------
+
+After that, you can go back to what you were working on with
+`git stash apply`:
+
+------------------------------------------------
+$ git stash apply
+------------------------------------------------
+
+
[[ensuring-good-performance]]
Ensuring good performance
-------------------------
@@ -1555,7 +1592,7 @@ history.
Fortunately, git also keeps a log, called a "reflog", of all the
previous values of each branch. So in this case you can still find the
-old history using, for example,
+old history using, for example,
-------------------------------------------------
$ git log master@{1}
@@ -1631,7 +1668,7 @@ If you decide you want the history back, you can always create a new
reference pointing to it, for example, a new branch:
------------------------------------------------
-$ git branch recovered-branch 7281251ddd
+$ git branch recovered-branch 7281251ddd
------------------------------------------------
Other types of dangling objects (blobs and trees) are also possible, and
@@ -1667,24 +1704,19 @@ one step:
$ git pull origin master
-------------------------------------------------
-In fact, "origin" is normally the default repository to pull from,
-and the default branch is normally the HEAD of the remote repository,
-so often you can accomplish the above with just
+In fact, if you have "master" checked out, then by default "git pull"
+merges from the HEAD branch of the origin repository. So often you can
+accomplish the above with just a simple
-------------------------------------------------
$ git pull
-------------------------------------------------
-See the descriptions of the branch.<name>.remote and branch.<name>.merge
-options in gitlink:git-config[1] to learn how to control these defaults
-depending on the current branch. Also note that the --track option to
-gitlink:git-branch[1] and gitlink:git-checkout[1] can be used to
-automatically set the default remote branch to pull from at the time
-that a branch is created:
-
--------------------------------------------------
-$ git checkout --track -b maint origin/maint
--------------------------------------------------
+More generally, a branch that is created from a remote branch will pull
+by default from that branch. See the descriptions of the
+branch.<name>.remote and branch.<name>.merge options in
+gitlink:git-config[1], and the discussion of the --track option in
+gitlink:git-checkout[1], to learn how to control these defaults.
In addition to saving you keystrokes, "git pull" also helps you by
producing a default commit message documenting the branch and
@@ -1803,7 +1835,7 @@ like this:
you push
your personal repo ------------------> your public repo
- ^ |
+ ^ |
| |
| you pull | they pull
| |
@@ -2377,7 +2409,7 @@ the result would create a new merge commit, like this:
\ \
a--b--c--m <-- mywork
................................................
-
+
However, if you prefer to keep the history in mywork a simple series of
commits without any merges, you may instead choose to use
gitlink:git-rebase[1]:
@@ -2479,8 +2511,10 @@ $ gitk origin..mywork &
And browse through the list of patches in the mywork branch using gitk,
applying them (possibly in a different order) to mywork-new using
-cherry-pick, and possibly modifying them as you go using commit
---amend.
+cherry-pick, and possibly modifying them as you go using commit --amend.
+The git-gui[1] command may also help as it allows you to individually
+select diff hunks for inclusion in the index (by right-clicking on the
+diff hunk and choosing "Stage Hunk for Commit").
Another technique is to use git-format-patch to create a series of
patches, then reset the state to before the patches:
@@ -2753,7 +2787,7 @@ must have at least one root, and while you can tie several different
root objects together into one project by creating a commit object which
has two or more separate roots as its ultimate parents, that's probably
just going to confuse people. So aim for the notion of "one root object
-per project", even if git itself does not enforce that.
+per project", even if git itself does not enforce that.
A <<def_tag_object,"tag" object>> symbolically identifies and can be
used to sign other objects. It contains the identifier and type of
@@ -2972,15 +3006,15 @@ cache, and the normal operation is to re-generate it completely from a
known tree object, or update/compare it with a live tree that is being
developed. If you blow the directory cache away entirely, you generally
haven't lost any information as long as you have the name of the tree
-that it described.
+that it described.
-At the same time, the index is at the same time also the
-staging area for creating new trees, and creating a new tree always
-involves a controlled modification of the index file. In particular,
-the index file can have the representation of an intermediate tree that
-has not yet been instantiated. So the index can be thought of as a
-write-back cache, which can contain dirty information that has not yet
-been written back to the backing store.
+At the same time, the index is also the staging area for creating
+new trees, and creating a new tree always involves a controlled
+modification of the index file. In particular, the index file can
+have the representation of an intermediate tree that has not yet been
+instantiated. So the index can be thought of as a write-back cache,
+which can contain dirty information that has not yet been written back
+to the backing store.
@@ -2992,7 +3026,7 @@ Generally, all "git" operations work on the index file. Some operations
work *purely* on the index file (showing the current state of the
index), but most operations move data to and from the index file. Either
from the database or from the working directory. Thus there are four
-main combinations:
+main combinations:
[[working-directory-to-index]]
working directory -> index
@@ -3455,7 +3489,7 @@ because you interrupted a "git fetch" with ^C or something like that,
leaving _some_ of the new objects in the object database, but just
dangling and useless.
-Anyway, once you are sure that you're not interested in any dangling
+Anyway, once you are sure that you're not interested in any dangling
state, you can just prune all unreachable objects:
------------------------------------------------
@@ -3466,12 +3500,12 @@ and they'll be gone. But you should only run "git prune" on a quiescent
repository - it's kind of like doing a filesystem fsck recovery: you
don't want to do that while the filesystem is mounted.
-(The same is true of "git-fsck" itself, btw - but since
-git-fsck never actually *changes* the repository, it just reports
-on what it found, git-fsck itself is never "dangerous" to run.
-Running it while somebody is actually changing the repository can cause
-confusing and scary messages, but it won't actually do anything bad. In
-contrast, running "git prune" while somebody is actively changing the
+(The same is true of "git-fsck" itself, btw - but since
+git-fsck never actually *changes* the repository, it just reports
+on what it found, git-fsck itself is never "dangerous" to run.
+Running it while somebody is actually changing the repository can cause
+confusing and scary messages, but it won't actually do anything bad. In
+contrast, running "git prune" while somebody is actively changing the
repository is a *BAD* idea).
[[birdview-on-the-source-code]]
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index a62248deb1..3c0032cec5 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.2.5.GIT
+DEF_VER=v1.5.3.GIT
LF='
'
diff --git a/INSTALL b/INSTALL
index 361c65bacc..289b046a44 100644
--- a/INSTALL
+++ b/INSTALL
@@ -5,8 +5,8 @@ Normally you can just do "make" followed by "make install", and that
will install the git programs in your own ~/bin/ directory. If you want
to do a global install, you can do
- $ make prefix=/usr all doc ;# as yourself
- # make prefix=/usr install install-doc ;# as root
+ $ make prefix=/usr all doc info ;# as yourself
+ # make prefix=/usr install install-doc install-info ;# as root
(or prefix=/usr/local, of course). Just like any program suite
that uses $prefix, the built results have some paths encoded,
@@ -31,7 +31,7 @@ Issues of note:
interactive tools. None of the core git stuff needs the wrapper,
it's just a convenient shorthand and while it is documented in some
places, you can always replace "git commit" with "git-commit"
- instead.
+ instead.
But let's face it, most of us don't have GNU interactive tools, and
even if we had it, we wouldn't know what it does. I don't think it
@@ -56,9 +56,8 @@ Issues of note:
- "zlib", the compression library. Git won't build without it.
- - "openssl". The git-rev-list program uses bignum support from
- openssl, and unless you specify otherwise, you'll also get the
- SHA1 library from here.
+ - "openssl". Unless you specify otherwise, you'll get the SHA1
+ library from here.
If you don't have openssl, you can use one of the SHA1 libraries
that come with git (git includes the one from Mozilla, and has
@@ -73,7 +72,7 @@ Issues of note:
management over DAV. Similar to "curl" above, this is optional.
- "wish", the Tcl/Tk windowing shell is used in gitk to show the
- history graphically
+ history graphically, and in git-gui.
- "ssh" is used to push and pull over the net
@@ -89,10 +88,20 @@ Issues of note:
will include them. Note that config.mak is not distributed;
the name is reserved for local settings.
- - To build and install documentation suite, you need to have the
- asciidoc/xmlto toolchain. Alternatively, pre-formatted
- documentation are available in "html" and "man" branches of the git
- repository itself. For example, you could:
+ - To build and install documentation suite, you need to have
+ the asciidoc/xmlto toolchain. Because not many people are
+ inclined to install the tools, the default build target
+ ("make all") does _not_ build them.
+
+ Building and installing the info file additionally requires
+ makeinfo and docbook2X. Version 0.8.3 is known to work.
+
+ The documentation is written for AsciiDoc 7, but "make
+ ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.
+
+ Alternatively, pre-formatted documentation are available in
+ "html" and "man" branches of the git repository itself. For
+ example, you could:
$ mkdir manual && cd manual
$ git init
@@ -112,3 +121,6 @@ Issues of note:
http://www.kernel.org/pub/software/scm/git/docs/
+ It has been reported that docbook-xsl version 1.72 and 1.73 are
+ buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
+ the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
diff --git a/Makefile b/Makefile
index eb443bddb9..4eb463797a 100644
--- a/Makefile
+++ b/Makefile
@@ -112,8 +112,6 @@ all::
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
# MakeMaker (e.g. using ActiveState under Cygwin).
#
-# Define WITH_P4IMPORT to build and install Python git-p4import script.
-#
# Define NO_TCLTK if you do not want Tcl/Tk GUI.
#
# The TCL_PATH variable governs the location of the Tcl interpreter
@@ -146,13 +144,14 @@ STRIP ?= strip
prefix = $(HOME)
bindir = $(prefix)/bin
gitexecdir = $(bindir)
-sharedir = $(prefix)/share/
-template_dir = $(sharedir)/git-core/templates/
+sharedir = $(prefix)/share
+template_dir = $(sharedir)/git-core/templates
ifeq ($(prefix),/usr)
sysconfdir = /etc
else
sysconfdir = $(prefix)/etc
endif
+lib = lib
ETC_GITCONFIG = $(sysconfdir)/gitconfig
# DESTDIR=
@@ -176,7 +175,9 @@ export prefix bindir gitexecdir sharedir template_dir sysconfdir
CC = gcc
AR = ar
+RM = rm -f
TAR = tar
+FIND = find
INSTALL = install
RPMBUILD = rpmbuild
TCL_PATH = tclsh
@@ -204,14 +205,15 @@ SCRIPT_SH = \
git-fetch.sh \
git-ls-remote.sh \
git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
- git-pull.sh git-rebase.sh \
+ git-pull.sh git-rebase.sh git-rebase--interactive.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-sh-setup.sh \
- git-tag.sh git-verify-tag.sh \
- git-applymbox.sh git-applypatch.sh git-am.sh \
+ git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh \
- git-lost-found.sh git-quiltimport.sh
+ git-lost-found.sh git-quiltimport.sh git-submodule.sh \
+ git-filter-branch.sh \
+ git-stash.sh
SCRIPT_PERL = \
git-add--interactive.perl \
@@ -220,27 +222,15 @@ SCRIPT_PERL = \
git-svnimport.perl git-cvsexportcommit.perl \
git-send-email.perl git-svn.perl
-SCRIPT_PYTHON = \
- git-p4import.py
-
-ifdef WITH_P4IMPORT
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
- $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
git-status git-instaweb
-else
-SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
- $(patsubst %.perl,%,$(SCRIPT_PERL)) \
- git-status git-instaweb
-endif
-
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
git-convert-objects$X git-fetch-pack$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \
git-fast-import$X \
- git-merge-base$X \
git-daemon$X \
git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
git-peek-remote$X git-receive-pack$X \
@@ -284,9 +274,6 @@ endif
ifndef PERL_PATH
PERL_PATH = /usr/bin/perl
endif
-ifndef PYTHON_PATH
- PYTHON_PATH = /usr/local/bin/python
-endif
export PERL_PATH
@@ -298,7 +285,8 @@ LIB_H = \
diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
- utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h mailmap.h
+ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
+ mailmap.h remote.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -320,7 +308,7 @@ LIB_OBJS = \
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
- convert.o attr.o decorate.o progress.o mailmap.o symlinks.o
+ convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o
BUILTIN_OBJS = \
builtin-add.o \
@@ -374,18 +362,20 @@ BUILTIN_OBJS = \
builtin-show-branch.o \
builtin-stripspace.o \
builtin-symbolic-ref.o \
+ builtin-tag.o \
builtin-tar-tree.o \
builtin-unpack-objects.o \
builtin-update-index.o \
builtin-update-ref.o \
builtin-upload-archive.o \
builtin-verify-pack.o \
+ builtin-verify-tag.o \
builtin-write-tree.o \
builtin-show-ref.o \
builtin-pack-refs.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
-EXTLIBS = -lz
+EXTLIBS =
#
# Platform specific tweaks
@@ -469,6 +459,10 @@ ifeq ($(uname_S),AIX)
NO_STRLCPY = YesPlease
NEEDS_LIBICONV=YesPlease
endif
+ifeq ($(uname_S),GNU)
+ # GNU/Hurd
+ NO_STRLCPY=YesPlease
+endif
ifeq ($(uname_S),IRIX64)
NO_IPV6=YesPlease
NO_SETENV=YesPlease
@@ -512,9 +506,9 @@ endif
ifndef NO_CURL
ifdef CURLDIR
- # Try "-Wl,-rpath=$(CURLDIR)/lib" in such a case.
+ # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
BASIC_CFLAGS += -I$(CURLDIR)/include
- CURL_LIBCURL = -L$(CURLDIR)/lib $(CC_LD_DYNPATH)$(CURLDIR)/lib -lcurl
+ CURL_LIBCURL = -L$(CURLDIR)/$(lib) $(CC_LD_DYNPATH)$(CURLDIR)/$(lib) -lcurl
else
CURL_LIBCURL = -lcurl
endif
@@ -530,11 +524,17 @@ ifndef NO_CURL
endif
endif
+ifdef ZLIB_PATH
+ BASIC_CFLAGS += -I$(ZLIB_PATH)/include
+ EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
+endif
+EXTLIBS += -lz
+
ifndef NO_OPENSSL
OPENSSL_LIBSSL = -lssl
ifdef OPENSSLDIR
BASIC_CFLAGS += -I$(OPENSSLDIR)/include
- OPENSSL_LINK = -L$(OPENSSLDIR)/lib $(CC_LD_DYNPATH)$(OPENSSLDIR)/lib
+ OPENSSL_LINK = -L$(OPENSSLDIR)/$(lib) $(CC_LD_DYNPATH)$(OPENSSLDIR)/$(lib)
else
OPENSSL_LINK =
endif
@@ -551,7 +551,7 @@ endif
ifdef NEEDS_LIBICONV
ifdef ICONVDIR
BASIC_CFLAGS += -I$(ICONVDIR)/include
- ICONV_LINK = -L$(ICONVDIR)/lib $(CC_LD_DYNPATH)$(ICONVDIR)/lib
+ ICONV_LINK = -L$(ICONVDIR)/$(lib) $(CC_LD_DYNPATH)$(ICONVDIR)/$(lib)
else
ICONV_LINK =
endif
@@ -708,7 +708,6 @@ prefix_SQ = $(subst ','\'',$(prefix))
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
-PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
LIBS = $(GITLIBS) $(EXTLIBS)
@@ -727,7 +726,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS)
ifneq (,$X)
- $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$p';)
+ $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$p';)
endif
all::
@@ -741,29 +740,34 @@ strip: $(PROGRAMS) git$X
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
gitk-wish: gitk GIT-GUI-VARS
- $(QUIET_GEN)rm -f $@ $@+ && \
+ $(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \
chmod +x $@+ && \
mv -f $@+ $@
-git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS
- $(QUIET_LINK)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
- $(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
+git.o: git.c common-cmds.h GIT-CFLAGS
+ $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
+ $(ALL_CFLAGS) -c $(filter %.c,$^)
+
+git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
help.o: common-cmds.h
git-merge-subtree$X: git-merge-recursive$X
- $(QUIET_BUILT_IN)rm -f $@ && ln git-merge-recursive$X $@
+ $(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
$(BUILT_INS): git$X
- $(QUIET_BUILT_IN)rm -f $@ && ln git$X $@
+ $(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
+
+common-cmds.h: ./generate-cmdlist.sh
common-cmds.h: $(wildcard Documentation/git-*.txt)
$(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
- $(QUIET_GEN)rm -f $@ $@+ && \
+ $(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
@@ -774,20 +778,11 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
-$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
- rm -f $@ $@+
- sed -e '1s|#!.*/python|#!$(PYTHON_PATH_SQ)|' \
- -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
- -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
- $@.py >$@+
- chmod +x $@+
- mv $@+ $@
-
perl/perl.mak: GIT-CFLAGS
$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
- $(QUIET_GEN)rm -f $@ $@+ && \
+ $(QUIET_GEN)$(RM) $@ $@+ && \
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
sed -e '1{' \
-e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \
@@ -806,7 +801,7 @@ git-status: git-commit
$(QUIET_GEN)cp $< $@+ && mv $@+ $@
gitweb/gitweb.cgi: gitweb/gitweb.perl
- $(QUIET_GEN)rm -f $@ $@+ && \
+ $(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
-e 's|++GIT_BINDIR++|$(bindir)|g' \
@@ -829,7 +824,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
mv $@+ $@
git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
- $(QUIET_GEN)rm -f $@ $@+ && \
+ $(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
@@ -842,20 +837,22 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
mv $@+ $@
configure: configure.ac
- $(QUIET_GEN)rm -f $@ $<+ && \
+ $(QUIET_GEN)$(RM) $@ $<+ && \
sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$< > $<+ && \
autoconf -o $@ $<+ && \
- rm -f $<+
+ $(RM) $<+
# These can record GIT_VERSION
-git$X git.spec \
+git.o git.spec \
$(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
: GIT-VERSION-FILE
%.o: %.c GIT-CFLAGS
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+%.s: %.c GIT-CFLAGS
+ $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
%.o: %.S
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
@@ -899,7 +896,7 @@ $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
$(DIFF_OBJS): diffcore.h
$(LIB_FILE): $(LIB_OBJS)
- $(QUIET_AR)rm -f $@ && $(AR) rcs $@ $(LIB_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
xdiff/xmerge.o
@@ -907,7 +904,7 @@ $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
$(XDIFF_LIB): $(XDIFF_OBJS)
- $(QUIET_AR)rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
@@ -917,13 +914,16 @@ perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
doc:
$(MAKE) -C Documentation all
+info:
+ $(MAKE) -C Documentation info
+
TAGS:
- rm -f TAGS
- find . -name '*.[hcS]' -print | xargs etags -a
+ $(RM) TAGS
+ $(FIND) . -name '*.[hcS]' -print | xargs etags -a
tags:
- rm -f tags
- find . -name '*.[hcS]' -print | xargs ctags -a
+ $(RM) tags
+ $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
### Detect prefix changes
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
@@ -952,7 +952,7 @@ endif
### Testing rules
-TEST_PROGRAMS = test-chmtime$X test-genrandom$X
+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X
all:: $(TEST_PROGRAMS)
@@ -965,26 +965,12 @@ export NO_SVN_TESTS
test: all
$(MAKE) -C t/ all
-test-date$X: test-date.c date.o ctype.o
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
-
-test-delta$X: test-delta.o diff-delta.o patch-delta.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+test-date$X: date.o ctype.o
-test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+test-delta$X: diff-delta.o patch-delta.o
-test-sha1$X: test-sha1.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-
-test-match-trees$X: test-match-trees.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-
-test-chmtime$X: test-chmtime.c
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
-
-test-genrandom$X: test-genrandom.c
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
+test-%$X: test-%.o $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
check-sha1:: test-sha1$X
./test-sha1.sh
@@ -992,7 +978,8 @@ check-sha1:: test-sha1$X
check: common-cmds.h
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
-
+remove-dashes:
+ ./fixup-builtins $(BUILT_INS)
### Installation rules
@@ -1014,14 +1001,17 @@ endif
cp '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
'$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \
fi
- $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
+ $(foreach p,$(BUILT_INS), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
ifneq (,$X)
- $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';)
+ $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';)
endif
install-doc:
$(MAKE) -C Documentation install
+install-info:
+ $(MAKE) -C Documentation install-info
+
quick-install-doc:
$(MAKE) -C Documentation quick-install
@@ -1034,18 +1024,19 @@ git.spec: git.spec.in
mv $@+ $@
GIT_TARNAME=git-$(GIT_VERSION)
-dist: git.spec git-archive
+dist: git.spec git-archive configure
./git-archive --format=tar \
--prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
@mkdir -p $(GIT_TARNAME)
- @cp git.spec $(GIT_TARNAME)
+ @cp git.spec 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 \
$(GIT_TARNAME)/git.spec \
+ $(GIT_TARNAME)/configure \
$(GIT_TARNAME)/version \
$(GIT_TARNAME)/git-gui/version
- @rm -rf $(GIT_TARNAME)
+ @$(RM) -r $(GIT_TARNAME)
gzip -f -9 $(GIT_TARNAME).tar
rpm: dist
@@ -1054,13 +1045,13 @@ rpm: dist
htmldocs = git-htmldocs-$(GIT_VERSION)
manpages = git-manpages-$(GIT_VERSION)
dist-doc:
- rm -fr .doc-tmp-dir
+ $(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 .
gzip -n -9 -f $(htmldocs).tar
:
- rm -fr .doc-tmp-dir
+ $(RM) -r .doc-tmp-dir
mkdir -p .doc-tmp-dir/man1 .doc-tmp-dir/man5 .doc-tmp-dir/man7
$(MAKE) -C Documentation DESTDIR=./ \
man1dir=../.doc-tmp-dir/man1 \
@@ -1069,30 +1060,31 @@ dist-doc:
install
cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
gzip -n -9 -f $(manpages).tar
- rm -fr .doc-tmp-dir
+ $(RM) -r .doc-tmp-dir
### Cleaning rules
clean:
- rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
- test-chmtime$X test-genrandom$X $(LIB_FILE) $(XDIFF_LIB)
- rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
- rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
- rm -rf autom4te.cache
- rm -f configure config.log config.mak.autogen config.mak.append config.status config.cache
- rm -rf $(GIT_TARNAME) .doc-tmp-dir
- rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
- rm -f $(htmldocs).tar.gz $(manpages).tar.gz
- rm -f gitweb/gitweb.cgi
+ $(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
+ $(LIB_FILE) $(XDIFF_LIB)
+ $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
+ $(RM) $(TEST_PROGRAMS)
+ $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
+ $(RM) -r autom4te.cache
+ $(RM) configure config.log config.mak.autogen config.mak.append config.status config.cache
+ $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
+ $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
+ $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
+ $(RM) gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean
$(MAKE) -C perl clean
$(MAKE) -C templates/ clean
$(MAKE) -C t/ clean
ifndef NO_TCLTK
- rm -f gitk-wish
+ $(RM) gitk-wish
$(MAKE) -C git-gui clean
endif
- rm -f GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
+ $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
.PHONY: all install clean strip
.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS
diff --git a/RelNotes b/RelNotes
index 215243c7cd..0de5e663a2 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.2.5.txt \ No newline at end of file
+Documentation/RelNotes-1.5.3.txt \ No newline at end of file
diff --git a/archive-tar.c b/archive-tar.c
index 33e76576f1..66fe3e375b 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -167,7 +167,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
} else {
if (verbose)
fprintf(stderr, "%.*s\n", path->len, path->buf);
- if (S_ISDIR(mode) || S_ISDIRLNK(mode)) {
+ if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
*header.typeflag = TYPEFLAG_DIR;
mode = (mode | 0777) & ~tar_umask;
} else if (S_ISLNK(mode)) {
@@ -280,7 +280,7 @@ static int write_tar_entry(const unsigned char *sha1,
memcpy(path.buf + baselen, filename, filenamelen);
path.len = baselen + filenamelen;
path.buf[path.len] = '\0';
- if (S_ISDIR(mode) || S_ISDIRLNK(mode)) {
+ if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
strbuf_append_string(&path, "/");
buffer = NULL;
size = 0;
diff --git a/archive-zip.c b/archive-zip.c
index 3cbf6bb8ac..444e1623db 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -182,7 +182,7 @@ static int write_zip_entry(const unsigned char *sha1,
goto out;
}
- if (S_ISDIR(mode) || S_ISDIRLNK(mode)) {
+ if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
method = 0;
attr2 = 16;
result = (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
diff --git a/arm/sha1.c b/arm/sha1.c
index 11b1a048b4..9e3ae038e8 100644
--- a/arm/sha1.c
+++ b/arm/sha1.c
@@ -49,7 +49,7 @@ void SHA1_Update(SHA_CTX *c, const void *p, unsigned long n)
void SHA1_Final(unsigned char *hash, SHA_CTX *c)
{
uint64_t bitlen;
- uint32_t bitlen_hi, bitlen_lo;
+ uint32_t bitlen_hi, bitlen_lo;
unsigned int i, offset, padlen;
unsigned char bits[8];
static const unsigned char padding[64] = { 0x80, };
@@ -69,7 +69,7 @@ void SHA1_Final(unsigned char *hash, SHA_CTX *c)
bits[5] = bitlen_lo >> 16;
bits[6] = bitlen_lo >> 8;
bits[7] = bitlen_lo;
- SHA1_Update(c, bits, 8);
+ SHA1_Update(c, bits, 8);
for (i = 0; i < 5; i++) {
uint32_t v = c->hash[i];
diff --git a/arm/sha1_arm.S b/arm/sha1_arm.S
index a328b73375..8c1cb99fb4 100644
--- a/arm/sha1_arm.S
+++ b/arm/sha1_arm.S
@@ -181,4 +181,3 @@ sha_transform:
.L_sha_K:
.word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
-
diff --git a/attr.c b/attr.c
index a0712543b2..129399310a 100644
--- a/attr.c
+++ b/attr.c
@@ -257,6 +257,7 @@ static struct attr_stack {
struct attr_stack *prev;
char *origin;
unsigned num_matches;
+ unsigned alloc;
struct match_attr **attrs;
} *attr_stack;
@@ -287,6 +288,26 @@ static const char *builtin_attr[] = {
NULL,
};
+static void handle_attr_line(struct attr_stack *res,
+ const char *line,
+ const char *src,
+ int lineno,
+ int macro_ok)
+{
+ struct match_attr *a;
+
+ a = parse_attr_line(line, src, lineno, macro_ok);
+ if (!a)
+ return;
+ if (res->alloc <= res->num_matches) {
+ res->alloc = alloc_nr(res->num_matches);
+ res->attrs = xrealloc(res->attrs,
+ sizeof(struct match_attr *) *
+ res->alloc);
+ }
+ res->attrs[res->num_matches++] = a;
+}
+
static struct attr_stack *read_attr_from_array(const char **list)
{
struct attr_stack *res;
@@ -294,42 +315,91 @@ static struct attr_stack *read_attr_from_array(const char **list)
int lineno = 0;
res = xcalloc(1, sizeof(*res));
- while ((line = *(list++)) != NULL) {
- struct match_attr *a;
-
- a = parse_attr_line(line, "[builtin]", ++lineno, 1);
- if (!a)
- continue;
- res->attrs = xrealloc(res->attrs,
- sizeof(struct match_attr *) * (res->num_matches + 1));
- res->attrs[res->num_matches++] = a;
- }
+ while ((line = *(list++)) != NULL)
+ handle_attr_line(res, line, "[builtin]", ++lineno, 1);
return res;
}
static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
{
- FILE *fp;
+ FILE *fp = fopen(path, "r");
struct attr_stack *res;
char buf[2048];
int lineno = 0;
- res = xcalloc(1, sizeof(*res));
- fp = fopen(path, "r");
if (!fp)
+ return NULL;
+ res = xcalloc(1, sizeof(*res));
+ while (fgets(buf, sizeof(buf), fp))
+ handle_attr_line(res, buf, path, ++lineno, macro_ok);
+ fclose(fp);
+ return res;
+}
+
+static void *read_index_data(const char *path)
+{
+ int pos, len;
+ unsigned long sz;
+ enum object_type type;
+ void *data;
+
+ len = strlen(path);
+ pos = cache_name_pos(path, len);
+ if (pos < 0) {
+ /*
+ * We might be in the middle of a merge, in which
+ * case we would read stage #2 (ours).
+ */
+ int i;
+ for (i = -pos - 1;
+ (pos < 0 && i < active_nr &&
+ !strcmp(active_cache[i]->name, path));
+ i++)
+ if (ce_stage(active_cache[i]) == 2)
+ pos = i;
+ }
+ if (pos < 0)
+ return NULL;
+ data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
+ if (!data || type != OBJ_BLOB) {
+ free(data);
+ return NULL;
+ }
+ return data;
+}
+
+static struct attr_stack *read_attr(const char *path, int macro_ok)
+{
+ struct attr_stack *res;
+ char *buf, *sp;
+ int lineno = 0;
+
+ res = read_attr_from_file(path, macro_ok);
+ if (res)
return res;
- while (fgets(buf, sizeof(buf), fp)) {
- struct match_attr *a;
+ res = xcalloc(1, sizeof(*res));
- a = parse_attr_line(buf, path, ++lineno, macro_ok);
- if (!a)
- continue;
- res->attrs = xrealloc(res->attrs,
- sizeof(struct match_attr *) * (res->num_matches + 1));
- res->attrs[res->num_matches++] = a;
+ /*
+ * There is no checked out .gitattributes file there, but
+ * we might have it in the index. We allow operation in a
+ * sparsely checked out work tree, so read from it.
+ */
+ buf = read_index_data(path);
+ if (!buf)
+ return res;
+
+ for (sp = buf; *sp; ) {
+ char *ep;
+ int more;
+ for (ep = sp; *ep && *ep != '\n'; ep++)
+ ;
+ more = (*ep == '\n');
+ *ep = '\0';
+ handle_attr_line(res, sp, path, ++lineno, macro_ok);
+ sp = ep + more;
}
- fclose(fp);
+ free(buf);
return res;
}
@@ -370,13 +440,15 @@ static void bootstrap_attr_stack(void)
elem->prev = attr_stack;
attr_stack = elem;
- elem = read_attr_from_file(GITATTRIBUTES_FILE, 1);
+ elem = read_attr(GITATTRIBUTES_FILE, 1);
elem->origin = strdup("");
elem->prev = attr_stack;
attr_stack = elem;
debug_push(elem);
elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
+ if (!elem)
+ elem = xcalloc(1, sizeof(*elem));
elem->origin = NULL;
elem->prev = attr_stack;
attr_stack = elem;
@@ -441,7 +513,7 @@ static void prepare_attr_stack(const char *path, int dirlen)
memcpy(pathbuf + dirlen, "/", 2);
cp = strchr(pathbuf + len + 1, '/');
strcpy(cp + 1, GITATTRIBUTES_FILE);
- elem = read_attr_from_file(pathbuf, 0);
+ elem = read_attr(pathbuf, 0);
*cp = '\0';
elem->origin = strdup(pathbuf);
elem->prev = attr_stack;
diff --git a/builtin-add.c b/builtin-add.c
index 07e3ddfd0a..3dd4ded937 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -40,42 +40,29 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
dir->nr = dst - dir->entries;
for (i = 0; i < specs; i++) {
- struct stat st;
- const char *match;
- if (seen[i])
- continue;
-
- match = pathspec[i];
- if (!match[0])
- continue;
-
- /* Existing file? We must have ignored it */
- if (!lstat(match, &st)) {
- struct dir_entry *ent;
-
- ent = dir_add_name(dir, match, strlen(match));
- ent->ignored = 1;
- if (S_ISDIR(st.st_mode))
- ent->ignored_dir = 1;
- continue;
- }
- die("pathspec '%s' did not match any files", match);
+ if (!seen[i] && !file_exists(pathspec[i]))
+ die("pathspec '%s' did not match any files",
+ pathspec[i]);
}
}
-static void fill_directory(struct dir_struct *dir, const char **pathspec)
+static void fill_directory(struct dir_struct *dir, const char **pathspec,
+ int ignored_too)
{
const char *path, *base;
int baselen;
/* Set up the default git porcelain excludes */
memset(dir, 0, sizeof(*dir));
- dir->exclude_per_dir = ".gitignore";
- path = git_path("info/exclude");
- if (!access(path, R_OK))
- add_excludes_from_file(dir, path);
- if (!access(excludes_file, R_OK))
- add_excludes_from_file(dir, excludes_file);
+ if (!ignored_too) {
+ dir->collect_ignored = 1;
+ dir->exclude_per_dir = ".gitignore";
+ path = git_path("info/exclude");
+ if (!access(path, R_OK))
+ add_excludes_from_file(dir, path);
+ if (excludes_file != NULL && !access(excludes_file, R_OK))
+ add_excludes_from_file(dir, excludes_file);
+ }
/*
* Calculate common prefix for the pathspec, and
@@ -137,6 +124,23 @@ static void update(int verbose, const char *prefix, const char **files)
run_diff_files(&rev, 0);
}
+static void refresh(int verbose, const char **pathspec)
+{
+ char *seen;
+ int i, specs;
+
+ for (specs = 0; pathspec[specs]; specs++)
+ /* nothing */;
+ seen = xcalloc(specs, 1);
+ if (read_cache() < 0)
+ die("index file corrupt");
+ refresh_index(&the_index, verbose ? 0 : REFRESH_QUIET, pathspec, seen);
+ for (i = 0; i < specs; i++) {
+ if (!seen[i])
+ die("pathspec '%s' did not match any files", pathspec[i]);
+ }
+}
+
static int git_add_config(const char *var, const char *value)
{
if (!strcmp(var, "core.excludesfile")) {
@@ -157,7 +161,7 @@ static const char ignore_warning[] =
int cmd_add(int argc, const char **argv, const char *prefix)
{
int i, newfd;
- int verbose = 0, show_only = 0, ignored_too = 0;
+ int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
const char **pathspec;
struct dir_struct dir;
int add_interactive = 0;
@@ -205,6 +209,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
take_worktree_changes = 1;
continue;
}
+ if (!strcmp(arg, "--refresh")) {
+ refresh_only = 1;
+ continue;
+ }
usage(builtin_add_usage);
}
@@ -220,13 +228,16 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
pathspec = get_pathspec(prefix, argv + i);
- fill_directory(&dir, pathspec);
+ if (refresh_only) {
+ refresh(verbose, pathspec);
+ goto finish;
+ }
+
+ fill_directory(&dir, pathspec, ignored_too);
if (show_only) {
const char *sep = "", *eof = "";
for (i = 0; i < dir.nr; i++) {
- if (!ignored_too && dir.entries[i]->ignored)
- continue;
printf("%s%s", sep, dir.entries[i]->name);
sep = " ";
eof = "\n";
@@ -238,25 +249,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (read_cache() < 0)
die("index file corrupt");
- if (!ignored_too) {
- int has_ignored = 0;
- for (i = 0; i < dir.nr; i++)
- if (dir.entries[i]->ignored)
- has_ignored = 1;
- if (has_ignored) {
- fprintf(stderr, ignore_warning);
- for (i = 0; i < dir.nr; i++) {
- if (!dir.entries[i]->ignored)
- continue;
- fprintf(stderr, "%s", dir.entries[i]->name);
- if (dir.entries[i]->ignored_dir)
- fprintf(stderr, " (directory)");
- fputc('\n', stderr);
- }
- fprintf(stderr,
- "Use -f if you really want to add them.\n");
- exit(1);
+ if (dir.ignored_nr) {
+ fprintf(stderr, ignore_warning);
+ for (i = 0; i < dir.ignored_nr; i++) {
+ fprintf(stderr, "%s\n", dir.ignored[i]->name);
}
+ fprintf(stderr, "Use -f if you really want to add them.\n");
+ exit(1);
}
for (i = 0; i < dir.nr; i++)
diff --git a/builtin-annotate.c b/builtin-annotate.c
index 9db7cfe74c..fc43eed36b 100644
--- a/builtin-annotate.c
+++ b/builtin-annotate.c
@@ -22,4 +22,3 @@ int cmd_annotate(int argc, const char **argv, const char *prefix)
return cmd_blame(argc + 1, nargv, prefix);
}
-
diff --git a/builtin-apply.c b/builtin-apply.c
index 9ee9393662..da270755a7 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -55,7 +55,7 @@ static enum whitespace_eol {
} new_whitespace = warn_on_whitespace;
static int whitespace_error;
static int squelch_whitespace_errors = 5;
-static int applied_after_stripping;
+static int applied_after_fixing_ws;
static const char *patch_input_file;
static void parse_whitespace_option(const char *option)
@@ -1661,7 +1661,7 @@ static int apply_line(char *output, const char *patch, int plen)
if (add_nl_to_tail)
output[plen++] = '\n';
if (fixed)
- applied_after_stripping++;
+ applied_after_fixing_ws++;
return output + plen - buf;
}
@@ -1675,6 +1675,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
char *new = xmalloc(size);
const char *oldlines, *newlines;
int oldsize = 0, newsize = 0;
+ int new_blank_lines_at_end = 0;
unsigned long leading, trailing;
int pos, lines;
@@ -1682,6 +1683,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
char first;
int len = linelen(patch, size);
int plen;
+ int added_blank_line = 0;
if (!len)
break;
@@ -1703,6 +1705,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
else if (first == '+')
first = '-';
}
+
switch (first) {
case '\n':
/* Newer GNU diff, empty context line */
@@ -1720,9 +1723,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
break;
/* Fall-through for ' ' */
case '+':
- if (first != '+' || !no_add)
- newsize += apply_line(new + newsize, patch,
- plen);
+ if (first != '+' || !no_add) {
+ int added = apply_line(new + newsize, patch,
+ plen);
+ newsize += added;
+ if (first == '+' &&
+ added == 1 && new[newsize-1] == '\n')
+ added_blank_line = 1;
+ }
break;
case '@': case '\\':
/* Ignore it, we already handled it */
@@ -1732,6 +1740,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
error("invalid start of line: '%c'", first);
return -1;
}
+ if (added_blank_line)
+ new_blank_lines_at_end++;
+ else
+ new_blank_lines_at_end = 0;
patch += len;
size -= len;
}
@@ -1774,9 +1786,16 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
if (match_beginning && offset)
offset = -1;
if (offset >= 0) {
- int diff = newsize - oldsize;
- unsigned long size = desc->size + diff;
- unsigned long alloc = desc->alloc;
+ int diff;
+ unsigned long size, alloc;
+
+ if (new_whitespace == strip_whitespace &&
+ (desc->size - oldsize - offset == 0)) /* end of file? */
+ newsize -= new_blank_lines_at_end;
+
+ diff = newsize - oldsize;
+ size = desc->size + diff;
+ alloc = desc->alloc;
/* Warn if it was necessary to reduce the number
* of context lines.
@@ -2869,18 +2888,17 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
squelched == 1 ? "" : "s");
}
if (new_whitespace == error_on_whitespace)
- die("%d line%s add%s trailing whitespaces.",
+ die("%d line%s add%s whitespace errors.",
whitespace_error,
whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : "");
- if (applied_after_stripping)
+ if (applied_after_fixing_ws)
fprintf(stderr, "warning: %d line%s applied after"
- " stripping trailing whitespaces.\n",
- applied_after_stripping,
- applied_after_stripping == 1 ? "" : "s");
+ " fixing whitespace errors.\n",
+ applied_after_fixing_ws,
+ applied_after_fixing_ws == 1 ? "" : "s");
else if (whitespace_error)
- fprintf(stderr, "warning: %d line%s add%s trailing"
- " whitespaces.\n",
+ fprintf(stderr, "warning: %d line%s add%s whitespace errors.\n",
whitespace_error,
whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : "");
diff --git a/builtin-archive.c b/builtin-archive.c
index 7f4e409c99..187491bc17 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -45,7 +45,7 @@ static int run_remote_archiver(const char *remote, int argc,
}
url = xstrdup(remote);
- pid = git_connect(fd, url, exec);
+ pid = git_connect(fd, url, exec, 0);
if (pid < 0)
return pid;
diff --git a/builtin-blame.c b/builtin-blame.c
index 35471fc261..0519339098 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -20,7 +20,7 @@
#include "mailmap.h"
static char blame_usage[] =
-"git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
+"git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
" -c Use the same output mode as git-annotate (Default: off)\n"
" -b Show blank SHA-1 for boundary commits (Default: off)\n"
" -l Show long commit SHA1 (Default: off)\n"
@@ -30,6 +30,7 @@ static char blame_usage[] =
" -n, --show-number Show original linenumber (Default: off)\n"
" -s Suppress author name and timestamp (Default: off)\n"
" -p, --porcelain Show in a format designed for machine consumption\n"
+" -w Ignore whitespace differences\n"
" -L n,m Process only line range n,m, counting from 1\n"
" -M, -C Find line movements within and across files\n"
" --incremental Show blame entries as we find them, incrementally\n"
@@ -45,6 +46,7 @@ static int show_root;
static int blank_boundary;
static int incremental;
static int cmd_is_annotate;
+static int xdl_opts = XDF_NEED_MINIMAL;
static struct path_list mailmap;
#ifndef DEBUG
@@ -515,9 +517,9 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
xdemitconf_t xecfg;
xdemitcb_t ecb;
- xpp.flags = XDF_NEED_MINIMAL;
+ xpp.flags = xdl_opts;
+ memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = context;
- xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = &state;
memset(&state, 0, sizeof(state));
@@ -1457,6 +1459,7 @@ static void found_guilty_entry(struct blame_entry *ent)
printf("boundary\n");
}
write_filename_info(suspect->path);
+ maybe_flush_or_die(stdout, "stdout");
}
}
@@ -1744,11 +1747,11 @@ static int read_ancestry(const char *graft_file)
*/
static int lineno_width(int lines)
{
- int i, width;
+ int i, width;
- for (width = 1, i = 10; i <= lines + 1; width++)
- i *= 10;
- return width;
+ for (width = 1, i = 10; i <= lines + 1; width++)
+ i *= 10;
+ return width;
}
/*
@@ -2159,6 +2162,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
output_option |= OUTPUT_LONG_OBJECT_NAME;
else if (!strcmp("-s", arg))
output_option |= OUTPUT_NO_AUTHOR;
+ else if (!strcmp("-w", arg))
+ xdl_opts |= XDF_IGNORE_WHITESPACE;
else if (!strcmp("-S", arg) && ++i < argc)
revs_file = argv[i];
else if (!prefixcmp(arg, "-M")) {
diff --git a/builtin-branch.c b/builtin-branch.c
index 94dba6e232..5f5c1823cb 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -1,7 +1,7 @@
/*
* Builtin "git branch"
*
- * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
+ * Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
* Based on git-branch.sh by Junio C Hamano.
*/
@@ -10,6 +10,7 @@
#include "refs.h"
#include "commit.h"
#include "builtin.h"
+#include "remote.h"
static const char builtin_branch_usage[] =
"git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
@@ -22,7 +23,7 @@ static const char builtin_branch_usage[] =
static const char *head;
static unsigned char head_sha1[20];
-static int branch_track_remotes;
+static int branch_track = 1;
static int branch_use_color;
static char branch_colors[][COLOR_MAXLEN] = {
@@ -55,7 +56,7 @@ static int parse_branch_color_slot(const char *var, int ofs)
die("bad config variable '%s'", var);
}
-int git_branch_config(const char *var, const char *value)
+static int git_branch_config(const char *var, const char *value)
{
if (!strcmp(var, "color.branch")) {
branch_use_color = git_config_colorbool(var, value);
@@ -67,12 +68,12 @@ int git_branch_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "branch.autosetupmerge"))
- branch_track_remotes = git_config_bool(var, value);
+ branch_track = git_config_bool(var, value);
return git_default_config(var, value);
}
-const char *branch_get_color(enum color_branch ix)
+static const char *branch_get_color(enum color_branch ix)
{
if (branch_use_color)
return branch_colors[ix];
@@ -85,6 +86,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
unsigned char sha1[20];
char *name = NULL;
const char *fmt, *remote;
+ char section[PATH_MAX];
int i;
int ret = 0;
@@ -152,9 +154,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
error("Error deleting %sbranch '%s'", remote,
argv[i]);
ret = 1;
- } else
+ } else {
printf("Deleted %sbranch %s.\n", remote, argv[i]);
-
+ snprintf(section, sizeof(section), "branch.%s",
+ argv[i]);
+ if (git_config_rename_section(section, NULL) < 0)
+ warning("Update of config-file failed");
+ }
}
if (name)
@@ -242,7 +248,6 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
char c;
int color;
struct commit *commit;
- char subject[256];
switch (item->kind) {
case REF_LOCAL_BRANCH:
@@ -263,17 +268,23 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
}
if (verbose) {
+ char *subject = NULL;
+ unsigned long subject_len = 0;
+ const char *sub = " **** invalid ref ****";
+
commit = lookup_commit(item->sha1);
- if (commit && !parse_commit(commit))
+ if (commit && !parse_commit(commit)) {
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
- subject, sizeof(subject), 0,
+ &subject, &subject_len, 0,
NULL, NULL, 0);
- else
- strcpy(subject, " **** invalid ref ****");
+ sub = subject;
+ }
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
maxwidth, item->name,
branch_get_color(COLOR_BRANCH_RESET),
- find_unique_abbrev(item->sha1, abbrev), subject);
+ find_unique_abbrev(item->sha1, abbrev), sub);
+ if (subject)
+ free(subject);
} else {
printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
branch_get_color(COLOR_BRANCH_RESET));
@@ -314,125 +325,70 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
free_ref_list(&ref_list);
}
-static char *config_repo;
-static char *config_remote;
-static const char *start_ref;
+struct tracking {
+ struct refspec spec;
+ char *src;
+ const char *remote;
+ int matches;
+};
-static int get_remote_branch_name(const char *value)
+static int find_tracked_branch(struct remote *remote, void *priv)
{
- const char *colon;
- const char *end;
-
- if (*value == '+')
- value++;
-
- colon = strchr(value, ':');
- if (!colon)
- return 0;
-
- end = value + strlen(value);
-
- /*
- * Try an exact match first. I.e. handle the case where the
- * value is "$anything:refs/foo/bar/baz" and start_ref is exactly
- * "refs/foo/bar/baz". Then the name at the remote is $anything.
- */
- if (!strcmp(colon + 1, start_ref)) {
- /* Truncate the value before the colon. */
- nfasprintf(&config_repo, "%.*s", colon - value, value);
- return 1;
+ struct tracking *tracking = priv;
+
+ if (!remote_find_tracking(remote, &tracking->spec)) {
+ if (++tracking->matches == 1) {
+ tracking->src = tracking->spec.src;
+ tracking->remote = remote->name;
+ } else {
+ free(tracking->spec.src);
+ if (tracking->src) {
+ free(tracking->src);
+ tracking->src = NULL;
+ }
+ }
+ tracking->spec.src = NULL;
}
- /*
- * Is this a wildcard match?
- */
- if ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||
- (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')
- return 0;
-
- /*
- * Value is "refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>"
- * and start_ref begins with "refs/baz/boa/"; the name at the
- * remote is refs/foo/bar/ with the remaining part of the
- * start_ref. The length of the prefix on the RHS is (end -
- * colon - 2), including the slash immediately before the
- * asterisk.
- */
- if ((strlen(start_ref) < end - colon - 2) ||
- memcmp(start_ref, colon + 1, end - colon - 2))
- return 0; /* does not match prefix */
-
- /* Replace the asterisk with the remote branch name. */
- nfasprintf(&config_repo, "%.*s%s",
- (colon - 1) - value, value,
- start_ref + (end - colon - 2));
- return 1;
-}
-
-static int get_remote_config(const char *key, const char *value)
-{
- const char *var;
- if (prefixcmp(key, "remote."))
- return 0;
-
- var = strrchr(key, '.');
- if (var == key + 6 || strcmp(var, ".fetch"))
- return 0;
- /*
- * Ok, we are looking at key == "remote.$foo.fetch";
- */
- if (get_remote_branch_name(value))
- nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
-
return 0;
}
-static void set_branch_merge(const char *name, const char *config_remote,
- const char *config_repo)
+
+/*
+ * This is called when new_ref is branched off of orig_ref, and tries
+ * to infer the settings for branch.<new_ref>.{remote,merge} from the
+ * config.
+ */
+static int setup_tracking(const char *new_ref, const char *orig_ref)
{
char key[1024];
- if (sizeof(key) <=
- snprintf(key, sizeof(key), "branch.%s.remote", name))
- die("what a long branch name you have!");
- git_config_set(key, config_remote);
-
- /*
- * We do not have to check if we have enough space for
- * the 'merge' key, since it's shorter than the
- * previous 'remote' key, which we already checked.
- */
- snprintf(key, sizeof(key), "branch.%s.merge", name);
- git_config_set(key, config_repo);
-}
+ struct tracking tracking;
-static void set_branch_defaults(const char *name, const char *real_ref)
-{
- /*
- * name is the name of new branch under refs/heads;
- * real_ref is typically refs/remotes/$foo/$bar, where
- * $foo is the remote name (there typically are no slashes)
- * and $bar is the branch name we map from the remote
- * (it could have slashes).
- */
- start_ref = real_ref;
- git_config(get_remote_config);
- if (!config_repo && !config_remote &&
- !prefixcmp(real_ref, "refs/heads/")) {
- set_branch_merge(name, ".", real_ref);
- printf("Branch %s set up to track local branch %s.\n",
- name, real_ref);
- }
+ if (strlen(new_ref) > 1024 - 7 - 7 - 1)
+ return error("Tracking not set up: name too long: %s",
+ new_ref);
- if (config_repo && config_remote) {
- set_branch_merge(name, config_remote, config_repo);
+ memset(&tracking, 0, sizeof(tracking));
+ tracking.spec.dst = (char *)orig_ref;
+ if (for_each_remote(find_tracked_branch, &tracking) ||
+ !tracking.matches)
+ return 1;
+
+ if (tracking.matches > 1)
+ return error("Not tracking: ambiguous information for ref %s",
+ orig_ref);
+
+ if (tracking.matches == 1) {
+ sprintf(key, "branch.%s.remote", new_ref);
+ git_config_set(key, tracking.remote ? tracking.remote : ".");
+ sprintf(key, "branch.%s.merge", new_ref);
+ git_config_set(key, tracking.src);
+ free(tracking.src);
printf("Branch %s set up to track remote branch %s.\n",
- name, real_ref);
+ new_ref, orig_ref);
}
- if (config_repo)
- free(config_repo);
- if (config_remote)
- free(config_remote);
+ return 0;
}
static void create_branch(const char *name, const char *start_name,
@@ -477,7 +433,7 @@ static void create_branch(const char *name, const char *start_name,
die("Not a valid branch point: '%s'.", start_name);
hashcpy(sha1, commit->object.sha1);
- lock = lock_any_ref_for_update(ref, NULL);
+ lock = lock_any_ref_for_update(ref, NULL, 0);
if (!lock)
die("Failed to lock ref for update: %s.", strerror(errno));
@@ -495,7 +451,7 @@ static void create_branch(const char *name, const char *start_name,
automatically merges from there. So far, this is only done for
remotes registered via .git/config. */
if (real_ref && track)
- set_branch_defaults(name, real_ref);
+ setup_tracking(name, real_ref);
if (write_ref_sha1(lock, sha1, msg) < 0)
die("Failed to write ref: %s.", strerror(errno));
@@ -554,7 +510,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int i;
git_config(git_branch_config);
- track = branch_track_remotes;
+ track = branch_track;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
diff --git a/builtin-bundle.c b/builtin-bundle.c
index 306ad29597..1b650069c9 100644
--- a/builtin-bundle.c
+++ b/builtin-bundle.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "object.h"
#include "commit.h"
@@ -43,38 +44,21 @@ struct bundle_header {
struct ref_list references;
};
-/* this function returns the length of the string */
-static int read_string(int fd, char *buffer, int size)
-{
- int i;
- for (i = 0; i < size - 1; i++) {
- ssize_t count = xread(fd, buffer + i, 1);
- if (count < 0)
- return error("Read error: %s", strerror(errno));
- if (count == 0) {
- i--;
- break;
- }
- if (buffer[i] == '\n')
- break;
- }
- buffer[i + 1] = '\0';
- return i + 1;
-}
-
/* returns an fd */
static int read_header(const char *path, struct bundle_header *header) {
char buffer[1024];
- int fd = open(path, O_RDONLY);
+ int fd;
+ long fpos;
+ FILE *ffd = fopen(path, "rb");
- if (fd < 0)
+ if (!ffd)
return error("could not open '%s'", path);
- if (read_string(fd, buffer, sizeof(buffer)) < 0 ||
+ if (!fgets(buffer, sizeof(buffer), ffd) ||
strcmp(buffer, bundle_signature)) {
- close(fd);
+ fclose(ffd);
return error("'%s' does not look like a v2 bundle file", path);
}
- while (read_string(fd, buffer, sizeof(buffer)) > 0
+ while (fgets(buffer, sizeof(buffer), ffd)
&& buffer[0] != '\n') {
int is_prereq = buffer[0] == '-';
int offset = is_prereq ? 1 : 0;
@@ -96,6 +80,12 @@ static int read_header(const char *path, struct bundle_header *header) {
add_to_ref_list(sha1, isspace(delim) ?
buffer + 41 + offset : "", list);
}
+ fpos = ftell(ffd);
+ fclose(ffd);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return error("could not open '%s'", path);
+ lseek(fd, fpos, SEEK_SET);
return fd;
}
@@ -199,18 +189,22 @@ static int list_heads(struct bundle_header *header, int argc, const char **argv)
static int create_bundle(struct bundle_header *header, const char *path,
int argc, const char **argv)
{
+ static struct lock_file lock;
int bundle_fd = -1;
+ int bundle_to_stdout;
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
const char **argv_pack = xmalloc(5 * sizeof(const char *));
int i, ref_count = 0;
char buffer[1024];
struct rev_info revs;
struct child_process rls;
+ FILE *rls_fout;
- bundle_fd = (!strcmp(path, "-") ? 1 :
- open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
- if (bundle_fd < 0)
- return error("Could not create '%s': %s", path, strerror(errno));
+ bundle_to_stdout = !strcmp(path, "-");
+ if (bundle_to_stdout)
+ bundle_fd = 1;
+ else
+ bundle_fd = hold_lock_file_for_update(&lock, path, 1);
/* write signature */
write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
@@ -231,10 +225,11 @@ static int create_bundle(struct bundle_header *header, const char *path,
rls.git_cmd = 1;
if (start_command(&rls))
return -1;
- while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) {
+ rls_fout = fdopen(rls.out, "r");
+ while (fgets(buffer, sizeof(buffer), rls_fout)) {
unsigned char sha1[20];
if (buffer[0] == '-') {
- write_or_die(bundle_fd, buffer, i);
+ write_or_die(bundle_fd, buffer, strlen(buffer));
if (!get_sha1_hex(buffer + 1, sha1)) {
struct object *object = parse_object(sha1);
object->flags |= UNINTERESTING;
@@ -245,6 +240,7 @@ static int create_bundle(struct bundle_header *header, const char *path,
object->flags |= SHOWN;
}
}
+ fclose(rls_fout);
if (finish_command(&rls))
return error("rev-list died");
@@ -266,12 +262,49 @@ static int create_bundle(struct bundle_header *header, const char *path,
* Make sure the refs we wrote out is correct; --max-count and
* other limiting options could have prevented all the tips
* from getting output.
+ *
+ * Non commit objects such as tags and blobs do not have
+ * this issue as they are not affected by those extra
+ * constraints.
*/
- if (!(e->item->flags & SHOWN)) {
+ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
warning("ref '%s' is excluded by the rev-list options",
e->name);
+ free(ref);
+ continue;
+ }
+ /*
+ * If you run "git bundle create bndl v1.0..v2.0", the
+ * name of the positive ref is "v2.0" but that is the
+ * commit that is referenced by the tag, and not the tag
+ * itself.
+ */
+ if (hashcmp(sha1, e->item->sha1)) {
+ /*
+ * Is this the positive end of a range expressed
+ * in terms of a tag (e.g. v2.0 from the range
+ * "v1.0..v2.0")?
+ */
+ struct commit *one = lookup_commit_reference(sha1);
+ struct object *obj;
+
+ if (e->item == &(one->object)) {
+ /*
+ * Need to include e->name as an
+ * independent ref to the pack-objects
+ * input, so that the tag is included
+ * in the output; otherwise we would
+ * end up triggering "empty bundle"
+ * error.
+ */
+ obj = parse_object(sha1);
+ obj->flags |= SHOWN;
+ add_pending_object(&revs, obj, e->name);
+ }
+ free(ref);
continue;
}
+
ref_count++;
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
write_or_die(bundle_fd, " ", 1);
@@ -307,6 +340,9 @@ static int create_bundle(struct bundle_header *header, const char *path,
}
if (finish_command(&rls))
return error ("pack-objects died");
+ close(bundle_fd);
+ if (!bundle_to_stdout)
+ commit_lock_file(&lock);
return 0;
}
diff --git a/builtin-check-attr.c b/builtin-check-attr.c
index 9d77f76ff1..d94973379c 100644
--- a/builtin-check-attr.c
+++ b/builtin-check-attr.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "cache.h"
#include "attr.h"
#include "quote.h"
@@ -10,6 +11,10 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
struct git_attr_check *check;
int cnt, i, doubledash;
+ if (read_cache() < 0) {
+ die("invalid cache");
+ }
+
doubledash = -1;
for (i = 1; doubledash < 0 && i < argc; i++) {
if (!strcmp(argv[i], "--"))
diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c
index 8460f97b66..75377b9cab 100644
--- a/builtin-checkout-index.c
+++ b/builtin-checkout-index.c
@@ -36,6 +36,7 @@
* of "-a" causing problems (not possible in the above example,
* but get used to it in scripting!).
*/
+#include "builtin.h"
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
diff --git a/builtin-config.c b/builtin-config.c
index dbc2339d0f..0a605e01ac 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -2,7 +2,7 @@
#include "cache.h"
static const char git_config_set_usage[] =
-"git-config [ --global | --system ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list";
+"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list";
static char *key;
static regex_t *key_regexp;
@@ -12,14 +12,17 @@ static int use_key_regexp;
static int do_all;
static int do_not_match;
static int seen;
+static char delim = '=';
+static char key_delim = ' ';
+static char term = '\n';
static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
static int show_all_config(const char *key_, const char *value_)
{
if (value_)
- printf("%s=%s\n", key_, value_);
+ printf("%s%c%s%c", key_, delim, value_, term);
else
- printf("%s\n", key_);
+ printf("%s%c", key_, term);
return 0;
}
@@ -40,7 +43,7 @@ static int show_config(const char* key_, const char* value_)
if (show_keys) {
if (value_)
- printf("%s ", key_);
+ printf("%s%c", key_, key_delim);
else
printf("%s", key_);
}
@@ -58,7 +61,7 @@ static int show_config(const char* key_, const char* value_)
key_, vptr);
}
else
- printf("%s\n", vptr);
+ printf("%s%c", vptr, term);
return 0;
}
@@ -135,9 +138,33 @@ free_strings:
return ret;
}
+char *normalize_value(const char *key, const char *value)
+{
+ char *normalized;
+
+ if (!value)
+ return NULL;
+
+ if (type == T_RAW)
+ normalized = xstrdup(value);
+ else {
+ normalized = xmalloc(64);
+ if (type == T_INT) {
+ int v = git_config_int(key, value);
+ sprintf(normalized, "%d", v);
+ }
+ else if (type == T_BOOL)
+ sprintf(normalized, "%s",
+ git_config_bool(key, value) ? "true" : "false");
+ }
+
+ return normalized;
+}
+
int cmd_config(int argc, const char **argv, const char *prefix)
{
int nongit = 0;
+ char* value;
setup_git_directory_gently(&nongit);
while (1 < argc) {
@@ -151,14 +178,26 @@ int cmd_config(int argc, const char **argv, const char *prefix)
char *home = getenv("HOME");
if (home) {
char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
- setenv("GIT_CONFIG", user_config, 1);
+ setenv(CONFIG_ENVIRONMENT, user_config, 1);
free(user_config);
} else {
die("$HOME not set");
}
}
else if (!strcmp(argv[1], "--system"))
- setenv("GIT_CONFIG", ETC_GITCONFIG, 1);
+ setenv(CONFIG_ENVIRONMENT, ETC_GITCONFIG, 1);
+ else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
+ if (argc < 3)
+ usage(git_config_set_usage);
+ setenv(CONFIG_ENVIRONMENT, argv[2], 1);
+ argc--;
+ argv++;
+ }
+ else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) {
+ term = '\0';
+ delim = '\n';
+ key_delim = '\n';
+ }
else if (!strcmp(argv[1], "--rename-section")) {
int ret;
if (argc != 4)
@@ -209,9 +248,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)
use_key_regexp = 1;
do_all = 1;
return get_value(argv[2], NULL);
- } else
-
- return git_config_set(argv[1], argv[2]);
+ } else {
+ value = normalize_value(argv[1], argv[2]);
+ return git_config_set(argv[1], value);
+ }
case 4:
if (!strcmp(argv[1], "--unset"))
return git_config_set_multivar(argv[2], NULL, argv[3], 0);
@@ -227,17 +267,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)
use_key_regexp = 1;
do_all = 1;
return get_value(argv[2], argv[3]);
- } else if (!strcmp(argv[1], "--add"))
- return git_config_set_multivar(argv[2], argv[3], "^$", 0);
- else if (!strcmp(argv[1], "--replace-all"))
-
- return git_config_set_multivar(argv[2], argv[3], NULL, 1);
- else
-
- return git_config_set_multivar(argv[1], argv[2], argv[3], 0);
+ } else if (!strcmp(argv[1], "--add")) {
+ value = normalize_value(argv[2], argv[3]);
+ return git_config_set_multivar(argv[2], value, "^$", 0);
+ } else if (!strcmp(argv[1], "--replace-all")) {
+ value = normalize_value(argv[2], argv[3]);
+ return git_config_set_multivar(argv[2], value, NULL, 1);
+ } else {
+ value = normalize_value(argv[1], argv[2]);
+ return git_config_set_multivar(argv[1], value, argv[3], 0);
+ }
case 5:
- if (!strcmp(argv[1], "--replace-all"))
- return git_config_set_multivar(argv[2], argv[3], argv[4], 1);
+ if (!strcmp(argv[1], "--replace-all")) {
+ value = normalize_value(argv[2], argv[3]);
+ return git_config_set_multivar(argv[2], value, argv[4], 1);
+ }
case 1:
default:
usage(git_config_set_usage);
diff --git a/builtin-count-objects.c b/builtin-count-objects.c
index ff90ebd465..4274ec1950 100644
--- a/builtin-count-objects.c
+++ b/builtin-count-objects.c
@@ -111,6 +111,8 @@ int cmd_count_objects(int ac, const char **av, const char *prefix)
for (p = packed_git; p; p = p->next) {
if (!p->pack_local)
continue;
+ if (open_pack_index(p))
+ continue;
packed += p->num_objects;
num_pack++;
}
diff --git a/builtin-describe.c b/builtin-describe.c
index 165917e40d..669110cb06 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -3,6 +3,7 @@
#include "tag.h"
#include "refs.h"
#include "builtin.h"
+#include "exec_cmd.h"
#define SEEN (1u<<0)
#define MAX_TAGS (FLAG_BITS - 1)
@@ -242,12 +243,15 @@ static void describe(const char *arg, int last_one)
int cmd_describe(int argc, const char **argv, const char *prefix)
{
int i;
+ int contains = 0;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (*arg != '-')
break;
+ else if (!strcmp(arg, "--contains"))
+ contains = 1;
else if (!strcmp(arg, "--debug"))
debug = 1;
else if (!strcmp(arg, "--all"))
@@ -272,6 +276,16 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
save_commit_buffer = 0;
+ if (contains) {
+ const char **args = xmalloc((4 + argc - i) * sizeof(char*));
+ args[0] = "name-rev";
+ args[1] = "--name-only";
+ args[2] = "--tags";
+ memcpy(args + 3, argv + i, (argc - i) * sizeof(char*));
+ args[3 + argc - i] = NULL;
+ return cmd_name_rev(3 + argc - i, args, prefix);
+ }
+
if (argc <= i)
describe("HEAD", 1);
else
diff --git a/builtin-diff-index.c b/builtin-diff-index.c
index d90eba95a6..81e7167438 100644
--- a/builtin-diff-index.c
+++ b/builtin-diff-index.c
@@ -23,7 +23,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
argc = setup_revisions(argc, argv, &rev, NULL);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
-
+
if (!strcmp(arg, "--cached"))
cached = 1;
else
diff --git a/builtin-diff.c b/builtin-diff.c
index 7f367b6b9d..6ed7b6842e 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -222,6 +222,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
prefix = setup_git_directory_gently(&nongit);
git_config(git_diff_ui_config);
init_revisions(&rev, prefix);
+ rev.diffopt.skip_stat_unmatch = 1;
if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix))
argc = 0;
@@ -233,6 +234,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
die("diff_setup_done failed");
}
rev.diffopt.allow_external = 1;
+ rev.diffopt.recursive = 1;
+
+ /* If the user asked for our exit code then don't start a
+ * pager or we would end up reporting its exit code instead.
+ */
+ if (!rev.diffopt.exit_with_status)
+ setup_pager();
/* Do we have --cached and not have a pending object, then
* default to HEAD by hand. Eek.
@@ -337,5 +345,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
ent, ents);
if (rev.diffopt.exit_with_status)
result = rev.diffopt.has_changes;
+
+ if ((rev.diffopt.output_format & DIFF_FORMAT_PATCH)
+ && (1 < rev.diffopt.skip_stat_unmatch))
+ printf("Warning: %d path%s touched but unmodified. "
+ "Consider running git-status.\n",
+ rev.diffopt.skip_stat_unmatch - 1,
+ rev.diffopt.skip_stat_unmatch == 2 ? "" : "s");
return result;
}
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index 12adb3833c..e2f8ede9ae 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "commit.h"
@@ -42,7 +43,7 @@ static int update_ref(const char *action,
if (!rla)
rla = "(reflog update)";
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
- lock = lock_any_ref_for_update(refname, oldval);
+ lock = lock_any_ref_for_update(refname, oldval, 0);
if (!lock)
return 1;
if (write_ref_sha1(lock, sha1, msg) < 0)
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index 5c145d2165..ae60fccea7 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -357,4 +357,3 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
return 0;
}
-
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index 2b218425aa..0afa1c5c41 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "object.h"
@@ -796,7 +797,7 @@ static struct ref_sort *default_sort(void)
return sort;
}
-int cmd_for_each_ref(int ac, const char **av, char *prefix)
+int cmd_for_each_ref(int ac, const char **av, const char *prefix)
{
int i, num_refs;
const char *format = NULL;
diff --git a/builtin-fsck.c b/builtin-fsck.c
index 44ce629a49..8d12287f03 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "commit.h"
#include "tree.h"
@@ -20,6 +21,8 @@ static int check_strict;
static int keep_cache_objects;
static unsigned char head_sha1[20];
static int errors_found;
+static int write_lost_and_found;
+static int verbose;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
@@ -137,6 +140,31 @@ static void check_unreachable_object(struct object *obj)
if (!obj->used) {
printf("dangling %s %s\n", typename(obj->type),
sha1_to_hex(obj->sha1));
+ if (write_lost_and_found) {
+ char *filename = git_path("lost-found/%s/%s",
+ obj->type == OBJ_COMMIT ? "commit" : "other",
+ sha1_to_hex(obj->sha1));
+ FILE *f;
+
+ if (safe_create_leading_directories(filename)) {
+ error("Could not create lost-found");
+ return;
+ }
+ if (!(f = fopen(filename, "w")))
+ die("Could not open %s", filename);
+ if (obj->type == OBJ_BLOB) {
+ enum object_type type;
+ unsigned long size;
+ char *buf = read_sha1_file(obj->sha1,
+ &type, &size);
+ if (buf) {
+ fwrite(buf, size, 1, f);
+ free(buf);
+ }
+ } else
+ fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
+ fclose(f);
+ }
return;
}
@@ -149,6 +177,9 @@ static void check_unreachable_object(struct object *obj)
static void check_object(struct object *obj)
{
+ if (verbose)
+ fprintf(stderr, "Checking %s\n", sha1_to_hex(obj->sha1));
+
if (obj->flags & REACHABLE)
check_reachable_object(obj);
else
@@ -161,6 +192,9 @@ static void check_connectivity(void)
/* Look up all the requirements, warn about missing objects.. */
max = get_max_object_index();
+ if (verbose)
+ fprintf(stderr, "Checking connectivity (%d objects)\n", max);
+
for (i = 0; i < max; i++) {
struct object *obj = get_indexed_object(i);
@@ -229,6 +263,10 @@ static int fsck_tree(struct tree *item)
const char *o_name;
const unsigned char *o_sha1;
+ if (verbose)
+ fprintf(stderr, "Checking tree %s\n",
+ sha1_to_hex(item->object.sha1));
+
init_tree_desc(&desc, item->buffer, item->size);
o_mode = 0;
@@ -256,7 +294,7 @@ static int fsck_tree(struct tree *item)
case S_IFREG | 0644:
case S_IFLNK:
case S_IFDIR:
- case S_IFDIRLNK:
+ case S_IFGITLINK:
break;
/*
* This is nonstandard, but we had a few of these
@@ -317,6 +355,10 @@ static int fsck_commit(struct commit *commit)
char *buffer = commit->buffer;
unsigned char tree_sha1[20], sha1[20];
+ if (verbose)
+ fprintf(stderr, "Checking commit %s\n",
+ sha1_to_hex(commit->object.sha1));
+
if (memcmp(buffer, "tree ", 5))
return objerror(&commit->object, "invalid format - expected 'tree' line");
if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
@@ -336,7 +378,7 @@ static int fsck_commit(struct commit *commit)
if (!commit->parents && show_root)
printf("root %s\n", sha1_to_hex(commit->object.sha1));
if (!commit->date)
- printf("bad commit date in %s\n",
+ printf("bad commit date in %s\n",
sha1_to_hex(commit->object.sha1));
return 0;
}
@@ -345,6 +387,10 @@ static int fsck_tag(struct tag *tag)
{
struct object *tagged = tag->tagged;
+ if (verbose)
+ fprintf(stderr, "Checking tag %s\n",
+ sha1_to_hex(tag->object.sha1));
+
if (!tagged) {
return objerror(&tag->object, "could not load tagged object");
}
@@ -446,6 +492,9 @@ static void fsck_dir(int i, char *path)
if (!dir)
return;
+ if (verbose)
+ fprintf(stderr, "Checking directory %s\n", path);
+
while ((de = readdir(dir)) != NULL) {
char name[100];
unsigned char sha1[20];
@@ -480,6 +529,10 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
{
struct object *obj;
+ if (verbose)
+ fprintf(stderr, "Checking reflog %s->%s\n",
+ sha1_to_hex(osha1), sha1_to_hex(nsha1));
+
if (!is_null_sha1(osha1)) {
obj = lookup_object(osha1);
if (obj) {
@@ -549,6 +602,10 @@ static void get_default_heads(void)
static void fsck_object_dir(const char *path)
{
int i;
+
+ if (verbose)
+ fprintf(stderr, "Checking object directory\n");
+
for (i = 0; i < 256; i++) {
static char dir[4096];
sprintf(dir, "%s/%02x", path, i);
@@ -564,6 +621,9 @@ static int fsck_head_link(void)
int null_is_error = 0;
const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
+ if (verbose)
+ fprintf(stderr, "Checking HEAD link\n");
+
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
@@ -586,6 +646,9 @@ static int fsck_cache_tree(struct cache_tree *it)
int i;
int err = 0;
+ if (verbose)
+ fprintf(stderr, "Checking cache tree\n");
+
if (0 <= it->entry_count) {
struct object *obj = parse_object(it->sha1);
if (!obj) {
@@ -605,9 +668,9 @@ static int fsck_cache_tree(struct cache_tree *it)
static const char fsck_usage[] =
"git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] "
-"[--strict] <head-sha1>*]";
+"[--strict] [--verbose] <head-sha1>*]";
-int cmd_fsck(int argc, char **argv, const char *prefix)
+int cmd_fsck(int argc, const char **argv, const char *prefix)
{
int i, heads;
@@ -645,6 +708,16 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
check_strict = 1;
continue;
}
+ if (!strcmp(arg, "--verbose")) {
+ verbose = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--lost-found")) {
+ check_full = 1;
+ include_reflogs = 0;
+ write_lost_and_found = 1;
+ continue;
+ }
if (*arg == '-')
usage(fsck_usage);
}
@@ -668,7 +741,10 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
verify_pack(p, 0);
for (p = packed_git; p; p = p->next) {
- uint32_t i, num = p->num_objects;
+ uint32_t i, num;
+ if (open_pack_index(p))
+ continue;
+ num = p->num_objects;
for (i = 0; i < num; i++)
fsck_sha1(nth_packed_object_sha1(p, i));
}
@@ -676,7 +752,7 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
heads = 0;
for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ const char *arg = argv[i];
if (*arg == '-')
continue;
@@ -715,7 +791,7 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
struct object *obj;
mode = ntohl(active_cache[i]->ce_mode);
- if (S_ISDIRLNK(mode))
+ if (S_ISGITLINK(mode))
continue;
blob = lookup_blob(active_cache[i]->sha1);
if (!blob)
diff --git a/builtin-gc.c b/builtin-gc.c
index 3b1f8c2f3e..9397482610 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -10,18 +10,21 @@
* Copyright (c) 2006 Shawn O. Pearce
*/
+#include "builtin.h"
#include "cache.h"
#include "run-command.h"
#define FAILED_RUN "failed to run %s"
-static const char builtin_gc_usage[] = "git-gc [--prune]";
+static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
-static int pack_refs = -1;
+static int pack_refs = 1;
+static int aggressive_window = -1;
-static const char *argv_pack_refs[] = {"pack-refs", "--prune", NULL};
+#define MAX_ADD 10
+static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
-static const char *argv_repack[] = {"repack", "-a", "-d", "-l", NULL};
+static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL};
static const char *argv_prune[] = {"prune", NULL};
static const char *argv_rerere[] = {"rerere", "gc", NULL};
@@ -34,13 +37,31 @@ static int gc_config(const char *var, const char *value)
pack_refs = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "gc.aggressivewindow")) {
+ aggressive_window = git_config_int(var, value);
+ return 0;
+ }
return git_default_config(var, value);
}
+static void append_option(const char **cmd, const char *opt, int max_length)
+{
+ int i;
+
+ for (i = 0; cmd[i]; i++)
+ ;
+
+ if (i + 2 >= max_length)
+ die("Too many options specified");
+ cmd[i++] = opt;
+ cmd[i] = NULL;
+}
+
int cmd_gc(int argc, const char **argv, const char *prefix)
{
int i;
int prune = 0;
+ char buf[80];
git_config(gc_config);
@@ -53,6 +74,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
prune = 1;
continue;
}
+ if (!strcmp(arg, "--aggressive")) {
+ append_option(argv_repack, "-f", MAX_ADD);
+ if (aggressive_window > 0) {
+ sprintf(buf, "--window=%d", aggressive_window);
+ append_option(argv_repack, buf, MAX_ADD);
+ }
+ continue;
+ }
/* perhaps other parameters later... */
break;
}
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 4df9fd0fad..0d9b1e0559 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -7,7 +7,7 @@
#include "builtin.h"
#ifndef DEFAULT_GIT_TEMPLATE_DIR
-#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates/"
+#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
#endif
#ifdef NO_TRUSTABLE_FILEMODE
@@ -40,7 +40,8 @@ static int copy_file(const char *dst, const char *src, int mode)
return fdo;
}
status = copy_fd(fdi, fdo);
- close(fdo);
+ if (close(fdo) != 0)
+ return error("%s: write error: %s", dst, strerror(errno));
if (!status && adjust_shared_perm(dst))
return -1;
@@ -252,20 +253,22 @@ static int create_default_files(const char *git_dir, const char *template_path)
}
git_config_set("core.filemode", filemode ? "true" : "false");
- if (is_bare_repository()) {
+ if (is_bare_repository())
git_config_set("core.bare", "true");
- }
else {
+ const char *work_tree = get_git_work_tree();
git_config_set("core.bare", "false");
/* allow template config file to override the default */
if (log_all_ref_updates == -1)
git_config_set("core.logallrefupdates", "true");
+ if (work_tree != git_work_tree_cfg)
+ git_config_set("core.worktree", work_tree);
}
return reinit;
}
static const char init_db_usage[] =
-"git-init [--template=<template-directory>] [--shared]";
+"git-init [-q | --quiet] [--template=<template-directory>] [--shared]";
/*
* If you want to, you can share the DB area with any number of branches.
@@ -280,6 +283,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *template_dir = NULL;
char *path;
int len, i, reinit;
+ int quiet = 0;
for (i = 1; i < argc; i++, argv++) {
const char *arg = argv[1];
@@ -289,10 +293,18 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
shared_repository = PERM_GROUP;
else if (!prefixcmp(arg, "--shared="))
shared_repository = git_config_perm("arg", arg+9);
+ else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
+ quiet = 1;
else
usage(init_db_usage);
}
+ git_work_tree_cfg = xcalloc(PATH_MAX, 1);
+ if (!getcwd(git_work_tree_cfg, PATH_MAX))
+ die ("Cannot access current working directory.");
+ if (access(get_git_work_tree(), X_OK))
+ die ("Cannot access work tree '%s'", get_git_work_tree());
+
/*
* Set up the default .git directory contents
*/
@@ -335,10 +347,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
git_config_set("receive.denyNonFastforwards", "true");
}
- printf("%s%s Git repository in %s/\n",
- reinit ? "Reinitialized existing" : "Initialized empty",
- shared_repository ? " shared" : "",
- git_dir);
+ if (!quiet)
+ printf("%s%s Git repository in %s/\n",
+ reinit ? "Reinitialized existing" : "Initialized empty",
+ shared_repository ? " shared" : "",
+ git_dir);
return 0;
}
diff --git a/builtin-log.c b/builtin-log.c
index 37447123f9..13bae3110e 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -16,6 +16,7 @@
#include "refs.h"
static int default_show_root = 1;
+static const char *fmt_patch_subject_prefix = "PATCH";
/* this is in builtin-diff.c */
void add_head(struct rev_info *revs);
@@ -55,18 +56,18 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->commit_format = CMIT_FMT_DEFAULT;
rev->verbose_header = 1;
rev->show_root_diff = default_show_root;
+ rev->subject_prefix = fmt_patch_subject_prefix;
argc = setup_revisions(argc, argv, rev, "HEAD");
if (rev->diffopt.pickaxe || rev->diffopt.filter)
rev->always_show_header = 0;
+ if (rev->diffopt.follow_renames) {
+ rev->always_show_header = 0;
+ if (rev->diffopt.nr_paths != 1)
+ usage("git logs can only follow renames on one pathname at a time");
+ }
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
- if (!prefixcmp(arg, "--encoding=")) {
- arg += 11;
- if (strcmp(arg, "none"))
- git_log_output_encoding = xstrdup(arg);
- else
- git_log_output_encoding = "";
- } else if (!strcmp(arg, "--decorate")) {
+ if (!strcmp(arg, "--decorate")) {
if (!decorate)
for_each_ref(add_ref_decoration, NULL);
decorate = 1;
@@ -95,6 +96,12 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value)
{
+ if (!strcmp(var, "format.subjectprefix")) {
+ if (!value)
+ die("format.subjectprefix without value");
+ fmt_patch_subject_prefix = xstrdup(value);
+ return 0;
+ }
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
@@ -291,6 +298,7 @@ static int git_format_config(const char *var, const char *value)
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
return 0;
}
+
return git_log_config(var, value);
}
@@ -298,7 +306,8 @@ static int git_format_config(const char *var, const char *value)
static FILE *realstdout = NULL;
static const char *output_directory = NULL;
-static int reopen_stdout(struct commit *commit, int nr, int keep_subject)
+static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
+ int numbered_files)
{
char filename[PATH_MAX];
char *sol;
@@ -315,53 +324,61 @@ static int reopen_stdout(struct commit *commit, int nr, int keep_subject)
filename[len++] = '/';
}
- sprintf(filename + len, "%04d", nr);
- len = strlen(filename);
-
- sol = strstr(commit->buffer, "\n\n");
- if (sol) {
- int j, space = 1;
-
- sol += 2;
- /* strip [PATCH] or [PATCH blabla] */
- if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
- char *eos = strchr(sol + 6, ']');
- if (eos) {
- while (isspace(*eos))
- eos++;
- sol = eos;
- }
- }
+ if (numbered_files) {
+ sprintf(filename + len, "%d", nr);
+ len = strlen(filename);
+
+ } else {
+ sprintf(filename + len, "%04d", nr);
+ len = strlen(filename);
- for (j = 0;
- j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
- len < sizeof(filename) - suffix_len &&
- sol[j] && sol[j] != '\n';
- j++) {
- if (istitlechar(sol[j])) {
- if (space) {
- filename[len++] = '-';
- space = 0;
+ sol = strstr(commit->buffer, "\n\n");
+ if (sol) {
+ int j, space = 1;
+
+ sol += 2;
+ /* strip [PATCH] or [PATCH blabla] */
+ if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
+ char *eos = strchr(sol + 6, ']');
+ if (eos) {
+ while (isspace(*eos))
+ eos++;
+ sol = eos;
}
- filename[len++] = sol[j];
- if (sol[j] == '.')
- while (sol[j + 1] == '.')
- j++;
- } else
- space = 1;
+ }
+
+ for (j = 0;
+ j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
+ len < sizeof(filename) - suffix_len &&
+ sol[j] && sol[j] != '\n';
+ j++) {
+ if (istitlechar(sol[j])) {
+ if (space) {
+ filename[len++] = '-';
+ space = 0;
+ }
+ filename[len++] = sol[j];
+ if (sol[j] == '.')
+ while (sol[j + 1] == '.')
+ j++;
+ } else
+ space = 1;
+ }
+ while (filename[len - 1] == '.'
+ || filename[len - 1] == '-')
+ len--;
+ filename[len] = 0;
}
- while (filename[len - 1] == '.' || filename[len - 1] == '-')
- len--;
- filename[len] = 0;
+ if (len + suffix_len >= sizeof(filename))
+ return error("Patch pathname too long");
+ strcpy(filename + len, fmt_patch_suffix);
}
- if (len + suffix_len >= sizeof(filename))
- return error("Patch pathname too long");
- strcpy(filename + len, fmt_patch_suffix);
+
fprintf(realstdout, "%s\n", filename);
if (freopen(filename, "w", stdout) == NULL)
return error("Cannot open patch file %s",filename);
- return 0;
+ return 0;
}
static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
@@ -431,6 +448,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int numbered = 0;
int start_number = -1;
int keep_subject = 0;
+ int numbered_files = 0; /* _just_ numbers */
int subject_prefix = 0;
int ignore_if_in_upstream = 0;
int thread = 0;
@@ -450,6 +468,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.msg_sep = "";
rev.diffopt.recursive = 1;
+ rev.subject_prefix = fmt_patch_subject_prefix;
rev.extra_headers = extra_headers;
/*
@@ -465,6 +484,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
numbered = 1;
else if (!prefixcmp(argv[i], "--start-number="))
start_number = strtol(argv[i] + 15, NULL, 10);
+ else if (!strcmp(argv[i], "--numbered-files"))
+ numbered_files = 1;
else if (!strcmp(argv[i], "--start-number")) {
i++;
if (i == argc)
@@ -540,6 +561,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
die ("-n and -k are mutually exclusive.");
if (keep_subject && subject_prefix)
die ("--subject-prefix and -k are mutually exclusive.");
+ if (numbered_files && use_stdout)
+ die ("--numbered-files and --stdout are mutually exclusive.");
argc = setup_revisions(argc, argv, &rev, "HEAD");
if (argc > 1)
@@ -576,7 +599,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
get_patch_ids(&rev, &ids, prefix);
if (!use_stdout)
- realstdout = fdopen(dup(1), "w");
+ realstdout = xfdopen(xdup(1), "w");
prepare_revision_walk(&rev);
while ((commit = get_revision(&rev)) != NULL) {
@@ -614,7 +637,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.message_id = message_id;
}
if (!use_stdout)
- if (reopen_stdout(commit, rev.nr, keep_subject))
+ if (reopen_stdout(commit, rev.nr, keep_subject,
+ numbered_files))
die("Failed to create output files");
shown = log_tree_commit(&rev, commit);
free(commit->buffer);
@@ -733,11 +757,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
sign = '-';
if (verbose) {
- static char buf[16384];
+ char *buf = NULL;
+ unsigned long buflen = 0;
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
- buf, sizeof(buf), 0, NULL, NULL, 0);
+ &buf, &buflen, 0, NULL, NULL, 0);
printf("%c %s %s\n", sign,
sha1_to_hex(commit->object.sha1), buf);
+ free(buf);
}
else {
printf("%c %s\n", sign,
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index f7c066b24b..d36181a755 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -117,7 +117,7 @@ static void show_other_files(struct dir_struct *dir)
if (0 <= pos)
continue; /* exact match */
pos = -pos - 1;
- if (pos < active_nr) {
+ if (pos < active_nr) {
ce = active_cache[pos];
if (ce_namelen(ce) == len &&
!memcmp(ce->name, ent->name, len))
@@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
break;
}
- if (require_work_tree &&
- (is_bare_repository() || is_inside_git_dir()))
- die("This operation must be run in a work tree");
+ if (require_work_tree && !is_inside_work_tree()) {
+ const char *work_tree = get_git_work_tree();
+ if (!work_tree || chdir(work_tree))
+ die("This operation must be run in a work tree");
+ }
pathspec = get_pathspec(prefix, argv + i);
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
index 1cb4dca277..cb4be4fabb 100644
--- a/builtin-ls-tree.c
+++ b/builtin-ls-tree.c
@@ -15,6 +15,7 @@ static int line_termination = '\n';
#define LS_TREE_ONLY 2
#define LS_SHOW_TREES 4
#define LS_NAME_ONLY 8
+#define LS_SHOW_SIZE 16
static int abbrev;
static int ls_options;
static const char **pathspec;
@@ -22,7 +23,7 @@ static int chomp_prefix;
static const char *ls_tree_prefix;
static const char ls_tree_usage[] =
- "git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
+ "git-ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
static int show_recursive(const char *base, int baselen, const char *pathname)
{
@@ -59,8 +60,9 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
{
int retval = 0;
const char *type = blob_type;
+ unsigned long size;
- if (S_ISDIRLNK(mode)) {
+ if (S_ISGITLINK(mode)) {
/*
* Maybe we want to have some recursive version here?
*
@@ -92,10 +94,24 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
(baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
return 0;
- if (!(ls_options & LS_NAME_ONLY))
- printf("%06o %s %s\t", mode, type,
- abbrev ? find_unique_abbrev(sha1,abbrev)
- : sha1_to_hex(sha1));
+ if (!(ls_options & LS_NAME_ONLY)) {
+ if (ls_options & LS_SHOW_SIZE) {
+ if (!strcmp(type, blob_type)) {
+ sha1_object_info(sha1, &size);
+ printf("%06o %s %s %7lu\t", mode, type,
+ abbrev ? find_unique_abbrev(sha1, abbrev)
+ : sha1_to_hex(sha1),
+ size);
+ } else
+ printf("%06o %s %s %7c\t", mode, type,
+ abbrev ? find_unique_abbrev(sha1, abbrev)
+ : sha1_to_hex(sha1),
+ '-');
+ } else
+ printf("%06o %s %s\t", mode, type,
+ abbrev ? find_unique_abbrev(sha1, abbrev)
+ : sha1_to_hex(sha1));
+ }
write_name_quoted(base + chomp_prefix, baselen - chomp_prefix,
pathname,
line_termination, stdout);
@@ -126,12 +142,19 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
case 't':
ls_options |= LS_SHOW_TREES;
break;
+ case 'l':
+ ls_options |= LS_SHOW_SIZE;
+ break;
case '-':
if (!strcmp(argv[1]+2, "name-only") ||
!strcmp(argv[1]+2, "name-status")) {
ls_options |= LS_NAME_ONLY;
break;
}
+ if (!strcmp(argv[1]+2, "long")) {
+ ls_options |= LS_SHOW_SIZE;
+ break;
+ }
if (!strcmp(argv[1]+2, "full-name")) {
chomp_prefix = 0;
break;
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index c95e477e83..b558754142 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -237,8 +237,6 @@ static int eatspace(char *line)
static char *cleanup_subject(char *subject)
{
- if (keep_subject)
- return subject;
for (;;) {
char *p;
int len, remove;
@@ -425,6 +423,7 @@ static int read_one_header_line(char *line, int sz, FILE *in)
if (addlen >= sz - len)
addlen = sz - len - 1;
memcpy(line + len, continuation, addlen);
+ line[len] = '\n';
len += addlen;
}
}
@@ -499,15 +498,42 @@ static int decode_b_segment(char *in, char *ot, char *ep)
return 0;
}
+/*
+ * When there is no known charset, guess.
+ *
+ * Right now we assume that if the target is UTF-8 (the default),
+ * and it already looks like UTF-8 (which includes US-ASCII as its
+ * subset, of course) then that is what it is and there is nothing
+ * to do.
+ *
+ * Otherwise, we default to assuming it is Latin1 for historical
+ * reasons.
+ */
+static const char *guess_charset(const char *line, const char *target_charset)
+{
+ if (is_encoding_utf8(target_charset)) {
+ if (is_utf8(line))
+ return NULL;
+ }
+ return "latin1";
+}
+
static void convert_to_utf8(char *line, const char *charset)
{
- static const char latin_one[] = "latin1";
- const char *input_charset = *charset ? charset : latin_one;
- char *out = reencode_string(line, metainfo_charset, input_charset);
+ char *out;
+
+ if (!charset || !*charset) {
+ charset = guess_charset(line, metainfo_charset);
+ if (!charset)
+ return;
+ }
+ if (!strcmp(metainfo_charset, charset))
+ return;
+ out = reencode_string(line, metainfo_charset, charset);
if (!out)
die("cannot convert from %s to %s\n",
- input_charset, metainfo_charset);
+ charset, metainfo_charset);
strcpy(line, out);
free(out);
}
@@ -819,6 +845,22 @@ static void handle_body(void)
return;
}
+static void output_header_lines(FILE *fout, const char *hdr, char *data)
+{
+ while (1) {
+ char *ep = strchr(data, '\n');
+ int len;
+ if (!ep)
+ len = strlen(data);
+ else
+ len = ep - data;
+ fprintf(fout, "%s: %.*s\n", hdr, len, data);
+ if (!ep)
+ break;
+ data = ep + 1;
+ }
+}
+
static void handle_info(void)
{
char *sub;
@@ -836,9 +878,13 @@ static void handle_info(void)
continue;
if (!memcmp(header[i], "Subject", 7)) {
- sub = cleanup_subject(hdr);
- cleanup_space(sub);
- fprintf(fout, "Subject: %s\n", sub);
+ if (keep_subject)
+ sub = hdr;
+ else {
+ sub = cleanup_subject(hdr);
+ cleanup_space(sub);
+ }
+ output_header_lines(fout, "Subject", sub);
} else if (!memcmp(header[i], "From", 4)) {
handle_from(hdr);
fprintf(fout, "Author: %s\n", name);
@@ -851,8 +897,8 @@ static void handle_info(void)
fprintf(fout, "\n");
}
-int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
- const char *msg, const char *patch)
+static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
+ const char *msg, const char *patch)
{
keep_subject = ks;
metainfo_charset = encoding;
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
index 3bca855aae..43fc373a15 100644
--- a/builtin-mailsplit.c
+++ b/builtin-mailsplit.c
@@ -6,9 +6,10 @@
*/
#include "cache.h"
#include "builtin.h"
+#include "path-list.h"
static const char git_mailsplit_usage[] =
-"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>...";
+"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>|<Maildir>...";
static int is_from_line(const char *line, int len)
{
@@ -96,44 +97,107 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
exit(1);
}
-int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip)
+static int populate_maildir_list(struct path_list *list, const char *path)
{
- char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip));
+ DIR *dir;
+ struct dirent *dent;
+
+ if ((dir = opendir(path)) == NULL) {
+ error("cannot opendir %s (%s)", path, strerror(errno));
+ return -1;
+ }
+
+ while ((dent = readdir(dir)) != NULL) {
+ if (dent->d_name[0] == '.')
+ continue;
+ path_list_insert(dent->d_name, list);
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+static int split_maildir(const char *maildir, const char *dir,
+ int nr_prec, int skip)
+{
+ char file[PATH_MAX];
+ char curdir[PATH_MAX];
+ char name[PATH_MAX];
int ret = -1;
+ int i;
+ struct path_list list = {NULL, 0, 0, 1};
- while (*mbox) {
- const char *file = *mbox++;
- FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
- int file_done = 0;
+ snprintf(curdir, sizeof(curdir), "%s/cur", maildir);
+ if (populate_maildir_list(&list, curdir) < 0)
+ goto out;
- if ( !f ) {
- error("cannot open mbox %s", file);
+ for (i = 0; i < list.nr; i++) {
+ FILE *f;
+ snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path);
+ f = fopen(file, "r");
+ if (!f) {
+ error("cannot open mail %s (%s)", file, strerror(errno));
goto out;
}
if (fgets(buf, sizeof(buf), f) == NULL) {
- if (f == stdin)
- break; /* empty stdin is OK */
- error("cannot read mbox %s", file);
+ error("cannot read mail %s (%s)", file, strerror(errno));
goto out;
}
- while (!file_done) {
- sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
- file_done = split_one(f, name, allow_bare);
+ sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+ split_one(f, name, 1);
+
+ fclose(f);
+ }
+
+ path_list_clear(&list, 1);
+
+ ret = skip;
+out:
+ return ret;
+}
+
+static int split_mbox(const char *file, const char *dir, int allow_bare,
+ int nr_prec, int skip)
+{
+ char name[PATH_MAX];
+ int ret = -1;
+
+ FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
+ int file_done = 0;
+
+ if (!f) {
+ error("cannot open mbox %s", file);
+ goto out;
+ }
+
+ if (fgets(buf, sizeof(buf), f) == NULL) {
+ /* empty stdin is OK */
+ if (f != stdin) {
+ error("cannot read mbox %s", file);
+ goto out;
}
+ file_done = 1;
+ }
- if (f != stdin)
- fclose(f);
+ while (!file_done) {
+ sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+ file_done = split_one(f, name, allow_bare);
}
+
+ if (f != stdin)
+ fclose(f);
+
ret = skip;
out:
- free(name);
return ret;
}
+
int cmd_mailsplit(int argc, const char **argv, const char *prefix)
{
- int nr = 0, nr_prec = 4, ret;
+ int nr = 0, nr_prec = 4, num = 0;
int allow_bare = 0;
const char *dir = NULL;
const char **argp;
@@ -186,9 +250,41 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
argp = stdin_only;
}
- ret = split_mbox(argp, dir, allow_bare, nr_prec, nr);
- if (ret != -1)
- printf("%d\n", ret);
+ while (*argp) {
+ const char *arg = *argp++;
+ struct stat argstat;
+ int ret = 0;
+
+ if (arg[0] == '-' && arg[1] == 0) {
+ ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+ if (ret < 0) {
+ error("cannot split patches from stdin");
+ return 1;
+ }
+ num += (ret - nr);
+ nr = ret;
+ continue;
+ }
+
+ if (stat(arg, &argstat) == -1) {
+ error("cannot stat %s (%s)", arg, strerror(errno));
+ return 1;
+ }
+
+ if (S_ISDIR(argstat.st_mode))
+ ret = split_maildir(arg, dir, nr_prec, nr);
+ else
+ ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
+
+ if (ret < 0) {
+ error("cannot split patches from %s", arg);
+ return 1;
+ }
+ num += (ret - nr);
+ nr = ret;
+ }
+
+ printf("%d\n", num);
- return ret == -1;
+ return 0;
}
diff --git a/builtin-merge-base.c b/builtin-merge-base.c
index e35d362f26..0108e22ade 100644
--- a/builtin-merge-base.c
+++ b/builtin-merge-base.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "commit.h"
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 9135773908..58deb62ac0 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
@@ -5,9 +6,9 @@
static const char merge_file_usage[] =
"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
-int cmd_merge_file(int argc, char **argv, char **envp)
+int cmd_merge_file(int argc, const char **argv, const char *prefix)
{
- char *names[3];
+ const char *names[3];
mmfile_t mmfs[3];
mmbuffer_t result = {NULL, 0};
xpparam_t xpp = {XDF_NEED_MINIMAL};
@@ -36,9 +37,13 @@ int cmd_merge_file(int argc, char **argv, char **envp)
for (; i < 3; i++)
names[i] = argv[i + 1];
- for (i = 0; i < 3; i++)
+ for (i = 0; i < 3; i++) {
if (read_mmfile(mmfs + i, argv[i + 1]))
return -1;
+ if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
+ return error("Cannot merge binary files: %s\n",
+ argv[i + 1]);
+ }
ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
&xpp, XDL_MERGE_ZEALOUS, &result);
@@ -47,7 +52,7 @@ int cmd_merge_file(int argc, char **argv, char **envp)
free(mmfs[i].ptr);
if (ret >= 0) {
- char *filename = argv[1];
+ const char *filename = argv[1];
FILE *f = to_stdout ? stdout : fopen(filename, "wb");
if (!f)
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index 2d94eaaa6a..61eba343ab 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -85,6 +85,7 @@ copy_data:
struct name_ref_data {
int tags_only;
+ int name_only;
const char *ref_filter;
};
@@ -112,6 +113,10 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
if (!prefixcmp(path, "refs/heads/"))
path = path + 11;
+ else if (data->tags_only
+ && data->name_only
+ && !prefixcmp(path, "refs/tags/"))
+ path = path + 10;
else if (!prefixcmp(path, "refs/"))
path = path + 5;
@@ -151,7 +156,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = { 0, 0, NULL };
int as_is = 0, all = 0, transform_stdin = 0;
- struct name_ref_data data = { 0, NULL };
+ struct name_ref_data data = { 0, 0, NULL };
git_config(git_default_config);
@@ -167,6 +172,9 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
if (!strcmp(*argv, "--")) {
as_is = 1;
continue;
+ } else if (!strcmp(*argv, "--name-only")) {
+ data.name_only = 1;
+ continue;
} else if (!strcmp(*argv, "--tags")) {
data.tags_only = 1;
continue;
@@ -267,16 +275,18 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
struct object * obj = get_indexed_object(i);
if (!obj)
continue;
- printf("%s %s\n", sha1_to_hex(obj->sha1), get_rev_name(obj));
+ if (!data.name_only)
+ printf("%s ", sha1_to_hex(obj->sha1));
+ printf("%s\n", get_rev_name(obj));
}
} else {
int i;
- for (i = 0; i < revs.nr; i++)
- printf("%s %s\n",
- revs.objects[i].name,
- get_rev_name(revs.objects[i].item));
+ for (i = 0; i < revs.nr; i++) {
+ if (!data.name_only)
+ printf("%s ", revs.objects[i].name);
+ printf("%s\n", get_rev_name(revs.objects[i].item));
+ }
}
return 0;
}
-
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 966f843e43..51a850e111 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "attr.h"
#include "object.h"
#include "blob.h"
#include "commit.h"
@@ -16,18 +17,15 @@
static const char pack_usage[] = "\
git-pack-objects [{ -q | --progress | --all-progress }] \n\
- [--local] [--incremental] [--window=N] [--depth=N] \n\
- [--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\
- [--revs [--unpacked | --all]*] [--reflog] [--stdout | base-name] \n\
- [<ref-list | <object-list]";
+ [--max-pack-size=N] [--local] [--incremental] \n\
+ [--window=N] [--window-memory=N] [--depth=N] \n\
+ [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
+ [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
+ [--stdout | base-name] [<ref-list | <object-list]";
struct object_entry {
- unsigned char sha1[20];
- uint32_t crc32; /* crc of raw pack data for this object */
- off_t offset; /* offset into the final pack file */
+ struct pack_idx_entry idx;
unsigned long size; /* uncompressed size */
- unsigned int hash; /* name hint hash */
- unsigned int depth; /* delta depth */
struct packed_git *in_pack; /* already in pack */
off_t in_pack_offset;
struct object_entry *delta; /* delta base object */
@@ -35,14 +33,17 @@ struct object_entry {
struct object_entry *delta_sibling; /* other deltified objects who
* uses the same base as me
*/
+ void *delta_data; /* cached delta (uncompressed) */
unsigned long delta_size; /* delta data size (uncompressed) */
+ unsigned int hash; /* name hint hash */
enum object_type type;
enum object_type in_pack_type; /* could be delta */
unsigned char in_pack_header_size;
unsigned char preferred_base; /* we do not pack this, but is available
- * to be used as the base objectto delta
+ * to be used as the base object to delta
* objects against.
*/
+ unsigned char no_try_delta;
};
/*
@@ -52,22 +53,33 @@ struct object_entry {
* nice "minimum seek" order.
*/
static struct object_entry *objects;
-static uint32_t nr_objects, nr_alloc, nr_result;
+static struct object_entry **written_list;
+static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
static int non_empty;
-static int no_reuse_delta;
+static int no_reuse_delta, no_reuse_object;
static int local;
static int incremental;
static int allow_ofs_delta;
static const char *pack_tmp_name, *idx_tmp_name;
static char tmpname[PATH_MAX];
-static unsigned char pack_file_sha1[20];
+static const char *base_name;
static int progress = 1;
static int window = 10;
+static uint32_t pack_size_limit;
static int depth = 50;
static int pack_to_stdout;
static int num_preferred_base;
static struct progress progress_state;
+static int pack_compression_level = Z_DEFAULT_COMPRESSION;
+static int pack_compression_seen;
+
+static unsigned long delta_cache_size = 0;
+static unsigned long max_delta_cache_size = 0;
+static unsigned long cache_max_small_delta_size = 1000;
+
+static unsigned long window_memory_usage = 0;
+static unsigned long window_memory_limit = 0;
/*
* The object names in objects array are hashed with this hashtable,
@@ -234,15 +246,15 @@ static void *delta_against(void *buf, unsigned long size, struct object_entry *e
{
unsigned long othersize, delta_size;
enum object_type type;
- void *otherbuf = read_sha1_file(entry->delta->sha1, &type, &othersize);
+ void *otherbuf = read_sha1_file(entry->delta->idx.sha1, &type, &othersize);
void *delta_buf;
if (!otherbuf)
- die("unable to read %s", sha1_to_hex(entry->delta->sha1));
+ die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
delta_buf = diff_delta(otherbuf, othersize,
buf, size, &delta_size, 0);
if (!delta_buf || delta_size != entry->delta_size)
- die("delta size changed");
+ die("delta size changed");
free(buf);
free(otherbuf);
return delta_buf;
@@ -346,76 +358,45 @@ static void copy_pack_data(struct sha1file *f,
}
}
-static int check_loose_inflate(unsigned char *data, unsigned long len, unsigned long expect)
-{
- z_stream stream;
- unsigned char fakebuf[4096];
- int st;
-
- memset(&stream, 0, sizeof(stream));
- stream.next_in = data;
- stream.avail_in = len;
- stream.next_out = fakebuf;
- stream.avail_out = sizeof(fakebuf);
- inflateInit(&stream);
-
- while (1) {
- st = inflate(&stream, Z_FINISH);
- if (st == Z_STREAM_END || st == Z_OK) {
- st = (stream.total_out == expect &&
- stream.total_in == len) ? 0 : -1;
- break;
- }
- if (st != Z_BUF_ERROR) {
- st = -1;
- break;
- }
- stream.next_out = fakebuf;
- stream.avail_out = sizeof(fakebuf);
- }
- inflateEnd(&stream);
- return st;
-}
-
-static int revalidate_loose_object(struct object_entry *entry,
- unsigned char *map,
- unsigned long mapsize)
-{
- /* we already know this is a loose object with new type header. */
- enum object_type type;
- unsigned long size, used;
-
- if (pack_to_stdout)
- return 0;
-
- used = unpack_object_header_gently(map, mapsize, &type, &size);
- if (!used)
- return -1;
- map += used;
- mapsize -= used;
- return check_loose_inflate(map, mapsize, size);
-}
-
static unsigned long write_object(struct sha1file *f,
- struct object_entry *entry)
+ struct object_entry *entry,
+ off_t write_offset)
{
unsigned long size;
enum object_type type;
void *buf;
unsigned char header[10];
+ unsigned char dheader[10];
unsigned hdrlen;
off_t datalen;
enum object_type obj_type;
int to_reuse = 0;
+ /* write limit if limited packsize and not first object */
+ unsigned long limit = pack_size_limit && nr_written ?
+ pack_size_limit - write_offset : 0;
+ /* no if no delta */
+ int usable_delta = !entry->delta ? 0 :
+ /* yes if unlimited packfile */
+ !pack_size_limit ? 1 :
+ /* no if base written to previous pack */
+ entry->delta->idx.offset == (off_t)-1 ? 0 :
+ /* otherwise double-check written to this
+ * pack, like we do below
+ */
+ entry->delta->idx.offset ? 1 : 0;
if (!pack_to_stdout)
crc32_begin(f);
obj_type = entry->type;
- if (! entry->in_pack)
+ if (no_reuse_object)
+ to_reuse = 0; /* explicit */
+ else if (!entry->in_pack)
to_reuse = 0; /* can't reuse what we don't have */
else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
- to_reuse = 1; /* check_object() decided it for us */
+ /* check_object() decided it for us ... */
+ to_reuse = usable_delta;
+ /* ... but pack split may override that */
else if (obj_type != entry->in_pack_type)
to_reuse = 0; /* pack has delta which is unusable */
else if (entry->delta)
@@ -425,44 +406,49 @@ static unsigned long write_object(struct sha1file *f,
* and we do not need to deltify it.
*/
- if (!entry->in_pack && !entry->delta) {
- unsigned char *map;
- unsigned long mapsize;
- map = map_sha1_file(entry->sha1, &mapsize);
- if (map && !legacy_loose_object(map)) {
- /* We can copy straight into the pack file */
- if (revalidate_loose_object(entry, map, mapsize))
- die("corrupt loose object %s",
- sha1_to_hex(entry->sha1));
- sha1write(f, map, mapsize);
- munmap(map, mapsize);
- written++;
- reused++;
- return mapsize;
- }
- if (map)
- munmap(map, mapsize);
- }
-
if (!to_reuse) {
- buf = read_sha1_file(entry->sha1, &type, &size);
- if (!buf)
- die("unable to read %s", sha1_to_hex(entry->sha1));
- if (size != entry->size)
- die("object %s size inconsistency (%lu vs %lu)",
- sha1_to_hex(entry->sha1), size, entry->size);
- if (entry->delta) {
+ z_stream stream;
+ unsigned long maxsize;
+ void *out;
+ if (!usable_delta) {
+ buf = read_sha1_file(entry->idx.sha1, &obj_type, &size);
+ if (!buf)
+ die("unable to read %s", sha1_to_hex(entry->idx.sha1));
+ } else if (entry->delta_data) {
+ size = entry->delta_size;
+ buf = entry->delta_data;
+ entry->delta_data = NULL;
+ obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
+ OBJ_OFS_DELTA : OBJ_REF_DELTA;
+ } else {
+ buf = read_sha1_file(entry->idx.sha1, &type, &size);
+ if (!buf)
+ die("unable to read %s", sha1_to_hex(entry->idx.sha1));
buf = delta_against(buf, size, entry);
size = entry->delta_size;
- obj_type = (allow_ofs_delta && entry->delta->offset) ?
+ obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
OBJ_OFS_DELTA : OBJ_REF_DELTA;
}
+ /* compress the data to store and put compressed length in datalen */
+ memset(&stream, 0, sizeof(stream));
+ deflateInit(&stream, pack_compression_level);
+ maxsize = deflateBound(&stream, size);
+ out = xmalloc(maxsize);
+ /* Compress it */
+ stream.next_in = buf;
+ stream.avail_in = size;
+ stream.next_out = out;
+ stream.avail_out = maxsize;
+ while (deflate(&stream, Z_FINISH) == Z_OK)
+ /* nothing */;
+ deflateEnd(&stream);
+ datalen = stream.total_out;
+ deflateEnd(&stream);
/*
* The object header is a byte of 'type' followed by zero or
* more bytes of length.
*/
hdrlen = encode_header(obj_type, size, header);
- sha1write(f, header, hdrlen);
if (obj_type == OBJ_OFS_DELTA) {
/*
@@ -470,22 +456,42 @@ static unsigned long write_object(struct sha1file *f,
* encoding of the relative offset for the delta
* base from this object's position in the pack.
*/
- off_t ofs = entry->offset - entry->delta->offset;
- unsigned pos = sizeof(header) - 1;
- header[pos] = ofs & 127;
+ off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+ unsigned pos = sizeof(dheader) - 1;
+ dheader[pos] = ofs & 127;
while (ofs >>= 7)
- header[--pos] = 128 | (--ofs & 127);
- sha1write(f, header + pos, sizeof(header) - pos);
- hdrlen += sizeof(header) - pos;
+ dheader[--pos] = 128 | (--ofs & 127);
+ if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+ free(out);
+ free(buf);
+ return 0;
+ }
+ sha1write(f, header, hdrlen);
+ sha1write(f, dheader + pos, sizeof(dheader) - pos);
+ hdrlen += sizeof(dheader) - pos;
} else if (obj_type == OBJ_REF_DELTA) {
/*
* Deltas with a base reference contain
* an additional 20 bytes for the base sha1.
*/
- sha1write(f, entry->delta->sha1, 20);
+ if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+ free(out);
+ free(buf);
+ return 0;
+ }
+ sha1write(f, header, hdrlen);
+ sha1write(f, entry->delta->idx.sha1, 20);
hdrlen += 20;
+ } else {
+ if (limit && hdrlen + datalen + 20 >= limit) {
+ free(out);
+ free(buf);
+ return 0;
+ }
+ sha1write(f, header, hdrlen);
}
- datalen = sha1write_compressed(f, buf, size);
+ sha1write(f, out, datalen);
+ free(out);
free(buf);
}
else {
@@ -495,45 +501,54 @@ static unsigned long write_object(struct sha1file *f,
off_t offset;
if (entry->delta) {
- obj_type = (allow_ofs_delta && entry->delta->offset) ?
+ obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
OBJ_OFS_DELTA : OBJ_REF_DELTA;
reused_delta++;
}
hdrlen = encode_header(obj_type, entry->size, header);
- sha1write(f, header, hdrlen);
- if (obj_type == OBJ_OFS_DELTA) {
- off_t ofs = entry->offset - entry->delta->offset;
- unsigned pos = sizeof(header) - 1;
- header[pos] = ofs & 127;
- while (ofs >>= 7)
- header[--pos] = 128 | (--ofs & 127);
- sha1write(f, header + pos, sizeof(header) - pos);
- hdrlen += sizeof(header) - pos;
- } else if (obj_type == OBJ_REF_DELTA) {
- sha1write(f, entry->delta->sha1, 20);
- hdrlen += 20;
- }
-
offset = entry->in_pack_offset;
revidx = find_packed_object(p, offset);
datalen = revidx[1].offset - offset;
if (!pack_to_stdout && p->index_version > 1 &&
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
- die("bad packed object CRC for %s", sha1_to_hex(entry->sha1));
+ die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
offset += entry->in_pack_header_size;
datalen -= entry->in_pack_header_size;
+ if (obj_type == OBJ_OFS_DELTA) {
+ off_t ofs = entry->idx.offset - entry->delta->idx.offset;
+ unsigned pos = sizeof(dheader) - 1;
+ dheader[pos] = ofs & 127;
+ while (ofs >>= 7)
+ dheader[--pos] = 128 | (--ofs & 127);
+ if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
+ return 0;
+ sha1write(f, header, hdrlen);
+ sha1write(f, dheader + pos, sizeof(dheader) - pos);
+ hdrlen += sizeof(dheader) - pos;
+ } else if (obj_type == OBJ_REF_DELTA) {
+ if (limit && hdrlen + 20 + datalen + 20 >= limit)
+ return 0;
+ sha1write(f, header, hdrlen);
+ sha1write(f, entry->delta->idx.sha1, 20);
+ hdrlen += 20;
+ } else {
+ if (limit && hdrlen + datalen + 20 >= limit)
+ return 0;
+ sha1write(f, header, hdrlen);
+ }
+
if (!pack_to_stdout && p->index_version == 1 &&
check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
- die("corrupt packed object for %s", sha1_to_hex(entry->sha1));
+ die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
copy_pack_data(f, p, &w_curs, offset, datalen);
unuse_pack(&w_curs);
reused++;
}
- if (entry->delta)
+ if (usable_delta)
written_delta++;
written++;
if (!pack_to_stdout)
- entry->crc32 = crc32_end(f);
+ entry->idx.crc32 = crc32_end(f);
return hdrlen + datalen;
}
@@ -544,15 +559,23 @@ static off_t write_one(struct sha1file *f,
unsigned long size;
/* offset is non zero if object is written already. */
- if (e->offset || e->preferred_base)
+ if (e->idx.offset || e->preferred_base)
return offset;
/* if we are deltified, write out base object first. */
- if (e->delta)
+ if (e->delta) {
offset = write_one(f, e->delta, offset);
+ if (!offset)
+ return 0;
+ }
- e->offset = offset;
- size = write_object(f, e);
+ e->idx.offset = offset;
+ size = write_object(f, e, offset);
+ if (!size) {
+ e->idx.offset = 0;
+ return 0;
+ }
+ written_list[nr_written++] = e;
/* make sure off_t is sufficiently large not to wrap */
if (offset > offset + size)
@@ -563,177 +586,115 @@ static off_t write_one(struct sha1file *f,
static int open_object_dir_tmp(const char *path)
{
snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path);
- return mkstemp(tmpname);
+ return xmkstemp(tmpname);
}
-static off_t write_pack_file(void)
+/* forward declaration for write_pack_file */
+static int adjust_perm(const char *path, mode_t mode);
+
+static void write_pack_file(void)
{
- uint32_t i;
+ uint32_t i = 0, j;
struct sha1file *f;
- off_t offset, last_obj_offset = 0;
+ off_t offset, offset_one, last_obj_offset = 0;
struct pack_header hdr;
- int do_progress = progress;
-
- if (pack_to_stdout) {
- f = sha1fd(1, "<stdout>");
- do_progress >>= 1;
- } else {
- int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
- if (fd < 0)
- die("unable to create %s: %s\n", tmpname, strerror(errno));
- pack_tmp_name = xstrdup(tmpname);
- f = sha1fd(fd, pack_tmp_name);
- }
+ int do_progress = progress >> pack_to_stdout;
+ uint32_t nr_remaining = nr_result;
if (do_progress)
start_progress(&progress_state, "Writing %u objects...", "", nr_result);
+ written_list = xmalloc(nr_objects * sizeof(struct object_entry *));
- hdr.hdr_signature = htonl(PACK_SIGNATURE);
- hdr.hdr_version = htonl(PACK_VERSION);
- hdr.hdr_entries = htonl(nr_result);
- sha1write(f, &hdr, sizeof(hdr));
- offset = sizeof(hdr);
- if (!nr_result)
- goto done;
- for (i = 0; i < nr_objects; i++) {
- last_obj_offset = offset;
- offset = write_one(f, objects + i, offset);
- if (do_progress)
- display_progress(&progress_state, written);
- }
- if (do_progress)
- stop_progress(&progress_state);
- done:
- if (written != nr_result)
- die("wrote %u objects while expecting %u", written, nr_result);
- sha1close(f, pack_file_sha1, 1);
-
- return last_obj_offset;
-}
-
-static int sha1_sort(const void *_a, const void *_b)
-{
- const struct object_entry *a = *(struct object_entry **)_a;
- const struct object_entry *b = *(struct object_entry **)_b;
- return hashcmp(a->sha1, b->sha1);
-}
-
-static uint32_t index_default_version = 1;
-static uint32_t index_off32_limit = 0x7fffffff;
-
-static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
-{
- struct sha1file *f;
- struct object_entry **sorted_by_sha, **list, **last;
- uint32_t array[256];
- uint32_t i, index_version;
- SHA_CTX ctx;
-
- int fd = open_object_dir_tmp("tmp_idx_XXXXXX");
- if (fd < 0)
- die("unable to create %s: %s\n", tmpname, strerror(errno));
- idx_tmp_name = xstrdup(tmpname);
- f = sha1fd(fd, idx_tmp_name);
-
- if (nr_result) {
- uint32_t j = 0;
- sorted_by_sha =
- xcalloc(nr_result, sizeof(struct object_entry *));
- for (i = 0; i < nr_objects; i++)
- if (!objects[i].preferred_base)
- sorted_by_sha[j++] = objects + i;
- if (j != nr_result)
- die("listed %u objects while expecting %u", j, nr_result);
- qsort(sorted_by_sha, nr_result, sizeof(*sorted_by_sha), sha1_sort);
- list = sorted_by_sha;
- last = sorted_by_sha + nr_result;
- } else
- sorted_by_sha = list = last = NULL;
-
- /* if last object's offset is >= 2^31 we should use index V2 */
- index_version = (last_obj_offset >> 31) ? 2 : index_default_version;
+ do {
+ unsigned char sha1[20];
+
+ if (pack_to_stdout) {
+ f = sha1fd(1, "<stdout>");
+ } else {
+ int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
+ pack_tmp_name = xstrdup(tmpname);
+ f = sha1fd(fd, pack_tmp_name);
+ }
- /* index versions 2 and above need a header */
- if (index_version >= 2) {
- struct pack_idx_header hdr;
- hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
- hdr.idx_version = htonl(index_version);
+ hdr.hdr_signature = htonl(PACK_SIGNATURE);
+ hdr.hdr_version = htonl(PACK_VERSION);
+ hdr.hdr_entries = htonl(nr_remaining);
sha1write(f, &hdr, sizeof(hdr));
- }
-
- /*
- * Write the first-level table (the list is sorted,
- * but we use a 256-entry lookup to be able to avoid
- * having to do eight extra binary search iterations).
- */
- for (i = 0; i < 256; i++) {
- struct object_entry **next = list;
- while (next < last) {
- struct object_entry *entry = *next;
- if (entry->sha1[0] != i)
+ offset = sizeof(hdr);
+ nr_written = 0;
+ for (; i < nr_objects; i++) {
+ last_obj_offset = offset;
+ offset_one = write_one(f, objects + i, offset);
+ if (!offset_one)
break;
- next++;
+ offset = offset_one;
+ if (do_progress)
+ display_progress(&progress_state, written);
}
- array[i] = htonl(next - sorted_by_sha);
- list = next;
- }
- sha1write(f, array, 256 * 4);
-
- /* Compute the SHA1 hash of sorted object names. */
- SHA1_Init(&ctx);
-
- /* Write the actual SHA1 entries. */
- list = sorted_by_sha;
- for (i = 0; i < nr_result; i++) {
- struct object_entry *entry = *list++;
- if (index_version < 2) {
- uint32_t offset = htonl(entry->offset);
- sha1write(f, &offset, 4);
- }
- sha1write(f, entry->sha1, 20);
- SHA1_Update(&ctx, entry->sha1, 20);
- }
- if (index_version >= 2) {
- unsigned int nr_large_offset = 0;
-
- /* write the crc32 table */
- list = sorted_by_sha;
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *entry = *list++;
- uint32_t crc32_val = htonl(entry->crc32);
- sha1write(f, &crc32_val, 4);
+ /*
+ * Did we write the wrong # entries in the header?
+ * If so, rewrite it like in fast-import
+ */
+ if (pack_to_stdout || nr_written == nr_remaining) {
+ sha1close(f, sha1, 1);
+ } else {
+ sha1close(f, sha1, 0);
+ fixup_pack_header_footer(f->fd, sha1, pack_tmp_name, nr_written);
+ close(f->fd);
}
- /* write the 32-bit offset table */
- list = sorted_by_sha;
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *entry = *list++;
- uint32_t offset = (entry->offset <= index_off32_limit) ?
- entry->offset : (0x80000000 | nr_large_offset++);
- offset = htonl(offset);
- sha1write(f, &offset, 4);
+ if (!pack_to_stdout) {
+ mode_t mode = umask(0);
+
+ umask(mode);
+ mode = 0444 & ~mode;
+
+ idx_tmp_name = write_idx_file(NULL,
+ (struct pack_idx_entry **) written_list, nr_written, sha1);
+ snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
+ base_name, sha1_to_hex(sha1));
+ if (adjust_perm(pack_tmp_name, mode))
+ die("unable to make temporary pack file readable: %s",
+ strerror(errno));
+ if (rename(pack_tmp_name, tmpname))
+ die("unable to rename temporary pack file: %s",
+ strerror(errno));
+ snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
+ base_name, sha1_to_hex(sha1));
+ if (adjust_perm(idx_tmp_name, mode))
+ die("unable to make temporary index file readable: %s",
+ strerror(errno));
+ if (rename(idx_tmp_name, tmpname))
+ die("unable to rename temporary index file: %s",
+ strerror(errno));
+ puts(sha1_to_hex(sha1));
}
- /* write the large offset table */
- list = sorted_by_sha;
- while (nr_large_offset) {
- struct object_entry *entry = *list++;
- uint64_t offset = entry->offset;
- if (offset > index_off32_limit) {
- uint32_t split[2];
- split[0] = htonl(offset >> 32);
- split[1] = htonl(offset & 0xffffffff);
- sha1write(f, split, 8);
- nr_large_offset--;
- }
+ /* mark written objects as written to previous pack */
+ for (j = 0; j < nr_written; j++) {
+ written_list[j]->idx.offset = (off_t)-1;
}
- }
+ nr_remaining -= nr_written;
+ } while (nr_remaining && i < nr_objects);
- sha1write(f, pack_file_sha1, 20);
- sha1close(f, NULL, 1);
- free(sorted_by_sha);
- SHA1_Final(sha1, &ctx);
+ free(written_list);
+ if (do_progress)
+ stop_progress(&progress_state);
+ if (written != nr_result)
+ die("wrote %u objects while expecting %u", written, nr_result);
+ /*
+ * We have scanned through [0 ... i). Since we have written
+ * the correct number of objects, the remaining [i ... nr_objects)
+ * items must be either already written (due to out-of-order delta base)
+ * or a preferred base. Count those which are neither and complain if any.
+ */
+ for (j = 0; i < nr_objects; i++) {
+ struct object_entry *e = objects + i;
+ j += !e->idx.offset && !e->preferred_base;
+ }
+ if (j)
+ die("wrote %u objects as expected but %u unwritten", written, j);
}
static int locate_object_entry_hash(const unsigned char *sha1)
@@ -743,7 +704,7 @@ static int locate_object_entry_hash(const unsigned char *sha1)
memcpy(&ui, sha1, sizeof(unsigned int));
i = ui % object_ix_hashsz;
while (0 < object_ix[i]) {
- if (!hashcmp(sha1, objects[object_ix[i] - 1].sha1))
+ if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))
return i;
if (++i == object_ix_hashsz)
i = 0;
@@ -775,7 +736,7 @@ static void rehash_objects(void)
object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
- int ix = locate_object_entry_hash(oe->sha1);
+ int ix = locate_object_entry_hash(oe->idx.sha1);
if (0 <= ix)
continue;
ix = -1 - ix;
@@ -788,6 +749,9 @@ static unsigned name_hash(const char *name)
unsigned char c;
unsigned hash = 0;
+ if (!name)
+ return 0;
+
/*
* This effectively just creates a sortable number from the
* last sixteen non-whitespace characters. Last characters
@@ -801,13 +765,36 @@ static unsigned name_hash(const char *name)
return hash;
}
+static void setup_delta_attr_check(struct git_attr_check *check)
+{
+ static struct git_attr *attr_delta;
+
+ if (!attr_delta)
+ attr_delta = git_attr("delta", 5);
+
+ check[0].attr = attr_delta;
+}
+
+static int no_try_delta(const char *path)
+{
+ struct git_attr_check check[1];
+
+ setup_delta_attr_check(check);
+ if (git_checkattr(path, ARRAY_SIZE(check), check))
+ return 0;
+ if (ATTR_FALSE(check->value))
+ return 1;
+ return 0;
+}
+
static int add_object_entry(const unsigned char *sha1, enum object_type type,
- unsigned hash, int exclude)
+ const char *name, int exclude)
{
struct object_entry *entry;
struct packed_git *p, *found_pack = NULL;
off_t found_offset = 0;
int ix;
+ unsigned hash = name_hash(name);
ix = nr_objects ? locate_object_entry_hash(sha1) : -1;
if (ix >= 0) {
@@ -843,7 +830,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
entry = objects + nr_objects++;
memset(entry, 0, sizeof(*entry));
- hashcpy(entry->sha1, sha1);
+ hashcpy(entry->idx.sha1, sha1);
entry->hash = hash;
if (type)
entry->type = type;
@@ -864,6 +851,9 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
if (progress)
display_progress(&progress_state, nr_objects);
+ if (name && no_try_delta(name))
+ entry->no_try_delta = 1;
+
return 1;
}
@@ -996,10 +986,9 @@ static void add_pbase_object(struct tree_desc *tree,
if (cmp < 0)
return;
if (name[cmplen] != '/') {
- unsigned hash = name_hash(fullname);
add_object_entry(entry.sha1,
S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB,
- hash, 1);
+ fullname, 1);
return;
}
if (S_ISDIR(entry.mode)) {
@@ -1059,10 +1048,11 @@ static int check_pbase_path(unsigned hash)
return 0;
}
-static void add_preferred_base_object(const char *name, unsigned hash)
+static void add_preferred_base_object(const char *name)
{
struct pbase_tree *it;
int cmplen;
+ unsigned hash = name_hash(name);
if (!num_preferred_base || check_pbase_path(hash))
return;
@@ -1070,7 +1060,7 @@ static void add_preferred_base_object(const char *name, unsigned hash)
cmplen = name_cmp_len(name);
for (it = pbase_tree; it; it = it->next) {
if (cmplen == 0) {
- add_object_entry(it->pcache.sha1, OBJ_TREE, 0, 1);
+ add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
}
else {
struct tree_desc tree;
@@ -1125,8 +1115,8 @@ static void check_object(struct object_entry *entry)
buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail);
/*
- * We want in_pack_type even if we do not reuse delta.
- * There is no point not reusing non-delta representations.
+ * We want in_pack_type even if we do not reuse delta
+ * since non-delta representations could still be reused.
*/
used = unpack_object_header_gently(buf, avail,
&entry->in_pack_type,
@@ -1160,13 +1150,13 @@ static void check_object(struct object_entry *entry)
ofs += 1;
if (!ofs || MSB(ofs, 7))
die("delta base offset overflow in pack for %s",
- sha1_to_hex(entry->sha1));
+ sha1_to_hex(entry->idx.sha1));
c = buf[used_0++];
ofs = (ofs << 7) + (c & 127);
}
if (ofs >= entry->in_pack_offset)
die("delta base offset out of bound for %s",
- sha1_to_hex(entry->sha1));
+ sha1_to_hex(entry->idx.sha1));
ofs = entry->in_pack_offset - ofs;
if (!no_reuse_delta && !entry->preferred_base)
base_ref = find_packed_object_name(p, ofs);
@@ -1213,10 +1203,10 @@ static void check_object(struct object_entry *entry)
unuse_pack(&w_curs);
}
- entry->type = sha1_object_info(entry->sha1, &entry->size);
+ entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
if (entry->type < 0)
die("unable to get type of object %s",
- sha1_to_hex(entry->sha1));
+ sha1_to_hex(entry->idx.sha1));
}
static int pack_offset_sort(const void *_a, const void *_b)
@@ -1226,7 +1216,7 @@ static int pack_offset_sort(const void *_a, const void *_b)
/* avoid filesystem trashing with loose objects */
if (!a->in_pack && !b->in_pack)
- return hashcmp(a->sha1, b->sha1);
+ return hashcmp(a->idx.sha1, b->idx.sha1);
if (a->in_pack < b->in_pack)
return -1;
@@ -1280,8 +1270,26 @@ struct unpacked {
struct object_entry *entry;
void *data;
struct delta_index *index;
+ unsigned depth;
};
+static int delta_cacheable(struct unpacked *trg, struct unpacked *src,
+ unsigned long src_size, unsigned long trg_size,
+ unsigned long delta_size)
+{
+ if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size)
+ return 0;
+
+ if (delta_size < cache_max_small_delta_size)
+ return 1;
+
+ /* cache delta, if objects are large enough compared to delta size */
+ if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
+ return 1;
+
+ return 0;
+}
+
/*
* We search for deltas _backwards_ in a list sorted by type and
* by size, so that we see progressively smaller and smaller files.
@@ -1296,6 +1304,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
struct object_entry *trg_entry = trg->entry;
struct object_entry *src_entry = src->entry;
unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
+ unsigned ref_depth;
enum object_type type;
void *delta_buf;
@@ -1320,49 +1329,79 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
/* Let's not bust the allowed depth. */
- if (src_entry->depth >= max_depth)
+ if (src->depth >= max_depth)
return 0;
/* Now some size filtering heuristics. */
trg_size = trg_entry->size;
- max_size = trg_size/2 - 20;
- max_size = max_size * (max_depth - src_entry->depth) / max_depth;
+ if (!trg_entry->delta) {
+ max_size = trg_size/2 - 20;
+ ref_depth = 1;
+ } else {
+ max_size = trg_entry->delta_size;
+ ref_depth = trg->depth;
+ }
+ max_size = max_size * (max_depth - src->depth) /
+ (max_depth - ref_depth + 1);
if (max_size == 0)
return 0;
- if (trg_entry->delta && trg_entry->delta_size <= max_size)
- max_size = trg_entry->delta_size-1;
src_size = src_entry->size;
sizediff = src_size < trg_size ? trg_size - src_size : 0;
if (sizediff >= max_size)
return 0;
+ if (trg_size < src_size / 32)
+ return 0;
/* Load data if not already done */
if (!trg->data) {
- trg->data = read_sha1_file(trg_entry->sha1, &type, &sz);
+ trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
if (sz != trg_size)
die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(trg_entry->sha1), sz, trg_size);
+ sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
+ window_memory_usage += sz;
}
if (!src->data) {
- src->data = read_sha1_file(src_entry->sha1, &type, &sz);
+ src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
if (sz != src_size)
die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(src_entry->sha1), sz, src_size);
+ sha1_to_hex(src_entry->idx.sha1), sz, src_size);
+ window_memory_usage += sz;
}
if (!src->index) {
src->index = create_delta_index(src->data, src_size);
- if (!src->index)
- die("out of memory");
+ if (!src->index) {
+ static int warned = 0;
+ if (!warned++)
+ warning("suboptimal pack - out of memory");
+ return 0;
+ }
+ window_memory_usage += sizeof_delta_index(src->index);
}
delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
if (!delta_buf)
return 0;
+ if (trg_entry->delta_data) {
+ /* Prefer only shallower same-sized deltas. */
+ if (delta_size == trg_entry->delta_size &&
+ src->depth + 1 >= trg->depth) {
+ free(delta_buf);
+ return 0;
+ }
+ delta_cache_size -= trg_entry->delta_size;
+ free(trg_entry->delta_data);
+ trg_entry->delta_data = NULL;
+ }
trg_entry->delta = src_entry;
trg_entry->delta_size = delta_size;
- trg_entry->depth = src_entry->depth + 1;
- free(delta_buf);
+ trg->depth = src->depth + 1;
+
+ if (delta_cacheable(src, trg, src_size, trg_size, delta_size)) {
+ trg_entry->delta_data = xrealloc(delta_buf, delta_size);
+ delta_cache_size += trg_entry->delta_size;
+ } else
+ free(delta_buf);
return 1;
}
@@ -1379,9 +1418,23 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
return m;
}
+static void free_unpacked(struct unpacked *n)
+{
+ window_memory_usage -= sizeof_delta_index(n->index);
+ free_delta_index(n->index);
+ n->index = NULL;
+ if (n->data) {
+ free(n->data);
+ n->data = NULL;
+ window_memory_usage -= n->entry->size;
+ }
+ n->entry = NULL;
+ n->depth = 0;
+}
+
static void find_deltas(struct object_entry **list, int window, int depth)
{
- uint32_t i = nr_objects, idx = 0, processed = 0;
+ uint32_t i = nr_objects, idx = 0, count = 0, processed = 0;
unsigned int array_size = window * sizeof(struct unpacked);
struct unpacked *array;
int max_depth;
@@ -1412,12 +1465,21 @@ static void find_deltas(struct object_entry **list, int window, int depth)
if (entry->size < 50)
continue;
- free_delta_index(n->index);
- n->index = NULL;
- free(n->data);
- n->data = NULL;
+
+ if (entry->no_try_delta)
+ continue;
+
+ free_unpacked(n);
n->entry = entry;
+ while (window_memory_limit &&
+ window_memory_usage > window_memory_limit &&
+ count > 1) {
+ uint32_t tail = (idx + window - count) % window;
+ free_unpacked(array + tail);
+ count--;
+ }
+
/*
* If the current object is at pack edge, take the depth the
* objects that depend on the current object into account
@@ -1447,11 +1509,13 @@ static void find_deltas(struct object_entry **list, int window, int depth)
* depth, leaving it in the window is pointless. we
* should evict it first.
*/
- if (entry->delta && depth <= entry->depth)
+ if (entry->delta && depth <= n->depth)
continue;
next:
idx++;
+ if (count + 1 < window)
+ count++;
if (idx >= window)
idx = 0;
} while (i > 0);
@@ -1490,10 +1554,32 @@ static int git_pack_config(const char *k, const char *v)
window = git_config_int(k, v);
return 0;
}
- if(!strcmp(k, "pack.depth")) {
+ if (!strcmp(k, "pack.windowmemory")) {
+ window_memory_limit = git_config_ulong(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "pack.depth")) {
depth = git_config_int(k, v);
return 0;
}
+ if (!strcmp(k, "pack.compression")) {
+ int level = git_config_int(k, v);
+ if (level == -1)
+ level = Z_DEFAULT_COMPRESSION;
+ else if (level < 0 || level > Z_BEST_COMPRESSION)
+ die("bad pack compression level %d", level);
+ pack_compression_level = level;
+ pack_compression_seen = 1;
+ return 0;
+ }
+ if (!strcmp(k, "pack.deltacachesize")) {
+ max_delta_cache_size = git_config_int(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "pack.deltacachelimit")) {
+ cache_max_small_delta_size = git_config_int(k, v);
+ return 0;
+ }
return git_default_config(k, v);
}
@@ -1501,7 +1587,6 @@ static void read_object_list_from_stdin(void)
{
char line[40 + 1 + PATH_MAX + 2];
unsigned char sha1[20];
- unsigned hash;
for (;;) {
if (!fgets(line, sizeof(line), stdin)) {
@@ -1524,22 +1609,20 @@ static void read_object_list_from_stdin(void)
if (get_sha1_hex(line, sha1))
die("expected sha1, got garbage:\n %s", line);
- hash = name_hash(line+41);
- add_preferred_base_object(line+41, hash);
- add_object_entry(sha1, 0, hash, 0);
+ add_preferred_base_object(line+41);
+ add_object_entry(sha1, 0, line+41, 0);
}
}
static void show_commit(struct commit *commit)
{
- add_object_entry(commit->object.sha1, OBJ_COMMIT, 0, 0);
+ add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
}
static void show_object(struct object_array_entry *p)
{
- unsigned hash = name_hash(p->name);
- add_preferred_base_object(p->name, hash);
- add_object_entry(p->item->sha1, p->item->type, hash, 0);
+ add_preferred_base_object(p->name);
+ add_object_entry(p->item->sha1, p->item->type, p->name, 0);
}
static void show_edge(struct commit *commit)
@@ -1592,8 +1675,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
int use_internal_rev_list = 0;
int thin = 0;
uint32_t i;
- off_t last_obj_offset;
- const char *base_name = NULL;
const char **rp_av;
int rp_ac_alloc = 64;
int rp_ac;
@@ -1605,6 +1686,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
rp_ac = 2;
git_config(git_pack_config);
+ if (!pack_compression_seen && core_compression_seen)
+ pack_compression_level = core_compression_level;
progress = isatty(2);
for (i = 1; i < argc; i++) {
@@ -1625,6 +1708,25 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
incremental = 1;
continue;
}
+ if (!prefixcmp(arg, "--compression=")) {
+ char *end;
+ int level = strtoul(arg+14, &end, 0);
+ if (!arg[14] || *end)
+ usage(pack_usage);
+ if (level == -1)
+ level = Z_DEFAULT_COMPRESSION;
+ else if (level < 0 || level > Z_BEST_COMPRESSION)
+ die("bad pack compression level %d", level);
+ pack_compression_level = level;
+ continue;
+ }
+ if (!prefixcmp(arg, "--max-pack-size=")) {
+ char *end;
+ pack_size_limit = strtoul(arg+16, &end, 0) * 1024 * 1024;
+ if (!arg[16] || *end)
+ usage(pack_usage);
+ continue;
+ }
if (!prefixcmp(arg, "--window=")) {
char *end;
window = strtoul(arg+9, &end, 0);
@@ -1632,6 +1734,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
usage(pack_usage);
continue;
}
+ if (!prefixcmp(arg, "--window-memory=")) {
+ if (!git_parse_ulong(arg+16, &window_memory_limit))
+ usage(pack_usage);
+ continue;
+ }
if (!prefixcmp(arg, "--depth=")) {
char *end;
depth = strtoul(arg+8, &end, 0);
@@ -1655,6 +1762,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
no_reuse_delta = 1;
continue;
}
+ if (!strcmp("--no-reuse-object", arg)) {
+ no_reuse_object = no_reuse_delta = 1;
+ continue;
+ }
if (!strcmp("--delta-base-offset", arg)) {
allow_ofs_delta = 1;
continue;
@@ -1688,12 +1799,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
if (!prefixcmp(arg, "--index-version=")) {
char *c;
- index_default_version = strtoul(arg + 16, &c, 10);
- if (index_default_version > 2)
+ pack_idx_default_version = strtoul(arg + 16, &c, 10);
+ if (pack_idx_default_version > 2)
die("bad %s", arg);
if (*c == ',')
- index_off32_limit = strtoul(c+1, &c, 0);
- if (*c || index_off32_limit & 0x80000000)
+ pack_idx_off32_limit = strtoul(c+1, &c, 0);
+ if (*c || pack_idx_off32_limit & 0x80000000)
die("bad %s", arg);
continue;
}
@@ -1719,6 +1830,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (pack_to_stdout != !base_name)
usage(pack_usage);
+ if (pack_to_stdout && pack_size_limit)
+ die("--max-pack-size cannot be used to build a pack for transfer.");
+
if (!pack_to_stdout && thin)
die("--thin cannot be used to build an indexable pack.");
@@ -1744,33 +1858,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
fprintf(stderr, "Result has %u objects.\n", nr_result);
if (nr_result)
prepare_pack(window, depth);
- last_obj_offset = write_pack_file();
- if (!pack_to_stdout) {
- unsigned char object_list_sha1[20];
- mode_t mode = umask(0);
-
- umask(mode);
- mode = 0444 & ~mode;
-
- write_index_file(last_obj_offset, object_list_sha1);
- snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
- base_name, sha1_to_hex(object_list_sha1));
- if (adjust_perm(pack_tmp_name, mode))
- die("unable to make temporary pack file readable: %s",
- strerror(errno));
- if (rename(pack_tmp_name, tmpname))
- die("unable to rename temporary pack file: %s",
- strerror(errno));
- snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
- base_name, sha1_to_hex(object_list_sha1));
- if (adjust_perm(idx_tmp_name, mode))
- die("unable to make temporary index file readable: %s",
- strerror(errno));
- if (rename(idx_tmp_name, tmpname))
- die("unable to rename temporary index file: %s",
- strerror(errno));
- puts(sha1_to_hex(object_list_sha1));
- }
+ write_pack_file();
if (progress)
fprintf(stderr, "Total %u (delta %u), reused %u (delta %u)\n",
written, written_delta, reused, reused_delta);
diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
index d080e30d67..09df4e11a8 100644
--- a/builtin-pack-refs.c
+++ b/builtin-pack-refs.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "object.h"
@@ -12,9 +13,11 @@ struct ref_to_prune {
char name[FLEX_ARRAY];
};
+#define PACK_REFS_PRUNE 0x0001
+#define PACK_REFS_ALL 0x0002
+
struct pack_refs_cb_data {
- int prune;
- int all;
+ unsigned int flags;
struct ref_to_prune *ref_to_prune;
FILE *refs_file;
};
@@ -39,7 +42,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
is_tag_ref = !prefixcmp(path, "refs/tags/");
/* ALWAYS pack refs that were already packed or are tags */
- if (!cb->all && !is_tag_ref && !(flags & REF_ISPACKED))
+ if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED))
return 0;
fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
@@ -53,7 +56,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
}
}
- if (cb->prune && !do_not_prune(flags)) {
+ if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) {
int namelen = strlen(path) + 1;
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
hashcpy(n->sha1, sha1);
@@ -85,26 +88,53 @@ static void prune_refs(struct ref_to_prune *r)
static struct lock_file packed;
-int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+static int pack_refs(unsigned int flags)
{
- int fd, i;
+ int fd;
struct pack_refs_cb_data cbdata;
memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.flags = flags;
+
+ fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
+ cbdata.refs_file = fdopen(fd, "w");
+ if (!cbdata.refs_file)
+ die("unable to create ref-pack file structure (%s)",
+ strerror(errno));
+
+ /* perhaps other traits later as well */
+ fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
+
+ for_each_ref(handle_one_ref, &cbdata);
+ if (ferror(cbdata.refs_file))
+ die("failed to write ref-pack file");
+ if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file))
+ die("failed to write ref-pack file (%s)", strerror(errno));
+ if (commit_lock_file(&packed) < 0)
+ die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+ if (cbdata.flags & PACK_REFS_PRUNE)
+ prune_refs(cbdata.ref_to_prune);
+ return 0;
+}
- cbdata.prune = 1;
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ unsigned int flags;
+
+ flags = PACK_REFS_PRUNE;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--prune")) {
- cbdata.prune = 1; /* now the default */
+ flags |= PACK_REFS_PRUNE; /* now the default */
continue;
}
if (!strcmp(arg, "--no-prune")) {
- cbdata.prune = 0;
+ flags &= ~PACK_REFS_PRUNE;
continue;
}
if (!strcmp(arg, "--all")) {
- cbdata.all = 1;
+ flags |= PACK_REFS_ALL;
continue;
}
/* perhaps other parameters later... */
@@ -113,22 +143,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
if (i != argc)
usage(builtin_pack_refs_usage);
- fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
- cbdata.refs_file = fdopen(fd, "w");
- if (!cbdata.refs_file)
- die("unable to create ref-pack file structure (%s)",
- strerror(errno));
-
- /* perhaps other traits later as well */
- fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
-
- for_each_ref(handle_one_ref, &cbdata);
- fflush(cbdata.refs_file);
- fsync(fd);
- fclose(cbdata.refs_file);
- if (commit_lock_file(&packed) < 0)
- die("unable to overwrite old ref-pack file (%s)", strerror(errno));
- if (cbdata.prune)
- prune_refs(cbdata.ref_to_prune);
- return 0;
+ return pack_refs(flags);
}
diff --git a/builtin-push.c b/builtin-push.c
index cb78401c94..2612f07f74 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -5,17 +5,13 @@
#include "refs.h"
#include "run-command.h"
#include "builtin.h"
-
-#define MAX_URI (16)
+#include "remote.h"
static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
-static int all, tags, force, thin = 1, verbose;
+static int all, force, thin = 1, verbose;
static const char *receivepack;
-#define BUF_SIZE (2084)
-static char buffer[BUF_SIZE];
-
static const char **refspec;
static int refspec_nr;
@@ -27,285 +23,47 @@ static void add_refspec(const char *ref)
refspec_nr = nr;
}
-static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
-{
- /* Ignore the "refs/" at the beginning of the refname */
- ref += 5;
-
- if (!prefixcmp(ref, "tags/"))
- add_refspec(xstrdup(ref));
- return 0;
-}
-
-static void expand_refspecs(void)
-{
- if (all) {
- if (refspec_nr)
- die("cannot mix '--all' and a refspec");
-
- /*
- * No need to expand "--all" - we'll just use
- * the "--all" flag to send-pack
- */
- return;
- }
- if (!tags)
- return;
- for_each_ref(expand_one_ref, NULL);
-}
-
-struct wildcard_cb {
- const char *from_prefix;
- int from_prefix_len;
- const char *to_prefix;
- int to_prefix_len;
- int force;
-};
-
-static int expand_wildcard_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
-{
- struct wildcard_cb *cb = cb_data;
- int len = strlen(ref);
- char *expanded, *newref;
-
- if (len < cb->from_prefix_len ||
- memcmp(cb->from_prefix, ref, cb->from_prefix_len))
- return 0;
- expanded = xmalloc(len * 2 + cb->force +
- (cb->to_prefix_len - cb->from_prefix_len) + 2);
- newref = expanded + cb->force;
- if (cb->force)
- expanded[0] = '+';
- memcpy(newref, ref, len);
- newref[len] = ':';
- memcpy(newref + len + 1, cb->to_prefix, cb->to_prefix_len);
- strcpy(newref + len + 1 + cb->to_prefix_len,
- ref + cb->from_prefix_len);
- add_refspec(expanded);
- return 0;
-}
-
-static int wildcard_ref(const char *ref)
-{
- int len;
- const char *colon;
- struct wildcard_cb cb;
-
- memset(&cb, 0, sizeof(cb));
- if (ref[0] == '+') {
- cb.force = 1;
- ref++;
- }
- len = strlen(ref);
- colon = strchr(ref, ':');
- if (! (colon && ref < colon &&
- colon[-2] == '/' && colon[-1] == '*' &&
- /* "<mine>/<asterisk>:<yours>/<asterisk>" is at least 7 bytes */
- 7 <= len &&
- ref[len-2] == '/' && ref[len-1] == '*') )
- return 0 ;
- cb.from_prefix = ref;
- cb.from_prefix_len = colon - ref - 1;
- cb.to_prefix = colon + 1;
- cb.to_prefix_len = len - (colon - ref) - 2;
- for_each_ref(expand_wildcard_ref, &cb);
- return 1;
-}
-
static void set_refspecs(const char **refs, int nr)
{
- if (nr) {
- int i;
- for (i = 0; i < nr; i++) {
- const char *ref = refs[i];
- if (!strcmp("tag", ref)) {
- char *tag;
- int len;
- if (nr <= ++i)
- die("tag shorthand without <tag>");
- len = strlen(refs[i]) + 11;
- tag = xmalloc(len);
- strcpy(tag, "refs/tags/");
- strcat(tag, refs[i]);
- ref = tag;
- }
- else if (wildcard_ref(ref))
- continue;
- add_refspec(ref);
- }
- }
- expand_refspecs();
-}
-
-static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
-{
- int n = 0;
- FILE *f = fopen(git_path("remotes/%s", repo), "r");
- int has_explicit_refspec = refspec_nr || all || tags;
-
- if (!f)
- return -1;
- while (fgets(buffer, BUF_SIZE, f)) {
- int is_refspec;
- char *s, *p;
-
- if (!prefixcmp(buffer, "URL:")) {
- is_refspec = 0;
- s = buffer + 4;
- } else if (!prefixcmp(buffer, "Push:")) {
- is_refspec = 1;
- s = buffer + 5;
- } else
- continue;
-
- /* Remove whitespace at the head.. */
- while (isspace(*s))
- s++;
- if (!*s)
- continue;
-
- /* ..and at the end */
- p = s + strlen(s);
- while (isspace(p[-1]))
- *--p = 0;
-
- if (!is_refspec) {
- if (n < MAX_URI)
- uri[n++] = xstrdup(s);
- else
- error("more than %d URL's specified, ignoring the rest", MAX_URI);
- }
- else if (is_refspec && !has_explicit_refspec) {
- if (!wildcard_ref(s))
- add_refspec(xstrdup(s));
- }
- }
- fclose(f);
- if (!n)
- die("remote '%s' has no URL", repo);
- return n;
-}
-
-static const char **config_uri;
-static const char *config_repo;
-static int config_repo_len;
-static int config_current_uri;
-static int config_get_refspecs;
-static int config_get_receivepack;
-
-static int get_remote_config(const char* key, const char* value)
-{
- if (!prefixcmp(key, "remote.") &&
- !strncmp(key + 7, config_repo, config_repo_len)) {
- if (!strcmp(key + 7 + config_repo_len, ".url")) {
- if (config_current_uri < MAX_URI)
- config_uri[config_current_uri++] = xstrdup(value);
- else
- error("more than %d URL's specified, ignoring the rest", MAX_URI);
- }
- else if (config_get_refspecs &&
- !strcmp(key + 7 + config_repo_len, ".push")) {
- if (!wildcard_ref(value))
- add_refspec(xstrdup(value));
- }
- else if (config_get_receivepack &&
- !strcmp(key + 7 + config_repo_len, ".receivepack")) {
- if (!receivepack) {
- char *rp = xmalloc(strlen(value) + 16);
- sprintf(rp, "--receive-pack=%s", value);
- receivepack = rp;
- } else
- error("more than one receivepack given, using the first");
- }
- }
- return 0;
-}
-
-static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
-{
- config_repo_len = strlen(repo);
- config_repo = repo;
- config_current_uri = 0;
- config_uri = uri;
- config_get_refspecs = !(refspec_nr || all || tags);
- config_get_receivepack = (receivepack == NULL);
-
- git_config(get_remote_config);
- return config_current_uri;
-}
-
-static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
-{
- const char *slash = strchr(repo, '/');
- int n = slash ? slash - repo : 1000;
- FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
- char *s, *p;
- int len;
-
- if (!f)
- return 0;
- s = fgets(buffer, BUF_SIZE, f);
- fclose(f);
- if (!s)
- return 0;
- while (isspace(*s))
- s++;
- if (!*s)
- return 0;
- p = s + strlen(s);
- while (isspace(p[-1]))
- *--p = 0;
- len = p - s;
- if (slash)
- len += strlen(slash);
- p = xmalloc(len + 1);
- strcpy(p, s);
- if (slash)
- strcat(p, slash);
- uri[0] = p;
- return 1;
-}
-
-/*
- * Read remotes and branches file, fill the push target URI
- * list. If there is no command line refspecs, read Push: lines
- * to set up the *refspec list as well.
- * return the number of push target URIs
- */
-static int read_config(const char *repo, const char *uri[MAX_URI])
-{
- int n;
-
- if (*repo != '/') {
- n = get_remotes_uri(repo, uri);
- if (n > 0)
- return n;
-
- n = get_config_remotes_uri(repo, uri);
- if (n > 0)
- return n;
-
- n = get_branches_uri(repo, uri);
- if (n > 0)
- return n;
+ int i;
+ for (i = 0; i < nr; i++) {
+ const char *ref = refs[i];
+ if (!strcmp("tag", ref)) {
+ char *tag;
+ int len;
+ if (nr <= ++i)
+ die("tag shorthand without <tag>");
+ len = strlen(refs[i]) + 11;
+ tag = xmalloc(len);
+ strcpy(tag, "refs/tags/");
+ strcat(tag, refs[i]);
+ ref = tag;
+ }
+ add_refspec(ref);
}
-
- uri[0] = repo;
- return 1;
}
static int do_push(const char *repo)
{
- const char *uri[MAX_URI];
- int i, n, errs;
+ int i, errs;
int common_argc;
const char **argv;
int argc;
+ struct remote *remote = remote_get(repo);
- n = read_config(repo, uri);
- if (n <= 0)
+ if (!remote)
die("bad repository '%s'", repo);
+ if (remote->receivepack) {
+ char *rp = xmalloc(strlen(remote->receivepack) + 16);
+ sprintf(rp, "--receive-pack=%s", remote->receivepack);
+ receivepack = rp;
+ }
+ if (!refspec && !all && remote->push_refspec_nr) {
+ refspec = remote->push_refspec;
+ refspec_nr = remote->push_refspec_nr;
+ }
+
argv = xmalloc((refspec_nr + 10) * sizeof(char *));
argv[0] = "dummy-send-pack";
argc = 1;
@@ -318,18 +76,23 @@ static int do_push(const char *repo)
common_argc = argc;
errs = 0;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < remote->uri_nr; i++) {
int err;
int dest_argc = common_argc;
int dest_refspec_nr = refspec_nr;
const char **dest_refspec = refspec;
- const char *dest = uri[i];
+ const char *dest = remote->uri[i];
const char *sender = "send-pack";
if (!prefixcmp(dest, "http://") ||
!prefixcmp(dest, "https://"))
sender = "http-push";
- else if (thin)
- argv[dest_argc++] = "--thin";
+ else {
+ char *rem = xmalloc(strlen(remote->name) + 10);
+ sprintf(rem, "--remote=%s", remote->name);
+ argv[dest_argc++] = rem;
+ if (thin)
+ argv[dest_argc++] = "--thin";
+ }
argv[0] = sender;
argv[dest_argc++] = dest;
while (dest_refspec_nr--)
@@ -341,7 +104,7 @@ static int do_push(const char *repo)
if (!err)
continue;
- error("failed to push to '%s'", uri[i]);
+ error("failed to push to '%s'", remote->uri[i]);
switch (err) {
case -ERR_RUN_COMMAND_FORK:
error("unable to fork for %s", sender);
@@ -362,7 +125,7 @@ static int do_push(const char *repo)
int cmd_push(int argc, const char **argv, const char *prefix)
{
int i;
- const char *repo = "origin"; /* default repository */
+ const char *repo = NULL; /* default repository */
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -385,7 +148,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--tags")) {
- tags = 1;
+ add_refspec("refs/tags/*");
continue;
}
if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
@@ -411,5 +174,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
usage(push_usage);
}
set_refspecs(argv + i, argc - i);
+ if (all && refspec)
+ usage(push_usage);
+
return do_push(repo);
}
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 316fb0f8da..f6764b9739 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -13,14 +13,20 @@
#include "dir.h"
#include "builtin.h"
-static struct object_list *trees;
+#define MAX_TREES 4
+static int nr_trees;
+static struct tree *trees[MAX_TREES];
static int list_tree(unsigned char *sha1)
{
- struct tree *tree = parse_tree_indirect(sha1);
+ struct tree *tree;
+
+ if (nr_trees >= 4)
+ return -1;
+ tree = parse_tree_indirect(sha1);
if (!tree)
return -1;
- object_list_append(&tree->object, &trees);
+ trees[nr_trees++] = tree;
return 0;
}
@@ -76,15 +82,14 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
static void prime_cache_tree(void)
{
- struct tree *tree = (struct tree *)trees->item;
- if (!tree)
+ if (!nr_trees)
return;
active_cache_tree = cache_tree();
- prime_cache_tree_rec(active_cache_tree, tree);
+ prime_cache_tree_rec(active_cache_tree, trees[0]);
}
-static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])";
+static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])";
static struct lock_file lock_file;
@@ -92,12 +97,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, newfd, stage = 0;
unsigned char sha1[20];
+ struct tree_desc t[MAX_TREES];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
- setup_git_directory();
git_config(git_default_config);
newfd = hold_locked_index(&lock_file, 1);
@@ -259,7 +264,15 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
opts.head_idx = 1;
}
- unpack_trees(trees, &opts);
+ if (MAX_TREES < nr_trees)
+ die("I cannot read more than %d trees", MAX_TREES);
+
+ for (i = 0; i < nr_trees; i++) {
+ struct tree *tree = trees[i];
+ parse_tree(tree);
+ init_tree_desc(t+i, tree->buffer, tree->size);
+ }
+ unpack_trees(nr_trees, t, &opts);
/*
* When reading only one tree (either the most basic form,
@@ -267,7 +280,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
* valid cache-tree because the index must match exactly
* what came from the tree.
*/
- if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) {
+ if (nr_trees && !opts.prefix && (!opts.merge || (stage == 2))) {
cache_tree_free(&active_cache_tree);
prime_cache_tree();
}
diff --git a/builtin-reflog.c b/builtin-reflog.c
index 4c39f1da98..ce093cad78 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -249,7 +249,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
/* we take the lock for the ref itself to prevent it from
* getting updated.
*/
- lock = lock_any_ref_for_update(ref, sha1);
+ lock = lock_any_ref_for_update(ref, sha1, 0);
if (!lock)
return error("cannot lock ref '%s'", ref);
log_file = xstrdup(git_path("logs/%s", ref));
diff --git a/builtin-rerere.c b/builtin-rerere.c
index 8c2c8bdc18..29d057c98c 100644
--- a/builtin-rerere.c
+++ b/builtin-rerere.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "path-list.h"
#include "xdiff/xdiff.h"
@@ -12,6 +13,9 @@ static const char git_rerere_usage[] =
static int cutoff_noresolve = 15;
static int cutoff_resolve = 60;
+/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
+static int rerere_enabled = -1;
+
static char *merge_rr_path;
static const char *rr_path(const char *name, const char *file)
@@ -57,7 +61,8 @@ static int write_rr(struct path_list *rr, int out_fd)
write_in_full(out_fd, path, length) != length)
die("unable to write rerere record");
}
- close(out_fd);
+ if (close(out_fd) != 0)
+ die("unable to write rerere record");
return commit_lock_file(&write_lock);
}
@@ -164,19 +169,16 @@ static int find_conflict(struct path_list *conflict)
int i;
if (read_cache() < 0)
return error("Could not read index");
- for (i = 0; i + 2 < active_nr; i++) {
- struct cache_entry *e1 = active_cache[i];
- struct cache_entry *e2 = active_cache[i+1];
- struct cache_entry *e3 = active_cache[i+2];
- if (ce_stage(e1) == 1 &&
- ce_stage(e2) == 2 &&
+ for (i = 0; i+1 < active_nr; i++) {
+ struct cache_entry *e2 = active_cache[i];
+ struct cache_entry *e3 = active_cache[i+1];
+ if (ce_stage(e2) == 2 &&
ce_stage(e3) == 3 &&
- ce_same_name(e1, e2) && ce_same_name(e1, e3) &&
- S_ISREG(ntohl(e1->ce_mode)) &&
+ ce_same_name(e2, e3) &&
S_ISREG(ntohl(e2->ce_mode)) &&
S_ISREG(ntohl(e3->ce_mode))) {
- path_list_insert((const char *)e1->name, conflict);
- i += 2;
+ path_list_insert((const char *)e2->name, conflict);
+ i++; /* skip over both #2 and #3 */
}
}
return 0;
@@ -281,8 +283,8 @@ static int diff_two(const char *file1, const char *label1,
printf("--- a/%s\n+++ b/%s\n", label1, label2);
fflush(stdout);
xpp.flags = XDF_NEED_MINIMAL;
+ memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
- xecfg.flags = 0;
ecb.outf = outf;
xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
@@ -386,21 +388,41 @@ static int git_rerere_config(const char *var, const char *value)
cutoff_resolve = git_config_int(var, value);
else if (!strcmp(var, "gc.rerereunresolved"))
cutoff_noresolve = git_config_int(var, value);
+ else if (!strcmp(var, "rerere.enabled"))
+ rerere_enabled = git_config_bool(var, value);
else
return git_default_config(var, value);
return 0;
}
-int cmd_rerere(int argc, const char **argv, const char *prefix)
+static int is_rerere_enabled(void)
{
- struct path_list merge_rr = { NULL, 0, 0, 1 };
- int i, fd = -1;
struct stat st;
+ const char *rr_cache;
+ int rr_cache_exists;
- if (stat(git_path("rr-cache"), &st) || !S_ISDIR(st.st_mode))
+ if (!rerere_enabled)
return 0;
+ rr_cache = git_path("rr-cache");
+ rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
+ if (rerere_enabled < 0)
+ return rr_cache_exists;
+
+ if (!rr_cache_exists &&
+ (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
+ die("Could not create directory %s", rr_cache);
+ return 1;
+}
+
+int cmd_rerere(int argc, const char **argv, const char *prefix)
+{
+ struct path_list merge_rr = { NULL, 0, 0, 1 };
+ int i, fd = -1;
+
git_config(git_rerere_config);
+ if (!is_rerere_enabled())
+ return 0;
merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
@@ -410,6 +432,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
return do_plain_rerere(&merge_rr, fd);
else if (!strcmp(argv[1], "clear")) {
for (i = 0; i < merge_rr.nr; i++) {
+ struct stat st;
const char *name = (const char *)merge_rr.items[i].util;
if (!stat(git_path("rr-cache/%s", name), &st) &&
S_ISDIR(st.st_mode) &&
@@ -434,4 +457,3 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
path_list_clear(&merge_rr, 1);
return 0;
}
-
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 2eb919f39c..ac551d59f3 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -70,21 +70,9 @@ static void show_commit(struct commit *commit)
if (revs.parents) {
struct commit_list *parents = commit->parents;
while (parents) {
- struct object *o = &(parents->item->object);
+ printf(" %s", sha1_to_hex(parents->item->object.sha1));
parents = parents->next;
- if (o->flags & TMP_MARK)
- continue;
- printf(" %s", sha1_to_hex(o->sha1));
- o->flags |= TMP_MARK;
}
- /* TMP_MARK is a general purpose flag that can
- * be used locally, but the user should clean
- * things up after it is done with them.
- */
- for (parents = commit->parents;
- parents;
- parents = parents->next)
- parents->item->object.flags &= ~TMP_MARK;
}
if (revs.commit_format == CMIT_FMT_ONELINE)
putchar(' ');
@@ -92,13 +80,15 @@ static void show_commit(struct commit *commit)
putchar('\n');
if (revs.verbose_header) {
- static char pretty_header[16384];
+ char *buf = NULL;
+ unsigned long buflen = 0;
pretty_print_commit(revs.commit_format, commit, ~0,
- pretty_header, sizeof(pretty_header),
+ &buf, &buflen,
revs.abbrev, NULL, NULL, revs.date_mode);
- printf("%s%c", pretty_header, hdr_termination);
+ printf("%s%c", buf, hdr_termination);
+ free(buf);
}
- fflush(stdout);
+ maybe_flush_or_die(stdout, "stdout");
if (commit->parents) {
free_commit_list(commit->parents);
commit->parents = NULL;
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 37addb25fa..8d78b69c90 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -321,6 +321,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--show-cdup")) {
const char *pfx = prefix;
+ if (!is_inside_work_tree()) {
+ const char *work_tree =
+ get_git_work_tree();
+ if (work_tree)
+ printf("%s\n", work_tree);
+ continue;
+ }
while (pfx) {
pfx = strchr(pfx, '/');
if (pfx) {
@@ -352,6 +359,16 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
: "false");
continue;
}
+ if (!strcmp(arg, "--is-inside-work-tree")) {
+ printf("%s\n", is_inside_work_tree() ? "true"
+ : "false");
+ continue;
+ }
+ if (!strcmp(arg, "--is-bare-repository")) {
+ printf("%s\n", is_bare_repository() ? "true"
+ : "false");
+ continue;
+ }
if (!prefixcmp(arg, "--since=")) {
show_datestring("--max-age=", arg+8);
continue;
diff --git a/builtin-revert.c b/builtin-revert.c
index ea2f15b977..499bbe7343 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -25,7 +25,7 @@ static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x]
static int edit;
static int replay;
-enum { REVERT, CHERRY_PICK } action;
+static enum { REVERT, CHERRY_PICK } action;
static int no_commit;
static struct commit *commit;
static int needed_deref;
@@ -45,8 +45,10 @@ static void parse_options(int argc, const char **argv)
if (argc < 2)
usage(usage_str);
- for (i = 1; i < argc - 1; i++) {
+ for (i = 1; i < argc; i++) {
arg = argv[i];
+ if (arg[0] != '-')
+ break;
if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit"))
no_commit = 1;
else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit"))
@@ -59,7 +61,8 @@ static void parse_options(int argc, const char **argv)
else if (strcmp(arg, "-r"))
usage(usage_str);
}
-
+ if (i != argc - 1)
+ usage(usage_str);
arg = argv[argc - 1];
if (get_sha1(arg, sha1))
die ("Cannot find '%s'", arg);
@@ -104,7 +107,7 @@ static char *get_oneline(const char *message)
return result;
}
-char *get_encoding(const char *message)
+static char *get_encoding(const char *message)
{
const char *p = message, *eol;
@@ -126,7 +129,7 @@ char *get_encoding(const char *message)
return NULL;
}
-struct lock_file msg_file;
+static struct lock_file msg_file;
static int msg_fd;
static void add_to_msg(const char *string)
diff --git a/builtin-rm.c b/builtin-rm.c
index 4a0bd93c8b..9a808c1bf9 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -46,7 +46,7 @@ static int remove_file(const char *name)
return ret;
}
-static int check_local_mod(unsigned char *head)
+static int check_local_mod(unsigned char *head, int index_only)
{
/* items in list are already sorted in the cache order,
* so we could do this a lot more efficiently by using
@@ -65,6 +65,8 @@ static int check_local_mod(unsigned char *head)
const char *name = list.name[i];
unsigned char sha1[20];
unsigned mode;
+ int local_changes = 0;
+ int staged_changes = 0;
pos = cache_name_pos(name, strlen(name));
if (pos < 0)
@@ -87,14 +89,32 @@ static int check_local_mod(unsigned char *head)
continue;
}
if (ce_match_stat(ce, &st, 0))
- errs = error("'%s' has local modifications "
- "(hint: try -f)", ce->name);
+ local_changes = 1;
if (no_head
|| get_tree_entry(head, name, sha1, &mode)
|| ce->ce_mode != create_ce_mode(mode)
|| hashcmp(ce->sha1, sha1))
- errs = error("'%s' has changes staged in the index "
- "(hint: try -f)", name);
+ staged_changes = 1;
+
+ if (local_changes && staged_changes)
+ errs = error("'%s' has staged content different "
+ "from both the file and the HEAD\n"
+ "(use -f to force removal)", name);
+ else if (!index_only) {
+ /* It's not dangerous to git-rm --cached a
+ * file if the index matches the file or the
+ * HEAD, since it means the deleted content is
+ * still available somewhere.
+ */
+ if (staged_changes)
+ errs = error("'%s' has changes staged in the index\n"
+ "(use --cached to keep the file, "
+ "or -f to force removal)", name);
+ if (local_changes)
+ errs = error("'%s' has local modifications\n"
+ "(use --cached to keep the file, "
+ "or -f to force removal)", name);
+ }
}
return errs;
}
@@ -192,7 +212,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
unsigned char sha1[20];
if (get_sha1("HEAD", sha1))
hashclr(sha1);
- if (check_local_mod(sha1))
+ if (check_local_mod(sha1, index_only))
exit(1);
}
diff --git a/builtin-runstatus.c b/builtin-runstatus.c
index 4b489b1214..2db25c88bf 100644
--- a/builtin-runstatus.c
+++ b/builtin-runstatus.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "wt-status.h"
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 8d3f742d43..16af6199ab 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -331,4 +331,3 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
return 0;
}
-
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index c892f1f7a6..4fa87f6081 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -259,17 +259,19 @@ static void join_revs(struct commit_list **list_p,
static void show_one_commit(struct commit *commit, int no_name)
{
- char pretty[256], *cp;
+ char *pretty = NULL;
+ const char *pretty_str = "(unavailable)";
+ unsigned long pretty_len = 0;
struct commit_name *name = commit->util;
- if (commit->object.parsed)
+
+ if (commit->object.parsed) {
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
- pretty, sizeof(pretty), 0, NULL, NULL, 0);
- else
- strcpy(pretty, "(unavailable)");
- if (!prefixcmp(pretty, "[PATCH] "))
- cp = pretty + 8;
- else
- cp = pretty;
+ &pretty, &pretty_len,
+ 0, NULL, NULL, 0);
+ pretty_str = pretty;
+ }
+ if (!prefixcmp(pretty_str, "[PATCH] "))
+ pretty_str += 8;
if (!no_name) {
if (name && name->head_name) {
@@ -286,7 +288,8 @@ static void show_one_commit(struct commit *commit, int no_name)
printf("[%s] ",
find_unique_abbrev(commit->object.sha1, 7));
}
- puts(cp);
+ puts(pretty_str);
+ free(pretty);
}
static char *ref_name[MAX_REVS + 1];
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 9463ff0e69..65051d14fd 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "object.h"
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index f0d4d9e2d1..916355ca5d 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -1,58 +1,99 @@
#include "builtin.h"
+#include "cache.h"
/*
- * Remove empty lines from the beginning and end.
+ * Returns the length of a line, without trailing spaces.
*
- * Turn multiple consecutive empty lines into just one
- * empty line. Return true if it is an incomplete line.
+ * If the line ends with newline, it will be removed too.
*/
-static int cleanup(char *line)
+static size_t cleanup(char *line, size_t len)
{
- int len = strlen(line);
+ if (len) {
+ if (line[len - 1] == '\n')
+ len--;
- if (len && line[len-1] == '\n') {
- if (len == 1)
- return 0;
- do {
- unsigned char c = line[len-2];
+ while (len) {
+ unsigned char c = line[len - 1];
if (!isspace(c))
break;
- line[len-2] = '\n';
len--;
- line[len] = 0;
- } while (len > 1);
- return 0;
+ }
}
- return 1;
+ return len;
}
-void stripspace(FILE *in, FILE *out)
+/*
+ * Remove empty lines from the beginning and end
+ * and also trailing spaces from every line.
+ *
+ * Note that the buffer will not be NUL-terminated.
+ *
+ * Turn multiple consecutive empty lines between paragraphs
+ * into just one empty line.
+ *
+ * If the input has only empty lines and spaces,
+ * no output will be produced.
+ *
+ * If last line has a newline at the end, it will be removed.
+ *
+ * Enable skip_comments to skip every line starting with "#".
+ */
+size_t stripspace(char *buffer, size_t length, int skip_comments)
{
int empties = -1;
- int incomplete = 0;
- char line[1024];
+ size_t i, j, len, newlen;
+ char *eol;
- while (fgets(line, sizeof(line), in)) {
- incomplete = cleanup(line);
+ for (i = j = 0; i < length; i += len, j += newlen) {
+ eol = memchr(buffer + i, '\n', length - i);
+ len = eol ? eol - (buffer + i) + 1 : length - i;
+
+ if (skip_comments && len && buffer[i] == '#') {
+ newlen = 0;
+ continue;
+ }
+ newlen = cleanup(buffer + i, len);
/* Not just an empty line? */
- if (line[0] != '\n') {
+ if (newlen) {
+ if (empties != -1)
+ buffer[j++] = '\n';
if (empties > 0)
- fputc('\n', out);
+ buffer[j++] = '\n';
empties = 0;
- fputs(line, out);
+ memmove(buffer + j, buffer + i, newlen);
continue;
}
if (empties < 0)
continue;
empties++;
}
- if (incomplete)
- fputc('\n', out);
+
+ return j;
}
int cmd_stripspace(int argc, const char **argv, const char *prefix)
{
- stripspace(stdin, stdout);
+ char *buffer;
+ unsigned long size;
+ int strip_comments = 0;
+
+ if (argc > 1 && (!strcmp(argv[1], "-s") ||
+ !strcmp(argv[1], "--strip-comments")))
+ strip_comments = 1;
+
+ size = 1024;
+ buffer = xmalloc(size);
+ if (read_fd(0, &buffer, &size)) {
+ free(buffer);
+ die("could not read the input");
+ }
+
+ size = stripspace(buffer, size, strip_comments);
+ write_or_die(1, buffer, size);
+ if (size)
+ putc('\n', stdout);
+
+ free(buffer);
return 0;
}
diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c
index d41b40640b..9eb95e50da 100644
--- a/builtin-symbolic-ref.c
+++ b/builtin-symbolic-ref.c
@@ -43,8 +43,6 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
msg = argv[1];
if (!*msg)
die("Refusing to perform update with empty message");
- if (strchr(msg, '\n'))
- die("Refusing to perform update with \\n in message");
}
else if (!strcmp("--", arg)) {
argc--;
diff --git a/builtin-tag.c b/builtin-tag.c
new file mode 100644
index 0000000000..d6d38ad123
--- /dev/null
+++ b/builtin-tag.c
@@ -0,0 +1,460 @@
+/*
+ * Builtin "git tag"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
+ * Carlos Rica <jasampler@gmail.com>
+ * Based on git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "refs.h"
+#include "tag.h"
+#include "run-command.h"
+
+static const char builtin_tag_usage[] =
+ "git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
+
+static char signingkey[1000];
+
+static void launch_editor(const char *path, char **buffer, unsigned long *len)
+{
+ const char *editor, *terminal;
+ struct child_process child;
+ const char *args[3];
+ int fd;
+
+ editor = getenv("GIT_EDITOR");
+ if (!editor && editor_program)
+ editor = editor_program;
+ if (!editor)
+ editor = getenv("VISUAL");
+ if (!editor)
+ editor = getenv("EDITOR");
+
+ terminal = getenv("TERM");
+ if (!editor && (!terminal || !strcmp(terminal, "dumb"))) {
+ fprintf(stderr,
+ "Terminal is dumb but no VISUAL nor EDITOR defined.\n"
+ "Please supply the message using either -m or -F option.\n");
+ exit(1);
+ }
+
+ if (!editor)
+ editor = "vi";
+
+ memset(&child, 0, sizeof(child));
+ child.argv = args;
+ args[0] = editor;
+ args[1] = path;
+ args[2] = NULL;
+
+ if (run_command(&child))
+ die("There was a problem with the editor %s.", editor);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ die("could not open '%s': %s", path, strerror(errno));
+ if (read_fd(fd, buffer, len)) {
+ free(*buffer);
+ die("could not read message file '%s': %s",
+ path, strerror(errno));
+ }
+ close(fd);
+}
+
+struct tag_filter {
+ const char *pattern;
+ int lines;
+};
+
+#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
+
+static int show_reference(const char *refname, const unsigned char *sha1,
+ int flag, void *cb_data)
+{
+ struct tag_filter *filter = cb_data;
+
+ if (!fnmatch(filter->pattern, refname, 0)) {
+ int i;
+ unsigned long size;
+ enum object_type type;
+ char *buf, *sp, *eol;
+ size_t len;
+
+ if (!filter->lines) {
+ printf("%s\n", refname);
+ return 0;
+ }
+ printf("%-15s ", refname);
+
+ sp = buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ return 0;
+ if (!size) {
+ free(buf);
+ return 0;
+ }
+ /* skip header */
+ while (sp + 1 < buf + size &&
+ !(sp[0] == '\n' && sp[1] == '\n'))
+ sp++;
+ /* only take up to "lines" lines, and strip the signature */
+ for (i = 0, sp += 2;
+ i < filter->lines && sp < buf + size &&
+ prefixcmp(sp, PGP_SIGNATURE "\n");
+ i++) {
+ if (i)
+ printf("\n ");
+ eol = memchr(sp, '\n', size - (sp - buf));
+ len = eol ? eol - sp : size - (sp - buf);
+ fwrite(sp, len, 1, stdout);
+ if (!eol)
+ break;
+ sp = eol + 1;
+ }
+ putchar('\n');
+ free(buf);
+ }
+
+ return 0;
+}
+
+static int list_tags(const char *pattern, int lines)
+{
+ struct tag_filter filter;
+ char *newpattern;
+
+ if (pattern == NULL)
+ pattern = "";
+
+ /* prepend/append * to the shell pattern: */
+ newpattern = xmalloc(strlen(pattern) + 3);
+ sprintf(newpattern, "*%s*", pattern);
+
+ filter.pattern = newpattern;
+ filter.lines = lines;
+
+ for_each_tag_ref(show_reference, (void *) &filter);
+
+ free(newpattern);
+
+ return 0;
+}
+
+typedef int (*each_tag_name_fn)(const char *name, const char *ref,
+ const unsigned char *sha1);
+
+static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
+{
+ const char **p;
+ char ref[PATH_MAX];
+ int had_error = 0;
+ unsigned char sha1[20];
+
+ for (p = argv; *p; p++) {
+ if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
+ >= sizeof(ref)) {
+ error("tag name too long: %.*s...", 50, *p);
+ had_error = 1;
+ continue;
+ }
+ if (!resolve_ref(ref, sha1, 1, NULL)) {
+ error("tag '%s' not found.", *p);
+ had_error = 1;
+ continue;
+ }
+ if (fn(*p, ref, sha1))
+ had_error = 1;
+ }
+ return had_error;
+}
+
+static int delete_tag(const char *name, const char *ref,
+ const unsigned char *sha1)
+{
+ if (delete_ref(ref, sha1))
+ return 1;
+ printf("Deleted tag '%s'\n", name);
+ return 0;
+}
+
+static int verify_tag(const char *name, const char *ref,
+ const unsigned char *sha1)
+{
+ const char *argv_verify_tag[] = {"git-verify-tag",
+ "-v", "SHA1_HEX", NULL};
+ argv_verify_tag[2] = sha1_to_hex(sha1);
+
+ if (run_command_v_opt(argv_verify_tag, 0))
+ return error("could not verify the tag '%s'", name);
+ return 0;
+}
+
+static ssize_t do_sign(char *buffer, size_t size, size_t max)
+{
+ struct child_process gpg;
+ const char *args[4];
+ char *bracket;
+ int len;
+
+ if (!*signingkey) {
+ if (strlcpy(signingkey, git_committer_info(1),
+ sizeof(signingkey)) > sizeof(signingkey) - 1)
+ return error("committer info too long.");
+ bracket = strchr(signingkey, '>');
+ if (bracket)
+ bracket[1] = '\0';
+ }
+
+ memset(&gpg, 0, sizeof(gpg));
+ gpg.argv = args;
+ gpg.in = -1;
+ gpg.out = -1;
+ args[0] = "gpg";
+ args[1] = "-bsau";
+ args[2] = signingkey;
+ args[3] = NULL;
+
+ if (start_command(&gpg))
+ return error("could not run gpg.");
+
+ write_or_die(gpg.in, buffer, size);
+ close(gpg.in);
+ gpg.close_in = 0;
+ len = read_in_full(gpg.out, buffer + size, max - size);
+
+ finish_command(&gpg);
+
+ if (len == max - size)
+ return error("could not read the entire signature from gpg.");
+
+ return size + len;
+}
+
+static const char tag_template[] =
+ "\n"
+ "#\n"
+ "# Write a tag message\n"
+ "#\n";
+
+static int git_tag_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "user.signingkey")) {
+ if (!value)
+ die("user.signingkey without value");
+ if (strlcpy(signingkey, value, sizeof(signingkey))
+ >= sizeof(signingkey))
+ die("user.signingkey value too long");
+ return 0;
+ }
+
+ return git_default_config(var, value);
+}
+
+#define MAX_SIGNATURE_LENGTH 1024
+/* message must be NULL or allocated, it will be reallocated and freed */
+static void create_tag(const unsigned char *object, const char *tag,
+ char *message, int sign, unsigned char *result)
+{
+ enum object_type type;
+ char header_buf[1024], *buffer = NULL;
+ int header_len, max_size;
+ unsigned long size = 0;
+
+ type = sha1_object_info(object, NULL);
+ if (type <= OBJ_NONE)
+ die("bad object type.");
+
+ header_len = snprintf(header_buf, sizeof(header_buf),
+ "object %s\n"
+ "type %s\n"
+ "tag %s\n"
+ "tagger %s\n\n",
+ sha1_to_hex(object),
+ typename(type),
+ tag,
+ git_committer_info(1));
+
+ if (header_len > sizeof(header_buf) - 1)
+ die("tag header too big.");
+
+ if (!message) {
+ char *path;
+ int fd;
+
+ /* write the template message before editing: */
+ path = xstrdup(git_path("TAG_EDITMSG"));
+ fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ if (fd < 0)
+ die("could not create file '%s': %s",
+ path, strerror(errno));
+ write_or_die(fd, tag_template, strlen(tag_template));
+ close(fd);
+
+ launch_editor(path, &buffer, &size);
+
+ unlink(path);
+ free(path);
+ }
+ else {
+ buffer = message;
+ size = strlen(message);
+ }
+
+ size = stripspace(buffer, size, 1);
+
+ if (!message && !size)
+ die("no tag message?");
+
+ /* insert the header and add the '\n' if needed: */
+ max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1;
+ buffer = xrealloc(buffer, max_size);
+ if (size)
+ buffer[size++] = '\n';
+ memmove(buffer + header_len, buffer, size);
+ memcpy(buffer, header_buf, header_len);
+ size += header_len;
+
+ if (sign) {
+ size = do_sign(buffer, size, max_size);
+ if (size < 0)
+ die("unable to sign the tag");
+ }
+
+ if (write_sha1_file(buffer, size, tag_type, result) < 0)
+ die("unable to write tag file");
+ free(buffer);
+}
+
+int cmd_tag(int argc, const char **argv, const char *prefix)
+{
+ unsigned char object[20], prev[20];
+ int annotate = 0, sign = 0, force = 0, lines = 0;
+ char *message = NULL;
+ char ref[PATH_MAX];
+ const char *object_ref, *tag;
+ int i;
+ struct ref_lock *lock;
+
+ git_config(git_tag_config);
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-')
+ break;
+ if (!strcmp(arg, "-a")) {
+ annotate = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-s")) {
+ annotate = 1;
+ sign = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-f")) {
+ force = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-n")) {
+ if (i + 1 == argc || *argv[i + 1] == '-')
+ /* no argument */
+ lines = 1;
+ else
+ lines = isdigit(*argv[++i]) ?
+ atoi(argv[i]) : 1;
+ continue;
+ }
+ if (!strcmp(arg, "-m")) {
+ annotate = 1;
+ i++;
+ if (i == argc)
+ die("option -m needs an argument.");
+ if (message)
+ die("only one -F or -m option is allowed.");
+ message = xstrdup(argv[i]);
+ continue;
+ }
+ if (!strcmp(arg, "-F")) {
+ unsigned long len;
+ int fd;
+
+ annotate = 1;
+ i++;
+ if (i == argc)
+ die("option -F needs an argument.");
+ if (message)
+ die("only one -F or -m option is allowed.");
+
+ if (!strcmp(argv[i], "-"))
+ fd = 0;
+ else {
+ fd = open(argv[i], O_RDONLY);
+ if (fd < 0)
+ die("could not open '%s': %s",
+ argv[i], strerror(errno));
+ }
+ len = 1024;
+ message = xmalloc(len);
+ if (read_fd(fd, &message, &len)) {
+ free(message);
+ die("cannot read %s", argv[i]);
+ }
+ continue;
+ }
+ if (!strcmp(arg, "-u")) {
+ annotate = 1;
+ sign = 1;
+ i++;
+ if (i == argc)
+ die("option -u needs an argument.");
+ if (strlcpy(signingkey, argv[i], sizeof(signingkey))
+ >= sizeof(signingkey))
+ die("argument to option -u too long");
+ continue;
+ }
+ if (!strcmp(arg, "-l"))
+ return list_tags(argv[i + 1], lines);
+ if (!strcmp(arg, "-d"))
+ return for_each_tag_name(argv + i + 1, delete_tag);
+ if (!strcmp(arg, "-v"))
+ return for_each_tag_name(argv + i + 1, verify_tag);
+ usage(builtin_tag_usage);
+ }
+
+ if (i == argc) {
+ if (annotate)
+ usage(builtin_tag_usage);
+ return list_tags(NULL, lines);
+ }
+ tag = argv[i++];
+
+ object_ref = i < argc ? argv[i] : "HEAD";
+ if (i + 1 < argc)
+ die("too many params");
+
+ if (get_sha1(object_ref, object))
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+ if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) > sizeof(ref) - 1)
+ die("tag name too long: %.*s...", 50, tag);
+ if (check_ref_format(ref))
+ die("'%s' is not a valid tag name.", tag);
+
+ if (!resolve_ref(ref, prev, 1, NULL))
+ hashclr(prev);
+ else if (!force)
+ die("tag '%s' already exists", tag);
+
+ if (annotate)
+ create_tag(object, tag, message, sign, object);
+
+ lock = lock_any_ref_for_update(ref, prev, 0);
+ if (!lock)
+ die("%s: cannot lock the ref", ref);
+ if (write_ref_sha1(lock, object, NULL) < 0)
+ die("%s: cannot update the ref", ref);
+
+ return 0;
+}
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 8f9899178b..a7a4574f2b 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -86,9 +86,15 @@ static int process_lstat_error(const char *path, int err)
static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
{
- int option, size = cache_entry_size(len);
- struct cache_entry *ce = xcalloc(1, size);
+ int option, size;
+ struct cache_entry *ce;
+
+ /* Was the old index entry already up-to-date? */
+ if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
+ return 0;
+ size = cache_entry_size(len);
+ ce = xcalloc(1, size);
memcpy(ce->name, path, len);
ce->ce_flags = htons(len);
fill_stat_cache_info(ce, st);
@@ -134,7 +140,7 @@ static int process_directory(const char *path, int len, struct stat *st)
/* Exact match: file or existing gitlink */
if (pos >= 0) {
struct cache_entry *ce = active_cache[pos];
- if (S_ISDIRLNK(ntohl(ce->ce_mode))) {
+ if (S_ISGITLINK(ntohl(ce->ce_mode))) {
/* Do nothing to the index if there is no HEAD! */
if (resolve_gitlink_ref(path, "HEAD", sha1) < 0)
@@ -178,7 +184,7 @@ static int process_file(const char *path, int len, struct stat *st)
int pos = cache_name_pos(path, len);
struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
- if (ce && S_ISDIRLNK(ntohl(ce->ce_mode)))
+ if (ce && S_ISGITLINK(ntohl(ce->ce_mode)))
return error("%s is already a gitlink, not replacing", path);
return add_one_path(ce, path, len, st);
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 5ee960bf41..8339cf19e2 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -3,16 +3,17 @@
#include "builtin.h"
static const char git_update_ref_usage[] =
-"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])";
+"git-update-ref [-m <reason>] (-d <refname> <value> | [--no-deref] <refname> <value> [<oldval>])";
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
struct ref_lock *lock;
unsigned char sha1[20], oldsha1[20];
- int i, delete;
+ int i, delete, ref_flags;
delete = 0;
+ ref_flags = 0;
git_config(git_default_config);
for (i = 1; i < argc; i++) {
@@ -22,14 +23,16 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
msg = argv[++i];
if (!*msg)
die("Refusing to perform update with empty message.");
- if (strchr(msg, '\n'))
- die("Refusing to perform update with \\n in message.");
continue;
}
if (!strcmp("-d", argv[i])) {
delete = 1;
continue;
}
+ if (!strcmp("--no-deref", argv[i])) {
+ ref_flags |= REF_NODEREF;
+ continue;
+ }
if (!refname) {
refname = argv[i];
continue;
@@ -59,7 +62,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
- lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);
+ lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags);
if (!lock)
die("%s: cannot lock the ref", refname);
if (write_ref_sha1(lock, sha1, msg) < 0)
diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c
new file mode 100644
index 0000000000..dfcfcd0455
--- /dev/null
+++ b/builtin-verify-tag.c
@@ -0,0 +1,110 @@
+/*
+ * Builtin "git verify-tag"
+ *
+ * Copyright (c) 2007 Carlos Rica <jasampler@gmail.com>
+ *
+ * Based on git-verify-tag.sh
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "tag.h"
+#include "run-command.h"
+#include <signal.h>
+
+static const char builtin_verify_tag_usage[] =
+ "git-verify-tag [-v|--verbose] <tag>...";
+
+#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
+
+static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
+{
+ struct child_process gpg;
+ const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
+ char path[PATH_MAX], *eol;
+ size_t len;
+ int fd, ret;
+
+ fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
+ if (fd < 0)
+ return error("could not create temporary file '%s': %s",
+ path, strerror(errno));
+ if (write_in_full(fd, buf, size) < 0)
+ return error("failed writing temporary file '%s': %s",
+ path, strerror(errno));
+ close(fd);
+
+ /* find the length without signature */
+ len = 0;
+ while (len < size && prefixcmp(buf + len, PGP_SIGNATURE "\n")) {
+ eol = memchr(buf + len, '\n', size - len);
+ len += eol ? eol - (buf + len) + 1 : size - len;
+ }
+ if (verbose)
+ write_in_full(1, buf, len);
+
+ memset(&gpg, 0, sizeof(gpg));
+ gpg.argv = args_gpg;
+ gpg.in = -1;
+ gpg.out = 1;
+ args_gpg[2] = path;
+ if (start_command(&gpg))
+ return error("could not run gpg.");
+
+ write_in_full(gpg.in, buf, len);
+ close(gpg.in);
+ gpg.close_in = 0;
+ ret = finish_command(&gpg);
+
+ unlink(path);
+
+ return ret;
+}
+
+static int verify_tag(const char *name, int verbose)
+{
+ enum object_type type;
+ unsigned char sha1[20];
+ char *buf;
+ unsigned long size;
+ int ret;
+
+ if (get_sha1(name, sha1))
+ return error("tag '%s' not found.", name);
+
+ type = sha1_object_info(sha1, NULL);
+ if (type != OBJ_TAG)
+ return error("%s: cannot verify a non-tag object of type %s.",
+ name, typename(type));
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ return error("%s: unable to read file.", name);
+
+ ret = run_gpg_verify(buf, size, verbose);
+
+ free(buf);
+ return ret;
+}
+
+int cmd_verify_tag(int argc, const char **argv, const char *prefix)
+{
+ int i = 1, verbose = 0, had_error = 0;
+
+ git_config(git_default_config);
+
+ if (argc == 1)
+ usage(builtin_verify_tag_usage);
+
+ if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) {
+ verbose = 1;
+ i++;
+ }
+
+ /* sometimes the program was terminated because this signal
+ * was received in the process of writing the gpg input: */
+ signal(SIGPIPE, SIG_IGN);
+ while (i < argc)
+ if (verify_tag(argv[i++], verbose))
+ had_error = 1;
+ return had_error;
+}
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 391de53972..88f34ba7d6 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -52,6 +52,8 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
if (prefix) {
struct cache_tree *subtree =
cache_tree_find(active_cache_tree, prefix);
+ if (!subtree)
+ die("git-write-tree: prefix %s not found", prefix);
hashcpy(sha1, subtree->sha1);
}
else
diff --git a/builtin.h b/builtin.h
index d3f3a7496e..bb720004af 100644
--- a/builtin.h
+++ b/builtin.h
@@ -7,9 +7,7 @@ extern const char git_version_string[];
extern const char git_usage_string[];
extern void help_unknown_cmd(const char *cmd);
-extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
-extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
-extern void stripspace(FILE *in, FILE *out);
+extern size_t stripspace(char *buffer, size_t length, int skip_comments);
extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
extern void prune_packed_objects(int);
@@ -72,12 +70,14 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_tag(int argc, const char **argv, const char *prefix);
extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
extern int cmd_update_index(int argc, const char **argv, const char *prefix);
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
+extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
diff --git a/cache-tree.c b/cache-tree.c
index 6369cc7c53..077f034369 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -326,7 +326,7 @@ static int update_one(struct cache_tree *it,
mode = ntohl(ce->ce_mode);
entlen = pathlen - baselen;
}
- if (mode != S_IFDIRLNK && !missing_ok && !has_sha1_file(sha1))
+ if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
return error("invalid object %s", sha1_to_hex(sha1));
if (!ce->ce_mode)
@@ -478,7 +478,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
if (0 <= it->entry_count) {
if (size < 20)
goto free_return;
- hashcpy(it->sha1, (unsigned char*)buf);
+ hashcpy(it->sha1, (const unsigned char*)buf);
buf += 20;
size -= 20;
}
diff --git a/cache.h b/cache.h
index 5dff2f1d73..c7e00e7b05 100644
--- a/cache.h
+++ b/cache.h
@@ -40,8 +40,8 @@
* happens that everybody shares the same bit representation
* in the UNIX world (and apparently wider too..)
*/
-#define S_IFDIRLNK 0160000
-#define S_ISDIRLNK(m) (((m) & S_IFMT) == S_IFDIRLNK)
+#define S_IFGITLINK 0160000
+#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
/*
* Intensive research over the course of many years has shown that
@@ -123,8 +123,8 @@ static inline unsigned int create_ce_mode(unsigned int mode)
{
if (S_ISLNK(mode))
return htonl(S_IFLNK);
- if (S_ISDIR(mode) || S_ISDIRLNK(mode))
- return htonl(S_IFDIRLNK);
+ if (S_ISDIR(mode) || S_ISGITLINK(mode))
+ return htonl(S_IFGITLINK);
return htonl(S_IFREG | ce_permissions(mode));
}
static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
@@ -142,7 +142,7 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in
}
#define canon_mode(mode) \
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
- S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFDIRLNK)
+ S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
@@ -173,7 +173,7 @@ extern struct index_state the_index;
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
-#define refresh_cache(flags) refresh_index(&the_index, flags)
+#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really))
#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really))
#endif
@@ -192,6 +192,7 @@ enum object_type {
};
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -207,11 +208,15 @@ enum object_type {
extern int is_bare_repository_cfg;
extern int is_bare_repository(void);
extern int is_inside_git_dir(void);
+extern char *git_work_tree_cfg;
+extern int is_inside_work_tree(void);
extern const char *get_git_dir(void);
extern char *get_object_directory(void);
extern char *get_refs_directory(void);
extern char *get_index_file(void);
extern char *get_graft_file(void);
+extern int set_git_dir(const char *path);
+extern const char *get_git_work_tree(void);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
@@ -225,6 +230,24 @@ extern void verify_non_filename(const char *prefix, const char *name);
#define alloc_nr(x) (((x)+16)*3/2)
+/*
+ * Realloc the buffer pointed at by variable 'x' so that it can hold
+ * at least 'nr' entries; the number of entries currently allocated
+ * is 'alloc', using the standard growing factor alloc_nr() macro.
+ *
+ * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
+ */
+#define ALLOC_GROW(x, nr, alloc) \
+ do { \
+ if ((nr) > alloc) { \
+ if (alloc_nr(alloc) < (nr)) \
+ alloc = (nr); \
+ else \
+ alloc = alloc_nr(alloc); \
+ x = xrealloc((x), alloc * sizeof(*(x))); \
+ } \
+ } while(0)
+
/* Initialize and use the cache information */
extern int read_index(struct index_state *);
extern int read_index_from(struct index_state *, const char *path);
@@ -235,6 +258,7 @@ extern int index_name_pos(struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
+#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_index_entry_at(struct index_state *, int pos);
@@ -245,7 +269,7 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
+extern int read_fd(int fd, char **return_buf, unsigned long *return_size);
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
@@ -254,7 +278,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
-extern int refresh_index(struct index_state *, unsigned int flags);
+extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
struct lock_file {
struct lock_file *next;
@@ -273,8 +297,8 @@ extern void rollback_lock_file(struct lock_file *);
extern int delete_ref(const char *, const unsigned char *sha1);
/* Environment bits from configuration mechanism */
-extern int use_legacy_headers;
extern int trust_executable_bit;
+extern int quote_path_fully;
extern int has_symlinks;
extern int assume_unchanged;
extern int prefer_symlink_refs;
@@ -283,6 +307,8 @@ extern int warn_ambiguous_refs;
extern int shared_repository;
extern const char *apply_default_whitespace;
extern int zlib_compression_level;
+extern int core_compression_level;
+extern int core_compression_seen;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
@@ -336,6 +362,11 @@ int git_config_perm(const char *var, const char *value);
int adjust_shared_perm(const char *path);
int safe_create_leading_directories(char *path);
char *enter_repo(char *path, int strict);
+static inline int is_absolute_path(const char *path)
+{
+ return path[0] == '/';
+}
+const char *make_absolute_path(const char *path);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
@@ -353,8 +384,6 @@ extern int move_temp_to_file(const char *tmpfile, const char *filename);
extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
extern int has_sha1_file(const unsigned char *sha1);
-extern void *map_sha1_file(const unsigned char *sha1, unsigned long *);
-extern int legacy_loose_object(unsigned char *);
extern int has_pack_file(const unsigned char *sha1);
extern int has_pack_index(const unsigned char *sha1);
@@ -389,9 +418,16 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned long *size,
unsigned char *sha1_ret);
-enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT, DATE_LOCAL };
+enum date_mode {
+ DATE_NORMAL = 0,
+ DATE_RELATIVE,
+ DATE_SHORT,
+ DATE_LOCAL,
+ DATE_ISO8601,
+ DATE_RFC2822
+};
+
const char *show_date(unsigned long time, int timezone, enum date_mode mode);
-const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
@@ -463,11 +499,10 @@ struct ref {
#define REF_HEADS (1u << 1)
#define REF_TAGS (1u << 2)
-extern pid_t git_connect(int fd[2], char *url, const char *prog);
+#define CONNECT_VERBOSE (1u << 0)
+extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags);
extern int finish_connect(pid_t pid);
extern int path_match(const char *path, int nr, char **match);
-extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
- int nr_refspec, char **refspec, int all);
extern int get_ack(int fd, unsigned char *result_sha1);
extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
extern int server_supports(const char *feature);
@@ -480,14 +515,15 @@ extern void prepare_packed_git(void);
extern void reprepare_packed_git(void);
extern void install_packed_git(struct packed_git *pack);
-extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
+extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
struct packed_git *packs);
extern void pack_report(void);
+extern int open_pack_index(struct packed_git *);
extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
extern void unuse_pack(struct pack_window **);
extern struct packed_git *add_packed_git(const char *, int, int);
-extern const unsigned char *nth_packed_object_sha1(const struct packed_git *, uint32_t);
+extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
@@ -501,7 +537,10 @@ typedef int (*config_fn_t)(const char *, const char *);
extern int git_default_config(const char *, const char *);
extern int git_config_from_file(config_fn_t fn, const char *);
extern int git_config(config_fn_t fn);
+extern int git_parse_long(const char *, long *);
+extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *);
+extern unsigned long git_config_ulong(const char *, const char *);
extern int git_config_bool(const char *, const char *);
extern int git_config_set(const char *, const char *);
extern int git_config_set_multivar(const char *, const char *, const char *, int);
@@ -515,6 +554,8 @@ extern char git_default_name[MAX_GITNAME];
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
+/* IO helper functions */
+extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd);
extern int read_in_full(int fd, void *buf, size_t count);
extern int write_in_full(int fd, const void *buf, size_t count);
@@ -524,9 +565,12 @@ extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char
/* pager.c */
extern void setup_pager(void);
+extern char *pager_program;
extern int pager_in_use;
extern int pager_use_color;
+extern char *editor_program;
+
/* base85 */
int decode_85(char *dst, const char *line, int linelen);
void encode_85(char *buf, const unsigned char *data, int bytes);
diff --git a/combine-diff.c b/combine-diff.c
index ea3ca5f950..ef622340a5 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -215,8 +215,7 @@ static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
parent_file.ptr = grab_blob(parent, &sz);
parent_file.size = sz;
xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = 0;
- xecfg.flags = 0;
+ memset(&xecfg, 0, sizeof(xecfg));
ecb.outf = xdiff_outf;
ecb.priv = &state;
memset(&state, 0, sizeof(state));
diff --git a/commit.c b/commit.c
index 5632e32685..dc5a0643f3 100644
--- a/commit.c
+++ b/commit.c
@@ -27,7 +27,7 @@ struct sort_node
const char *commit_type = "commit";
-struct cmt_fmt_map {
+static struct cmt_fmt_map {
const char *n;
size_t cmp_len;
enum cmit_fmt v;
@@ -148,7 +148,7 @@ static int commit_graft_pos(const unsigned char *sha1)
int register_commit_graft(struct commit_graft *graft, int ignore_dups)
{
int pos = commit_graft_pos(graft->sha1);
-
+
if (0 <= pos) {
if (ignore_dups)
free(graft);
@@ -406,7 +406,7 @@ struct commit_list * insert_by_date(struct commit *item, struct commit_list **li
return commit_list_insert(item, pp);
}
-
+
void sort_by_date(struct commit_list **list)
{
struct commit_list *ret = NULL;
@@ -529,6 +529,14 @@ static int add_rfc2047(char *buf, const char *line, int len,
return bp - buf;
}
+static unsigned long bound_rfc2047(unsigned long len, const char *encoding)
+{
+ /* upper bound of q encoded string of length 'len' */
+ unsigned long elen = strlen(encoding);
+
+ return len * 3 + elen + 100;
+}
+
static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
const char *line, enum date_mode dmode,
const char *encoding)
@@ -577,7 +585,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
break;
case CMIT_FMT_EMAIL:
ret += sprintf(buf + ret, "Date: %s\n",
- show_rfc2822_date(time, tz));
+ show_date(time, tz, DATE_RFC2822));
break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what,
@@ -713,7 +721,10 @@ static char *logmsg_reencode(const struct commit *commit,
encoding = get_header(commit, "encoding");
use_encoding = encoding ? encoding : utf8;
if (!strcmp(use_encoding, output_encoding))
- out = xstrdup(commit->buffer);
+ if (encoding) /* we'll strip encoding header later */
+ out = xstrdup(commit->buffer);
+ else
+ return NULL; /* nothing to do */
else
out = reencode_string(commit->buffer,
output_encoding, use_encoding);
@@ -770,13 +781,14 @@ static void fill_person(struct interp *table, const char *msg, int len)
tz = -tz;
}
- interp_set_entry(table, 2, show_date(date, tz, 0));
- interp_set_entry(table, 3, show_rfc2822_date(date, tz));
- interp_set_entry(table, 4, show_date(date, tz, 1));
+ interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
+ interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
+ interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
+ interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
}
static long format_commit_message(const struct commit *commit,
- const char *msg, char *buf, unsigned long space)
+ const char *msg, char **buf_p, unsigned long *space_p)
{
struct interp table[] = {
{ "%H" }, /* commit hash */
@@ -791,12 +803,14 @@ static long format_commit_message(const struct commit *commit,
{ "%aD" }, /* author date, RFC2822 style */
{ "%ar" }, /* author date, relative */
{ "%at" }, /* author date, UNIX timestamp */
+ { "%ai" }, /* author date, ISO 8601 */
{ "%cn" }, /* committer name */
{ "%ce" }, /* committer email */
{ "%cd" }, /* committer date */
{ "%cD" }, /* committer date, RFC2822 style */
{ "%cr" }, /* committer date, relative */
{ "%ct" }, /* committer date, UNIX timestamp */
+ { "%ci" }, /* committer date, ISO 8601 */
{ "%e" }, /* encoding */
{ "%s" }, /* subject */
{ "%b" }, /* body */
@@ -813,10 +827,11 @@ static long format_commit_message(const struct commit *commit,
IPARENTS, IPARENTS_ABBREV,
IAUTHOR_NAME, IAUTHOR_EMAIL,
IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
- IAUTHOR_TIMESTAMP,
+ IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
ICOMMITTER_NAME, ICOMMITTER_EMAIL,
ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
+ ICOMMITTER_ISO8601,
IENCODING,
ISUBJECT,
IBODY,
@@ -905,31 +920,252 @@ static long format_commit_message(const struct commit *commit,
if (!table[i].value)
interp_set_entry(table, i, "<unknown>");
- interpolate(buf, space, user_format, table, ARRAY_SIZE(table));
+ do {
+ char *buf = *buf_p;
+ unsigned long space = *space_p;
+
+ space = interpolate(buf, space, user_format,
+ table, ARRAY_SIZE(table));
+ if (!space)
+ break;
+ buf = xrealloc(buf, space);
+ *buf_p = buf;
+ *space_p = space;
+ } while (1);
interp_clear_table(table, ARRAY_SIZE(table));
- return strlen(buf);
+ return strlen(*buf_p);
+}
+
+static void pp_header(enum cmit_fmt fmt,
+ int abbrev,
+ enum date_mode dmode,
+ const char *encoding,
+ const struct commit *commit,
+ const char **msg_p,
+ unsigned long *len_p,
+ unsigned long *ofs_p,
+ char **buf_p,
+ unsigned long *space_p)
+{
+ int parents_shown = 0;
+
+ for (;;) {
+ const char *line = *msg_p;
+ char *dst;
+ int linelen = get_one_line(*msg_p, *len_p);
+ unsigned long len;
+
+ if (!linelen)
+ return;
+ *msg_p += linelen;
+ *len_p -= linelen;
+
+ if (linelen == 1)
+ /* End of header */
+ return;
+
+ ALLOC_GROW(*buf_p, linelen + *ofs_p + 20, *space_p);
+ dst = *buf_p + *ofs_p;
+
+ if (fmt == CMIT_FMT_RAW) {
+ memcpy(dst, line, linelen);
+ *ofs_p += linelen;
+ continue;
+ }
+
+ if (!memcmp(line, "parent ", 7)) {
+ if (linelen != 48)
+ die("bad parent line in commit");
+ continue;
+ }
+
+ if (!parents_shown) {
+ struct commit_list *parent;
+ int num;
+ for (parent = commit->parents, num = 0;
+ parent;
+ parent = parent->next, num++)
+ ;
+ /* with enough slop */
+ num = *ofs_p + num * 50 + 20;
+ ALLOC_GROW(*buf_p, num, *space_p);
+ dst = *buf_p + *ofs_p;
+ *ofs_p += add_merge_info(fmt, dst, commit, abbrev);
+ parents_shown = 1;
+ }
+
+ /*
+ * MEDIUM == DEFAULT shows only author with dates.
+ * FULL shows both authors but not dates.
+ * FULLER shows both authors and dates.
+ */
+ if (!memcmp(line, "author ", 7)) {
+ len = linelen;
+ if (fmt == CMIT_FMT_EMAIL)
+ len = bound_rfc2047(linelen, encoding);
+ ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p);
+ dst = *buf_p + *ofs_p;
+ *ofs_p += add_user_info("Author", fmt, dst,
+ line + 7, dmode, encoding);
+ }
+
+ if (!memcmp(line, "committer ", 10) &&
+ (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
+ len = linelen;
+ if (fmt == CMIT_FMT_EMAIL)
+ len = bound_rfc2047(linelen, encoding);
+ ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p);
+ dst = *buf_p + *ofs_p;
+ *ofs_p += add_user_info("Commit", fmt, dst,
+ line + 10, dmode, encoding);
+ }
+ }
+}
+
+static void pp_title_line(enum cmit_fmt fmt,
+ const char **msg_p,
+ unsigned long *len_p,
+ unsigned long *ofs_p,
+ char **buf_p,
+ unsigned long *space_p,
+ int indent,
+ const char *subject,
+ const char *after_subject,
+ const char *encoding,
+ int plain_non_ascii)
+{
+ char *title;
+ unsigned long title_alloc, title_len;
+ unsigned long len;
+
+ title_len = 0;
+ title_alloc = 80;
+ title = xmalloc(title_alloc);
+ for (;;) {
+ const char *line = *msg_p;
+ int linelen = get_one_line(line, *len_p);
+ *msg_p += linelen;
+ *len_p -= linelen;
+
+ if (!linelen || is_empty_line(line, &linelen))
+ break;
+
+ if (title_alloc <= title_len + linelen + 2) {
+ title_alloc = title_len + linelen + 80;
+ title = xrealloc(title, title_alloc);
+ }
+ len = 0;
+ if (title_len) {
+ if (fmt == CMIT_FMT_EMAIL) {
+ len++;
+ title[title_len++] = '\n';
+ }
+ len++;
+ title[title_len++] = ' ';
+ }
+ memcpy(title + title_len, line, linelen);
+ title_len += linelen;
+ }
+
+ /* Enough slop for the MIME header and rfc2047 */
+ len = bound_rfc2047(title_len, encoding)+ 1000;
+ if (subject)
+ len += strlen(subject);
+ if (after_subject)
+ len += strlen(after_subject);
+ if (encoding)
+ len += strlen(encoding);
+ ALLOC_GROW(*buf_p, title_len + *ofs_p + len, *space_p);
+
+ if (subject) {
+ len = strlen(subject);
+ memcpy(*buf_p + *ofs_p, subject, len);
+ *ofs_p += len;
+ *ofs_p += add_rfc2047(*buf_p + *ofs_p,
+ title, title_len, encoding);
+ } else {
+ memcpy(*buf_p + *ofs_p, title, title_len);
+ *ofs_p += title_len;
+ }
+ (*buf_p)[(*ofs_p)++] = '\n';
+ if (plain_non_ascii) {
+ const char *header_fmt =
+ "MIME-Version: 1.0\n"
+ "Content-Type: text/plain; charset=%s\n"
+ "Content-Transfer-Encoding: 8bit\n";
+ *ofs_p += snprintf(*buf_p + *ofs_p,
+ *space_p - *ofs_p,
+ header_fmt, encoding);
+ }
+ if (after_subject) {
+ len = strlen(after_subject);
+ memcpy(*buf_p + *ofs_p, after_subject, len);
+ *ofs_p += len;
+ }
+ free(title);
+ if (fmt == CMIT_FMT_EMAIL) {
+ ALLOC_GROW(*buf_p, *ofs_p + 20, *space_p);
+ (*buf_p)[(*ofs_p)++] = '\n';
+ }
+}
+
+static void pp_remainder(enum cmit_fmt fmt,
+ const char **msg_p,
+ unsigned long *len_p,
+ unsigned long *ofs_p,
+ char **buf_p,
+ unsigned long *space_p,
+ int indent)
+{
+ int first = 1;
+ for (;;) {
+ const char *line = *msg_p;
+ int linelen = get_one_line(line, *len_p);
+ *msg_p += linelen;
+ *len_p -= linelen;
+
+ if (!linelen)
+ break;
+
+ if (is_empty_line(line, &linelen)) {
+ if (first)
+ continue;
+ if (fmt == CMIT_FMT_SHORT)
+ break;
+ }
+ first = 0;
+
+ ALLOC_GROW(*buf_p, *ofs_p + linelen + indent + 20, *space_p);
+ if (indent) {
+ memset(*buf_p + *ofs_p, ' ', indent);
+ *ofs_p += indent;
+ }
+ memcpy(*buf_p + *ofs_p, line, linelen);
+ *ofs_p += linelen;
+ (*buf_p)[(*ofs_p)++] = '\n';
+ }
}
unsigned long pretty_print_commit(enum cmit_fmt fmt,
const struct commit *commit,
unsigned long len,
- char *buf, unsigned long space,
+ char **buf_p, unsigned long *space_p,
int abbrev, const char *subject,
const char *after_subject,
enum date_mode dmode)
{
- int hdr = 1, body = 0, seen_title = 0;
unsigned long offset = 0;
+ unsigned long beginning_of_body;
int indent = 4;
- int parents_shown = 0;
const char *msg = commit->buffer;
int plain_non_ascii = 0;
char *reencoded;
const char *encoding;
+ char *buf;
if (fmt == CMIT_FMT_USERFORMAT)
- return format_commit_message(commit, msg, buf, space);
+ return format_commit_message(commit, msg, buf_p, space_p);
encoding = (git_log_output_encoding
? git_log_output_encoding
@@ -937,8 +1173,10 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
if (!encoding)
encoding = "utf-8";
reencoded = logmsg_reencode(commit, encoding);
- if (reencoded)
+ if (reencoded) {
msg = reencoded;
+ len = strlen(reencoded);
+ }
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
indent = 0;
@@ -969,137 +1207,57 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
}
}
+ pp_header(fmt, abbrev, dmode, encoding,
+ commit, &msg, &len,
+ &offset, buf_p, space_p);
+ if (fmt != CMIT_FMT_ONELINE && !subject) {
+ ALLOC_GROW(*buf_p, offset + 20, *space_p);
+ (*buf_p)[offset++] = '\n';
+ }
+
+ /* Skip excess blank lines at the beginning of body, if any... */
for (;;) {
- const char *line = msg;
int linelen = get_one_line(msg, len);
-
+ int ll = linelen;
if (!linelen)
break;
-
- /*
- * We want some slop for indentation and a possible
- * final "...". Thus the "+ 20".
- */
- if (offset + linelen + 20 > space) {
- memcpy(buf + offset, " ...\n", 8);
- offset += 8;
+ if (!is_empty_line(msg, &ll))
break;
- }
-
msg += linelen;
len -= linelen;
- if (hdr) {
- if (linelen == 1) {
- hdr = 0;
- if ((fmt != CMIT_FMT_ONELINE) && !subject)
- buf[offset++] = '\n';
- continue;
- }
- if (fmt == CMIT_FMT_RAW) {
- memcpy(buf + offset, line, linelen);
- offset += linelen;
- continue;
- }
- if (!memcmp(line, "parent ", 7)) {
- if (linelen != 48)
- die("bad parent line in commit");
- continue;
- }
-
- if (!parents_shown) {
- offset += add_merge_info(fmt, buf + offset,
- commit, abbrev);
- parents_shown = 1;
- continue;
- }
- /*
- * MEDIUM == DEFAULT shows only author with dates.
- * FULL shows both authors but not dates.
- * FULLER shows both authors and dates.
- */
- if (!memcmp(line, "author ", 7))
- offset += add_user_info("Author", fmt,
- buf + offset,
- line + 7,
- dmode,
- encoding);
- if (!memcmp(line, "committer ", 10) &&
- (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
- offset += add_user_info("Commit", fmt,
- buf + offset,
- line + 10,
- dmode,
- encoding);
- continue;
- }
+ }
- if (!subject)
- body = 1;
+ /* These formats treat the title line specially. */
+ if (fmt == CMIT_FMT_ONELINE
+ || fmt == CMIT_FMT_EMAIL)
+ pp_title_line(fmt, &msg, &len, &offset,
+ buf_p, space_p, indent,
+ subject, after_subject, encoding,
+ plain_non_ascii);
- if (is_empty_line(line, &linelen)) {
- if (!seen_title)
- continue;
- if (!body)
- continue;
- if (subject)
- continue;
- if (fmt == CMIT_FMT_SHORT)
- break;
- }
+ beginning_of_body = offset;
+ if (fmt != CMIT_FMT_ONELINE)
+ pp_remainder(fmt, &msg, &len, &offset,
+ buf_p, space_p, indent);
- seen_title = 1;
- if (subject) {
- int slen = strlen(subject);
- memcpy(buf + offset, subject, slen);
- offset += slen;
- offset += add_rfc2047(buf + offset, line, linelen,
- encoding);
- }
- else {
- memset(buf + offset, ' ', indent);
- memcpy(buf + offset + indent, line, linelen);
- offset += linelen + indent;
- }
- buf[offset++] = '\n';
- if (fmt == CMIT_FMT_ONELINE)
- break;
- if (subject && plain_non_ascii) {
- int sz;
- char header[512];
- const char *header_fmt =
- "MIME-Version: 1.0\n"
- "Content-Type: text/plain; charset=%s\n"
- "Content-Transfer-Encoding: 8bit\n";
- sz = snprintf(header, sizeof(header), header_fmt,
- encoding);
- if (sizeof(header) < sz)
- die("Encoding name %s too long", encoding);
- memcpy(buf + offset, header, sz);
- offset += sz;
- }
- if (after_subject) {
- int slen = strlen(after_subject);
- if (slen > space - offset - 1)
- slen = space - offset - 1;
- memcpy(buf + offset, after_subject, slen);
- offset += slen;
- after_subject = NULL;
- }
- subject = NULL;
- }
- while (offset && isspace(buf[offset-1]))
+ while (offset && isspace((*buf_p)[offset-1]))
offset--;
+
+ ALLOC_GROW(*buf_p, offset + 20, *space_p);
+ buf = *buf_p;
+
/* Make sure there is an EOLN for the non-oneline case */
if (fmt != CMIT_FMT_ONELINE)
buf[offset++] = '\n';
+
/*
- * make sure there is another EOLN to separate the headers from whatever
- * body the caller appends if we haven't already written a body
+ * The caller may append additional body text in e-mail
+ * format. Make sure we did not strip the blank line
+ * between the header and the body.
*/
- if (fmt == CMIT_FMT_EMAIL && !body)
+ if (fmt == CMIT_FMT_EMAIL && offset <= beginning_of_body)
buf[offset++] = '\n';
buf[offset] = '\0';
-
free(reencoded);
return offset;
}
@@ -1116,15 +1274,6 @@ struct commit *pop_commit(struct commit_list **stack)
return item;
}
-int count_parents(struct commit * commit)
-{
- int count;
- struct commit_list * parents = commit->parents;
- for (count = 0; parents; parents = parents->next,count++)
- ;
- return count;
-}
-
void topo_sort_default_setter(struct commit *c, void *data)
{
c->util = data;
@@ -1160,7 +1309,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
next = next->next;
count++;
}
-
+
if (!count)
return;
/* allocate an array to help sort the list */
@@ -1188,11 +1337,11 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
}
next=next->next;
}
- /*
+ /*
* find the tips
*
- * tips are nodes not reachable from any other node in the list
- *
+ * tips are nodes not reachable from any other node in the list
+ *
* the tips serve as a starting set for the work queue.
*/
next=*list;
@@ -1220,7 +1369,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
if (pn) {
/*
- * parents are only enqueued for emission
+ * parents are only enqueued for emission
* when all their children have been emitted thereby
* guaranteeing topological order.
*/
diff --git a/commit.h b/commit.h
index 86e8dca0c9..467872eeca 100644
--- a/commit.h
+++ b/commit.h
@@ -61,20 +61,18 @@ enum cmit_fmt {
};
extern enum cmit_fmt get_commit_format(const char *arg);
-extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
+extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
/** Removes the first commit from a list sorted by date, and adds all
* of its parents.
**/
-struct commit *pop_most_recent_commit(struct commit_list **list,
+struct commit *pop_most_recent_commit(struct commit_list **list,
unsigned int mark);
struct commit *pop_commit(struct commit_list **stack);
void clear_commit_marks(struct commit *commit, unsigned int mark);
-int count_parents(struct commit * commit);
-
/*
* Performs an in-place topological sort of list supplied.
*
diff --git a/compat/mmap.c b/compat/mmap.c
index 4cfaee3136..c9d46d1742 100644
--- a/compat/mmap.c
+++ b/compat/mmap.c
@@ -40,4 +40,3 @@ int git_munmap(void *start, size_t length)
free(start);
return 0;
}
-
diff --git a/config.c b/config.c
index 7b655fdb78..dc3148d456 100644
--- a/config.c
+++ b/config.c
@@ -12,6 +12,8 @@
static FILE *config_file;
static const char *config_file_name;
static int config_linenr;
+static int zlib_compression_seen;
+
static int get_next_char(void)
{
int c;
@@ -231,21 +233,55 @@ static int git_parse_file(config_fn_t fn)
die("bad config file line %d in %s", config_linenr, config_file_name);
}
-int git_config_int(const char *name, const char *value)
+static unsigned long get_unit_factor(const char *end)
+{
+ if (!*end)
+ return 1;
+ else if (!strcasecmp(end, "k"))
+ return 1024;
+ else if (!strcasecmp(end, "m"))
+ return 1024 * 1024;
+ else if (!strcasecmp(end, "g"))
+ return 1024 * 1024 * 1024;
+ die("unknown unit: '%s'", end);
+}
+
+int git_parse_long(const char *value, long *ret)
{
if (value && *value) {
char *end;
- int val = strtol(value, &end, 0);
- if (!*end)
- return val;
- if (!strcasecmp(end, "k"))
- return val * 1024;
- if (!strcasecmp(end, "m"))
- return val * 1024 * 1024;
- if (!strcasecmp(end, "g"))
- return val * 1024 * 1024 * 1024;
- }
- die("bad config value for '%s' in %s", name, config_file_name);
+ long val = strtol(value, &end, 0);
+ *ret = val * get_unit_factor(end);
+ return 1;
+ }
+ return 0;
+}
+
+int git_parse_ulong(const char *value, unsigned long *ret)
+{
+ if (value && *value) {
+ char *end;
+ unsigned long val = strtoul(value, &end, 0);
+ *ret = val * get_unit_factor(end);
+ return 1;
+ }
+ return 0;
+}
+
+int git_config_int(const char *name, const char *value)
+{
+ long ret;
+ if (!git_parse_long(value, &ret))
+ die("bad config value for '%s' in %s", name, config_file_name);
+ return ret;
+}
+
+unsigned long git_config_ulong(const char *name, const char *value)
+{
+ unsigned long ret;
+ if (!git_parse_ulong(value, &ret))
+ die("bad config value for '%s' in %s", name, config_file_name);
+ return ret;
}
int git_config_bool(const char *name, const char *value)
@@ -269,6 +305,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "core.quotepath")) {
+ quote_path_fully = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "core.symlinks")) {
has_symlinks = git_config_bool(var, value);
return 0;
@@ -299,8 +340,14 @@ int git_default_config(const char *var, const char *value)
return 0;
}
- if (!strcmp(var, "core.legacyheaders")) {
- use_legacy_headers = git_config_bool(var, value);
+ if (!strcmp(var, "core.loosecompression")) {
+ int level = git_config_int(var, value);
+ if (level == -1)
+ level = Z_DEFAULT_COMPRESSION;
+ else if (level < 0 || level > Z_BEST_COMPRESSION)
+ die("bad zlib compression level %d", level);
+ zlib_compression_level = level;
+ zlib_compression_seen = 1;
return 0;
}
@@ -310,7 +357,10 @@ int git_default_config(const char *var, const char *value)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
die("bad zlib compression level %d", level);
- zlib_compression_level = level;
+ core_compression_level = level;
+ core_compression_seen = 1;
+ if (!zlib_compression_seen)
+ zlib_compression_level = level;
return 0;
}
@@ -371,6 +421,16 @@ int git_default_config(const char *var, const char *value)
return 0;
}
+ if (!strcmp(var, "core.pager")) {
+ pager_program = xstrdup(value);
+ return 0;
+ }
+
+ if (!strcmp(var, "core.editor")) {
+ editor_program = xstrdup(value);
+ return 0;
+ }
+
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}
@@ -512,7 +572,7 @@ static int store_aux(const char* key, const char* value)
return 0;
}
-static int write_error()
+static int write_error(void)
{
fprintf(stderr, "Failed to write new configuration file\n");
@@ -610,7 +670,7 @@ static ssize_t find_beginning_of_line(const char* contents, size_t size,
size_t equal_offset = size, bracket_offset = size;
ssize_t offset;
- for (offset = offset_-2; offset > 0
+ for (offset = offset_-2; offset > 0
&& contents[offset] != '\n'; offset--)
switch (contents[offset]) {
case '=': equal_offset = offset; break;
@@ -660,7 +720,7 @@ int git_config_set_multivar(const char* key, const char* value,
int fd = -1, in_fd;
int ret;
char* config_filename;
- char* lock_file;
+ struct lock_file *lock = NULL;
const char* last_dot = strrchr(key, '.');
config_filename = getenv(CONFIG_ENVIRONMENT);
@@ -670,7 +730,6 @@ int git_config_set_multivar(const char* key, const char* value,
config_filename = git_path("config");
}
config_filename = xstrdup(config_filename);
- lock_file = xstrdup(mkpath("%s.lock", config_filename));
/*
* Since "key" actually contains the section name and the real
@@ -715,11 +774,12 @@ int git_config_set_multivar(const char* key, const char* value,
store.key[i] = 0;
/*
- * The lock_file serves a purpose in addition to locking: the new
+ * The lock serves a purpose in addition to locking: the new
* contents of .git/config will be written into it.
*/
- fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
- if (fd < 0 || adjust_shared_perm(lock_file)) {
+ lock = xcalloc(sizeof(struct lock_file), 1);
+ fd = hold_lock_file_for_update(lock, config_filename, 0);
+ if (fd < 0) {
fprintf(stderr, "could not lock config file\n");
free(store.key);
ret = -1;
@@ -859,25 +919,31 @@ int git_config_set_multivar(const char* key, const char* value,
goto write_err_out;
munmap(contents, contents_sz);
- unlink(config_filename);
}
- if (rename(lock_file, config_filename) < 0) {
- fprintf(stderr, "Could not rename the lock file?\n");
+ if (close(fd) || commit_lock_file(lock) < 0) {
+ fprintf(stderr, "Cannot commit config file!\n");
ret = 4;
goto out_free;
}
+ /* fd is closed, so don't try to close it below. */
+ fd = -1;
+ /*
+ * lock is committed, so don't try to roll it back below.
+ * NOTE: Since lockfile.c keeps a linked list of all created
+ * lock_file structures, it isn't safe to free(lock). It's
+ * better to just leave it hanging around.
+ */
+ lock = NULL;
ret = 0;
out_free:
if (0 <= fd)
close(fd);
+ if (lock)
+ rollback_lock_file(lock);
free(config_filename);
- if (lock_file) {
- unlink(lock_file);
- free(lock_file);
- }
return ret;
write_err_out:
@@ -978,4 +1044,3 @@ int git_config_rename_section(const char *old_name, const char *new_name)
free(config_filename);
return ret;
}
-
diff --git a/config.mak.in b/config.mak.in
index eb9d7a5549..a3032e389f 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -38,4 +38,3 @@ NO_STRCASESTR=@NO_STRCASESTR@
NO_STRLCPY=@NO_STRLCPY@
NO_SETENV=@NO_SETENV@
NO_ICONV=@NO_ICONV@
-
diff --git a/configure.ac b/configure.ac
index 50d2b85ace..84fd7f1e1f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -69,12 +69,26 @@ fi \
## Site configuration related to programs (before tests)
## --with-PACKAGE[=ARG] and --without-PACKAGE
#
+# Set lib to alternative name of lib directory (e.g. lib64)
+AC_ARG_WITH([lib],
+ [AS_HELP_STRING([--with-lib=ARG],
+ [ARG specifies alternative name for lib directory])],
+ [if test "$withval" = "no" -o "$withval" = "yes"; then \
+ AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
+else \
+ GIT_CONF_APPEND_LINE(lib=$withval); \
+fi; \
+],[])
+#
# Define SHELL_PATH to provide path to shell.
GIT_ARG_SET_PATH(shell)
#
# Define PERL_PATH to provide path to Perl.
GIT_ARG_SET_PATH(perl)
#
+# Define ZLIB_PATH to provide path to zlib.
+GIT_ARG_SET_PATH(zlib)
+#
# Declare the with-tcltk/without-tcltk options.
AC_ARG_WITH(tcltk,
AS_HELP_STRING([--with-tcltk],[use Tcl/Tk GUI (default is YES)])
diff --git a/connect.c b/connect.c
index da89c9cfcf..ae49c5a367 100644
--- a/connect.c
+++ b/connect.c
@@ -4,6 +4,7 @@
#include "quote.h"
#include "refs.h"
#include "run-command.h"
+#include "remote.h"
static char *server_capabilities;
@@ -71,7 +72,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
continue;
if (nr_match && !path_match(name, nr_match, match))
continue;
- ref = xcalloc(1, sizeof(*ref) + len - 40);
+ ref = alloc_ref(len - 40);
hashcpy(ref->old_sha1, old_sha1);
memcpy(ref->name, buffer + 41, len - 40);
*list = ref;
@@ -128,245 +129,6 @@ int path_match(const char *path, int nr, char **match)
return 0;
}
-struct refspec {
- char *src;
- char *dst;
- char force;
-};
-
-/*
- * A:B means fast forward remote B with local A.
- * +A:B means overwrite remote B with local A.
- * +A is a shorthand for +A:A.
- * A is a shorthand for A:A.
- * :B means delete remote B.
- */
-static struct refspec *parse_ref_spec(int nr_refspec, char **refspec)
-{
- int i;
- struct refspec *rs = xcalloc(sizeof(*rs), (nr_refspec + 1));
- for (i = 0; i < nr_refspec; i++) {
- char *sp, *dp, *ep;
- sp = refspec[i];
- if (*sp == '+') {
- rs[i].force = 1;
- sp++;
- }
- ep = strchr(sp, ':');
- if (ep) {
- dp = ep + 1;
- *ep = 0;
- }
- else
- dp = sp;
- rs[i].src = sp;
- rs[i].dst = dp;
- }
- rs[nr_refspec].src = rs[nr_refspec].dst = NULL;
- return rs;
-}
-
-static int count_refspec_match(const char *pattern,
- struct ref *refs,
- struct ref **matched_ref)
-{
- int patlen = strlen(pattern);
- struct ref *matched_weak = NULL;
- struct ref *matched = NULL;
- int weak_match = 0;
- int match = 0;
-
- for (weak_match = match = 0; refs; refs = refs->next) {
- char *name = refs->name;
- int namelen = strlen(name);
- int weak_match;
-
- if (namelen < patlen ||
- memcmp(name + namelen - patlen, pattern, patlen))
- continue;
- if (namelen != patlen && name[namelen - patlen - 1] != '/')
- continue;
-
- /* A match is "weak" if it is with refs outside
- * heads or tags, and did not specify the pattern
- * in full (e.g. "refs/remotes/origin/master") or at
- * least from the toplevel (e.g. "remotes/origin/master");
- * otherwise "git push $URL master" would result in
- * ambiguity between remotes/origin/master and heads/master
- * at the remote site.
- */
- if (namelen != patlen &&
- patlen != namelen - 5 &&
- prefixcmp(name, "refs/heads/") &&
- prefixcmp(name, "refs/tags/")) {
- /* We want to catch the case where only weak
- * matches are found and there are multiple
- * matches, and where more than one strong
- * matches are found, as ambiguous. One
- * strong match with zero or more weak matches
- * are acceptable as a unique match.
- */
- matched_weak = refs;
- weak_match++;
- }
- else {
- matched = refs;
- match++;
- }
- }
- if (!matched) {
- *matched_ref = matched_weak;
- return weak_match;
- }
- else {
- *matched_ref = matched;
- return match;
- }
-}
-
-static void link_dst_tail(struct ref *ref, struct ref ***tail)
-{
- **tail = ref;
- *tail = &ref->next;
- **tail = NULL;
-}
-
-static struct ref *try_explicit_object_name(const char *name)
-{
- unsigned char sha1[20];
- struct ref *ref;
- int len;
-
- if (!*name) {
- ref = xcalloc(1, sizeof(*ref) + 20);
- strcpy(ref->name, "(delete)");
- hashclr(ref->new_sha1);
- return ref;
- }
- if (get_sha1(name, sha1))
- return NULL;
- len = strlen(name) + 1;
- ref = xcalloc(1, sizeof(*ref) + len);
- memcpy(ref->name, name, len);
- hashcpy(ref->new_sha1, sha1);
- return ref;
-}
-
-static int match_explicit_refs(struct ref *src, struct ref *dst,
- struct ref ***dst_tail, struct refspec *rs)
-{
- int i, errs;
- for (i = errs = 0; rs[i].src; i++) {
- struct ref *matched_src, *matched_dst;
-
- matched_src = matched_dst = NULL;
- switch (count_refspec_match(rs[i].src, src, &matched_src)) {
- case 1:
- break;
- case 0:
- /* The source could be in the get_sha1() format
- * not a reference name. :refs/other is a
- * way to delete 'other' ref at the remote end.
- */
- matched_src = try_explicit_object_name(rs[i].src);
- if (matched_src)
- break;
- errs = 1;
- error("src refspec %s does not match any.",
- rs[i].src);
- break;
- default:
- errs = 1;
- error("src refspec %s matches more than one.",
- rs[i].src);
- break;
- }
- switch (count_refspec_match(rs[i].dst, dst, &matched_dst)) {
- case 1:
- break;
- case 0:
- if (!memcmp(rs[i].dst, "refs/", 5)) {
- int len = strlen(rs[i].dst) + 1;
- matched_dst = xcalloc(1, sizeof(*dst) + len);
- memcpy(matched_dst->name, rs[i].dst, len);
- link_dst_tail(matched_dst, dst_tail);
- }
- else if (!strcmp(rs[i].src, rs[i].dst) &&
- matched_src) {
- /* pushing "master:master" when
- * remote does not have master yet.
- */
- int len = strlen(matched_src->name) + 1;
- matched_dst = xcalloc(1, sizeof(*dst) + len);
- memcpy(matched_dst->name, matched_src->name,
- len);
- link_dst_tail(matched_dst, dst_tail);
- }
- else {
- errs = 1;
- error("dst refspec %s does not match any "
- "existing ref on the remote and does "
- "not start with refs/.", rs[i].dst);
- }
- break;
- default:
- errs = 1;
- error("dst refspec %s matches more than one.",
- rs[i].dst);
- break;
- }
- if (errs)
- continue;
- if (matched_dst->peer_ref) {
- errs = 1;
- error("dst ref %s receives from more than one src.",
- matched_dst->name);
- }
- else {
- matched_dst->peer_ref = matched_src;
- matched_dst->force = rs[i].force;
- }
- }
- return -errs;
-}
-
-static struct ref *find_ref_by_name(struct ref *list, const char *name)
-{
- for ( ; list; list = list->next)
- if (!strcmp(list->name, name))
- return list;
- return NULL;
-}
-
-int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
- int nr_refspec, char **refspec, int all)
-{
- struct refspec *rs = parse_ref_spec(nr_refspec, refspec);
-
- if (nr_refspec)
- return match_explicit_refs(src, dst, dst_tail, rs);
-
- /* pick the remainder */
- for ( ; src; src = src->next) {
- struct ref *dst_peer;
- if (src->peer_ref)
- continue;
- dst_peer = find_ref_by_name(dst, src->name);
- if ((dst_peer && dst_peer->peer_ref) || (!dst_peer && !all))
- continue;
- if (!dst_peer) {
- /* Create a new one and link it */
- int len = strlen(src->name) + 1;
- dst_peer = xcalloc(1, sizeof(*dst_peer) + len);
- memcpy(dst_peer->name, src->name, len);
- hashcpy(dst_peer->new_sha1, src->new_sha1);
- link_dst_tail(dst_peer, dst_tail);
- }
- dst_peer->peer_ref = src;
- }
- return 0;
-}
-
enum protocol {
PROTO_LOCAL = 1,
PROTO_SSH,
@@ -383,6 +145,8 @@ static enum protocol get_protocol(const char *name)
return PROTO_SSH;
if (!strcmp(name, "ssh+git"))
return PROTO_SSH;
+ if (!strcmp(name, "file"))
+ return PROTO_LOCAL;
die("I don't handle protocol '%s'", name);
}
@@ -391,16 +155,34 @@ static enum protocol get_protocol(const char *name)
#ifndef NO_IPV6
+static const char *ai_name(const struct addrinfo *ai)
+{
+ static char addr[INET_ADDRSTRLEN];
+ if ( AF_INET == ai->ai_family ) {
+ struct sockaddr_in *in;
+ in = (struct sockaddr_in *)ai->ai_addr;
+ inet_ntop(ai->ai_family, &in->sin_addr, addr, sizeof(addr));
+ } else if ( AF_INET6 == ai->ai_family ) {
+ struct sockaddr_in6 *in;
+ in = (struct sockaddr_in6 *)ai->ai_addr;
+ inet_ntop(ai->ai_family, &in->sin6_addr, addr, sizeof(addr));
+ } else {
+ strcpy(addr, "(unknown)");
+ }
+ return addr;
+}
+
/*
* Returns a connected socket() fd, or else die()s.
*/
-static int git_tcp_connect_sock(char *host)
+static int git_tcp_connect_sock(char *host, int flags)
{
int sockfd = -1, saved_errno = 0;
char *colon, *end;
const char *port = STR(DEFAULT_GIT_PORT);
struct addrinfo hints, *ai0, *ai;
int gai;
+ int cnt = 0;
if (host[0] == '[') {
end = strchr(host + 1, ']');
@@ -425,10 +207,16 @@ static int git_tcp_connect_sock(char *host)
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "Looking up %s ... ", host);
+
gai = getaddrinfo(host, port, &hints, &ai);
if (gai)
die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai));
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
+
for (ai0 = ai; ai; ai = ai->ai_next) {
sockfd = socket(ai->ai_family,
ai->ai_socktype, ai->ai_protocol);
@@ -438,10 +226,17 @@ static int git_tcp_connect_sock(char *host)
}
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
saved_errno = errno;
+ fprintf(stderr, "%s[%d: %s]: errno=%s\n",
+ host,
+ cnt,
+ ai_name(ai),
+ strerror(saved_errno));
close(sockfd);
sockfd = -1;
continue;
}
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "%s ", ai_name(ai));
break;
}
@@ -450,6 +245,9 @@ static int git_tcp_connect_sock(char *host)
if (sockfd < 0)
die("unable to connect a socket (%s)", strerror(saved_errno));
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "done.\n");
+
return sockfd;
}
@@ -458,7 +256,7 @@ static int git_tcp_connect_sock(char *host)
/*
* Returns a connected socket() fd, or else die()s.
*/
-static int git_tcp_connect_sock(char *host)
+static int git_tcp_connect_sock(char *host, int flags)
{
int sockfd = -1, saved_errno = 0;
char *colon, *end;
@@ -467,6 +265,7 @@ static int git_tcp_connect_sock(char *host)
struct sockaddr_in sa;
char **ap;
unsigned int nport;
+ int cnt;
if (host[0] == '[') {
end = strchr(host + 1, ']');
@@ -485,6 +284,9 @@ static int git_tcp_connect_sock(char *host)
port = colon + 1;
}
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "Looking up %s ... ", host);
+
he = gethostbyname(host);
if (!he)
die("Unable to look up %s (%s)", host, hstrerror(h_errno));
@@ -497,7 +299,10 @@ static int git_tcp_connect_sock(char *host)
nport = se->s_port;
}
- for (ap = he->h_addr_list; *ap; ap++) {
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
+
+ for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
if (sockfd < 0) {
saved_errno = errno;
@@ -511,25 +316,36 @@ static int git_tcp_connect_sock(char *host)
if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
saved_errno = errno;
+ fprintf(stderr, "%s[%d: %s]: errno=%s\n",
+ host,
+ cnt,
+ inet_ntoa(*(struct in_addr *)&sa.sin_addr),
+ strerror(saved_errno));
close(sockfd);
sockfd = -1;
continue;
}
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "%s ",
+ inet_ntoa(*(struct in_addr *)&sa.sin_addr));
break;
}
if (sockfd < 0)
die("unable to connect a socket (%s)", strerror(saved_errno));
+ if (flags & CONNECT_VERBOSE)
+ fprintf(stderr, "done.\n");
+
return sockfd;
}
#endif /* NO_IPV6 */
-static void git_tcp_connect(int fd[2], char *host)
+static void git_tcp_connect(int fd[2], char *host, int flags)
{
- int sockfd = git_tcp_connect_sock(host);
+ int sockfd = git_tcp_connect_sock(host, flags);
fd[0] = sockfd;
fd[1] = dup(sockfd);
@@ -574,7 +390,7 @@ static int git_proxy_command_options(const char *var, const char *value)
}
if (0 <= matchlen) {
/* core.gitproxy = none for kernel.org */
- if (matchlen == 4 &&
+ if (matchlen == 4 &&
!memcmp(value, "none", 4))
matchlen = 0;
git_proxy_command = xmalloc(matchlen + 1);
@@ -646,7 +462,7 @@ static void git_proxy_connect(int fd[2], char *host)
*
* Does not return a negative value on error; it just dies.
*/
-pid_t git_connect(int fd[2], char *url, const char *prog)
+pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
{
char *host, *path = url;
char *end;
@@ -684,13 +500,13 @@ pid_t git_connect(int fd[2], char *url, const char *prog)
end = host;
path = strchr(end, c);
- if (c == ':') {
- if (path) {
+ if (path) {
+ if (c == ':') {
protocol = PROTO_SSH;
*path++ = '\0';
- } else
- path = host;
- }
+ }
+ } else
+ path = end;
if (!path || !*path)
die("No path specified. See 'man git-pull' for valid url syntax");
@@ -719,7 +535,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog)
if (git_use_proxy(host))
git_proxy_connect(fd, host);
else
- git_tcp_connect(fd, host);
+ git_tcp_connect(fd, host, flags);
/*
* Separate original protocol components prog and path
* from extended components with a NUL byte.
@@ -773,6 +589,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog)
unsetenv(ALTERNATE_DB_ENVIRONMENT);
unsetenv(DB_ENVIRONMENT);
unsetenv(GIT_DIR_ENVIRONMENT);
+ unsetenv(GIT_WORK_TREE_ENVIRONMENT);
unsetenv(GRAFT_ENVIRONMENT);
unsetenv(INDEX_ENVIRONMENT);
execlp("sh", "sh", "-c", command, NULL);
diff --git a/contrib/README b/contrib/README
index e1c0a01ff3..05f291c1f1 100644
--- a/contrib/README
+++ b/contrib/README
@@ -41,4 +41,3 @@ submit a patch to create a subdirectory of contrib/ and put your
stuff there.
-jc
-
diff --git a/contrib/blameview/README b/contrib/blameview/README
index 50a6f67fd6..fada5ce909 100644
--- a/contrib/blameview/README
+++ b/contrib/blameview/README
@@ -7,4 +7,3 @@ To: Linus Torvalds <torvalds@linux-foundation.org>
Cc: git@vger.kernel.org
Date: Sat, 27 Jan 2007 18:52:38 -0500
Message-ID: <20070127235238.GA28706@coredump.intra.peff.net>
-
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 9e72f0f7b1..82b9ed40d8 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -682,8 +682,11 @@ _git_push ()
esac
__gitcomp "$(__git_refs "$remote")" "" "${cur#*:}"
;;
+ +*)
+ __gitcomp "$(__git_refs)" + "${cur#+}"
+ ;;
*)
- __gitcomp "$(__git_refs2)"
+ __gitcomp "$(__git_refs)"
;;
esac
;;
@@ -969,6 +972,11 @@ _git_show ()
__git_complete_file
}
+_git_stash ()
+{
+ __gitcomp 'list show apply clear'
+}
+
_git ()
{
local i c=1 command __git_dir
@@ -1025,6 +1033,7 @@ _git ()
shortlog) _git_shortlog ;;
show) _git_show ;;
show-branch) _git_log ;;
+ stash) _git_stash ;;
whatchanged) _git_log ;;
*) COMPREPLY=() ;;
esac
@@ -1070,6 +1079,7 @@ complete -o default -o nospace -F _git_remote git-remote
complete -o default -o nospace -F _git_reset git-reset
complete -o default -o nospace -F _git_shortlog git-shortlog
complete -o default -o nospace -F _git_show git-show
+complete -o default -o nospace -F _git_stash git-stash
complete -o default -o nospace -F _git_log git-show-branch
complete -o default -o nospace -F _git_log git-whatchanged
diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile
index 98aa0aae9b..a48540a92b 100644
--- a/contrib/emacs/Makefile
+++ b/contrib/emacs/Makefile
@@ -7,14 +7,15 @@ INSTALL ?= install
INSTALL_ELC = $(INSTALL) -m 644
prefix ?= $(HOME)
emacsdir = $(prefix)/share/emacs/site-lisp
+RM ?= rm -f
all: $(ELC)
install: all
$(INSTALL) -d $(DESTDIR)$(emacsdir)
- $(INSTALL_ELC) $(ELC) $(DESTDIR)$(emacsdir)
+ $(INSTALL_ELC) $(ELC:.elc=.el) $(ELC) $(DESTDIR)$(emacsdir)
%.elc: %.el
$(EMACS) -batch -f batch-byte-compile $<
-clean:; rm -f $(ELC)
+clean:; $(RM) $(ELC)
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index f60017948f..be44e06c45 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -314,8 +314,8 @@ and returns the process output as a string."
(sort-lines nil (point-min) (point-max))
(save-buffer))
(when created
- (git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
- (git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
+ (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
+ (git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
; propertize definition for XEmacs, stolen from erc-compat
(eval-when-compile
@@ -523,23 +523,39 @@ and returns the process output as a string."
" " (git-escape-file-name (git-fileinfo->name info))
(git-rename-as-string info))))
-(defun git-parse-status (status)
- "Parse the output of git-diff-index in the current buffer."
- (goto-char (point-min))
- (while (re-search-forward
- ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
- nil t 1)
- (let ((old-perm (string-to-number (match-string 1) 8))
- (new-perm (string-to-number (match-string 2) 8))
- (state (or (match-string 4) (match-string 6)))
- (name (or (match-string 5) (match-string 7)))
- (new-name (match-string 8)))
- (if new-name ; copy or rename
- (if (eq ?C (string-to-char state))
- (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name))
- (ewoc-enter-last status (git-create-fileinfo 'deleted name 0 0 'rename new-name))
- (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)))
- (ewoc-enter-last status (git-create-fileinfo (git-state-code state) name old-perm new-perm))))))
+(defun git-insert-fileinfo (status info &optional refresh)
+ "Insert INFO in the status buffer, optionally refreshing an existing one."
+ (let ((node (and refresh
+ (git-find-status-file status (git-fileinfo->name info)))))
+ (setf (git-fileinfo->needs-refresh info) t)
+ (when node ;preserve the marked flag
+ (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
+ (if node (setf (ewoc-data node) info) (ewoc-enter-last status info))))
+
+(defun git-run-diff-index (status files)
+ "Run git-diff-index on FILES and parse the results into STATUS.
+Return the list of files that haven't been handled."
+ (let ((refresh files))
+ (with-temp-buffer
+ (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
+ (goto-char (point-min))
+ (while (re-search-forward
+ ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
+ nil t 1)
+ (let ((old-perm (string-to-number (match-string 1) 8))
+ (new-perm (string-to-number (match-string 2) 8))
+ (state (or (match-string 4) (match-string 6)))
+ (name (or (match-string 5) (match-string 7)))
+ (new-name (match-string 8)))
+ (if new-name ; copy or rename
+ (if (eq ?C (string-to-char state))
+ (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
+ (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
+ (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
+ (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
+ (setq files (delete name files))
+ (when new-name (setq files (delete new-name files)))))))
+ files)
(defun git-find-status-file (status file)
"Find a given file in the status ewoc and return its node."
@@ -548,32 +564,69 @@ and returns the process output as a string."
(setq node (ewoc-next status node)))
node))
-(defun git-parse-ls-files (status default-state &optional skip-existing)
- "Parse the output of git-ls-files in the current buffer."
- (goto-char (point-min))
- (let (infolist)
- (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
- (let ((state (match-string 1))
- (name (match-string 2)))
- (unless (and skip-existing (git-find-status-file status name))
- (push (git-create-fileinfo (or (git-state-code state) default-state) name) infolist))))
- (dolist (info (nreverse infolist))
- (ewoc-enter-last status info))))
-
-(defun git-parse-ls-unmerged (status)
- "Parse the output of git-ls-files -u in the current buffer."
- (goto-char (point-min))
- (let (files)
- (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
- (let ((node (git-find-status-file status (match-string 1))))
- (when node (push (ewoc-data node) files))))
- (git-set-files-state files 'unmerged)))
-
-(defun git-add-status-file (state name)
- "Add a new file to the status list (if not existing already) and return its node."
+(defun git-run-ls-files (status files default-state &rest options)
+ "Run git-ls-files on FILES and parse the results into STATUS.
+Return the list of files that haven't been handled."
+ (let ((refresh files))
+ (with-temp-buffer
+ (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
+ (goto-char (point-min))
+ (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
+ (let ((state (match-string 1))
+ (name (match-string 2)))
+ (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
+ (setq files (delete name files))))))
+ files)
+
+(defun git-run-ls-unmerged (status files)
+ "Run git-ls-files -u on FILES and parse the results into STATUS."
+ (with-temp-buffer
+ (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files)
+ (goto-char (point-min))
+ (let (unmerged-files)
+ (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
+ (let ((node (git-find-status-file status (match-string 1))))
+ (when node (push (ewoc-data node) unmerged-files))))
+ (git-set-files-state unmerged-files 'unmerged))))
+
+(defun git-get-exclude-files ()
+ "Get the list of exclude files to pass to git-ls-files."
+ (let (files
+ (config (git-config "core.excludesfile")))
+ (when (file-readable-p ".git/info/exclude")
+ (push ".git/info/exclude" files))
+ (when (and config (file-readable-p config))
+ (push config files))
+ files))
+
+(defun git-update-status-files (files &optional default-state)
+ "Update the status of FILES from the index."
(unless git-status (error "Not in git-status buffer."))
- (or (git-find-status-file git-status name)
- (ewoc-enter-last git-status (git-create-fileinfo state name))))
+ (let* ((status git-status)
+ (remaining-files
+ (if (git-empty-db-p) ; we need some special handling for an empty db
+ (git-run-ls-files status files 'added "-c")
+ (git-run-diff-index status files))))
+ (git-run-ls-unmerged status files)
+ (when (or (not files) remaining-files)
+ (let ((exclude-files (git-get-exclude-files)))
+ (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o"
+ (concat "--exclude-per-directory=" git-per-dir-ignore-file)
+ (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
+ ; mark remaining files with the default state (or remove them if nil)
+ (when remaining-files
+ (if default-state
+ (ewoc-map (lambda (info)
+ (when (member (git-fileinfo->name info) remaining-files)
+ (git-set-files-state (list info) default-state))
+ nil)
+ status)
+ (ewoc-filter status
+ (lambda (info files)
+ (not (member (git-fileinfo->name info) files)))
+ remaining-files)))
+ (git-refresh-files)
+ (git-refresh-ewoc-hf status)))
(defun git-marked-files ()
"Return a list of all marked files, or if none a list containing just the file at cursor position."
@@ -681,8 +734,7 @@ and returns the process output as a string."
(condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
(with-current-buffer buffer (erase-buffer))
(git-set-files-state files 'uptodate)
- (when (file-directory-p ".git/rr-cache")
- (git-run-command nil nil "rerere"))
+ (git-run-command nil nil "rerere")
(git-refresh-files)
(git-refresh-ewoc-hf git-status)
(message "Committed %s." commit)
@@ -790,54 +842,34 @@ and returns the process output as a string."
(defun git-add-file ()
"Add marked file(s) to the index cache."
(interactive)
- (let ((files (git-marked-files-state 'unknown)))
+ (let ((files (git-get-filenames (git-marked-files-state 'unknown))))
(unless files
- (push (ewoc-data
- (git-add-status-file 'added (file-relative-name
- (read-file-name "File to add: " nil nil t))))
- files))
- (apply #'git-run-command nil nil "update-index" "--info-only" "--add" "--" (git-get-filenames files))
- (git-set-files-state files 'added)
- (git-refresh-files)))
+ (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
+ (apply #'git-run-command nil nil "update-index" "--add" "--" files)
+ (git-update-status-files files 'uptodate)))
(defun git-ignore-file ()
"Add marked file(s) to the ignore list."
(interactive)
- (let ((files (git-marked-files-state 'unknown)))
+ (let ((files (git-get-filenames (git-marked-files-state 'unknown))))
(unless files
- (push (ewoc-data
- (git-add-status-file 'unknown (file-relative-name
- (read-file-name "File to ignore: " nil nil t))))
- files))
- (dolist (info files) (git-append-to-ignore (git-fileinfo->name info)))
- (git-set-files-state files 'ignored)
- (git-refresh-files)))
+ (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
+ (dolist (f files) (git-append-to-ignore f))
+ (git-update-status-files files 'ignored)))
(defun git-remove-file ()
"Remove the marked file(s)."
(interactive)
- (let ((files (git-marked-files-state 'added 'modified 'unknown 'uptodate)))
+ (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate))))
(unless files
- (push (ewoc-data
- (git-add-status-file 'unknown (file-relative-name
- (read-file-name "File to remove: " nil nil t))))
- files))
+ (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
(if (yes-or-no-p
(format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
(progn
- (dolist (info files)
- (let ((name (git-fileinfo->name info)))
- (when (file-exists-p name) (delete-file name))))
- (apply #'git-run-command nil nil "update-index" "--info-only" "--remove" "--" (git-get-filenames files))
- ; remove unknown files from the list, set the others to deleted
- (ewoc-filter git-status
- (lambda (info files)
- (not (and (memq info files) (eq (git-fileinfo->state info) 'unknown))))
- files)
- (git-set-files-state files 'deleted)
- (git-refresh-files)
- (unless (ewoc-nth git-status 0) ; refresh header if list is empty
- (git-refresh-ewoc-hf git-status)))
+ (dolist (name files)
+ (when (file-exists-p name) (delete-file name)))
+ (apply #'git-run-command nil nil "update-index" "--remove" "--" files)
+ (git-update-status-files files nil))
(message "Aborting"))))
(defun git-revert-file ()
@@ -850,26 +882,23 @@ and returns the process output as a string."
(format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" ""))))
(dolist (info files)
(case (git-fileinfo->state info)
- ('added (push info added))
- ('deleted (push info modified))
- ('unmerged (push info modified))
- ('modified (push info modified))))
+ ('added (push (git-fileinfo->name info) added))
+ ('deleted (push (git-fileinfo->name info) modified))
+ ('unmerged (push (git-fileinfo->name info) modified))
+ ('modified (push (git-fileinfo->name info) modified))))
(when added
- (apply #'git-run-command nil nil "update-index" "--force-remove" "--" (git-get-filenames added))
- (git-set-files-state added 'unknown))
+ (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added))
(when modified
- (apply #'git-run-command nil nil "checkout" "HEAD" (git-get-filenames modified))
- (git-set-files-state modified 'uptodate))
- (git-refresh-files))))
+ (apply #'git-run-command nil nil "checkout" "HEAD" modified))
+ (git-update-status-files (append added modified) 'uptodate))))
(defun git-resolve-file ()
"Resolve conflicts in marked file(s)."
(interactive)
- (let ((files (git-marked-files-state 'unmerged)))
+ (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
(when files
- (apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files))
- (git-set-files-state files 'modified)
- (git-refresh-files))))
+ (apply #'git-run-command nil nil "update-index" "--" files)
+ (git-update-status-files files 'uptodate))))
(defun git-remove-handled ()
"Remove handled files from the status list."
@@ -883,10 +912,12 @@ and returns the process output as a string."
(defun git-setup-diff-buffer (buffer)
"Setup a buffer for displaying a diff."
- (with-current-buffer buffer
- (diff-mode)
- (goto-char (point-min))
- (setq buffer-read-only t))
+ (let ((dir default-directory))
+ (with-current-buffer buffer
+ (diff-mode)
+ (goto-char (point-min))
+ (setq default-directory dir)
+ (setq buffer-read-only t)))
(display-buffer buffer)
(shrink-window-if-larger-than-buffer))
@@ -936,7 +967,13 @@ and returns the process output as a string."
(defun git-diff-file-idiff ()
"Perform an interactive diff on the current file."
(interactive)
- (error "Interactive diffs not implemented yet."))
+ (let ((files (git-marked-files-state 'added 'deleted 'modified)))
+ (unless (eq 1 (length files))
+ (error "Cannot perform an interactive diff on multiple files."))
+ (let* ((filename (car (git-get-filenames files)))
+ (buff1 (find-file-noselect filename))
+ (buff2 (git-run-command-buffer (concat filename ".~HEAD~") "cat-file" "blob" (concat "HEAD:" filename))))
+ (ediff-buffers buff1 buff2))))
(defun git-log-file ()
"Display a log of changes to the marked file(s)."
@@ -1039,7 +1076,7 @@ and returns the process output as a string."
(let ((info (ewoc-data (ewoc-locate git-status))))
(find-file (git-fileinfo->name info))
(when (eq 'unmerged (git-fileinfo->state info))
- (smerge-mode))))
+ (smerge-mode 1))))
(defun git-find-file-other-window ()
"Visit the current file in its own buffer in another window."
@@ -1072,27 +1109,9 @@ and returns the process output as a string."
(pos (ewoc-locate status))
(cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
(unless status (error "Not in git-status buffer."))
+ (git-run-command nil nil "update-index" "--refresh")
(git-clear-status status)
- (git-run-command nil nil "update-index" "--info-only" "--refresh")
- (if (git-empty-db-p)
- ; we need some special handling for an empty db
- (with-temp-buffer
- (git-run-command t nil "ls-files" "-z" "-t" "-c")
- (git-parse-ls-files status 'added))
- (with-temp-buffer
- (git-run-command t nil "diff-index" "-z" "-M" "HEAD")
- (git-parse-status status)))
- (with-temp-buffer
- (git-run-command t nil "ls-files" "-z" "-u")
- (git-parse-ls-unmerged status))
- (when (file-readable-p ".git/info/exclude")
- (with-temp-buffer
- (git-run-command t nil "ls-files" "-z" "-t" "-o"
- "--exclude-from=.git/info/exclude"
- (concat "--exclude-per-directory=" git-per-dir-ignore-file))
- (git-parse-ls-files status 'unknown)))
- (git-refresh-files)
- (git-refresh-ewoc-hf status)
+ (git-update-status-files nil)
; move point to the current file name if any
(let ((node (and cur-name (git-find-status-file status cur-name))))
(when node (ewoc-goto-node status node)))))
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
index e456ab9712..b8f6be5c0a 100644
--- a/contrib/emacs/vc-git.el
+++ b/contrib/emacs/vc-git.el
@@ -81,6 +81,71 @@
(match-string 2 str)
str)))
+(defun vc-git-symbolic-commit (commit)
+ "Translate COMMIT string into symbolic form.
+Returns nil if not possible."
+ (and commit
+ (with-temp-buffer
+ (and
+ (zerop
+ (call-process "git" nil '(t nil) nil "name-rev"
+ "--name-only" "--tags"
+ commit))
+ (goto-char (point-min))
+ (= (forward-line 2) 1)
+ (bolp)
+ (buffer-substring-no-properties (point-min) (1- (point-max)))))))
+
+(defun vc-git-previous-version (file rev)
+ "git-specific version of `vc-previous-version'."
+ (let ((default-directory (file-name-directory (expand-file-name file)))
+ (file (file-name-nondirectory file)))
+ (vc-git-symbolic-commit
+ (with-temp-buffer
+ (and
+ (zerop
+ (call-process "git" nil '(t nil) nil "rev-list"
+ "-2" rev "--" file))
+ (goto-char (point-max))
+ (bolp)
+ (zerop (forward-line -1))
+ (not (bobp))
+ (buffer-substring-no-properties
+ (point)
+ (1- (point-max))))))))
+
+(defun vc-git-next-version (file rev)
+ "git-specific version of `vc-next-version'."
+ (let* ((default-directory (file-name-directory
+ (expand-file-name file)))
+ (file (file-name-nondirectory file))
+ (current-rev
+ (with-temp-buffer
+ (and
+ (zerop
+ (call-process "git" nil '(t nil) nil "rev-list"
+ "-1" rev "--" file))
+ (goto-char (point-max))
+ (bolp)
+ (zerop (forward-line -1))
+ (bobp)
+ (buffer-substring-no-properties
+ (point)
+ (1- (point-max)))))))
+ (and current-rev
+ (vc-git-symbolic-commit
+ (with-temp-buffer
+ (and
+ (zerop
+ (call-process "git" nil '(t nil) nil "rev-list"
+ "HEAD" "--" file))
+ (goto-char (point-min))
+ (search-forward current-rev nil t)
+ (zerop (forward-line -1))
+ (buffer-substring-no-properties
+ (point)
+ (progn (forward-line 1) (1- (point))))))))))
+
(defun vc-git-revert (file &optional contents-done)
"Revert FILE to the version stored in the git repository."
(if contents-done
diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh
index 436d7caff5..2ae235b081 100755
--- a/contrib/examples/git-gc.sh
+++ b/contrib/examples/git-gc.sh
@@ -30,8 +30,8 @@ notbare|"")
esac
test "true" != "$pack_refs" ||
-git-pack-refs --prune &&
-git-reflog expire --all &&
+git pack-refs --prune &&
+git reflog expire --all &&
git-repack -a -d -l &&
-$no_prune git-prune &&
-git-rerere gc || exit
+$no_prune git prune &&
+git rerere gc || exit
diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh
index 36b90e3849..0ee1bd898e 100755
--- a/contrib/examples/git-resolve.sh
+++ b/contrib/examples/git-resolve.sh
@@ -17,8 +17,8 @@ dropheads() {
"$GIT_DIR/LAST_MERGE" || exit 1
}
-head=$(git-rev-parse --verify "$1"^0) &&
-merge=$(git-rev-parse --verify "$2"^0) &&
+head=$(git rev-parse --verify "$1"^0) &&
+merge=$(git rev-parse --verify "$2"^0) &&
merge_name="$2" &&
merge_msg="$3" || usage
@@ -34,7 +34,7 @@ dropheads
echo $head > "$GIT_DIR"/ORIG_HEAD
echo $merge > "$GIT_DIR"/LAST_MERGE
-common=$(git-merge-base $head $merge)
+common=$(git merge-base $head $merge)
if [ -z "$common" ]; then
die "Unable to find common commit between" $merge $head
fi
@@ -46,11 +46,11 @@ case "$common" in
exit 0
;;
"$head")
- echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)"
- git-read-tree -u -m $head $merge || exit 1
- git-update-ref -m "resolve $merge_name: Fast forward" \
+ echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)"
+ git read-tree -u -m $head $merge || exit 1
+ git update-ref -m "resolve $merge_name: Fast forward" \
HEAD "$merge" "$head"
- git-diff-tree -p $head $merge | git-apply --stat
+ git diff-tree -p $head $merge | git apply --stat
dropheads
exit 0
;;
@@ -62,7 +62,7 @@ git var GIT_COMMITTER_IDENT >/dev/null || exit
# Find an optimum merge base if there are more than one candidates.
LF='
'
-common=$(git-merge-base -a $head $merge)
+common=$(git merge-base -a $head $merge)
case "$common" in
?*"$LF"?*)
echo "Trying to find the optimum merge base."
@@ -72,10 +72,10 @@ case "$common" in
for c in $common
do
rm -f $G
- GIT_INDEX_FILE=$G git-read-tree -m $c $head $merge \
+ GIT_INDEX_FILE=$G git read-tree -m $c $head $merge \
2>/dev/null || continue
# Count the paths that are unmerged.
- cnt=`GIT_INDEX_FILE=$G git-ls-files --unmerged | wc -l`
+ cnt=`GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l`
if test $best_cnt -le 0 -o $cnt -le $best_cnt
then
best=$c
@@ -92,9 +92,9 @@ case "$common" in
esac
echo "Trying to merge $merge into $head using $common."
-git-update-index --refresh 2>/dev/null
-git-read-tree -u -m $common $head $merge || exit 1
-result_tree=$(git-write-tree 2> /dev/null)
+git update-index --refresh 2>/dev/null
+git read-tree -u -m $common $head $merge || exit 1
+result_tree=$(git write-tree 2> /dev/null)
if [ $? -ne 0 ]; then
echo "Simple merge failed, trying Automatic merge"
git-merge-index -o git-merge-one-file -a
@@ -102,11 +102,11 @@ if [ $? -ne 0 ]; then
echo $merge > "$GIT_DIR"/MERGE_HEAD
die "Automatic merge failed, fix up by hand"
fi
- result_tree=$(git-write-tree) || exit 1
+ result_tree=$(git write-tree) || exit 1
fi
-result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree -p $head -p $merge)
+result_commit=$(echo "$merge_msg" | git commit-tree $result_tree -p $head -p $merge)
echo "Committed merge $result_commit"
-git-update-ref -m "resolve $merge_name: In-index merge" \
+git update-ref -m "resolve $merge_name: In-index merge" \
HEAD "$result_commit" "$head"
-git-diff-tree -p $head $result_commit | git-apply --stat
+git diff-tree -p $head $result_commit | git apply --stat
dropheads
diff --git a/git-tag.sh b/contrib/examples/git-tag.sh
index 6f0b7a7219..5ee3f50a3c 100755
--- a/git-tag.sh
+++ b/contrib/examples/git-tag.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Copyright (c) 2005 Linus Torvalds
-USAGE='-l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]'
+USAGE='[-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]'
SUBDIRECTORY_OK='Yes'
. git-sh-setup
@@ -13,36 +13,82 @@ message=
username=
list=
verify=
+LINES=0
while case "$#" in 0) break ;; esac
do
case "$1" in
-a)
annotate=1
+ shift
;;
-s)
annotate=1
signed=1
+ shift
;;
-f)
force=1
+ shift
;;
- -l)
- case "$#" in
- 1)
- set x . ;;
+ -n)
+ case "$#,$2" in
+ 1,* | *,-*)
+ LINES=1 # no argument
+ ;;
+ *) shift
+ LINES=$(expr "$1" : '\([0-9]*\)')
+ [ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used
+ ;;
esac
shift
- git rev-parse --symbolic --tags | sort | grep "$@"
- exit $?
+ ;;
+ -l)
+ list=1
+ shift
+ case $# in
+ 0) PATTERN=
+ ;;
+ *)
+ PATTERN="$1" # select tags by shell pattern, not re
+ shift
+ ;;
+ esac
+ git rev-parse --symbolic --tags | sort |
+ while read TAG
+ do
+ case "$TAG" in
+ *$PATTERN*) ;;
+ *) continue ;;
+ esac
+ [ "$LINES" -le 0 ] && { echo "$TAG"; continue ;}
+ OBJTYPE=$(git cat-file -t "$TAG")
+ case $OBJTYPE in
+ tag)
+ ANNOTATION=$(git cat-file tag "$TAG" |
+ sed -e '1,/^$/d' |
+ sed -n -e "
+ /^-----BEGIN PGP SIGNATURE-----\$/q
+ 2,\$s/^/ /
+ p
+ ${LINES}q
+ ")
+ printf "%-15s %s\n" "$TAG" "$ANNOTATION"
+ ;;
+ *) echo "$TAG"
+ ;;
+ esac
+ done
;;
-m)
- annotate=1
+ annotate=1
shift
message="$1"
if test "$#" = "0"; then
die "error: option -m needs an argument"
else
+ message="$1"
message_given=1
+ shift
fi
;;
-F)
@@ -53,25 +99,31 @@ do
else
message="$(cat "$1")"
message_given=1
+ shift
fi
;;
-u)
annotate=1
signed=1
shift
- username="$1"
+ if test "$#" = "0"; then
+ die "error: option -u needs an argument"
+ else
+ username="$1"
+ shift
+ fi
;;
-d)
- shift
+ shift
had_error=0
for tag
do
- cur=$(git-show-ref --verify --hash -- "refs/tags/$tag") || {
+ cur=$(git show-ref --verify --hash -- "refs/tags/$tag") || {
echo >&2 "Seriously, what tag are you talking about?"
had_error=1
continue
}
- git-update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || {
+ git update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || {
had_error=1
continue
}
@@ -82,7 +134,7 @@ do
-v)
shift
tag_name="$1"
- tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") ||
+ tag=$(git show-ref --verify --hash -- "refs/tags/$tag_name") ||
die "Seriously, what tag are you talking about?"
git-verify-tag -v "$tag"
exit $?
@@ -94,27 +146,28 @@ do
break
;;
esac
- shift
done
+[ -n "$list" ] && exit 0
+
name="$1"
[ "$name" ] || usage
prev=0000000000000000000000000000000000000000
-if git-show-ref --verify --quiet -- "refs/tags/$name"
+if git show-ref --verify --quiet -- "refs/tags/$name"
then
test -n "$force" || die "tag '$name' already exists"
prev=`git rev-parse "refs/tags/$name"`
fi
shift
-git-check-ref-format "tags/$name" ||
+git check-ref-format "tags/$name" ||
die "we do not like '$name' as a tag name."
-object=$(git-rev-parse --verify --default HEAD "$@") || exit 1
-type=$(git-cat-file -t $object) || exit 1
+object=$(git rev-parse --verify --default HEAD "$@") || exit 1
+type=$(git cat-file -t $object) || exit 1
tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
test -n "$username" ||
- username=$(git-repo-config user.signingkey) ||
+ username=$(git repo-config user.signingkey) ||
username=$(expr "z$tagger" : 'z\(.*>\)')
trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0
@@ -124,13 +177,13 @@ if [ "$annotate" ]; then
( echo "#"
echo "# Write a tag message"
echo "#" ) > "$GIT_DIR"/TAG_EDITMSG
- ${VISUAL:-${EDITOR:-vi}} "$GIT_DIR"/TAG_EDITMSG || exit
+ git_editor "$GIT_DIR"/TAG_EDITMSG || exit
else
printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG
fi
grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG |
- git-stripspace >"$GIT_DIR"/TAG_FINALMSG
+ git stripspace >"$GIT_DIR"/TAG_FINALMSG
[ -s "$GIT_DIR"/TAG_FINALMSG -o -n "$message_given" ] || {
echo >&2 "No tag message?"
@@ -150,4 +203,3 @@ if [ "$annotate" ]; then
fi
git update-ref "refs/tags/$name" "$object" "$prev"
-
diff --git a/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh
index 8db7dd0b7d..37b0023b27 100755
--- a/git-verify-tag.sh
+++ b/contrib/examples/git-verify-tag.sh
@@ -21,7 +21,7 @@ then
usage
fi
-type="$(git-cat-file -t "$1" 2>/dev/null)" ||
+type="$(git cat-file -t "$1" 2>/dev/null)" ||
die "$1: no such object."
test "$type" = tag ||
@@ -29,17 +29,17 @@ test "$type" = tag ||
case "$verbose" in
t)
- git-cat-file -p "$1" |
+ git cat-file -p "$1" |
sed -n -e '/^-----BEGIN PGP SIGNATURE-----/q' -e p
;;
esac
trap 'rm -f "$GIT_DIR/.tmp-vtag"' 0
-git-cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1
-
-cat "$GIT_DIR/.tmp-vtag" |
-sed '/-----BEGIN PGP/Q' |
+git cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1
+sed -n -e '
+ /^-----BEGIN PGP SIGNATURE-----$/q
+ p
+' <"$GIT_DIR/.tmp-vtag" |
gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1
rm -f "$GIT_DIR/.tmp-vtag"
-
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
new file mode 100755
index 0000000000..6d0106237a
--- /dev/null
+++ b/contrib/fast-import/git-p4
@@ -0,0 +1,1621 @@
+#!/usr/bin/env python
+#
+# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
+#
+# Author: Simon Hausmann <simon@lst.de>
+# Copyright: 2007 Simon Hausmann <simon@lst.de>
+# 2007 Trolltech ASA
+# License: MIT <http://www.opensource.org/licenses/mit-license.php>
+#
+
+import optparse, sys, os, marshal, popen2, subprocess, shelve
+import tempfile, getopt, sha, os.path, time, platform
+import re
+
+from sets import Set;
+
+verbose = False
+
+def die(msg):
+ if verbose:
+ raise Exception(msg)
+ else:
+ sys.stderr.write(msg + "\n")
+ sys.exit(1)
+
+def write_pipe(c, str):
+ if verbose:
+ sys.stderr.write('Writing pipe: %s\n' % c)
+
+ pipe = os.popen(c, 'w')
+ val = pipe.write(str)
+ if pipe.close():
+ die('Command failed: %s' % c)
+
+ return val
+
+def read_pipe(c, ignore_error=False):
+ if verbose:
+ sys.stderr.write('Reading pipe: %s\n' % c)
+
+ pipe = os.popen(c, 'rb')
+ val = pipe.read()
+ if pipe.close() and not ignore_error:
+ die('Command failed: %s' % c)
+
+ return val
+
+
+def read_pipe_lines(c):
+ if verbose:
+ sys.stderr.write('Reading pipe: %s\n' % c)
+ ## todo: check return status
+ pipe = os.popen(c, 'rb')
+ val = pipe.readlines()
+ if pipe.close():
+ die('Command failed: %s' % c)
+
+ return val
+
+def system(cmd):
+ if verbose:
+ sys.stderr.write("executing %s\n" % cmd)
+ if os.system(cmd) != 0:
+ die("command failed: %s" % cmd)
+
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
+ cmd = "p4 -G %s" % cmd
+ if verbose:
+ sys.stderr.write("Opening pipe: %s\n" % cmd)
+
+ # Use a temporary file to avoid deadlocks without
+ # subprocess.communicate(), which would put another copy
+ # of stdout into memory.
+ stdin_file = None
+ if stdin is not None:
+ stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
+ stdin_file.write(stdin)
+ stdin_file.flush()
+ stdin_file.seek(0)
+
+ p4 = subprocess.Popen(cmd, shell=True,
+ stdin=stdin_file,
+ stdout=subprocess.PIPE)
+
+ result = []
+ try:
+ while True:
+ entry = marshal.load(p4.stdout)
+ result.append(entry)
+ except EOFError:
+ pass
+ exitCode = p4.wait()
+ if exitCode != 0:
+ entry = {}
+ entry["p4ExitCode"] = exitCode
+ result.append(entry)
+
+ return result
+
+def p4Cmd(cmd):
+ list = p4CmdList(cmd)
+ result = {}
+ for entry in list:
+ result.update(entry)
+ return result;
+
+def p4Where(depotPath):
+ if not depotPath.endswith("/"):
+ depotPath += "/"
+ output = p4Cmd("where %s..." % depotPath)
+ if output["code"] == "error":
+ return ""
+ clientPath = ""
+ if "path" in output:
+ clientPath = output.get("path")
+ elif "data" in output:
+ data = output.get("data")
+ lastSpace = data.rfind(" ")
+ clientPath = data[lastSpace + 1:]
+
+ if clientPath.endswith("..."):
+ clientPath = clientPath[:-3]
+ return clientPath
+
+def currentGitBranch():
+ return read_pipe("git name-rev HEAD").split(" ")[1].strip()
+
+def isValidGitDir(path):
+ if (os.path.exists(path + "/HEAD")
+ and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
+ return True;
+ return False
+
+def parseRevision(ref):
+ return read_pipe("git rev-parse %s" % ref).strip()
+
+def extractLogMessageFromGitCommit(commit):
+ logMessage = ""
+
+ ## fixme: title is first line of commit, not 1st paragraph.
+ foundTitle = False
+ for log in read_pipe_lines("git cat-file commit %s" % commit):
+ if not foundTitle:
+ if len(log) == 1:
+ foundTitle = True
+ continue
+
+ logMessage += log
+ return logMessage
+
+def extractSettingsGitLog(log):
+ values = {}
+ for line in log.split("\n"):
+ line = line.strip()
+ m = re.search (r"^ *\[git-p4: (.*)\]$", line)
+ if not m:
+ continue
+
+ assignments = m.group(1).split (':')
+ for a in assignments:
+ vals = a.split ('=')
+ key = vals[0].strip()
+ val = ('='.join (vals[1:])).strip()
+ if val.endswith ('\"') and val.startswith('"'):
+ val = val[1:-1]
+
+ values[key] = val
+
+ paths = values.get("depot-paths")
+ if not paths:
+ paths = values.get("depot-path")
+ if paths:
+ values['depot-paths'] = paths.split(',')
+ return values
+
+def gitBranchExists(branch):
+ proc = subprocess.Popen(["git", "rev-parse", branch],
+ stderr=subprocess.PIPE, stdout=subprocess.PIPE);
+ return proc.wait() == 0;
+
+def gitConfig(key):
+ return read_pipe("git config %s" % key, ignore_error=True).strip()
+
+def p4BranchesInGit(branchesAreInRemotes = True):
+ branches = {}
+
+ cmdline = "git rev-parse --symbolic "
+ if branchesAreInRemotes:
+ cmdline += " --remotes"
+ else:
+ cmdline += " --branches"
+
+ for line in read_pipe_lines(cmdline):
+ line = line.strip()
+
+ ## only import to p4/
+ if not line.startswith('p4/') or line == "p4/HEAD":
+ continue
+ branch = line
+
+ # strip off p4
+ branch = re.sub ("^p4/", "", line)
+
+ branches[branch] = parseRevision(line)
+ return branches
+
+def findUpstreamBranchPoint(head = "HEAD"):
+ branches = p4BranchesInGit()
+ # map from depot-path to branch name
+ branchByDepotPath = {}
+ for branch in branches.keys():
+ tip = branches[branch]
+ log = extractLogMessageFromGitCommit(tip)
+ settings = extractSettingsGitLog(log)
+ if settings.has_key("depot-paths"):
+ paths = ",".join(settings["depot-paths"])
+ branchByDepotPath[paths] = "remotes/p4/" + branch
+
+ settings = None
+ parent = 0
+ while parent < 65535:
+ commit = head + "~%s" % parent
+ log = extractLogMessageFromGitCommit(commit)
+ settings = extractSettingsGitLog(log)
+ if settings.has_key("depot-paths"):
+ paths = ",".join(settings["depot-paths"])
+ if branchByDepotPath.has_key(paths):
+ return [branchByDepotPath[paths], settings]
+
+ parent = parent + 1
+
+ return ["", settings]
+
+class Command:
+ def __init__(self):
+ self.usage = "usage: %prog [options]"
+ self.needsGit = True
+
+class P4Debug(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [
+ optparse.make_option("--verbose", dest="verbose", action="store_true",
+ default=False),
+ ]
+ self.description = "A tool to debug the output of p4 -G."
+ self.needsGit = False
+ self.verbose = False
+
+ def run(self, args):
+ j = 0
+ for output in p4CmdList(" ".join(args)):
+ print 'Element: %d' % j
+ j += 1
+ print output
+ return True
+
+class P4RollBack(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [
+ optparse.make_option("--verbose", dest="verbose", action="store_true"),
+ optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
+ ]
+ self.description = "A tool to debug the multi-branch import. Don't use :)"
+ self.verbose = False
+ self.rollbackLocalBranches = False
+
+ def run(self, args):
+ if len(args) != 1:
+ return False
+ maxChange = int(args[0])
+
+ if "p4ExitCode" in p4Cmd("changes -m 1"):
+ die("Problems executing p4");
+
+ if self.rollbackLocalBranches:
+ refPrefix = "refs/heads/"
+ lines = read_pipe_lines("git rev-parse --symbolic --branches")
+ else:
+ refPrefix = "refs/remotes/"
+ lines = read_pipe_lines("git rev-parse --symbolic --remotes")
+
+ for line in lines:
+ if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
+ line = line.strip()
+ ref = refPrefix + line
+ log = extractLogMessageFromGitCommit(ref)
+ settings = extractSettingsGitLog(log)
+
+ depotPaths = settings['depot-paths']
+ change = settings['change']
+
+ changed = False
+
+ if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange)
+ for p in depotPaths]))) == 0:
+ print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
+ system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
+ continue
+
+ while change and int(change) > maxChange:
+ changed = True
+ if self.verbose:
+ print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
+ system("git update-ref %s \"%s^\"" % (ref, ref))
+ log = extractLogMessageFromGitCommit(ref)
+ settings = extractSettingsGitLog(log)
+
+
+ depotPaths = settings['depot-paths']
+ change = settings['change']
+
+ if changed:
+ print "%s rewound to %s" % (ref, change)
+
+ return True
+
+class P4Submit(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [
+ optparse.make_option("--continue", action="store_false", dest="firstTime"),
+ optparse.make_option("--verbose", dest="verbose", action="store_true"),
+ optparse.make_option("--origin", dest="origin"),
+ optparse.make_option("--reset", action="store_true", dest="reset"),
+ optparse.make_option("--log-substitutions", dest="substFile"),
+ optparse.make_option("--dry-run", action="store_true"),
+ optparse.make_option("--direct", dest="directSubmit", action="store_true"),
+ optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"),
+ ]
+ self.description = "Submit changes from git to the perforce depot."
+ self.usage += " [name of git branch to submit into perforce depot]"
+ self.firstTime = True
+ self.reset = False
+ self.interactive = True
+ self.dryRun = False
+ self.substFile = ""
+ self.firstTime = True
+ self.origin = ""
+ self.directSubmit = False
+ self.trustMeLikeAFool = False
+ self.verbose = False
+ self.isWindows = (platform.system() == "Windows")
+
+ self.logSubstitutions = {}
+ self.logSubstitutions["<enter description here>"] = "%log%"
+ self.logSubstitutions["\tDetails:"] = "\tDetails: %log%"
+
+ def check(self):
+ if len(p4CmdList("opened ...")) > 0:
+ die("You have files opened with perforce! Close them before starting the sync.")
+
+ def start(self):
+ if len(self.config) > 0 and not self.reset:
+ die("Cannot start sync. Previous sync config found at %s\n"
+ "If you want to start submitting again from scratch "
+ "maybe you want to call git-p4 submit --reset" % self.configFile)
+
+ commits = []
+ if self.directSubmit:
+ commits.append("0")
+ else:
+ for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
+ commits.append(line.strip())
+ commits.reverse()
+
+ self.config["commits"] = commits
+
+ def prepareLogMessage(self, template, message):
+ result = ""
+
+ for line in template.split("\n"):
+ if line.startswith("#"):
+ result += line + "\n"
+ continue
+
+ substituted = False
+ for key in self.logSubstitutions.keys():
+ if line.find(key) != -1:
+ value = self.logSubstitutions[key]
+ value = value.replace("%log%", message)
+ if value != "@remove@":
+ result += line.replace(key, value) + "\n"
+ substituted = True
+ break
+
+ if not substituted:
+ result += line + "\n"
+
+ return result
+
+ def prepareSubmitTemplate(self):
+ # remove lines in the Files section that show changes to files outside the depot path we're committing into
+ template = ""
+ inFilesSection = False
+ for line in read_pipe_lines("p4 change -o"):
+ if inFilesSection:
+ if line.startswith("\t"):
+ # path starts and ends with a tab
+ path = line[1:]
+ lastTab = path.rfind("\t")
+ if lastTab != -1:
+ path = path[:lastTab]
+ if not path.startswith(self.depotPath):
+ continue
+ else:
+ inFilesSection = False
+ else:
+ if line.startswith("Files:"):
+ inFilesSection = True
+
+ template += line
+
+ return template
+
+ def applyCommit(self, id):
+ if self.directSubmit:
+ print "Applying local change in working directory/index"
+ diff = self.diffStatus
+ else:
+ print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
+ diff = read_pipe_lines("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id))
+ filesToAdd = set()
+ filesToDelete = set()
+ editedFiles = set()
+ for line in diff:
+ modifier = line[0]
+ path = line[1:].strip()
+ if modifier == "M":
+ system("p4 edit \"%s\"" % path)
+ editedFiles.add(path)
+ elif modifier == "A":
+ filesToAdd.add(path)
+ if path in filesToDelete:
+ filesToDelete.remove(path)
+ elif modifier == "D":
+ filesToDelete.add(path)
+ if path in filesToAdd:
+ filesToAdd.remove(path)
+ else:
+ die("unknown modifier %s for %s" % (modifier, path))
+
+ if self.directSubmit:
+ diffcmd = "cat \"%s\"" % self.diffFile
+ else:
+ diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
+ patchcmd = diffcmd + " | git apply "
+ tryPatchCmd = patchcmd + "--check -"
+ applyPatchCmd = patchcmd + "--check --apply -"
+
+ if os.system(tryPatchCmd) != 0:
+ print "Unfortunately applying the change failed!"
+ print "What do you want to do?"
+ response = "x"
+ while response != "s" and response != "a" and response != "w":
+ response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
+ "and with .rej files / [w]rite the patch to a file (patch.txt) ")
+ if response == "s":
+ print "Skipping! Good luck with the next patches..."
+ return
+ elif response == "a":
+ os.system(applyPatchCmd)
+ if len(filesToAdd) > 0:
+ print "You may also want to call p4 add on the following files:"
+ print " ".join(filesToAdd)
+ if len(filesToDelete):
+ print "The following files should be scheduled for deletion with p4 delete:"
+ print " ".join(filesToDelete)
+ die("Please resolve and submit the conflict manually and "
+ + "continue afterwards with git-p4 submit --continue")
+ elif response == "w":
+ system(diffcmd + " > patch.txt")
+ print "Patch saved to patch.txt in %s !" % self.clientPath
+ die("Please resolve and submit the conflict manually and "
+ "continue afterwards with git-p4 submit --continue")
+
+ system(applyPatchCmd)
+
+ for f in filesToAdd:
+ system("p4 add \"%s\"" % f)
+ for f in filesToDelete:
+ system("p4 revert \"%s\"" % f)
+ system("p4 delete \"%s\"" % f)
+
+ logMessage = ""
+ if not self.directSubmit:
+ logMessage = extractLogMessageFromGitCommit(id)
+ logMessage = logMessage.replace("\n", "\n\t")
+ if self.isWindows:
+ logMessage = logMessage.replace("\n", "\r\n")
+ logMessage = logMessage.strip()
+
+ template = self.prepareSubmitTemplate()
+
+ if self.interactive:
+ submitTemplate = self.prepareLogMessage(template, logMessage)
+ diff = read_pipe("p4 diff -du ...")
+
+ for newFile in filesToAdd:
+ diff += "==== new file ====\n"
+ diff += "--- /dev/null\n"
+ diff += "+++ %s\n" % newFile
+ f = open(newFile, "r")
+ for line in f.readlines():
+ diff += "+" + line
+ f.close()
+
+ separatorLine = "######## everything below this line is just the diff #######"
+ if platform.system() == "Windows":
+ separatorLine += "\r"
+ separatorLine += "\n"
+
+ response = "e"
+ if self.trustMeLikeAFool:
+ response = "y"
+
+ firstIteration = True
+ while response == "e":
+ if not firstIteration:
+ response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ")
+ firstIteration = False
+ if response == "e":
+ [handle, fileName] = tempfile.mkstemp()
+ tmpFile = os.fdopen(handle, "w+")
+ tmpFile.write(submitTemplate + separatorLine + diff)
+ tmpFile.close()
+ defaultEditor = "vi"
+ if platform.system() == "Windows":
+ defaultEditor = "notepad"
+ editor = os.environ.get("EDITOR", defaultEditor);
+ system(editor + " " + fileName)
+ tmpFile = open(fileName, "rb")
+ message = tmpFile.read()
+ tmpFile.close()
+ os.remove(fileName)
+ submitTemplate = message[:message.index(separatorLine)]
+ if self.isWindows:
+ submitTemplate = submitTemplate.replace("\r\n", "\n")
+
+ if response == "y" or response == "yes":
+ if self.dryRun:
+ print submitTemplate
+ raw_input("Press return to continue...")
+ else:
+ if self.directSubmit:
+ print "Submitting to git first"
+ os.chdir(self.oldWorkingDirectory)
+ write_pipe("git commit -a -F -", submitTemplate)
+ os.chdir(self.clientPath)
+
+ write_pipe("p4 submit -i", submitTemplate)
+ elif response == "s":
+ for f in editedFiles:
+ system("p4 revert \"%s\"" % f);
+ for f in filesToAdd:
+ system("p4 revert \"%s\"" % f);
+ system("rm %s" %f)
+ for f in filesToDelete:
+ system("p4 delete \"%s\"" % f);
+ return
+ else:
+ print "Not submitting!"
+ self.interactive = False
+ else:
+ fileName = "submit.txt"
+ file = open(fileName, "w+")
+ file.write(self.prepareLogMessage(template, logMessage))
+ file.close()
+ print ("Perforce submit template written as %s. "
+ + "Please review/edit and then use p4 submit -i < %s to submit directly!"
+ % (fileName, fileName))
+
+ def run(self, args):
+ if len(args) == 0:
+ self.master = currentGitBranch()
+ if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
+ die("Detecting current git branch failed!")
+ elif len(args) == 1:
+ self.master = args[0]
+ else:
+ return False
+
+ [upstream, settings] = findUpstreamBranchPoint()
+ self.depotPath = settings['depot-paths'][0]
+ if len(self.origin) == 0:
+ self.origin = upstream
+
+ if self.verbose:
+ print "Origin branch is " + self.origin
+
+ if len(self.depotPath) == 0:
+ print "Internal error: cannot locate perforce depot path from existing branches"
+ sys.exit(128)
+
+ self.clientPath = p4Where(self.depotPath)
+
+ if len(self.clientPath) == 0:
+ print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
+ sys.exit(128)
+
+ print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
+ self.oldWorkingDirectory = os.getcwd()
+
+ if self.directSubmit:
+ self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD")
+ if len(self.diffStatus) == 0:
+ print "No changes in working directory to submit."
+ return True
+ patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD")
+ self.diffFile = self.gitdir + "/p4-git-diff"
+ f = open(self.diffFile, "wb")
+ f.write(patch)
+ f.close();
+
+ os.chdir(self.clientPath)
+ response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath)
+ if response == "y" or response == "yes":
+ system("p4 sync ...")
+
+ if self.reset:
+ self.firstTime = True
+
+ if len(self.substFile) > 0:
+ for line in open(self.substFile, "r").readlines():
+ tokens = line.strip().split("=")
+ self.logSubstitutions[tokens[0]] = tokens[1]
+
+ self.check()
+ self.configFile = self.gitdir + "/p4-git-sync.cfg"
+ self.config = shelve.open(self.configFile, writeback=True)
+
+ if self.firstTime:
+ self.start()
+
+ commits = self.config.get("commits", [])
+
+ while len(commits) > 0:
+ self.firstTime = False
+ commit = commits[0]
+ commits = commits[1:]
+ self.config["commits"] = commits
+ self.applyCommit(commit)
+ if not self.interactive:
+ break
+
+ self.config.close()
+
+ if self.directSubmit:
+ os.remove(self.diffFile)
+
+ if len(commits) == 0:
+ if self.firstTime:
+ print "No changes found to apply between %s and current HEAD" % self.origin
+ else:
+ print "All changes applied!"
+ os.chdir(self.oldWorkingDirectory)
+ response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
+ if response == "y" or response == "yes":
+ rebase = P4Rebase()
+ rebase.run([])
+ os.remove(self.configFile)
+
+ return True
+
+class P4Sync(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [
+ optparse.make_option("--branch", dest="branch"),
+ optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
+ optparse.make_option("--changesfile", dest="changesFile"),
+ optparse.make_option("--silent", dest="silent", action="store_true"),
+ optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
+ optparse.make_option("--verbose", dest="verbose", action="store_true"),
+ optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
+ help="Import into refs/heads/ , not refs/remotes"),
+ optparse.make_option("--max-changes", dest="maxChanges"),
+ optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
+ help="Keep entire BRANCH/DIR/SUBDIR prefix during import")
+ ]
+ self.description = """Imports from Perforce into a git repository.\n
+ example:
+ //depot/my/project/ -- to import the current head
+ //depot/my/project/@all -- to import everything
+ //depot/my/project/@1,6 -- to import only from revision 1 to 6
+
+ (a ... is not needed in the path p4 specification, it's added implicitly)"""
+
+ self.usage += " //depot/path[@revRange]"
+ self.silent = False
+ self.createdBranches = Set()
+ self.committedChanges = Set()
+ self.branch = ""
+ self.detectBranches = False
+ self.detectLabels = False
+ self.changesFile = ""
+ self.syncWithOrigin = True
+ self.verbose = False
+ self.importIntoRemotes = True
+ self.maxChanges = ""
+ self.isWindows = (platform.system() == "Windows")
+ self.keepRepoPath = False
+ self.depotPaths = None
+ self.p4BranchesInGit = []
+
+ if gitConfig("git-p4.syncFromOrigin") == "false":
+ self.syncWithOrigin = False
+
+ def extractFilesFromCommit(self, commit):
+ files = []
+ fnum = 0
+ while commit.has_key("depotFile%s" % fnum):
+ path = commit["depotFile%s" % fnum]
+
+ found = [p for p in self.depotPaths
+ if path.startswith (p)]
+ if not found:
+ fnum = fnum + 1
+ continue
+
+ file = {}
+ file["path"] = path
+ file["rev"] = commit["rev%s" % fnum]
+ file["action"] = commit["action%s" % fnum]
+ file["type"] = commit["type%s" % fnum]
+ files.append(file)
+ fnum = fnum + 1
+ return files
+
+ def stripRepoPath(self, path, prefixes):
+ if self.keepRepoPath:
+ prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
+
+ for p in prefixes:
+ if path.startswith(p):
+ path = path[len(p):]
+
+ return path
+
+ def splitFilesIntoBranches(self, commit):
+ branches = {}
+ fnum = 0
+ while commit.has_key("depotFile%s" % fnum):
+ path = commit["depotFile%s" % fnum]
+ found = [p for p in self.depotPaths
+ if path.startswith (p)]
+ if not found:
+ fnum = fnum + 1
+ continue
+
+ file = {}
+ file["path"] = path
+ file["rev"] = commit["rev%s" % fnum]
+ file["action"] = commit["action%s" % fnum]
+ file["type"] = commit["type%s" % fnum]
+ fnum = fnum + 1
+
+ relPath = self.stripRepoPath(path, self.depotPaths)
+
+ for branch in self.knownBranches.keys():
+
+ # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
+ if relPath.startswith(branch + "/"):
+ if branch not in branches:
+ branches[branch] = []
+ branches[branch].append(file)
+ break
+
+ return branches
+
+ ## Should move this out, doesn't use SELF.
+ def readP4Files(self, files):
+ files = [f for f in files
+ if f['action'] != 'delete']
+
+ if not files:
+ return
+
+ filedata = p4CmdList('-x - print',
+ stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
+ for f in files]),
+ stdin_mode='w+')
+ if "p4ExitCode" in filedata[0]:
+ die("Problems executing p4. Error: [%d]."
+ % (filedata[0]['p4ExitCode']));
+
+ j = 0;
+ contents = {}
+ while j < len(filedata):
+ stat = filedata[j]
+ j += 1
+ text = ''
+ while j < len(filedata) and filedata[j]['code'] in ('text',
+ 'binary'):
+ text += filedata[j]['data']
+ j += 1
+
+
+ if not stat.has_key('depotFile'):
+ sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
+ continue
+
+ contents[stat['depotFile']] = text
+
+ for f in files:
+ assert not f.has_key('data')
+ f['data'] = contents[f['path']]
+
+ def commit(self, details, files, branch, branchPrefixes, parent = ""):
+ epoch = details["time"]
+ author = details["user"]
+
+ if self.verbose:
+ print "commit into %s" % branch
+
+ # start with reading files; if that fails, we should not
+ # create a commit.
+ new_files = []
+ for f in files:
+ if [p for p in branchPrefixes if f['path'].startswith(p)]:
+ new_files.append (f)
+ else:
+ sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
+ files = new_files
+ self.readP4Files(files)
+
+
+
+
+ self.gitStream.write("commit %s\n" % branch)
+# gitStream.write("mark :%s\n" % details["change"])
+ self.committedChanges.add(int(details["change"]))
+ committer = ""
+ if author not in self.users:
+ self.getUserMapFromPerforceServer()
+ if author in self.users:
+ committer = "%s %s %s" % (self.users[author], epoch, self.tz)
+ else:
+ committer = "%s <a@b> %s %s" % (author, epoch, self.tz)
+
+ self.gitStream.write("committer %s\n" % committer)
+
+ self.gitStream.write("data <<EOT\n")
+ self.gitStream.write(details["desc"])
+ self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
+ % (','.join (branchPrefixes), details["change"]))
+ if len(details['options']) > 0:
+ self.gitStream.write(": options = %s" % details['options'])
+ self.gitStream.write("]\nEOT\n\n")
+
+ if len(parent) > 0:
+ if self.verbose:
+ print "parent %s" % parent
+ self.gitStream.write("from %s\n" % parent)
+
+ for file in files:
+ if file["type"] == "apple":
+ print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
+ continue
+
+ relPath = self.stripRepoPath(file['path'], branchPrefixes)
+ if file["action"] == "delete":
+ self.gitStream.write("D %s\n" % relPath)
+ else:
+ data = file['data']
+
+ mode = "644"
+ if file["type"].startswith("x"):
+ mode = "755"
+ elif file["type"] == "symlink":
+ mode = "120000"
+ # p4 print on a symlink contains "target\n", so strip it off
+ data = data[:-1]
+
+ if self.isWindows and file["type"].endswith("text"):
+ data = data.replace("\r\n", "\n")
+
+ self.gitStream.write("M %s inline %s\n" % (mode, relPath))
+ self.gitStream.write("data %s\n" % len(data))
+ self.gitStream.write(data)
+ self.gitStream.write("\n")
+
+ self.gitStream.write("\n")
+
+ change = int(details["change"])
+
+ if self.labels.has_key(change):
+ label = self.labels[change]
+ labelDetails = label[0]
+ labelRevisions = label[1]
+ if self.verbose:
+ print "Change %s is labelled %s" % (change, labelDetails)
+
+ files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change)
+ for p in branchPrefixes]))
+
+ if len(files) == len(labelRevisions):
+
+ cleanedFiles = {}
+ for info in files:
+ if info["action"] == "delete":
+ continue
+ cleanedFiles[info["depotFile"]] = info["rev"]
+
+ if cleanedFiles == labelRevisions:
+ self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
+ self.gitStream.write("from %s\n" % branch)
+
+ owner = labelDetails["Owner"]
+ tagger = ""
+ if author in self.users:
+ tagger = "%s %s %s" % (self.users[owner], epoch, self.tz)
+ else:
+ tagger = "%s <a@b> %s %s" % (owner, epoch, self.tz)
+ self.gitStream.write("tagger %s\n" % tagger)
+ self.gitStream.write("data <<EOT\n")
+ self.gitStream.write(labelDetails["Description"])
+ self.gitStream.write("EOT\n\n")
+
+ else:
+ if not self.silent:
+ print ("Tag %s does not match with change %s: files do not match."
+ % (labelDetails["label"], change))
+
+ else:
+ if not self.silent:
+ print ("Tag %s does not match with change %s: file count is different."
+ % (labelDetails["label"], change))
+
+ def getUserCacheFilename(self):
+ home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
+ return home + "/.gitp4-usercache.txt"
+
+ def getUserMapFromPerforceServer(self):
+ if self.userMapFromPerforceServer:
+ return
+ self.users = {}
+
+ for output in p4CmdList("users"):
+ if not output.has_key("User"):
+ continue
+ self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
+
+
+ s = ''
+ for (key, val) in self.users.items():
+ s += "%s\t%s\n" % (key, val)
+
+ open(self.getUserCacheFilename(), "wb").write(s)
+ self.userMapFromPerforceServer = True
+
+ def loadUserMapFromCache(self):
+ self.users = {}
+ self.userMapFromPerforceServer = False
+ try:
+ cache = open(self.getUserCacheFilename(), "rb")
+ lines = cache.readlines()
+ cache.close()
+ for line in lines:
+ entry = line.strip().split("\t")
+ self.users[entry[0]] = entry[1]
+ except IOError:
+ self.getUserMapFromPerforceServer()
+
+ def getLabels(self):
+ self.labels = {}
+
+ l = p4CmdList("labels %s..." % ' '.join (self.depotPaths))
+ if len(l) > 0 and not self.silent:
+ print "Finding files belonging to labels in %s" % `self.depotPath`
+
+ for output in l:
+ label = output["label"]
+ revisions = {}
+ newestChange = 0
+ if self.verbose:
+ print "Querying files for label %s" % label
+ for file in p4CmdList("files "
+ + ' '.join (["%s...@%s" % (p, label)
+ for p in self.depotPaths])):
+ revisions[file["depotFile"]] = file["rev"]
+ change = int(file["change"])
+ if change > newestChange:
+ newestChange = change
+
+ self.labels[newestChange] = [output, revisions]
+
+ if self.verbose:
+ print "Label changes: %s" % self.labels.keys()
+
+ def guessProjectName(self):
+ for p in self.depotPaths:
+ if p.endswith("/"):
+ p = p[:-1]
+ p = p[p.strip().rfind("/") + 1:]
+ if not p.endswith("/"):
+ p += "/"
+ return p
+
+ def getBranchMapping(self):
+ lostAndFoundBranches = set()
+
+ for info in p4CmdList("branches"):
+ details = p4Cmd("branch -o %s" % info["branch"])
+ viewIdx = 0
+ while details.has_key("View%s" % viewIdx):
+ paths = details["View%s" % viewIdx].split(" ")
+ viewIdx = viewIdx + 1
+ # require standard //depot/foo/... //depot/bar/... mapping
+ if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."):
+ continue
+ source = paths[0]
+ destination = paths[1]
+ ## HACK
+ if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]):
+ source = source[len(self.depotPaths[0]):-4]
+ destination = destination[len(self.depotPaths[0]):-4]
+
+ if destination in self.knownBranches:
+ if not self.silent:
+ print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
+ print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
+ continue
+
+ self.knownBranches[destination] = source
+
+ lostAndFoundBranches.discard(destination)
+
+ if source not in self.knownBranches:
+ lostAndFoundBranches.add(source)
+
+
+ for branch in lostAndFoundBranches:
+ self.knownBranches[branch] = branch
+
+ def listExistingP4GitBranches(self):
+ # branches holds mapping from name to commit
+ branches = p4BranchesInGit(self.importIntoRemotes)
+ self.p4BranchesInGit = branches.keys()
+ for branch in branches.keys():
+ self.initialParents[self.refPrefix + branch] = branches[branch]
+
+ def createOrUpdateBranchesFromOrigin(self):
+ if not self.silent:
+ print ("Creating/updating branch(es) in %s based on origin branch(es)"
+ % self.refPrefix)
+
+ originPrefix = "origin/p4/"
+
+ for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
+ line = line.strip()
+ if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
+ continue
+
+ headName = line[len(originPrefix):]
+ remoteHead = self.refPrefix + headName
+ originHead = line
+
+ original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
+ if (not original.has_key('depot-paths')
+ or not original.has_key('change')):
+ continue
+
+ update = False
+ if not gitBranchExists(remoteHead):
+ if self.verbose:
+ print "creating %s" % remoteHead
+ update = True
+ else:
+ settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
+ if settings.has_key('change') > 0:
+ if settings['depot-paths'] == original['depot-paths']:
+ originP4Change = int(original['change'])
+ p4Change = int(settings['change'])
+ if originP4Change > p4Change:
+ print ("%s (%s) is newer than %s (%s). "
+ "Updating p4 branch from origin."
+ % (originHead, originP4Change,
+ remoteHead, p4Change))
+ update = True
+ else:
+ print ("Ignoring: %s was imported from %s while "
+ "%s was imported from %s"
+ % (originHead, ','.join(original['depot-paths']),
+ remoteHead, ','.join(settings['depot-paths'])))
+
+ if update:
+ system("git update-ref %s %s" % (remoteHead, originHead))
+
+ def updateOptionDict(self, d):
+ option_keys = {}
+ if self.keepRepoPath:
+ option_keys['keepRepoPath'] = 1
+
+ d["options"] = ' '.join(sorted(option_keys.keys()))
+
+ def readOptions(self, d):
+ self.keepRepoPath = (d.has_key('options')
+ and ('keepRepoPath' in d['options']))
+
+ def run(self, args):
+ self.depotPaths = []
+ self.changeRange = ""
+ self.initialParent = ""
+ self.previousDepotPaths = []
+
+ # map from branch depot path to parent branch
+ self.knownBranches = {}
+ self.initialParents = {}
+ self.hasOrigin = gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
+ if not self.syncWithOrigin:
+ self.hasOrigin = False
+
+ if self.importIntoRemotes:
+ self.refPrefix = "refs/remotes/p4/"
+ else:
+ self.refPrefix = "refs/heads/p4/"
+
+ if self.syncWithOrigin and self.hasOrigin:
+ if not self.silent:
+ print "Syncing with origin first by calling git fetch origin"
+ system("git fetch origin")
+
+ if len(self.branch) == 0:
+ self.branch = self.refPrefix + "master"
+ if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
+ system("git update-ref %s refs/heads/p4" % self.branch)
+ system("git branch -D p4");
+ # create it /after/ importing, when master exists
+ if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes:
+ system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
+
+ # TODO: should always look at previous commits,
+ # merge with previous imports, if possible.
+ if args == []:
+ if self.hasOrigin:
+ self.createOrUpdateBranchesFromOrigin()
+ self.listExistingP4GitBranches()
+
+ if len(self.p4BranchesInGit) > 1:
+ if not self.silent:
+ print "Importing from/into multiple branches"
+ self.detectBranches = True
+
+ if self.verbose:
+ print "branches: %s" % self.p4BranchesInGit
+
+ p4Change = 0
+ for branch in self.p4BranchesInGit:
+ logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch)
+
+ settings = extractSettingsGitLog(logMsg)
+
+ self.readOptions(settings)
+ if (settings.has_key('depot-paths')
+ and settings.has_key ('change')):
+ change = int(settings['change']) + 1
+ p4Change = max(p4Change, change)
+
+ depotPaths = sorted(settings['depot-paths'])
+ if self.previousDepotPaths == []:
+ self.previousDepotPaths = depotPaths
+ else:
+ paths = []
+ for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
+ for i in range(0, min(len(cur), len(prev))):
+ if cur[i] <> prev[i]:
+ i = i - 1
+ break
+
+ paths.append (cur[:i + 1])
+
+ self.previousDepotPaths = paths
+
+ if p4Change > 0:
+ self.depotPaths = sorted(self.previousDepotPaths)
+ self.changeRange = "@%s,#head" % p4Change
+ if not self.detectBranches:
+ self.initialParent = parseRevision(self.branch)
+ if not self.silent and not self.detectBranches:
+ print "Performing incremental import into %s git branch" % self.branch
+
+ if not self.branch.startswith("refs/"):
+ self.branch = "refs/heads/" + self.branch
+
+ if len(args) == 0 and self.depotPaths:
+ if not self.silent:
+ print "Depot paths: %s" % ' '.join(self.depotPaths)
+ else:
+ if self.depotPaths and self.depotPaths != args:
+ print ("previous import used depot path %s and now %s was specified. "
+ "This doesn't work!" % (' '.join (self.depotPaths),
+ ' '.join (args)))
+ sys.exit(1)
+
+ self.depotPaths = sorted(args)
+
+ self.revision = ""
+ self.users = {}
+
+ newPaths = []
+ for p in self.depotPaths:
+ if p.find("@") != -1:
+ atIdx = p.index("@")
+ self.changeRange = p[atIdx:]
+ if self.changeRange == "@all":
+ self.changeRange = ""
+ elif ',' not in self.changeRange:
+ self.revision = self.changeRange
+ self.changeRange = ""
+ p = p[:atIdx]
+ elif p.find("#") != -1:
+ hashIdx = p.index("#")
+ self.revision = p[hashIdx:]
+ p = p[:hashIdx]
+ elif self.previousDepotPaths == []:
+ self.revision = "#head"
+
+ p = re.sub ("\.\.\.$", "", p)
+ if not p.endswith("/"):
+ p += "/"
+
+ newPaths.append(p)
+
+ self.depotPaths = newPaths
+
+
+ self.loadUserMapFromCache()
+ self.labels = {}
+ if self.detectLabels:
+ self.getLabels();
+
+ if self.detectBranches:
+ ## FIXME - what's a P4 projectName ?
+ self.projectName = self.guessProjectName()
+
+ if not self.hasOrigin:
+ self.getBranchMapping();
+ if self.verbose:
+ print "p4-git branches: %s" % self.p4BranchesInGit
+ print "initial parents: %s" % self.initialParents
+ for b in self.p4BranchesInGit:
+ if b != "master":
+
+ ## FIXME
+ b = b[len(self.projectName):]
+ self.createdBranches.add(b)
+
+ self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
+
+ importProcess = subprocess.Popen(["git", "fast-import"],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE);
+ self.gitOutput = importProcess.stdout
+ self.gitStream = importProcess.stdin
+ self.gitError = importProcess.stderr
+
+ if self.revision:
+ print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch)
+
+ details = { "user" : "git perforce import user", "time" : int(time.time()) }
+ details["desc"] = ("Initial import of %s from the state at revision %s"
+ % (' '.join(self.depotPaths), self.revision))
+ details["change"] = self.revision
+ newestRevision = 0
+
+ fileCnt = 0
+ for info in p4CmdList("files "
+ + ' '.join(["%s...%s"
+ % (p, self.revision)
+ for p in self.depotPaths])):
+
+ if info['code'] == 'error':
+ sys.stderr.write("p4 returned an error: %s\n"
+ % info['data'])
+ sys.exit(1)
+
+
+ change = int(info["change"])
+ if change > newestRevision:
+ newestRevision = change
+
+ if info["action"] == "delete":
+ # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
+ #fileCnt = fileCnt + 1
+ continue
+
+ for prop in ["depotFile", "rev", "action", "type" ]:
+ details["%s%s" % (prop, fileCnt)] = info[prop]
+
+ fileCnt = fileCnt + 1
+
+ details["change"] = newestRevision
+ self.updateOptionDict(details)
+ try:
+ self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
+ except IOError:
+ print "IO error with git fast-import. Is your git version recent enough?"
+ print self.gitError.read()
+
+ else:
+ changes = []
+
+ if len(self.changesFile) > 0:
+ output = open(self.changesFile).readlines()
+ changeSet = Set()
+ for line in output:
+ changeSet.add(int(line))
+
+ for change in changeSet:
+ changes.append(change)
+
+ changes.sort()
+ else:
+ if self.verbose:
+ print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
+ self.changeRange)
+ assert self.depotPaths
+ output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange)
+ for p in self.depotPaths]))
+
+ for line in output:
+ changeNum = line.split(" ")[1]
+ changes.append(int(changeNum))
+
+ changes.sort()
+
+ if len(self.maxChanges) > 0:
+ changes = changes[:min(int(self.maxChanges), len(changes))]
+
+ if len(changes) == 0:
+ if not self.silent:
+ print "No changes to import!"
+ return True
+
+ if not self.silent and not self.detectBranches:
+ print "Import destination: %s" % self.branch
+
+ self.updatedBranches = set()
+
+ cnt = 1
+ for change in changes:
+ description = p4Cmd("describe %s" % change)
+ self.updateOptionDict(description)
+
+ if not self.silent:
+ sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+ sys.stdout.flush()
+ cnt = cnt + 1
+
+ try:
+ if self.detectBranches:
+ branches = self.splitFilesIntoBranches(description)
+ for branch in branches.keys():
+ ## HACK --hwn
+ branchPrefix = self.depotPaths[0] + branch + "/"
+
+ parent = ""
+
+ filesForCommit = branches[branch]
+
+ if self.verbose:
+ print "branch is %s" % branch
+
+ self.updatedBranches.add(branch)
+
+ if branch not in self.createdBranches:
+ self.createdBranches.add(branch)
+ parent = self.knownBranches[branch]
+ if parent == branch:
+ parent = ""
+ elif self.verbose:
+ print "parent determined through known branches: %s" % parent
+
+ # main branch? use master
+ if branch == "main":
+ branch = "master"
+ else:
+
+ ## FIXME
+ branch = self.projectName + branch
+
+ if parent == "main":
+ parent = "master"
+ elif len(parent) > 0:
+ ## FIXME
+ parent = self.projectName + parent
+
+ branch = self.refPrefix + branch
+ if len(parent) > 0:
+ parent = self.refPrefix + parent
+
+ if self.verbose:
+ print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+
+ if len(parent) == 0 and branch in self.initialParents:
+ parent = self.initialParents[branch]
+ del self.initialParents[branch]
+
+ self.commit(description, filesForCommit, branch, [branchPrefix], parent)
+ else:
+ files = self.extractFilesFromCommit(description)
+ self.commit(description, files, self.branch, self.depotPaths,
+ self.initialParent)
+ self.initialParent = ""
+ except IOError:
+ print self.gitError.read()
+ sys.exit(1)
+
+ if not self.silent:
+ print ""
+ if len(self.updatedBranches) > 0:
+ sys.stdout.write("Updated branches: ")
+ for b in self.updatedBranches:
+ sys.stdout.write("%s " % b)
+ sys.stdout.write("\n")
+
+
+ self.gitStream.close()
+ if importProcess.wait() != 0:
+ die("fast-import failed: %s" % self.gitError.read())
+ self.gitOutput.close()
+ self.gitError.close()
+
+ return True
+
+class P4Rebase(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [ ]
+ self.description = ("Fetches the latest revision from perforce and "
+ + "rebases the current work (branch) against it")
+ self.verbose = False
+
+ def run(self, args):
+ sync = P4Sync()
+ sync.run([])
+
+ [upstream, settings] = findUpstreamBranchPoint()
+ if len(upstream) == 0:
+ die("Cannot find upstream branchpoint for rebase")
+
+ # the branchpoint may be p4/foo~3, so strip off the parent
+ upstream = re.sub("~[0-9]+$", "", upstream)
+
+ print "Rebasing the current branch onto %s" % upstream
+ oldHead = read_pipe("git rev-parse HEAD").strip()
+ system("git rebase %s" % upstream)
+ system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
+ return True
+
+class P4Clone(P4Sync):
+ def __init__(self):
+ P4Sync.__init__(self)
+ self.description = "Creates a new git repository and imports from Perforce into it"
+ self.usage = "usage: %prog [options] //depot/path[@revRange]"
+ self.options.append(
+ optparse.make_option("--destination", dest="cloneDestination",
+ action='store', default=None,
+ help="where to leave result of the clone"))
+ self.cloneDestination = None
+ self.needsGit = False
+
+ def defaultDestination(self, args):
+ ## TODO: use common prefix of args?
+ depotPath = args[0]
+ depotDir = re.sub("(@[^@]*)$", "", depotPath)
+ depotDir = re.sub("(#[^#]*)$", "", depotDir)
+ depotDir = re.sub(r"\.\.\.$,", "", depotDir)
+ depotDir = re.sub(r"/$", "", depotDir)
+ return os.path.split(depotDir)[1]
+
+ def run(self, args):
+ if len(args) < 1:
+ return False
+
+ if self.keepRepoPath and not self.cloneDestination:
+ sys.stderr.write("Must specify destination for --keep-path\n")
+ sys.exit(1)
+
+ depotPaths = args
+
+ if not self.cloneDestination and len(depotPaths) > 1:
+ self.cloneDestination = depotPaths[-1]
+ depotPaths = depotPaths[:-1]
+
+ for p in depotPaths:
+ if not p.startswith("//"):
+ return False
+
+ if not self.cloneDestination:
+ self.cloneDestination = self.defaultDestination(args)
+
+ print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
+ if not os.path.exists(self.cloneDestination):
+ os.makedirs(self.cloneDestination)
+ os.chdir(self.cloneDestination)
+ system("git init")
+ self.gitdir = os.getcwd() + "/.git"
+ if not P4Sync.run(self, depotPaths):
+ return False
+ if self.branch != "master":
+ if gitBranchExists("refs/remotes/p4/master"):
+ system("git branch master refs/remotes/p4/master")
+ system("git checkout -f")
+ else:
+ print "Could not detect main branch. No checkout/master branch created."
+
+ return True
+
+class P4Branches(Command):
+ def __init__(self):
+ Command.__init__(self)
+ self.options = [ ]
+ self.description = ("Shows the git branches that hold imports and their "
+ + "corresponding perforce depot paths")
+ self.verbose = False
+
+ def run(self, args):
+ cmdline = "git rev-parse --symbolic "
+ cmdline += " --remotes"
+
+ for line in read_pipe_lines(cmdline):
+ line = line.strip()
+
+ if not line.startswith('p4/') or line == "p4/HEAD":
+ continue
+ branch = line
+
+ log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
+ settings = extractSettingsGitLog(log)
+
+ print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
+ return True
+
+class HelpFormatter(optparse.IndentedHelpFormatter):
+ def __init__(self):
+ optparse.IndentedHelpFormatter.__init__(self)
+
+ def format_description(self, description):
+ if description:
+ return description + "\n"
+ else:
+ return ""
+
+def printUsage(commands):
+ print "usage: %s <command> [options]" % sys.argv[0]
+ print ""
+ print "valid commands: %s" % ", ".join(commands)
+ print ""
+ print "Try %s <command> --help for command specific help." % sys.argv[0]
+ print ""
+
+commands = {
+ "debug" : P4Debug,
+ "submit" : P4Submit,
+ "sync" : P4Sync,
+ "rebase" : P4Rebase,
+ "clone" : P4Clone,
+ "rollback" : P4RollBack,
+ "branches" : P4Branches
+}
+
+
+def main():
+ if len(sys.argv[1:]) == 0:
+ printUsage(commands.keys())
+ sys.exit(2)
+
+ cmd = ""
+ cmdName = sys.argv[1]
+ try:
+ klass = commands[cmdName]
+ cmd = klass()
+ except KeyError:
+ print "unknown command %s" % cmdName
+ print ""
+ printUsage(commands.keys())
+ sys.exit(2)
+
+ options = cmd.options
+ cmd.gitdir = os.environ.get("GIT_DIR", None)
+
+ args = sys.argv[2:]
+
+ if len(options) > 0:
+ options.append(optparse.make_option("--git-dir", dest="gitdir"))
+
+ parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
+ options,
+ description = cmd.description,
+ formatter = HelpFormatter())
+
+ (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
+ global verbose
+ verbose = cmd.verbose
+ if cmd.needsGit:
+ if cmd.gitdir == None:
+ cmd.gitdir = os.path.abspath(".git")
+ if not isValidGitDir(cmd.gitdir):
+ cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
+ if os.path.exists(cmd.gitdir):
+ cdup = read_pipe("git rev-parse --show-cdup").strip()
+ if len(cdup) > 0:
+ os.chdir(cdup);
+
+ if not isValidGitDir(cmd.gitdir):
+ if isValidGitDir(cmd.gitdir + "/.git"):
+ cmd.gitdir += "/.git"
+ else:
+ die("fatal: cannot locate git repository at %s" % cmd.gitdir)
+
+ os.environ["GIT_DIR"] = cmd.gitdir
+
+ if not cmd.run(args):
+ parser.print_help()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat
new file mode 100644
index 0000000000..9f97e884f5
--- /dev/null
+++ b/contrib/fast-import/git-p4.bat
@@ -0,0 +1 @@
+@python "%~d0%~p0git-p4" %*
diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt
new file mode 100644
index 0000000000..b16a8384bc
--- /dev/null
+++ b/contrib/fast-import/git-p4.txt
@@ -0,0 +1,159 @@
+git-p4 - Perforce <-> Git converter using git-fast-import
+
+Usage
+=====
+
+git-p4 supports two main modes: Importing from Perforce to a Git repository is
+done using "git-p4 sync" or "git-p4 rebase". Submitting changes from Git back
+to Perforce is done using "git-p4 submit".
+
+Importing
+=========
+
+You can simply start with
+
+ git-p4 clone //depot/path/project
+
+or
+
+ git-p4 clone //depot/path/project myproject
+
+This will create an empty git repository in a subdirectory called "project" (or
+"myproject" with the second command), import the head revision from the
+specified perforce path into a git "p4" branch (remotes/p4 actually), create a
+master branch off it and check it out. If you want the entire history (not just
+the head revision) then you can simply append a "@all" to the depot path:
+
+ git-p4 clone //depot/project/main@all myproject
+
+
+
+If you want more control you can also use the git-p4 sync command directly:
+
+ mkdir repo-git
+ cd repo-git
+ git init
+ git-p4 sync //path/in/your/perforce/depot
+
+This will import the current head revision of the specified depot path into a
+"remotes/p4/master" branch of your git repository. You can use the
+--branch=mybranch option to use a different branch.
+
+If you want to import the entire history of a given depot path just use
+
+ git-p4 sync //path/in/depot@all
+
+To achieve optimal compression you may want to run 'git repack -a -d -f' after
+a big import. This may take a while.
+
+Support for Perforce integrations is still work in progress. Don't bother
+trying it unless you want to hack on it :)
+
+Incremental Imports
+===================
+
+After an initial import you can easily synchronize your git repository with
+newer changes from the Perforce depot by just calling
+
+ git-p4 sync
+
+in your git repository. By default the "remotes/p4/master" branch is updated.
+
+It is recommended to run 'git repack -a -d -f' from time to time when using
+incremental imports to optimally combine the individual git packs that each
+incremental import creates through the use of git-fast-import.
+
+
+A useful setup may be that you have a periodically updated git repository
+somewhere that contains a complete import of a Perforce project. That git
+repository can be used to clone the working repository from and one would
+import from Perforce directly after cloning using git-p4. If the connection to
+the Perforce server is slow and the working repository hasn't been synced for a
+while it may be desirable to fetch changes from the origin git repository using
+the efficient git protocol. git-p4 supports this setup by calling "git fetch origin"
+by default if there is an origin branch. You can disable this using
+
+ git config git-p4.syncFromOrigin false
+
+Updating
+========
+
+A common working pattern is to fetch the latest changes from the Perforce depot
+and merge them with local uncommitted changes. The recommended way is to use
+git's rebase mechanism to preserve linear history. git-p4 provides a convenient
+
+ git-p4 rebase
+
+command that calls git-p4 sync followed by git rebase to rebase the current
+working branch.
+
+Submitting
+==========
+
+git-p4 has support for submitting changes from a git repository back to the
+Perforce depot. This requires a Perforce checkout separate to your git
+repository. To submit all changes that are in the current git branch but not in
+the "p4" branch (or "origin" if "p4" doesn't exist) simply call
+
+ git-p4 submit
+
+in your git repository. If you want to submit changes in a specific branch that
+is not your current git branch you can also pass that as an argument:
+
+ git-p4 submit mytopicbranch
+
+You can override the reference branch with the --origin=mysourcebranch option.
+
+If a submit fails you may have to "p4 resolve" and submit manually. You can
+continue importing the remaining changes with
+
+ git-p4 submit --continue
+
+After submitting you should sync your perforce import branch ("p4" or "origin")
+from Perforce using git-p4's sync command.
+
+If you have changes in your working directory that you haven't committed into
+git yet but that you want to commit to Perforce directly ("quick fixes") then
+you do not have to go through the intermediate step of creating a git commit
+first but you can just call
+
+ git-p4 submit --direct
+
+
+Example
+=======
+
+# Clone a repository
+ git-p4 clone //depot/path/project
+# Enter the newly cloned directory
+ cd project
+# Do some work...
+ vi foo.h
+# ... and commit locally to gi
+ git commit foo.h
+# In the meantime somebody submitted changes to the Perforce depot. Rebase your latest
+# changes against the latest changes in Perforce:
+ git-p4 rebase
+# Submit your locally committed changes back to Perforce
+ git-p4 submit
+# ... and synchronize with Perforce
+ git-p4 rebase
+
+
+Implementation Details...
+=========================
+
+* Changesets from Perforce are imported using git fast-import.
+* The import does not require anything from the Perforce client view as it just uses
+ "p4 print //depot/path/file#revision" to get the actual file contents.
+* Every imported changeset has a special [git-p4...] line at the
+ end of the log message that gives information about the corresponding
+ Perforce change number and is also used by git-p4 itself to find out
+ where to continue importing when doing incremental imports.
+ Basically when syncing it extracts the perforce change number of the
+ latest commit in the "p4" branch and uses "p4 changes //depot/path/...@changenum,#head"
+ to find out which changes need to be imported.
+* git-p4 submit uses "git rev-list" to pick the commits between the "p4" branch
+ and the current branch.
+ The commits themselves are applied using git diff/format-patch ... | git apply
+
diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview
index 2d80e2bad2..5931766620 100755
--- a/contrib/gitview/gitview
+++ b/contrib/gitview/gitview
@@ -259,10 +259,13 @@ class CellRendererGraph(gtk.GenericCellRenderer):
self.set_colour(ctx, colour, 0.0, 0.5)
ctx.show_text(name)
-class Commit:
+class Commit(object):
""" This represent a commit object obtained after parsing the git-rev-list
output """
+ __slots__ = ['children_sha1', 'message', 'author', 'date', 'committer',
+ 'commit_date', 'commit_sha1', 'parent_sha1']
+
children_sha1 = {}
def __init__(self, commit_lines):
@@ -339,7 +342,7 @@ class Commit:
fp.close()
return diff
-class AnnotateWindow:
+class AnnotateWindow(object):
"""Annotate window.
This object represents and manages a single window containing the
annotate information of the file
@@ -349,6 +352,7 @@ class AnnotateWindow:
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_border_width(0)
self.window.set_title("Git repository browser annotation window")
+ self.prev_read = ""
# Use two thirds of the screen by default
screen = self.window.get_screen()
@@ -398,7 +402,10 @@ class AnnotateWindow:
def data_ready(self, source, condition):
while (1):
try :
- buffer = source.read(8192)
+ # A simple readline doesn't work
+ # a readline bug ??
+ buffer = source.read(100)
+
except:
# resource temporary not available
return True
@@ -408,6 +415,19 @@ class AnnotateWindow:
source.close()
return False
+ if (self.prev_read != ""):
+ buffer = self.prev_read + buffer
+ self.prev_read = ""
+
+ if (buffer[len(buffer) -1] != '\n'):
+ try:
+ newline_index = buffer.rindex("\n")
+ except ValueError:
+ newline_index = 0
+
+ self.prev_read = buffer[newline_index:(len(buffer))]
+ buffer = buffer[0:newline_index]
+
for buff in buffer.split("\n"):
annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$')
m = annotate_line.match(buff)
@@ -513,13 +533,13 @@ class AnnotateWindow:
self.add_file_data(filename, commit_sha1, line_num)
- fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1)
+ fp = os.popen("git blame --incremental -C -C -- " + filename + " " + commit_sha1)
flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL)
fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready)
-class DiffWindow:
+class DiffWindow(object):
"""Diff window.
This object represents and manages a single window containing the
differences between two revisions on a branch.
@@ -674,7 +694,7 @@ class DiffWindow:
fp.close()
dialog.destroy()
-class GitView:
+class GitView(object):
""" This is the main class
"""
version = "0.9"
@@ -1277,5 +1297,3 @@ if __name__ == "__main__":
view = GitView( without_diff != 1)
view.run(sys.argv[without_diff:])
-
-
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index d1bef9125b..c589a39a0c 100644
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -199,7 +199,7 @@ generate_email_footer()
hooks/post-receive
- --
+ --
$projectdesc
EOF
}
diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid
index 5ee1835c80..068fa37083 100644
--- a/contrib/hooks/update-paranoid
+++ b/contrib/hooks/update-paranoid
@@ -102,6 +102,8 @@ my ($this_user) = getpwuid $<; # REAL_USER_ID
my $repository_name;
my %user_committer;
my @allow_rules;
+my @path_rules;
+my %diff_cache;
sub deny ($) {
print STDERR "-Deny- $_[0]\n" if $debug;
@@ -118,22 +120,36 @@ sub info ($) {
print STDERR "-Info- $_[0]\n" if $debug;
}
-sub parse_config ($$) {
- my ($data, $fn) = @_;
- info "Loading $fn";
- open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn);
+sub git_value (@) {
+ open(T,'-|','git',@_); local $_ = <T>; chop; close T; $_;
+}
+
+sub match_string ($$) {
+ my ($acl_n, $ref) = @_;
+ ($acl_n eq $ref)
+ || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
+ || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:);
+}
+
+sub parse_config ($$$$) {
+ my $data = shift;
+ local $ENV{GIT_DIR} = shift;
+ my $br = shift;
+ my $fn = shift;
+ info "Loading $br:$fn";
+ open(I,'-|','git','cat-file','blob',"$br:$fn");
my $section = '';
while (<I>) {
chomp;
if (/^\s*$/ || /^\s*#/) {
} elsif (/^\[([a-z]+)\]$/i) {
- $section = $1;
+ $section = lc $1;
} elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) {
- $section = "$1.$2";
+ $section = join('.',lc $1,$2);
} elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) {
- push @{$data->{"$section.$1"}}, $2;
+ push @{$data->{join('.',$section,lc $1)}}, $2;
} else {
- deny "bad config file line $. in $fn";
+ deny "bad config file line $. in $br:$fn";
}
}
close I;
@@ -202,9 +218,40 @@ sub check_committers (@) {
}
}
-sub git_value (@) {
- open(T,'-|','git',@_); local $_ = <T>; chop; close T;
- $_;
+sub load_diff ($) {
+ my $base = shift;
+ my $d = $diff_cache{$base};
+ unless ($d) {
+ local $/ = "\0";
+ my %this_diff;
+ if ($base =~ /^0{40}$/) {
+ open(T,'-|','git','ls-tree',
+ '-r','--name-only','-z',
+ $new) or return undef;
+ while (<T>) {
+ chop;
+ $this_diff{$_} = 'A';
+ }
+ close T or return undef;
+ } else {
+ open(T,'-|','git','diff-tree',
+ '-r','--name-status','-z',
+ $base,$new) or return undef;
+ while (<T>) {
+ my $op = $_;
+ chop $op;
+
+ my $path = <T>;
+ chop $path;
+
+ $this_diff{$path} = $op;
+ }
+ close T or return undef;
+ }
+ $d = \%this_diff;
+ $diff_cache{$base} = $d;
+ }
+ return $d;
}
deny "No GIT_DIR inherited from caller" unless $git_dir;
@@ -231,14 +278,52 @@ $op = 'U' if ($op eq 'R'
&& $ref =~ m,^heads/,
&& $old eq git_value('merge-base',$old,$new));
-# Load the user's ACL file.
+# Load the user's ACL file. Expand groups (user.memberof) one level.
{
my %data = ('user.committer' => []);
- parse_config(\%data, "$acl_branch:users/$this_user.acl");
+ parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl");
+
+ %data = (
+ 'user.committer' => $data{'user.committer'},
+ 'user.memberof' => [],
+ );
+ parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl");
+
%user_committer = map {$_ => $_} @{$data{'user.committer'}};
- my $rules = $data{"repository.$repository_name.allow"} || [];
+ my $rule_key = "repository.$repository_name.allow";
+ my $rules = $data{$rule_key} || [];
+
+ foreach my $group (@{$data{'user.memberof'}}) {
+ my %g;
+ parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl");
+ my $group_rules = $g{$rule_key};
+ push @$rules, @$group_rules if $group_rules;
+ }
+
+RULE:
foreach (@$rules) {
- if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
+ while (/\${user\.([a-z][a-zA-Z0-9]+)}/) {
+ my $k = lc $1;
+ my $v = $data{"user.$k"};
+ next RULE unless defined $v;
+ next RULE if @$v != 1;
+ next RULE unless defined $v->[0];
+ s/\${user\.$k}/$v->[0]/g;
+ }
+
+ if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) {
+ my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4);
+ $ops =~ s/ //g;
+ $pth =~ s/\\\\/\\/g;
+ $ref =~ s/\\\\/\\/g;
+ push @path_rules, [$ops, $pth, $ref, $bst];
+ } elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) {
+ my ($ops, $pth, $ref) = ($1, $2, $3);
+ $ops =~ s/ //g;
+ $pth =~ s/\\\\/\\/g;
+ $ref =~ s/\\\\/\\/g;
+ push @path_rules, [$ops, $pth, $ref, $old];
+ } elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
my $ops = $1;
my $ref = $2;
$ops =~ s/ //g;
@@ -272,13 +357,65 @@ foreach my $acl_entry (@allow_rules) {
next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen.
next unless $acl_n;
next unless $op =~ /^[$acl_ops]$/;
+ next unless match_string $acl_n, $ref;
+
+ # Don't test path rules on branch deletes.
+ #
+ grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D';
+
+ # Aggregate matching path rules; allow if there aren't
+ # any matching this ref.
+ #
+ my %pr;
+ foreach my $p_entry (@path_rules) {
+ my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
+ next unless $p_ref;
+ push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref;
+ }
+ grant "Allowed by: $acl_ops for $acl_n" unless %pr;
- grant "Allowed by: $acl_ops for $acl_n"
- if (
- ($acl_n eq $ref)
- || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
- || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:)
- );
+ # Allow only if all changes against a single base are
+ # allowed by file path rules.
+ #
+ my @bad;
+ foreach my $p_bst (keys %pr) {
+ my $diff_ref = load_diff $p_bst;
+ deny "Cannot difference trees." unless ref $diff_ref;
+
+ my %fd = %$diff_ref;
+ foreach my $p_entry (@{$pr{$p_bst}}) {
+ my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
+ next unless $p_ops =~ /^[AMD]+$/;
+ next unless $p_n;
+
+ foreach my $f_n (keys %fd) {
+ my $f_op = $fd{$f_n};
+ next unless $f_op;
+ next unless $f_op =~ /^[$p_ops]$/;
+ delete $fd{$f_n} if match_string $p_n, $f_n;
+ }
+ last unless %fd;
+ }
+
+ if (%fd) {
+ push @bad, [$p_bst, \%fd];
+ } else {
+ # All changes relative to $p_bst were allowed.
+ #
+ grant "Allowed by: $acl_ops for $acl_n diff $p_bst";
+ }
+ }
+
+ foreach my $bad_ref (@bad) {
+ my ($p_bst, $fd) = @$bad_ref;
+ print STDERR "\n";
+ print STDERR "Not allowed to make the following changes:\n";
+ print STDERR "(base: $p_bst)\n";
+ foreach my $f_n (sort keys %$fd) {
+ print STDERR " $fd->{$f_n} $f_n\n";
+ }
+ }
+ deny "You are not permitted to $op $ref";
}
close A;
deny "You are not permitted to $op $ref";
diff --git a/contrib/p4import/README b/contrib/p4import/README
new file mode 100644
index 0000000000..b9892b6793
--- /dev/null
+++ b/contrib/p4import/README
@@ -0,0 +1 @@
+Please see contrib/fast-import/git-p4 for a better Perforce importer.
diff --git a/git-p4import.py b/contrib/p4import/git-p4import.py
index 60a758bfe3..0f3d97b67e 100644
--- a/git-p4import.py
+++ b/contrib/p4import/git-p4import.py
@@ -358,4 +358,3 @@ for id in changes:
if stitch == 1:
git.clean_directories()
stitch = 0
-
diff --git a/Documentation/git-p4import.txt b/contrib/p4import/git-p4import.txt
index 714abbe28e..9967587fe6 100644
--- a/Documentation/git-p4import.txt
+++ b/contrib/p4import/git-p4import.txt
@@ -165,4 +165,3 @@ Written by Sean Estabrooks <seanlkml@sympatico.ca>
GIT
---
Part of the gitlink:git[7] suite
-
diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch
new file mode 100644
index 0000000000..f2b08b4f4a
--- /dev/null
+++ b/contrib/patches/docbook-xsl-manpages-charmap.patch
@@ -0,0 +1,21 @@
+From: Ismail Dönmez <ismail@pardus.org.tr>
+
+Trying to build the documentation with docbook-xsl 1.73 may result in
+the following error. This patch fixes it.
+
+$ xmlto -m callouts.xsl man git-add.xml
+runtime error: file
+file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line
+129 element call-template
+The called template 'read-character-map' was not found.
+
+--- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap 2007-07-23 16:24:23.000000000 +0100
++++ docbook-xsl-1.73.0/manpages/docbook.xsl 2007-07-23 16:25:16.000000000 +0100
+@@ -37,6 +37,7 @@
+ <xsl:include href="lists.xsl"/>
+ <xsl:include href="endnotes.xsl"/>
+ <xsl:include href="table.xsl"/>
++ <xsl:include href="../common/charmap.xsl"/>
+
+ <!-- * we rename the following just to avoid using params with "man" -->
+ <!-- * prefixes in the table.xsl stylesheet (because that stylesheet -->
diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh
index dc09eae972..5838b3ab05 100644
--- a/contrib/remotes2config.sh
+++ b/contrib/remotes2config.sh
@@ -26,10 +26,8 @@ if [ -d "$GIT_DIR"/remotes ]; then
mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old
fi ;;
*)
- echo "git-config $key "$value" $regex"
- git-config $key "$value" $regex || error=1 ;;
+ echo "git config $key "$value" $regex"
+ git config $key "$value" $regex || error=1 ;;
esac
done
fi
-
-
diff --git a/contrib/stats/git-common-hash b/contrib/stats/git-common-hash
new file mode 100755
index 0000000000..e27fd088be
--- /dev/null
+++ b/contrib/stats/git-common-hash
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# This script displays the distribution of longest common hash prefixes.
+# This can be used to determine the minimum prefix length to use
+# for object names to be unique.
+
+git rev-list --objects --all | sort | perl -lne '
+ substr($_, 40) = "";
+ # uncomment next line for a distribution of bits instead of hex chars
+ # $_ = unpack("B*",pack("H*",$_));
+ if (defined $p) {
+ ($p ^ $_) =~ /^(\0*)/;
+ $common = length $1;
+ if (defined $pcommon) {
+ $count[$pcommon > $common ? $pcommon : $common]++;
+ } else {
+ $count[$common]++; # first item
+ }
+ }
+ $p = $_;
+ $pcommon = $common;
+ END {
+ $count[$common]++; # last item
+ print "$_: $count[$_]" for 0..$#count;
+ }
+'
diff --git a/contrib/stats/mailmap.pl b/contrib/stats/mailmap.pl
new file mode 100755
index 0000000000..4b852e2455
--- /dev/null
+++ b/contrib/stats/mailmap.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+my %mailmap = ();
+open I, "<", ".mailmap";
+while (<I>) {
+ chomp;
+ next if /^#/;
+ if (my ($author, $mail) = /^(.*?)\s+<(.+)>$/) {
+ $mailmap{$mail} = $author;
+ }
+}
+close I;
+
+my %mail2author = ();
+open I, "git log --pretty='format:%ae %an' |";
+while (<I>) {
+ chomp;
+ my ($mail, $author) = split(/\t/, $_);
+ next if exists $mailmap{$mail};
+ $mail2author{$mail} ||= {};
+ $mail2author{$mail}{$author} ||= 0;
+ $mail2author{$mail}{$author}++;
+}
+close I;
+
+while (my ($mail, $authorcount) = each %mail2author) {
+ # %$authorcount is ($author => $count);
+ # sort and show the names from the most frequent ones.
+ my @names = (map { $_->[0] }
+ sort { $b->[1] <=> $a->[1] }
+ map { [$_, $authorcount->{$_}] }
+ keys %$authorcount);
+ if (1 < @names) {
+ for (@names) {
+ print "$_ <$mail>\n";
+ }
+ }
+}
+
diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl
new file mode 100755
index 0000000000..aab501ea08
--- /dev/null
+++ b/contrib/stats/packinfo.pl
@@ -0,0 +1,212 @@
+#!/usr/bin/perl
+#
+# This tool will print vaguely pretty information about a pack. It
+# expects the output of "git-verify-pack -v" as input on stdin.
+#
+# $ git-verify-pack -v | packinfo.pl
+#
+# This prints some full-pack statistics; currently "all sizes", "all
+# path sizes", "tree sizes", "tree path sizes", and "depths".
+#
+# * "all sizes" stats are across every object size in the file;
+# full sizes for base objects, and delta size for deltas.
+# * "all path sizes" stats are across all object's "path sizes".
+# A path size is the sum of the size of the delta chain, including the
+# base object. In other words, it's how many bytes need be read to
+# reassemble the file from deltas.
+# * "tree sizes" are object sizes grouped into delta trees.
+# * "tree path sizes" are path sizes grouped into delta trees.
+# * "depths" should be obvious.
+#
+# When run as:
+#
+# $ git-verify-pack -v | packinfo.pl -tree
+#
+# the trees of objects are output along with the stats. This looks
+# like:
+#
+# 0 commit 031321c6... 803 803
+#
+# 0 blob 03156f21... 1767 1767
+# 1 blob f52a9d7f... 10 1777
+# 2 blob a8cc5739... 51 1828
+# 3 blob 660e90b1... 15 1843
+# 4 blob 0cb8e3bb... 33 1876
+# 2 blob e48607f0... 311 2088
+# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85
+# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26
+#
+# The first number after the sha1 is the object size, the second
+# number is the path size. The statistics are across all objects in
+# the previous delta tree. Obviously they are omitted for trees of
+# one object.
+#
+# When run as:
+#
+# $ git-verify-pack -v | packinfo.pl -tree -filenames
+#
+# it adds filenames to the tree. Getting this information is slow:
+#
+# 0 blob 03156f21... 1767 1767 Documentation/git-lost-found.txt @ tags/v1.2.0~142
+# 1 blob f52a9d7f... 10 1777 Documentation/git-lost-found.txt @ tags/v1.5.0-rc1~74
+# 2 blob a8cc5739... 51 1828 Documentation/git-lost+found.txt @ tags/v0.99.9h^0
+# 3 blob 660e90b1... 15 1843 Documentation/git-lost+found.txt @ master~3222^2~2
+# 4 blob 0cb8e3bb... 33 1876 Documentation/git-lost+found.txt @ master~3222^2~3
+# 2 blob e48607f0... 311 2088 Documentation/git-lost-found.txt @ tags/v1.5.2-rc3~4
+# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85
+# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26
+#
+# When run as:
+#
+# $ git-verify-pack -v | packinfo.pl -dump
+#
+# it prints out "sha1 size pathsize depth" for each sha1 in lexical
+# order.
+#
+# 000079a2eaef17b7eae70e1f0f635557ea67b644 30 472 7
+# 00013cafe6980411aa6fdd940784917b5ff50f0a 44 1542 4
+# 000182eacf99cde27d5916aa415921924b82972c 499 499 0
+# ...
+#
+# This is handy for comparing two packs. Adding "-filenames" will add
+# filenames, as per "-tree -filenames" above.
+
+use strict;
+use Getopt::Long;
+
+my $filenames = 0;
+my $tree = 0;
+my $dump = 0;
+GetOptions("tree" => \$tree,
+ "filenames" => \$filenames,
+ "dump" => \$dump);
+
+my %parents;
+my %children;
+my %sizes;
+my @roots;
+my %paths;
+my %types;
+my @commits;
+my %names;
+my %depths;
+my @depths;
+
+while (<STDIN>) {
+ my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_);
+ next unless ($sha1 =~ /^[0-9a-f]{40}$/);
+ $depths{$sha1} = $depth || 0;
+ push(@depths, $depth || 0);
+ push(@commits, $sha1) if ($type eq 'commit');
+ push(@roots, $sha1) unless $parent;
+ $parents{$sha1} = $parent;
+ $types{$sha1} = $type;
+ push(@{$children{$parent}}, $sha1);
+ $sizes{$sha1} = $size;
+}
+
+if ($filenames && ($tree || $dump)) {
+ open(NAMES, "git-name-rev --all|");
+ while (<NAMES>) {
+ if (/^(\S+)\s+(.*)$/) {
+ my ($sha1, $name) = ($1, $2);
+ $names{$sha1} = $name;
+ }
+ }
+ close NAMES;
+
+ for my $commit (@commits) {
+ my $name = $names{$commit};
+ open(TREE, "git-ls-tree -t -r $commit|");
+ print STDERR "Plumbing tree $name\n";
+ while (<TREE>) {
+ if (/^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
+ my ($mode, $type, $sha1, $path) = ($1, $2, $3, $4);
+ $paths{$sha1} = "$path @ $name";
+ }
+ }
+ close TREE;
+ }
+}
+
+sub stats {
+ my @data = sort {$a <=> $b} @_;
+ my $min = $data[0];
+ my $max = $data[$#data];
+ my $total = 0;
+ my $count = scalar @data;
+ for my $datum (@data) {
+ $total += $datum;
+ }
+ my $mean = $total / $count;
+ my $median = $data[int(@data / 2)];
+ my $diff_sum = 0;
+ for my $datum (@data) {
+ $diff_sum += ($datum - $mean)**2;
+ }
+ my $std_dev = sqrt($diff_sum / $count);
+ return ($count, $total, $min, $max, $mean, $median, $std_dev);
+}
+
+sub print_stats {
+ my $name = shift;
+ my ($count, $total, $min, $max, $mean, $median, $std_dev) = stats(@_);
+ printf("%s: count %s total %s min %s max %s mean %.2f median %s std_dev %.2f\n",
+ $name, $count, $total, $min, $max, $mean, $median, $std_dev);
+}
+
+my @sizes;
+my @path_sizes;
+my @all_sizes;
+my @all_path_sizes;
+my %path_sizes;
+
+sub dig {
+ my ($sha1, $depth, $path_size) = @_;
+ $path_size += $sizes{$sha1};
+ push(@sizes, $sizes{$sha1});
+ push(@all_sizes, $sizes{$sha1});
+ push(@path_sizes, $path_size);
+ push(@all_path_sizes, $path_size);
+ $path_sizes{$sha1} = $path_size;
+ if ($tree) {
+ printf("%3d%s %6s %s %8d %8d %s\n",
+ $depth, (" " x $depth), $types{$sha1},
+ $sha1, $sizes{$sha1}, $path_size, $paths{$sha1});
+ }
+ for my $child (@{$children{$sha1}}) {
+ dig($child, $depth + 1, $path_size);
+ }
+}
+
+my @tree_sizes;
+my @tree_path_sizes;
+
+for my $root (@roots) {
+ undef @sizes;
+ undef @path_sizes;
+ dig($root, 0, 0);
+ my ($aa, $sz_total) = stats(@sizes);
+ my ($bb, $psz_total) = stats(@path_sizes);
+ push(@tree_sizes, $sz_total);
+ push(@tree_path_sizes, $psz_total);
+ if ($tree) {
+ if (@sizes > 1) {
+ print_stats(" size", @sizes);
+ print_stats("path size", @path_sizes);
+ }
+ print "\n";
+ }
+}
+
+if ($dump) {
+ for my $sha1 (sort keys %sizes) {
+ print "$sha1 $sizes{$sha1} $path_sizes{$sha1} $depths{$sha1} $paths{$sha1}\n";
+ }
+} else {
+ print_stats(" all sizes", @all_sizes);
+ print_stats(" all path sizes", @all_path_sizes);
+ print_stats(" tree sizes", @tree_sizes);
+ print_stats("tree path sizes", @tree_path_sizes);
+ print_stats(" depths", @depths);
+}
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 9877b98508..3ff6bd166a 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -20,17 +20,24 @@ new_workdir=$2
branch=$3
# want to make sure that what is pointed to has a .git directory ...
-test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!"
+git_dir=$(cd "$orig_git" 2>/dev/null &&
+ git rev-parse --git-dir 2>/dev/null) ||
+ die "\"$orig_git\" is not a git repository!"
+
+if test "$git_dir" = ".git"
+then
+ git_dir="$orig_git/.git"
+fi
# don't link to a workdir
-if test -L "$orig_git/.git/config"
+if test -L "$git_dir/config"
then
die "\"$orig_git\" is a working directory only, please specify" \
"a complete repository."
fi
# make sure the the links use full paths
-orig_git=$(cd "$orig_git"; pwd)
+git_dir=$(cd "$git_dir"; pwd)
# create the workdir
mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
@@ -45,13 +52,13 @@ do
mkdir -p "$(dirname "$new_workdir/.git/$x")"
;;
esac
- ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x"
+ ln -s "$git_dir/$x" "$new_workdir/.git/$x"
done
# now setup the workdir
cd "$new_workdir"
# copy the HEAD from the original repository as a default branch
-cp "$orig_git/.git/HEAD" .git/HEAD
+cp "$git_dir/HEAD" .git/HEAD
# checkout the branch (either the same as HEAD from the original repository, or
# the one that was asked for)
git checkout -f $branch
diff --git a/convert-objects.c b/convert-objects.c
index cefbcebdca..90e7900e6d 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -194,7 +194,7 @@ static unsigned long parse_oldstyle_date(const char *buf)
fmt++;
} while (*buf && *fmt);
printf("left: %s\n", buf);
- return mktime(&tm);
+ return mktime(&tm);
}
static int convert_date_line(char *dst, void **buf, unsigned long *sp)
diff --git a/copy.c b/copy.c
index d340bb253e..c225d1b0ff 100644
--- a/copy.c
+++ b/copy.c
@@ -34,4 +34,3 @@ int copy_fd(int ifd, int ofd)
close(ifd);
return 0;
}
-
diff --git a/csum-file.c b/csum-file.c
index 7c806ada48..9ab997120d 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -29,18 +29,20 @@ static void sha1flush(struct sha1file *f, unsigned int count)
}
}
-int sha1close(struct sha1file *f, unsigned char *result, int update)
+int sha1close(struct sha1file *f, unsigned char *result, int final)
{
unsigned offset = f->offset;
if (offset) {
SHA1_Update(&f->ctx, f->buffer, offset);
sha1flush(f, offset);
+ f->offset = 0;
}
+ if (!final)
+ return 0; /* only want to flush (no checksum write, no close) */
SHA1_Final(f->buffer, &f->ctx);
if (result)
hashcpy(result, f->buffer);
- if (update)
- sha1flush(f, 20);
+ sha1flush(f, 20);
if (close(f->fd))
die("%s: sha1 file error on close (%s)", f->name, strerror(errno));
free(f);
@@ -71,33 +73,6 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count)
return 0;
}
-struct sha1file *sha1create(const char *fmt, ...)
-{
- struct sha1file *f;
- unsigned len;
- va_list arg;
- int fd;
-
- f = xmalloc(sizeof(*f));
-
- va_start(arg, fmt);
- len = vsnprintf(f->name, sizeof(f->name), fmt, arg);
- va_end(arg);
- if (len >= PATH_MAX)
- die("you wascally wabbit, you");
- f->namelen = len;
-
- fd = open(f->name, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (fd < 0)
- die("unable to open %s (%s)", f->name, strerror(errno));
- f->fd = fd;
- f->error = 0;
- f->offset = 0;
- f->do_crc = 0;
- SHA1_Init(&f->ctx);
- return f;
-}
-
struct sha1file *sha1fd(int fd, const char *name)
{
struct sha1file *f;
@@ -119,34 +94,6 @@ struct sha1file *sha1fd(int fd, const char *name)
return f;
}
-int sha1write_compressed(struct sha1file *f, void *in, unsigned int size)
-{
- z_stream stream;
- unsigned long maxsize;
- void *out;
-
- memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, zlib_compression_level);
- maxsize = deflateBound(&stream, size);
- out = xmalloc(maxsize);
-
- /* Compress it */
- stream.next_in = in;
- stream.avail_in = size;
-
- stream.next_out = out;
- stream.avail_out = maxsize;
-
- while (deflate(&stream, Z_FINISH) == Z_OK)
- /* nothing */;
- deflateEnd(&stream);
-
- size = stream.total_out;
- sha1write(f, out, size);
- free(out);
- return size;
-}
-
void crc32_begin(struct sha1file *f)
{
f->crc32 = crc32(0, Z_NULL, 0);
diff --git a/csum-file.h b/csum-file.h
index 7e1339189d..c3c792f1b5 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -13,10 +13,8 @@ struct sha1file {
};
extern struct sha1file *sha1fd(int fd, const char *name);
-extern struct sha1file *sha1create(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern int sha1close(struct sha1file *, unsigned char *, int);
extern int sha1write(struct sha1file *, void *, unsigned int);
-extern int sha1write_compressed(struct sha1file *, void *, unsigned int);
extern void crc32_begin(struct sha1file *);
extern uint32_t crc32_end(struct sha1file *);
diff --git a/ctype.c b/ctype.c
index 56bdffa636..ee06eb7f48 100644
--- a/ctype.c
+++ b/ctype.c
@@ -20,4 +20,3 @@ unsigned char sane_ctype[256] = {
AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, 0, 0, 0, 0, 0, /* 112-15 */
/* Nothing in the 128.. range */
};
-
diff --git a/daemon.c b/daemon.c
index 674e30dca3..9cf22fef41 100644
--- a/daemon.c
+++ b/daemon.c
@@ -16,7 +16,8 @@ static int reuseaddr;
static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--export-all]\n"
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-" [--base-path=path] [--user-path | --user-path=path]\n"
+" [--base-path=path] [--base-path-relaxed]\n"
+" [--user-path | --user-path=path]\n"
" [--interpolated-path=path]\n"
" [--reuseaddr] [--detach] [--pid-file=file]\n"
" [--[enable|disable|allow-override|forbid-override]=service]\n"
@@ -34,6 +35,7 @@ static int export_all_trees;
/* Take all paths relative to this one if non-NULL */
static char *base_path;
static char *interpolated_path;
+static int base_path_relaxed;
/* Flag indicating client sent extra args. */
static int saw_extended_args;
@@ -133,7 +135,7 @@ static int avoid_alias(char *p)
{
int sl, ndot;
- /*
+ /*
* This resurrects the belts and suspenders paranoia check by HPA
* done in <435560F7.4080006@zytor.com> thread, now enter_repo()
* does not do getcwd() based path canonicalizations.
@@ -180,6 +182,7 @@ static char *path_ok(struct interp *itable)
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
+ int retried_path = 0;
char *path;
char *dir;
@@ -235,7 +238,22 @@ static char *path_ok(struct interp *itable)
dir = rpath;
}
- path = enter_repo(dir, strict_paths);
+ do {
+ path = enter_repo(dir, strict_paths);
+ if (path)
+ break;
+
+ /*
+ * if we fail and base_path_relaxed is enabled, try without
+ * prefixing the base path
+ */
+ if (base_path && base_path_relaxed && !retried_path) {
+ dir = itable[INTERP_SLOT_DIR].value;
+ retried_path = 1;
+ continue;
+ }
+ break;
+ } while (1);
if (!path) {
logerror("'%s': unable to chdir or not a git archive", dir);
@@ -247,7 +265,7 @@ static char *path_ok(struct interp *itable)
int pathlen = strlen(path);
/* The validation is done on the paths after enter_repo
- * appends optional {.git,.git/.git} and friends, but
+ * appends optional {.git,.git/.git} and friends, but
* it does not use getcwd(). So if your /pub is
* a symlink to /mnt/pub, you can whitelist /pub and
* do not have to say /mnt/pub.
@@ -439,7 +457,7 @@ static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
}
}
-void fill_in_extra_table_entries(struct interp *itable)
+static void fill_in_extra_table_entries(struct interp *itable)
{
char *hp;
@@ -1061,6 +1079,10 @@ int main(int argc, char **argv)
base_path = arg+12;
continue;
}
+ if (!strcmp(arg, "--base-path-relaxed")) {
+ base_path_relaxed = 1;
+ continue;
+ }
if (!prefixcmp(arg, "--interpolated-path=")) {
interpolated_path = arg+20;
continue;
diff --git a/date.c b/date.c
index 4690371e55..93bef6efbe 100644
--- a/date.c
+++ b/date.c
@@ -137,6 +137,18 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
if (mode == DATE_SHORT)
sprintf(timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday);
+ else if (mode == DATE_ISO8601)
+ sprintf(timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ tz);
+ else if (mode == DATE_RFC2822)
+ sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
+ weekday_names[tm->tm_wday], tm->tm_mday,
+ month_names[tm->tm_mon], tm->tm_year + 1900,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
else
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
weekday_names[tm->tm_wday],
@@ -149,21 +161,6 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
return timebuf;
}
-const char *show_rfc2822_date(unsigned long time, int tz)
-{
- struct tm *tm;
- static char timebuf[200];
-
- tm = time_to_tm(time, tz);
- if (!tm)
- return NULL;
- sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
- weekday_names[tm->tm_wday], tm->tm_mday,
- month_names[tm->tm_mon], tm->tm_year + 1900,
- tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
- return timebuf;
-}
-
/*
* Check these. And note how it doesn't do the summer-time conversion.
*
@@ -403,7 +400,7 @@ static int match_multi_number(unsigned long num, char c, const char *date, char
}
/*
- * We've seen a digit. Time? Year? Date?
+ * We've seen a digit. Time? Year? Date?
*/
static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
{
@@ -495,7 +492,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
} else if (num > 0 && num < 13) {
tm->tm_mon = num-1;
}
-
+
return n;
}
@@ -569,13 +566,13 @@ int parse_date(const char *date, char *result, int maxlen)
if (!match) {
/* BAD CRAP */
match = 1;
- }
+ }
date += match;
}
/* mktime uses local timezone */
- then = my_mktime(&tm);
+ then = my_mktime(&tm);
if (offset == -1)
offset = (then - mktime(&tm)) / 60;
@@ -663,6 +660,14 @@ static void date_am(struct tm *tm, int *num)
tm->tm_hour = (hour % 12);
}
+static void date_never(struct tm *tm, int *num)
+{
+ tm->tm_mon = tm->tm_wday = tm->tm_yday
+ = tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
+ tm->tm_year = 70;
+ tm->tm_mday = 1;
+}
+
static const struct special {
const char *name;
void (*fn)(struct tm *, int *);
@@ -673,6 +678,7 @@ static const struct special {
{ "tea", date_tea },
{ "PM", date_pm },
{ "AM", date_am },
+ { "never", date_never },
{ NULL }
};
@@ -691,7 +697,7 @@ static const struct typelen {
{ "days", 24*60*60 },
{ "weeks", 7*24*60*60 },
{ NULL }
-};
+};
static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
{
diff --git a/delta.h b/delta.h
index 7b3f86d85f..40ccf5a1e9 100644
--- a/delta.h
+++ b/delta.h
@@ -24,6 +24,13 @@ create_delta_index(const void *buf, unsigned long bufsize);
extern void free_delta_index(struct delta_index *index);
/*
+ * sizeof_delta_index: returns memory usage of delta index
+ *
+ * Given pointer must be what create_delta_index() returned, or NULL.
+ */
+extern unsigned long sizeof_delta_index(struct delta_index *index);
+
+/*
* create_delta: create a delta from given index for the given buffer
*
* This function may be called multiple times with different buffers using
diff --git a/diff-delta.c b/diff-delta.c
index 9f998d0a73..3af583536f 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -1,21 +1,14 @@
/*
* diff-delta.c: generate a delta between two buffers
*
- * Many parts of this file have been lifted from LibXDiff version 0.10.
- * http://www.xmailserver.org/xdiff-lib.html
+ * This code was greatly inspired by parts of LibXDiff from Davide Libenzi
+ * http://www.xmailserver.org/xdiff-lib.html
*
- * LibXDiff was written by Davide Libenzi <davidel@xmailserver.org>
- * Copyright (C) 2003 Davide Libenzi
+ * Rewritten for GIT by Nicolas Pitre <nico@cam.org>, (C) 2005-2007
*
- * Many mods for GIT usage by Nicolas Pitre <nico@cam.org>, (C) 2005.
- *
- * This file is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * Use of this within git automatically means that the LGPL
- * licensing gets turned into GPLv2 within this project.
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#include "git-compat-util.h"
@@ -126,6 +119,7 @@ struct index_entry {
};
struct delta_index {
+ unsigned long memsize;
const void *src_buf;
unsigned long src_size;
unsigned int hash_mask;
@@ -166,6 +160,7 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
mem = hash + hsize;
entry = mem;
+ index->memsize = memsize;
index->src_buf = buf;
index->src_size = bufsize;
index->hash_mask = hmask;
@@ -235,6 +230,14 @@ void free_delta_index(struct delta_index *index)
free(index);
}
+unsigned long sizeof_delta_index(struct delta_index *index)
+{
+ if (index)
+ return index->memsize;
+ else
+ return 0;
+}
+
/*
* The maximum size for any opcode sequence, including the initial header
* plus Rabin window plus biggest copy.
@@ -246,7 +249,7 @@ create_delta(const struct delta_index *index,
const void *trg_buf, unsigned long trg_size,
unsigned long *delta_size, unsigned long max_size)
{
- unsigned int i, outpos, outsize, val;
+ unsigned int i, outpos, outsize, moff, msize, val;
int inscnt;
const unsigned char *ref_data, *ref_top, *data, *top;
unsigned char *out;
@@ -291,30 +294,33 @@ create_delta(const struct delta_index *index,
}
inscnt = i;
+ moff = 0;
+ msize = 0;
while (data < top) {
- unsigned int moff = 0, msize = 0;
- struct index_entry *entry;
- val ^= U[data[-RABIN_WINDOW]];
- val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
- i = val & index->hash_mask;
- for (entry = index->hash[i]; entry; entry = entry->next) {
- const unsigned char *ref = entry->ptr;
- const unsigned char *src = data;
- unsigned int ref_size = ref_top - ref;
- if (entry->val != val)
- continue;
- if (ref_size > top - src)
- ref_size = top - src;
- if (ref_size > 0x10000)
- ref_size = 0x10000;
- if (ref_size <= msize)
- break;
- while (ref_size-- && *src++ == *ref)
- ref++;
- if (msize < ref - entry->ptr) {
- /* this is our best match so far */
- msize = ref - entry->ptr;
- moff = entry->ptr - ref_data;
+ if (msize < 4096) {
+ struct index_entry *entry;
+ val ^= U[data[-RABIN_WINDOW]];
+ val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+ i = val & index->hash_mask;
+ for (entry = index->hash[i]; entry; entry = entry->next) {
+ const unsigned char *ref = entry->ptr;
+ const unsigned char *src = data;
+ unsigned int ref_size = ref_top - ref;
+ if (entry->val != val)
+ continue;
+ if (ref_size > top - src)
+ ref_size = top - src;
+ if (ref_size <= msize)
+ break;
+ while (ref_size-- && *src++ == *ref)
+ ref++;
+ if (msize < ref - entry->ptr) {
+ /* this is our best match so far */
+ msize = ref - entry->ptr;
+ moff = entry->ptr - ref_data;
+ if (msize >= 4096) /* good enough */
+ break;
+ }
}
}
@@ -327,27 +333,13 @@ create_delta(const struct delta_index *index,
out[outpos - inscnt - 1] = inscnt;
inscnt = 0;
}
+ msize = 0;
} else {
+ unsigned int left;
unsigned char *op;
- if (msize >= RABIN_WINDOW) {
- const unsigned char *sk;
- sk = data + msize - RABIN_WINDOW;
- val = 0;
- for (i = 0; i < RABIN_WINDOW; i++)
- val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
- } else {
- const unsigned char *sk = data + 1;
- for (i = 1; i < msize; i++) {
- val ^= U[sk[-RABIN_WINDOW]];
- val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
- }
- }
-
if (inscnt) {
while (moff && ref_data[moff-1] == data[-1]) {
- if (msize == 0x10000)
- break;
/* we can match one byte back */
msize++;
moff--;
@@ -363,23 +355,40 @@ create_delta(const struct delta_index *index,
inscnt = 0;
}
- data += msize;
+ /* A copy op is currently limited to 64KB (pack v2) */
+ left = (msize < 0x10000) ? 0 : (msize - 0x10000);
+ msize -= left;
+
op = out + outpos++;
i = 0x80;
- if (moff & 0xff) { out[outpos++] = moff; i |= 0x01; }
- moff >>= 8;
- if (moff & 0xff) { out[outpos++] = moff; i |= 0x02; }
- moff >>= 8;
- if (moff & 0xff) { out[outpos++] = moff; i |= 0x04; }
- moff >>= 8;
- if (moff & 0xff) { out[outpos++] = moff; i |= 0x08; }
+ if (moff & 0x000000ff)
+ out[outpos++] = moff >> 0, i |= 0x01;
+ if (moff & 0x0000ff00)
+ out[outpos++] = moff >> 8, i |= 0x02;
+ if (moff & 0x00ff0000)
+ out[outpos++] = moff >> 16, i |= 0x04;
+ if (moff & 0xff000000)
+ out[outpos++] = moff >> 24, i |= 0x08;
- if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; }
- msize >>= 8;
- if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
+ if (msize & 0x00ff)
+ out[outpos++] = msize >> 0, i |= 0x10;
+ if (msize & 0xff00)
+ out[outpos++] = msize >> 8, i |= 0x20;
*op = i;
+
+ data += msize;
+ moff += msize;
+ msize = left;
+
+ if (msize < 4096) {
+ int j;
+ val = 0;
+ for (j = -RABIN_WINDOW; j < 0; j++)
+ val = ((val << 8) | data[j])
+ ^ T[val >> RABIN_SHIFT];
+ }
}
if (outpos >= outsize - MAX_OP_SIZE) {
@@ -389,7 +398,7 @@ create_delta(const struct delta_index *index,
outsize = max_size + MAX_OP_SIZE + 1;
if (max_size && outpos > max_size)
break;
- out = xrealloc(out, outsize);
+ out = realloc(out, outsize);
if (!out) {
free(tmp);
return NULL;
diff --git a/diff-lib.c b/diff-lib.c
index 07f4e8106a..f5568c3b36 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -24,7 +24,7 @@ static int read_directory(const char *path, struct path_list *list)
while ((e = readdir(dir)))
if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
- path_list_insert(xstrdup(e->d_name), list);
+ path_list_insert(e->d_name, list);
closedir(dir);
return 0;
@@ -189,6 +189,7 @@ static int handle_diff_files_args(struct rev_info *revs,
!strcmp(argv[1], "--no-index")) {
revs->max_count = -2;
revs->diffopt.exit_with_status = 1;
+ revs->diffopt.no_index = 1;
}
else if (!strcmp(argv[1], "-q"))
*silent = 1;
@@ -204,8 +205,10 @@ static int handle_diff_files_args(struct rev_info *revs,
*/
read_cache();
if (!is_in_index(revs->diffopt.paths[0]) ||
- !is_in_index(revs->diffopt.paths[1]))
+ !is_in_index(revs->diffopt.paths[1])) {
revs->max_count = -2;
+ revs->diffopt.no_index = 1;
+ }
}
/*
@@ -293,6 +296,7 @@ int setup_diff_no_index(struct rev_info *revs,
else
revs->diffopt.paths = argv + argc - 2;
revs->diffopt.nr_paths = 2;
+ revs->diffopt.no_index = 1;
revs->max_count = -2;
return 0;
}
@@ -304,7 +308,7 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
return -1;
- if (revs->max_count == -2) {
+ if (revs->diffopt.no_index) {
if (revs->diffopt.nr_paths != 2)
return error("need two files/directories with --no-index");
if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
@@ -664,7 +668,7 @@ int run_diff_index(struct rev_info *revs, int cached)
const char *tree_name;
int match_missing = 0;
- /*
+ /*
* Backward compatibility wart - "diff-index -m" does
* not mean "do not ignore merges", but totally different.
*/
diff --git a/diff.c b/diff.c
index da992dd485..97cc5bc085 100644
--- a/diff.c
+++ b/diff.c
@@ -56,6 +56,14 @@ static struct ll_diff_driver {
char *cmd;
} *user_diff, **user_diff_tail;
+static void read_config_if_needed(void)
+{
+ if (!user_diff_tail) {
+ user_diff_tail = &user_diff;
+ git_config(git_diff_ui_config);
+ }
+}
+
/*
* Currently there is only "diff.<drivername>.command" variable;
* because there are "diff.color.<slot>" variables, we are parsing
@@ -94,6 +102,45 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val
}
/*
+ * 'diff.<what>.funcname' attribute can be specified in the configuration
+ * to define a customized regexp to find the beginning of a function to
+ * be used for hunk header lines of "diff -p" style output.
+ */
+static struct funcname_pattern {
+ char *name;
+ char *pattern;
+ struct funcname_pattern *next;
+} *funcname_pattern_list;
+
+static int parse_funcname_pattern(const char *var, const char *ep, const char *value)
+{
+ const char *name;
+ int namelen;
+ struct funcname_pattern *pp;
+
+ name = var + 5; /* "diff." */
+ namelen = ep - name;
+
+ for (pp = funcname_pattern_list; pp; pp = pp->next)
+ if (!strncmp(pp->name, name, namelen) && !pp->name[namelen])
+ break;
+ if (!pp) {
+ char *namebuf;
+ pp = xcalloc(1, sizeof(*pp));
+ namebuf = xmalloc(namelen + 1);
+ memcpy(namebuf, name, namelen);
+ namebuf[namelen] = 0;
+ pp->name = namebuf;
+ pp->next = funcname_pattern_list;
+ funcname_pattern_list = pp;
+ }
+ if (pp->pattern)
+ free(pp->pattern);
+ pp->pattern = xstrdup(value);
+ return 0;
+}
+
+/*
* These are to give UI layer defaults.
* The core-level commands such as git-diff-files should
* never be affected by the setting of diff.renames
@@ -122,8 +169,12 @@ int git_diff_ui_config(const char *var, const char *value)
if (!prefixcmp(var, "diff.")) {
const char *ep = strrchr(var, '.');
- if (ep != var + 4 && !strcmp(ep, ".command"))
- return parse_lldiff_command(var, ep, value);
+ if (ep != var + 4) {
+ if (!strcmp(ep, ".command"))
+ return parse_lldiff_command(var, ep, value);
+ if (!strcmp(ep, ".funcname"))
+ return parse_funcname_pattern(var, ep, value);
+ }
}
if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
@@ -390,6 +441,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
mmfile_t minus, plus;
int i;
+ memset(&xecfg, 0, sizeof(xecfg));
minus.size = diff_words->minus.text.size;
minus.ptr = xmalloc(minus.size);
memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
@@ -408,7 +460,6 @@ static void diff_words_show(struct diff_words_data *diff_words)
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
- xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = diff_words;
diff_words->xm.consume = fn_out_diff_words_aux;
@@ -1102,30 +1153,111 @@ static void setup_diff_attr_check(struct git_attr_check *check)
{
static struct git_attr *attr_diff;
- if (!attr_diff)
+ if (!attr_diff) {
attr_diff = git_attr("diff", 4);
- check->attr = attr_diff;
+ }
+ check[0].attr = attr_diff;
}
-static int file_is_binary(struct diff_filespec *one)
+static void diff_filespec_check_attr(struct diff_filespec *one)
{
struct git_attr_check attr_diff_check;
+ int check_from_data = 0;
+
+ if (one->checked_attr)
+ return;
setup_diff_attr_check(&attr_diff_check);
+ one->is_binary = 0;
+ one->funcname_pattern_ident = NULL;
+
if (!git_checkattr(one->path, 1, &attr_diff_check)) {
- const char *value = attr_diff_check.value;
+ const char *value;
+
+ /* binaryness */
+ value = attr_diff_check.value;
if (ATTR_TRUE(value))
- return 0;
+ ;
else if (ATTR_FALSE(value))
- return 1;
+ one->is_binary = 1;
+ else
+ check_from_data = 1;
+
+ /* funcname pattern ident */
+ if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
+ ;
+ else
+ one->funcname_pattern_ident = value;
}
- if (!one->data) {
- if (!DIFF_FILE_VALID(one))
- return 0;
- diff_populate_filespec(one, 0);
+ if (check_from_data) {
+ if (!one->data && DIFF_FILE_VALID(one))
+ diff_populate_filespec(one, 0);
+
+ if (one->data)
+ one->is_binary = buffer_is_binary(one->data, one->size);
}
- return buffer_is_binary(one->data, one->size);
+}
+
+int diff_filespec_is_binary(struct diff_filespec *one)
+{
+ diff_filespec_check_attr(one);
+ return one->is_binary;
+}
+
+static const char *funcname_pattern(const char *ident)
+{
+ struct funcname_pattern *pp;
+
+ read_config_if_needed();
+ for (pp = funcname_pattern_list; pp; pp = pp->next)
+ if (!strcmp(ident, pp->name))
+ return pp->pattern;
+ return NULL;
+}
+
+static struct builtin_funcname_pattern {
+ const char *name;
+ const char *pattern;
+} builtin_funcname_pattern[] = {
+ { "java", "!^[ ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
+ "new\\|return\\|switch\\|throw\\|while\\)\n"
+ "^[ ]*\\(\\([ ]*"
+ "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
+ "[ ]*([^;]*$\\)" },
+ { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
+};
+
+static const char *diff_funcname_pattern(struct diff_filespec *one)
+{
+ const char *ident, *pattern;
+ int i;
+
+ diff_filespec_check_attr(one);
+ ident = one->funcname_pattern_ident;
+
+ if (!ident)
+ /*
+ * If the config file has "funcname.default" defined, that
+ * regexp is used; otherwise NULL is returned and xemit uses
+ * the built-in default.
+ */
+ return funcname_pattern("default");
+
+ /* Look up custom "funcname.$ident" regexp from config. */
+ pattern = funcname_pattern(ident);
+ if (pattern)
+ return pattern;
+
+ /*
+ * And define built-in fallback patterns here. Note that
+ * these can be overriden by the user's config settings.
+ */
+ for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
+ if (!strcmp(ident, builtin_funcname_pattern[i].name))
+ return builtin_funcname_pattern[i].pattern;
+
+ return NULL;
}
static void builtin_diff(const char *name_a,
@@ -1182,7 +1314,8 @@ static void builtin_diff(const char *name_a,
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (!o->text && (file_is_binary(one) || file_is_binary(two))) {
+ if (!o->text &&
+ (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) {
/* Quite common confusing case */
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1201,7 +1334,13 @@ static void builtin_diff(const char *name_a,
xdemitconf_t xecfg;
xdemitcb_t ecb;
struct emit_callback ecbdata;
+ const char *funcname_pattern;
+ funcname_pattern = diff_funcname_pattern(one);
+ if (!funcname_pattern)
+ funcname_pattern = diff_funcname_pattern(two);
+
+ memset(&xecfg, 0, sizeof(xecfg));
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
ecbdata.color_diff = o->color_diff;
@@ -1209,6 +1348,8 @@ static void builtin_diff(const char *name_a,
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
+ if (funcname_pattern)
+ xdiff_set_find_func(&xecfg, funcname_pattern);
if (!diffopts)
;
else if (!prefixcmp(diffopts, "--unified="))
@@ -1260,7 +1401,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (file_is_binary(one) || file_is_binary(two)) {
+ if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
data->is_binary = 1;
data->added = mf2.size;
data->deleted = mf1.size;
@@ -1270,9 +1411,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
xdemitconf_t xecfg;
xdemitcb_t ecb;
+ memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
- xecfg.ctxlen = 0;
- xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = diffstat;
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
@@ -1302,7 +1442,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
- if (file_is_binary(two))
+ if (diff_filespec_is_binary(two))
goto free_and_return;
else {
/* Crazy xdl interfaces.. */
@@ -1310,9 +1450,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
xdemitconf_t xecfg;
xdemitcb_t ecb;
+ memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = 0;
- xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = &data;
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
@@ -1460,7 +1599,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
if (size_only && 0 < s->size)
return 0;
- if (S_ISDIRLNK(s->mode))
+ if (S_ISGITLINK(s->mode))
return diff_populate_gitlink(s, size_only);
if (!s->sha1_valid ||
@@ -1556,7 +1695,7 @@ static void prep_temp_blob(struct diff_tempfile *temp,
fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX");
if (fd < 0)
- die("unable to create temp-file");
+ die("unable to create temp-file: %s", strerror(errno));
if (write_in_full(fd, blob, size) != size)
die("unable to write temp-file");
close(fd);
@@ -1753,10 +1892,7 @@ static const char *external_diff_attr(const char *name)
!ATTR_UNSET(value)) {
struct ll_diff_driver *drv;
- if (!user_diff_tail) {
- user_diff_tail = &user_diff;
- git_config(git_diff_ui_config);
- }
+ read_config_if_needed();
for (drv = user_diff; drv; drv = drv->next)
if (!strcmp(drv->name, value))
return drv->cmd;
@@ -1813,6 +1949,11 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
hashclr(one->sha1);
}
+static int similarity_index(struct diff_filepair *p)
+{
+ return p->score * 100 / MAX_SCORE;
+}
+
static void run_diff(struct diff_filepair *p, struct diff_options *o)
{
const char *pgm = external_diff();
@@ -1847,23 +1988,20 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
"similarity index %d%%\n"
"copy from %s\n"
"copy to %s\n",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name_munged, other_munged);
+ similarity_index(p), name_munged, other_munged);
break;
case DIFF_STATUS_RENAMED:
len += snprintf(msg + len, sizeof(msg) - len,
"similarity index %d%%\n"
"rename from %s\n"
"rename to %s\n",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name_munged, other_munged);
+ similarity_index(p), name_munged, other_munged);
break;
case DIFF_STATUS_MODIFIED:
if (p->score) {
len += snprintf(msg + len, sizeof(msg) - len,
"dissimilarity index %d%%\n",
- (int)(0.5 + p->score *
- 100.0/MAX_SCORE));
+ similarity_index(p));
complete_rewrite = 1;
break;
}
@@ -1878,8 +2016,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
if (o->binary) {
mmfile_t mf;
- if ((!fill_mmfile(&mf, one) && file_is_binary(one)) ||
- (!fill_mmfile(&mf, two) && file_is_binary(two)))
+ if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
+ (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
abbrev = 40;
}
len += snprintf(msg + len, sizeof(msg) - len,
@@ -2103,6 +2241,8 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va
return 1;
}
+static int diff_scoreopt_parse(const char *opt);
+
int diff_opt_parse(struct diff_options *options, const char **av, int ac)
{
const char *arg = av[0];
@@ -2199,6 +2339,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->detect_rename = DIFF_DETECT_RENAME;
}
else if (!prefixcmp(arg, "-C")) {
+ if (options->detect_rename == DIFF_DETECT_COPY)
+ options->find_copies_harder = 1;
if ((options->rename_score =
diff_scoreopt_parse(arg)) == -1)
return -1;
@@ -2206,6 +2348,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
}
else if (!strcmp(arg, "--find-copies-harder"))
options->find_copies_harder = 1;
+ else if (!strcmp(arg, "--follow"))
+ options->follow_renames = 1;
else if (!strcmp(arg, "--abbrev"))
options->abbrev = DEFAULT_ABBREV;
else if (!prefixcmp(arg, "--abbrev=")) {
@@ -2233,6 +2377,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->exit_with_status = 1;
else if (!strcmp(arg, "--quiet"))
options->quiet = 1;
+ else if (!strcmp(arg, "--ext-diff"))
+ options->allow_external = 1;
+ else if (!strcmp(arg, "--no-ext-diff"))
+ options->allow_external = 0;
else
return 0;
return 1;
@@ -2274,7 +2422,7 @@ static int parse_num(const char **cp_p)
return (int)((num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale));
}
-int diff_scoreopt_parse(const char *opt)
+static int diff_scoreopt_parse(const char *opt)
{
int opt1, opt2, cmd;
@@ -2381,8 +2529,7 @@ static void diff_flush_raw(struct diff_filepair *p,
}
if (p->score)
- sprintf(status, "%c%03d", p->status,
- (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ sprintf(status, "%c%03d", p->status, similarity_index(p));
else {
status[0] = p->status;
status[1] = 0;
@@ -2665,8 +2812,7 @@ static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
{
char *names = pprint_rename(p->one->path, p->two->path);
- printf(" %s %s (%d%%)\n", renamecopy, names,
- (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ printf(" %s %s (%d%%)\n", renamecopy, names, similarity_index(p));
free(names);
show_mode_change(p, 0);
}
@@ -2690,7 +2836,7 @@ static void diff_summary(struct diff_filepair *p)
if (p->score) {
char *name = quote_one(p->two->path);
printf(" rewrite %s (%d%%)\n", name,
- (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ similarity_index(p));
free(name);
show_mode_change(p, 0);
} else show_mode_change(p, 1);
@@ -2754,6 +2900,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
struct diff_filepair *p = q->queue[i];
int len1, len2;
+ memset(&xecfg, 0, sizeof(xecfg));
if (p->status == 0)
return error("internal diff status error");
if (p->status == DIFF_STATUS_UNKNOWN)
@@ -2773,7 +2920,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
return error("unable to read files to diff");
/* Maybe hash p->two? into the patch id? */
- if (file_is_binary(p->two))
+ if (diff_filespec_is_binary(p->two))
continue;
len1 = remove_space(p->one->path, strlen(p->one->path));
@@ -2996,10 +3143,64 @@ static void diffcore_apply_filter(const char *filter)
*q = outq;
}
+static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
+{
+ int i;
+ struct diff_queue_struct *q = &diff_queued_diff;
+ struct diff_queue_struct outq;
+ outq.queue = NULL;
+ outq.nr = outq.alloc = 0;
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+
+ /*
+ * 1. Entries that come from stat info dirtyness
+ * always have both sides (iow, not create/delete),
+ * one side of the object name is unknown, with
+ * the same mode and size. Keep the ones that
+ * do not match these criteria. They have real
+ * differences.
+ *
+ * 2. At this point, the file is known to be modified,
+ * with the same mode and size, and the object
+ * name of one side is unknown. Need to inspect
+ * the identical contents.
+ */
+ if (!DIFF_FILE_VALID(p->one) || /* (1) */
+ !DIFF_FILE_VALID(p->two) ||
+ (p->one->sha1_valid && p->two->sha1_valid) ||
+ (p->one->mode != p->two->mode) ||
+ diff_populate_filespec(p->one, 1) ||
+ diff_populate_filespec(p->two, 1) ||
+ (p->one->size != p->two->size) ||
+
+ diff_populate_filespec(p->one, 0) || /* (2) */
+ diff_populate_filespec(p->two, 0) ||
+ memcmp(p->one->data, p->two->data, p->one->size))
+ diff_q(&outq, p);
+ else {
+ /*
+ * The caller can subtract 1 from skip_stat_unmatch
+ * to determine how many paths were dirty only
+ * due to stat info mismatch.
+ */
+ if (!diffopt->no_index)
+ diffopt->skip_stat_unmatch++;
+ diff_free_filepair(p);
+ }
+ }
+ free(q->queue);
+ *q = outq;
+}
+
void diffcore_std(struct diff_options *options)
{
if (options->quiet)
return;
+
+ if (options->skip_stat_unmatch && !options->find_copies_harder)
+ diffcore_skip_stat_unmatch(options);
if (options->break_opt != -1)
diffcore_break(options->break_opt);
if (options->detect_rename)
@@ -3032,7 +3233,7 @@ void diff_addremove(struct diff_options *options,
* entries to the diff-core. They will be prefixed
* with something like '=' or '*' (I haven't decided
* which but should not make any difference).
- * Feeding the same new and old to diff_change()
+ * Feeding the same new and old to diff_change()
* also has the same effect.
* Before the final output happens, they are pruned after
* merged into rename/copy pairs as appropriate.
@@ -3059,7 +3260,7 @@ void diff_change(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1,
const unsigned char *new_sha1,
- const char *base, const char *path)
+ const char *base, const char *path)
{
char concatpath[PATH_MAX];
struct diff_filespec *one, *two;
diff --git a/diff.h b/diff.h
index 63738c1dd4..4546aad219 100644
--- a/diff.h
+++ b/diff.h
@@ -55,15 +55,18 @@ struct diff_options {
full_index:1,
silent_on_remove:1,
find_copies_harder:1,
+ follow_renames:1,
color_diff:1,
color_diff_words:1,
has_changes:1,
quiet:1,
+ no_index:1,
allow_external:1,
exit_with_status:1;
int context;
int break_opt;
int detect_rename;
+ int skip_stat_unmatch;
int line_termination;
int output_format;
int pickaxe_opts;
@@ -155,8 +158,6 @@ extern void diff_unmerge(struct diff_options *,
unsigned mode,
const unsigned char *sha1);
-extern int diff_scoreopt_parse(const char *opt);
-
#define DIFF_SETUP_REVERSE 1
#define DIFF_SETUP_USE_CACHE 2
#define DIFF_SETUP_USE_SIZE_CACHE 4
diff --git a/diffcore-break.c b/diffcore-break.c
index 9c19b8cab7..ae8a7d03e2 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -66,8 +66,7 @@ static int should_break(struct diff_filespec *src,
if (base_size < MINIMUM_BREAK_SIZE)
return 0; /* we do not break too small filepair */
- if (diffcore_count_changes(src->data, src->size,
- dst->data, dst->size,
+ if (diffcore_count_changes(src, dst,
NULL, NULL,
0,
&src_copied, &literal_added))
diff --git a/diffcore-delta.c b/diffcore-delta.c
index 7338a40c59..d9729e5ec2 100644
--- a/diffcore-delta.c
+++ b/diffcore-delta.c
@@ -5,23 +5,20 @@
/*
* Idea here is very simple.
*
- * We have total of (sz-N+1) N-byte overlapping sequences in buf whose
- * size is sz. If the same N-byte sequence appears in both source and
- * destination, we say the byte that starts that sequence is shared
- * between them (i.e. copied from source to destination).
+ * Almost all data we are interested in are text, but sometimes we have
+ * to deal with binary data. So we cut them into chunks delimited by
+ * LF byte, or 64-byte sequence, whichever comes first, and hash them.
*
- * For each possible N-byte sequence, if the source buffer has more
- * instances of it than the destination buffer, that means the
- * difference are the number of bytes not copied from source to
- * destination. If the counts are the same, everything was copied
- * from source to destination. If the destination has more,
- * everything was copied, and destination added more.
+ * For those chunks, if the source buffer has more instances of it
+ * than the destination buffer, that means the difference are the
+ * number of bytes not copied from source to destination. If the
+ * counts are the same, everything was copied from source to
+ * destination. If the destination has more, everything was copied,
+ * and destination added more.
*
* We are doing an approximation so we do not really have to waste
* memory by actually storing the sequence. We just hash them into
* somewhere around 2^16 hashbuckets and count the occurrences.
- *
- * The length of the sequence is arbitrarily set to 8 for now.
*/
/* Wild guess at the initial hash size */
@@ -125,11 +122,14 @@ static struct spanhash_top *add_spanhash(struct spanhash_top *top,
}
}
-static struct spanhash_top *hash_chars(unsigned char *buf, unsigned int sz)
+static struct spanhash_top *hash_chars(struct diff_filespec *one)
{
int i, n;
unsigned int accum1, accum2, hashval;
struct spanhash_top *hash;
+ unsigned char *buf = one->data;
+ unsigned int sz = one->size;
+ int is_text = !diff_filespec_is_binary(one);
i = INITIAL_HASH_SIZE;
hash = xmalloc(sizeof(*hash) + sizeof(struct spanhash) * (1<<i));
@@ -143,6 +143,11 @@ static struct spanhash_top *hash_chars(unsigned char *buf, unsigned int sz)
unsigned int c = *buf++;
unsigned int old_1 = accum1;
sz--;
+
+ /* Ignore CR in CRLF sequence if text */
+ if (is_text && c == '\r' && sz && *buf == '\n')
+ continue;
+
accum1 = (accum1 << 7) ^ (accum2 >> 25);
accum2 = (accum2 << 7) ^ (old_1 >> 25);
accum1 += c;
@@ -156,8 +161,8 @@ static struct spanhash_top *hash_chars(unsigned char *buf, unsigned int sz)
return hash;
}
-int diffcore_count_changes(void *src, unsigned long src_size,
- void *dst, unsigned long dst_size,
+int diffcore_count_changes(struct diff_filespec *src,
+ struct diff_filespec *dst,
void **src_count_p,
void **dst_count_p,
unsigned long delta_limit,
@@ -172,14 +177,14 @@ int diffcore_count_changes(void *src, unsigned long src_size,
if (src_count_p)
src_count = *src_count_p;
if (!src_count) {
- src_count = hash_chars(src, src_size);
+ src_count = hash_chars(src);
if (src_count_p)
*src_count_p = src_count;
}
if (dst_count_p)
dst_count = *dst_count_p;
if (!dst_count) {
- dst_count = hash_chars(dst, dst_size);
+ dst_count = hash_chars(dst);
if (dst_count_p)
*dst_count_p = dst_count;
}
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index c4a77d71da..af9fffe6e8 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -102,7 +102,7 @@ void diffcore_pickaxe(const char *needle, int opts)
for (i = 0; i < q->nr; i++)
diff_free_filepair(q->queue[i]);
}
- else
+ else
/* Showing only the filepairs that has the needle */
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 93c40d9e04..6bde4396f2 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -119,10 +119,26 @@ static int is_exact_match(struct diff_filespec *src,
return 0;
}
+static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
+{
+ int src_len = strlen(src->path), dst_len = strlen(dst->path);
+ while (src_len && dst_len) {
+ char c1 = src->path[--src_len];
+ char c2 = dst->path[--dst_len];
+ if (c1 != c2)
+ return 0;
+ if (c1 == '/')
+ return 1;
+ }
+ return (!src_len || src->path[src_len - 1] == '/') &&
+ (!dst_len || dst->path[dst_len - 1] == '/');
+}
+
struct diff_score {
int src; /* index in rename_src */
int dst; /* index in rename_dst */
int score;
+ int name_score;
};
static int estimate_similarity(struct diff_filespec *src,
@@ -174,8 +190,7 @@ static int estimate_similarity(struct diff_filespec *src,
delta_limit = (unsigned long)
(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
- if (diffcore_count_changes(src->data, src->size,
- dst->data, dst->size,
+ if (diffcore_count_changes(src, dst,
&src->cnt_data, &dst->cnt_data,
delta_limit,
&src_copied, &literal_added))
@@ -224,6 +239,10 @@ static void record_rename_pair(int dst_index, int src_index, int score)
static int score_compare(const void *a_, const void *b_)
{
const struct diff_score *a = a_, *b = b_;
+
+ if (a->score == b->score)
+ return b->name_score - a->name_score;
+
return b->score - a->score;
}
@@ -295,9 +314,22 @@ void diffcore_rename(struct diff_options *options)
if (rename_dst[i].pair)
continue; /* dealt with an earlier round */
for (j = 0; j < rename_src_nr; j++) {
+ int k;
struct diff_filespec *one = rename_src[j].one;
if (!is_exact_match(one, two, contents_too))
continue;
+
+ /* see if there is a basename match, too */
+ for (k = j; k < rename_src_nr; k++) {
+ one = rename_src[k].one;
+ if (basename_same(one, two) &&
+ is_exact_match(one, two,
+ contents_too)) {
+ j = k;
+ break;
+ }
+ }
+
record_rename_pair(i, j, (int)MAX_SCORE);
rename_count++;
break; /* we are done with this entry */
@@ -329,6 +361,7 @@ void diffcore_rename(struct diff_options *options)
m->dst = i;
m->score = estimate_similarity(one, two,
minimum_score);
+ m->name_score = basename_same(one, two);
diff_free_filespec_data(one);
}
/* We do not need the text anymore */
diff --git a/diffcore.h b/diffcore.h
index 7b9294eab2..eef17c4ca2 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -27,6 +27,7 @@ struct diff_filespec {
char *path;
void *data;
void *cnt_data;
+ const char *funcname_pattern_ident;
unsigned long size;
int xfrm_flags; /* for use by the xfrm */
unsigned short mode; /* file mode */
@@ -37,6 +38,8 @@ struct diff_filespec {
#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
unsigned should_free : 1; /* data should be free()'ed */
unsigned should_munmap : 1; /* data should be munmap()'ed */
+ unsigned checked_attr : 1;
+ unsigned is_binary : 1; /* data should be considered "binary" */
};
extern struct diff_filespec *alloc_filespec(const char *);
@@ -45,6 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *,
extern int diff_populate_filespec(struct diff_filespec *, int);
extern void diff_free_filespec_data(struct diff_filespec *);
+extern int diff_filespec_is_binary(struct diff_filespec *);
struct diff_filepair {
struct diff_filespec *one;
@@ -103,8 +107,8 @@ void diff_debug_queue(const char *, struct diff_queue_struct *);
#define diff_debug_queue(a,b) do {} while(0)
#endif
-extern int diffcore_count_changes(void *src, unsigned long src_size,
- void *dst, unsigned long dst_size,
+extern int diffcore_count_changes(struct diff_filespec *src,
+ struct diff_filespec *dst,
void **src_count_p,
void **dst_count_p,
unsigned long delta_limit,
diff --git a/dir.c b/dir.c
index 11fab7f4bf..eb6c3abd30 100644
--- a/dir.c
+++ b/dir.c
@@ -271,27 +271,34 @@ int excluded(struct dir_struct *dir, const char *pathname)
return 0;
}
-struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
-{
+static struct dir_entry *dir_entry_new(const char *pathname, int len) {
struct dir_entry *ent;
- if (cache_name_pos(pathname, len) >= 0)
- return NULL;
-
- if (dir->nr == dir->alloc) {
- int alloc = alloc_nr(dir->alloc);
- dir->alloc = alloc;
- dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
- }
ent = xmalloc(sizeof(*ent) + len + 1);
- ent->ignored = ent->ignored_dir = 0;
ent->len = len;
memcpy(ent->name, pathname, len);
ent->name[len] = 0;
- dir->entries[dir->nr++] = ent;
return ent;
}
+struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
+{
+ if (cache_name_pos(pathname, len) >= 0)
+ return NULL;
+
+ ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
+ return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
+}
+
+struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+{
+ if (cache_name_pos(pathname, len) >= 0)
+ return NULL;
+
+ ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc);
+ return dir->ignored[dir->ignored_nr++] = dir_entry_new(pathname, len);
+}
+
enum exist_status {
index_nonexistent = 0,
index_directory,
@@ -321,7 +328,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
break;
if (endchar == '/')
return index_directory;
- if (!endchar && S_ISDIRLNK(ntohl(ce->ce_mode)))
+ if (!endchar && S_ISGITLINK(ntohl(ce->ce_mode)))
return index_gitdir;
}
return index_nonexistent;
@@ -356,7 +363,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
* also true and the directory is empty, in which case
* we just ignore it entirely.
* (b) if it looks like a git directory, and we don't have
- * 'no_dirlinks' set we treat it as a gitlink, and show it
+ * 'no_gitlinks' set we treat it as a gitlink, and show it
* as a directory.
* (c) otherwise, we recurse into it.
*/
@@ -383,7 +390,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
case index_nonexistent:
if (dir->show_other_directories)
break;
- if (!dir->no_dirlinks) {
+ if (!dir->no_gitlinks) {
unsigned char sha1[20];
if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
return show_directory;
@@ -424,6 +431,18 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli
return 0;
}
+static int in_pathspec(const char *path, int len, const struct path_simplify *simplify)
+{
+ if (simplify) {
+ for (; simplify->path; simplify++) {
+ if (len == simplify->len
+ && !memcmp(path, simplify->path, len))
+ return 1;
+ }
+ }
+ return 0;
+}
+
/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
@@ -464,6 +483,9 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
continue;
exclude = excluded(dir, fullname);
+ if (exclude && dir->collect_ignored
+ && in_pathspec(fullname, baselen + len, simplify))
+ dir_add_ignored(dir, fullname, baselen + len);
if (exclude != dir->show_ignored) {
if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
continue;
@@ -610,6 +632,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
read_directory_recursive(dir, path, base, baselen, 0, simplify);
free_simplify(simplify);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
+ qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr;
}
@@ -619,3 +642,46 @@ file_exists(const char *f)
struct stat sb;
return stat(f, &sb) == 0;
}
+
+/*
+ * get_relative_cwd() gets the prefix of the current working directory
+ * relative to 'dir'. If we are not inside 'dir', it returns NULL.
+ *
+ * As a convenience, it also returns NULL if 'dir' is already NULL. The
+ * reason for this behaviour is that it is natural for functions returning
+ * directory names to return NULL to say "this directory does not exist"
+ * or "this directory is invalid". These cases are usually handled the
+ * same as if the cwd is not inside 'dir' at all, so get_relative_cwd()
+ * returns NULL for both of them.
+ *
+ * Most notably, get_relative_cwd(buffer, size, get_git_work_tree())
+ * unifies the handling of "outside work tree" with "no work tree at all".
+ */
+char *get_relative_cwd(char *buffer, int size, const char *dir)
+{
+ char *cwd = buffer;
+
+ if (!dir)
+ return NULL;
+ if (!getcwd(buffer, size))
+ die("can't find the current directory: %s", strerror(errno));
+
+ if (!is_absolute_path(dir))
+ dir = make_absolute_path(dir);
+
+ while (*dir && *dir == *cwd) {
+ dir++;
+ cwd++;
+ }
+ if (*dir)
+ return NULL;
+ if (*cwd == '/')
+ return cwd + 1;
+ return cwd;
+}
+
+int is_inside_dir(const char *dir)
+{
+ char buffer[PATH_MAX];
+ return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
+}
diff --git a/dir.h b/dir.h
index 817c674da1..f55a87b2cd 100644
--- a/dir.h
+++ b/dir.h
@@ -13,9 +13,7 @@
struct dir_entry {
- unsigned int ignored : 1;
- unsigned int ignored_dir : 1;
- unsigned int len : 30;
+ unsigned int len;
char name[FLEX_ARRAY]; /* more */
};
@@ -31,11 +29,14 @@ struct exclude_list {
struct dir_struct {
int nr, alloc;
+ int ignored_nr, ignored_alloc;
unsigned int show_ignored:1,
show_other_directories:1,
hide_empty_directories:1,
- no_dirlinks:1;
+ no_gitlinks:1,
+ collect_ignored:1;
struct dir_entry **entries;
+ struct dir_entry **ignored;
/* Exclude info */
const char *exclude_per_dir;
@@ -60,4 +61,7 @@ extern void add_exclude(const char *string, const char *base,
extern int file_exists(const char *);
extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len);
+extern char *get_relative_cwd(char *buffer, int size, const char *dir);
+extern int is_inside_dir(const char *dir);
+
#endif
diff --git a/entry.c b/entry.c
index 23687af7d5..fc3a506ece 100644
--- a/entry.c
+++ b/entry.c
@@ -54,7 +54,7 @@ static void remove_subtree(const char *path)
struct dirent *de;
char pathbuf[PATH_MAX];
char *name;
-
+
if (!dir)
die("cannot opendir %s (%s)", path, strerror(errno));
strcpy(pathbuf, path);
@@ -112,6 +112,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
if (!new)
return error("git-checkout-index: unable to read sha1 file of %s (%s)",
path, sha1_to_hex(ce->sha1));
+
+ /*
+ * Convert from git internal format to working tree format
+ */
+ buf = convert_to_working_tree(ce->name, new, &size);
+ if (buf) {
+ free(new);
+ new = buf;
+ }
+
if (to_tempfile) {
strcpy(path, ".merge_file_XXXXXX");
fd = mkstemp(path);
@@ -123,15 +133,6 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
path, strerror(errno));
}
- /*
- * Convert from git internal format to working tree format
- */
- buf = convert_to_working_tree(ce->name, new, &size);
- if (buf) {
- free(new);
- new = buf;
- }
-
wrote = write_in_full(fd, new, size);
close(fd);
free(new);
@@ -168,7 +169,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
"symlink %s (%s)", path, strerror(errno));
}
break;
- case S_IFDIRLNK:
+ case S_IFGITLINK:
if (to_tempfile)
return error("git-checkout-index: cannot create temporary subproject %s", path);
if (mkdir(path, 0777) < 0)
@@ -217,7 +218,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
unlink(path);
if (S_ISDIR(st.st_mode)) {
/* If it is a gitlink, leave it alone! */
- if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+ if (S_ISGITLINK(ntohl(ce->ce_mode)))
return 0;
if (!state->force)
return error("%s is a directory", path);
diff --git a/environment.c b/environment.c
index 22316597df..b5a6c69f7c 100644
--- a/environment.c
+++ b/environment.c
@@ -11,8 +11,8 @@
char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
-int use_legacy_headers = 1;
int trust_executable_bit = 1;
+int quote_path_fully = 1;
int has_symlinks = 1;
int assume_unchanged;
int prefer_symlink_refs;
@@ -24,14 +24,22 @@ const char *git_commit_encoding;
const char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
-int zlib_compression_level = Z_DEFAULT_COMPRESSION;
+int zlib_compression_level = Z_BEST_SPEED;
+int core_compression_level;
+int core_compression_seen;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 16 * 1024 * 1024;
+char *pager_program;
int pager_in_use;
int pager_use_color = 1;
+char *editor_program;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
+/* This is set by setup_git_dir_gently() and/or git_default_config() */
+char *git_work_tree_cfg;
+static const char *work_tree;
+
static const char *git_dir;
static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
@@ -59,15 +67,8 @@ static void setup_git_env(void)
int is_bare_repository(void)
{
- const char *dir, *s;
- if (0 <= is_bare_repository_cfg)
- return is_bare_repository_cfg;
-
- dir = get_git_dir();
- if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
- return 0;
- s = strrchr(dir, '/');
- return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);
+ /* if core.bare is not 'false', let's see if there is a work tree */
+ return is_bare_repository_cfg && !get_git_work_tree();
}
const char *get_git_dir(void)
@@ -77,6 +78,26 @@ const char *get_git_dir(void)
return git_dir;
}
+const char *get_git_work_tree(void)
+{
+ static int initialized = 0;
+ if (!initialized) {
+ work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+ /* core.bare = true overrides implicit and config work tree */
+ if (!work_tree && is_bare_repository_cfg < 1) {
+ work_tree = git_work_tree_cfg;
+ /* make_absolute_path also normalizes the path */
+ if (work_tree && !is_absolute_path(work_tree))
+ work_tree = xstrdup(make_absolute_path(git_path(work_tree)));
+ } else if (work_tree)
+ work_tree = xstrdup(make_absolute_path(work_tree));
+ initialized = 1;
+ if (work_tree)
+ is_bare_repository_cfg = 0;
+ }
+ return work_tree;
+}
+
char *get_object_directory(void)
{
if (!git_object_dir)
@@ -105,4 +126,10 @@ char *get_graft_file(void)
return git_graft_file;
}
-
+int set_git_dir(const char *path)
+{
+ if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
+ return error("Could not set GIT_DIR to '%s'", path);
+ setup_git_env();
+ return 0;
+}
diff --git a/fast-import.c b/fast-import.c
index 17554f6849..170cccdceb 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -26,9 +26,16 @@ Format of STDIN stream:
lf;
commit_msg ::= data;
- file_change ::= file_clr | file_del | file_obm | file_inm;
+ file_change ::= file_clr
+ | file_del
+ | file_rnm
+ | file_cpy
+ | file_obm
+ | file_inm;
file_clr ::= 'deleteall' lf;
file_del ::= 'D' sp path_str lf;
+ file_rnm ::= 'R' sp path_str sp path_str lf;
+ file_cpy ::= 'C' sp path_str sp path_str lf;
file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
data;
@@ -622,6 +629,31 @@ static void release_tree_entry(struct tree_entry *e)
avail_tree_entry = e;
}
+static struct tree_content *dup_tree_content(struct tree_content *s)
+{
+ struct tree_content *d;
+ struct tree_entry *a, *b;
+ unsigned int i;
+
+ if (!s)
+ return NULL;
+ d = new_tree_content(s->entry_count);
+ for (i = 0; i < s->entry_count; i++) {
+ a = s->entries[i];
+ b = new_tree_entry();
+ memcpy(b, a, sizeof(*a));
+ if (a->tree && is_null_sha1(b->versions[1].sha1))
+ b->tree = dup_tree_content(a->tree);
+ else
+ b->tree = NULL;
+ d->entries[i] = b;
+ }
+ d->entry_count = s->entry_count;
+ d->delta_depth = s->delta_depth;
+
+ return d;
+}
+
static void start_packfile(void)
{
static char tmpfile[PATH_MAX];
@@ -631,9 +663,7 @@ static void start_packfile(void)
snprintf(tmpfile, sizeof(tmpfile),
"%s/tmp_pack_XXXXXX", get_object_directory());
- pack_fd = mkstemp(tmpfile);
- if (pack_fd < 0)
- die("Can't create %s: %s", tmpfile, strerror(errno));
+ pack_fd = xmkstemp(tmpfile);
p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
strcpy(p->pack_name, tmpfile);
p->pack_fd = pack_fd;
@@ -695,9 +725,7 @@ static char *create_index(void)
snprintf(tmpfile, sizeof(tmpfile),
"%s/tmp_idx_XXXXXX", get_object_directory());
- idx_fd = mkstemp(tmpfile);
- if (idx_fd < 0)
- die("Can't create %s: %s", tmpfile, strerror(errno));
+ idx_fd = xmkstemp(tmpfile);
f = sha1fd(idx_fd, tmpfile);
sha1write(f, array, 256 * sizeof(int));
SHA1_Init(&ctx);
@@ -1154,7 +1182,8 @@ static int tree_content_set(
struct tree_entry *root,
const char *p,
const unsigned char *sha1,
- const uint16_t mode)
+ const uint16_t mode,
+ struct tree_content *subtree)
{
struct tree_content *t = root->tree;
const char *slash1;
@@ -1168,20 +1197,22 @@ static int tree_content_set(
n = strlen(p);
if (!n)
die("Empty path component found in input");
+ if (!slash1 && !S_ISDIR(mode) && subtree)
+ die("Non-directories cannot have subtrees");
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
if (!slash1) {
- if (e->versions[1].mode == mode
+ if (!S_ISDIR(mode)
+ && e->versions[1].mode == mode
&& !hashcmp(e->versions[1].sha1, sha1))
return 0;
e->versions[1].mode = mode;
hashcpy(e->versions[1].sha1, sha1);
- if (e->tree) {
+ if (e->tree)
release_tree_content_recursive(e->tree);
- e->tree = NULL;
- }
+ e->tree = subtree;
hashclr(root->versions[1].sha1);
return 1;
}
@@ -1191,7 +1222,7 @@ static int tree_content_set(
}
if (!e->tree)
load_tree(e);
- if (tree_content_set(e, slash1 + 1, sha1, mode)) {
+ if (tree_content_set(e, slash1 + 1, sha1, mode, subtree)) {
hashclr(root->versions[1].sha1);
return 1;
}
@@ -1209,9 +1240,9 @@ static int tree_content_set(
if (slash1) {
e->tree = new_tree_content(8);
e->versions[1].mode = S_IFDIR;
- tree_content_set(e, slash1 + 1, sha1, mode);
+ tree_content_set(e, slash1 + 1, sha1, mode, subtree);
} else {
- e->tree = NULL;
+ e->tree = subtree;
e->versions[1].mode = mode;
hashcpy(e->versions[1].sha1, sha1);
}
@@ -1219,7 +1250,10 @@ static int tree_content_set(
return 1;
}
-static int tree_content_remove(struct tree_entry *root, const char *p)
+static int tree_content_remove(
+ struct tree_entry *root,
+ const char *p,
+ struct tree_entry *backup_leaf)
{
struct tree_content *t = root->tree;
const char *slash1;
@@ -1239,13 +1273,14 @@ static int tree_content_remove(struct tree_entry *root, const char *p)
goto del_entry;
if (!e->tree)
load_tree(e);
- if (tree_content_remove(e, slash1 + 1)) {
+ if (tree_content_remove(e, slash1 + 1, backup_leaf)) {
for (n = 0; n < e->tree->entry_count; n++) {
if (e->tree->entries[n]->versions[1].mode) {
hashclr(root->versions[1].sha1);
return 1;
}
}
+ backup_leaf = NULL;
goto del_entry;
}
return 0;
@@ -1254,16 +1289,54 @@ static int tree_content_remove(struct tree_entry *root, const char *p)
return 0;
del_entry:
- if (e->tree) {
+ if (backup_leaf)
+ memcpy(backup_leaf, e, sizeof(*backup_leaf));
+ else if (e->tree)
release_tree_content_recursive(e->tree);
- e->tree = NULL;
- }
+ e->tree = NULL;
e->versions[1].mode = 0;
hashclr(e->versions[1].sha1);
hashclr(root->versions[1].sha1);
return 1;
}
+static int tree_content_get(
+ struct tree_entry *root,
+ const char *p,
+ struct tree_entry *leaf)
+{
+ struct tree_content *t = root->tree;
+ const char *slash1;
+ unsigned int i, n;
+ struct tree_entry *e;
+
+ slash1 = strchr(p, '/');
+ if (slash1)
+ n = slash1 - p;
+ else
+ n = strlen(p);
+
+ for (i = 0; i < t->entry_count; i++) {
+ e = t->entries[i];
+ if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+ if (!slash1) {
+ memcpy(leaf, e, sizeof(*leaf));
+ if (e->tree && is_null_sha1(e->versions[1].sha1))
+ leaf->tree = dup_tree_content(e->tree);
+ else
+ leaf->tree = NULL;
+ return 1;
+ }
+ if (!S_ISDIR(e->versions[1].mode))
+ return 0;
+ if (!e->tree)
+ load_tree(e);
+ return tree_content_get(e, slash1 + 1, leaf);
+ }
+ }
+ return 0;
+}
+
static int update_branch(struct branch *b)
{
static const char *msg = "fast-import";
@@ -1272,7 +1345,7 @@ static int update_branch(struct branch *b)
if (read_ref(b->name, old_sha1))
hashclr(old_sha1);
- lock = lock_any_ref_for_update(b->name, old_sha1);
+ lock = lock_any_ref_for_update(b->name, old_sha1, 0);
if (!lock)
return error("Unable to lock %s", b->name);
if (!force_update && !is_null_sha1(old_sha1)) {
@@ -1629,7 +1702,7 @@ static void file_change_m(struct branch *b)
typename(type), command_buf.buf);
}
- tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode);
+ tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL);
free(p_uq);
}
@@ -1645,10 +1718,61 @@ static void file_change_d(struct branch *b)
die("Garbage after path in: %s", command_buf.buf);
p = p_uq;
}
- tree_content_remove(&b->branch_tree, p);
+ tree_content_remove(&b->branch_tree, p, NULL);
free(p_uq);
}
+static void file_change_cr(struct branch *b, int rename)
+{
+ const char *s, *d;
+ char *s_uq, *d_uq;
+ const char *endp;
+ struct tree_entry leaf;
+
+ s = command_buf.buf + 2;
+ s_uq = unquote_c_style(s, &endp);
+ if (s_uq) {
+ if (*endp != ' ')
+ die("Missing space after source: %s", command_buf.buf);
+ }
+ else {
+ endp = strchr(s, ' ');
+ if (!endp)
+ die("Missing space after source: %s", command_buf.buf);
+ s_uq = xmalloc(endp - s + 1);
+ memcpy(s_uq, s, endp - s);
+ s_uq[endp - s] = 0;
+ }
+ s = s_uq;
+
+ endp++;
+ if (!*endp)
+ die("Missing dest: %s", command_buf.buf);
+
+ d = endp;
+ d_uq = unquote_c_style(d, &endp);
+ if (d_uq) {
+ if (*endp)
+ die("Garbage after dest in: %s", command_buf.buf);
+ d = d_uq;
+ }
+
+ memset(&leaf, 0, sizeof(leaf));
+ if (rename)
+ tree_content_remove(&b->branch_tree, s, &leaf);
+ else
+ tree_content_get(&b->branch_tree, s, &leaf);
+ if (!leaf.versions[1].mode)
+ die("Path %s not in branch", s);
+ tree_content_set(&b->branch_tree, d,
+ leaf.versions[1].sha1,
+ leaf.versions[1].mode,
+ leaf.tree);
+
+ free(s_uq);
+ free(d_uq);
+}
+
static void file_change_deleteall(struct branch *b)
{
release_tree_content_recursive(b->branch_tree.tree);
@@ -1816,6 +1940,10 @@ static void cmd_new_commit(void)
file_change_m(b);
else if (!prefixcmp(command_buf.buf, "D "))
file_change_d(b);
+ else if (!prefixcmp(command_buf.buf, "R "))
+ file_change_cr(b, 1);
+ else if (!prefixcmp(command_buf.buf, "C "))
+ file_change_cr(b, 0);
else if (!strcmp("deleteall", command_buf.buf))
file_change_deleteall(b);
else
diff --git a/fetch-pack.c b/fetch-pack.c
index 06f4aeced4..9c81305be5 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -114,7 +114,7 @@ static const unsigned char* get_rev(void)
commit->object.flags |= POPPED;
if (!(commit->object.flags & COMMON))
non_common_revs--;
-
+
parents = commit->parents;
if (commit->object.flags & COMMON) {
@@ -733,7 +733,7 @@ int main(int argc, char **argv)
}
if (!dest)
usage(fetch_pack_usage);
- pid = git_connect(fd, dest, uploadpack);
+ pid = git_connect(fd, dest, uploadpack, verbose ? CONNECT_VERBOSE : 0);
if (pid < 0)
return 1;
if (heads && nr_heads)
diff --git a/fetch.c b/fetch.c
index 6f1f420be2..811be87a3c 100644
--- a/fetch.c
+++ b/fetch.c
@@ -15,7 +15,7 @@ int get_verbosely = 0;
int get_recover = 0;
static unsigned char current_commit_sha1[20];
-void pull_say(const char *fmt, const char *hex)
+void pull_say(const char *fmt, const char *hex)
{
if (get_verbosely)
fprintf(stderr, fmt, hex);
@@ -47,7 +47,7 @@ static int process_tree(struct tree *tree)
struct object *obj = NULL;
/* submodule commits are not stored in the superproject */
- if (S_ISDIRLNK(entry.mode))
+ if (S_ISGITLINK(entry.mode))
continue;
if (S_ISDIR(entry.mode)) {
struct tree *tree = lookup_tree(entry.sha1);
@@ -156,7 +156,7 @@ static int process(struct object *obj)
return 0;
prefetch(obj->sha1);
}
-
+
object_list_insert(obj, process_queue_end);
process_queue_end = &(*process_queue_end)->next;
return 0;
diff --git a/fixup-builtins b/fixup-builtins
new file mode 100755
index 0000000000..49e861d2ac
--- /dev/null
+++ b/fixup-builtins
@@ -0,0 +1,16 @@
+#!/bin/sh
+while [ "$1" ]
+do
+ old="$1"
+ new=$(echo "$1" | sed 's/git-/git /')
+ echo "Converting '$old' to '$new'"
+ git ls-files '*.sh' | while read file
+ do
+ sed "s/\\<$old\\>/$new/g" < $file > $file.new
+ chmod --reference=$file $file.new
+ mv $file.new $file
+ done
+ shift
+done
+git update-index --refresh >& /dev/null
+exit 0
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 975777f05e..17df47b950 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -7,7 +7,7 @@ struct cmdname_help
char help[80];
};
-struct cmdname_help common_cmds[] = {"
+static struct cmdname_help common_cmds[] = {"
sort <<\EOF |
add
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index dc3038091d..7921cde8cb 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -3,9 +3,16 @@
use strict;
sub run_cmd_pipe {
- my $fh = undef;
- open($fh, '-|', @_) or die;
- return <$fh>;
+ if ($^O eq 'MSWin32') {
+ my @invalid = grep {m/[":*]/} @_;
+ die "$^O does not support: @invalid\n" if @invalid;
+ my @args = map { m/ /o ? "\"$_\"": $_ } @_;
+ return qx{@args};
+ } else {
+ my $fh = undef;
+ open($fh, '-|', @_) or die;
+ return <$fh>;
+ }
}
my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
@@ -17,7 +24,7 @@ chomp($GIT_DIR);
sub refresh {
my $fh;
- open $fh, '-|', qw(git update-index --refresh)
+ open $fh, 'git update-index --refresh |'
or die;
while (<$fh>) {
;# ignore 'needs update'
@@ -296,7 +303,7 @@ sub revert_cmd {
my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
map { $_->{VALUE} } @update);
my $fh;
- open $fh, '|-', qw(git update-index --index-info)
+ open $fh, '| git update-index --index-info'
or die;
for (@lines) {
print $fh $_;
@@ -725,7 +732,7 @@ sub patch_update_cmd {
if (@result) {
my $fh;
- open $fh, '|-', qw(git apply --cached);
+ open $fh, '| git apply --cached';
for (@{$head->{TEXT}}, @result) {
print $fh $_;
}
diff --git a/git-am.sh b/git-am.sh
index 8b5712968e..b5ed8ca15c 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -60,17 +60,17 @@ fall_back_3way () {
mkdir "$dotest/patch-merge-tmp-dir"
# First see if the patch records the index info that we can use.
- git-apply -z --index-info "$dotest/patch" \
+ git apply -z --index-info "$dotest/patch" \
>"$dotest/patch-merge-index-info" &&
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
- git-update-index -z --index-info <"$dotest/patch-merge-index-info" &&
+ git update-index -z --index-info <"$dotest/patch-merge-index-info" &&
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
- git-write-tree >"$dotest/patch-merge-base+" ||
+ git write-tree >"$dotest/patch-merge-base+" ||
cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
echo Using index info to reconstruct a base tree...
if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
- git-apply $binary --cached <"$dotest/patch"
+ git apply $binary --cached <"$dotest/patch"
then
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
@@ -80,7 +80,7 @@ It does not apply to blobs recorded in its index."
fi
test -f "$dotest/patch-merge-index" &&
- his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree) &&
+ his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git write-tree) &&
orig_tree=$(cat "$dotest/patch-merge-base") &&
rm -fr "$dotest"/patch-merge-* || exit 1
@@ -95,10 +95,7 @@ It does not apply to blobs recorded in its index."
eval GITHEAD_$his_tree='"$SUBJECT"'
export GITHEAD_$his_tree
git-merge-recursive $orig_tree -- HEAD $his_tree || {
- if test -d "$GIT_DIR/rr-cache"
- then
- git-rerere
- fi
+ git rerere
echo Failed to merge in the changes.
exit 1
}
@@ -106,7 +103,8 @@ It does not apply to blobs recorded in its index."
}
prec=4
-dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg=
+dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
+resolvemsg= resume=
git_apply_opt=
while case "$#" in 0) break;; esac
@@ -198,7 +196,7 @@ else
# Start afresh.
mkdir -p "$dotest" || exit
- git-mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" || {
+ git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" || {
rm -fr "$dotest"
exit 1
}
@@ -216,7 +214,7 @@ fi
case "$resolved" in
'')
- files=$(git-diff-index --cached --name-only HEAD) || exit
+ files=$(git diff-index --cached --name-only HEAD) || exit
if [ "$files" ]; then
echo "Dirty index: cannot apply patches (dirty: $files)" >&2
exit 1
@@ -252,10 +250,7 @@ last=`cat "$dotest/last"`
this=`cat "$dotest/next"`
if test "$skip" = t
then
- if test -d "$GIT_DIR/rr-cache"
- then
- git-rerere clear
- fi
+ git rerere clear
this=`expr "$this" + 1`
resume=
fi
@@ -287,14 +282,20 @@ do
# by the user, or the user can tell us to do so by --resolved flag.
case "$resume" in
'')
- git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+ git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
+
+ # skip pine's internal folder data
+ grep '^Author: Mail System Internal Data$' \
+ <"$dotest"/info >/dev/null &&
+ go_next && continue
+
test -s $dotest/patch || {
echo "Patch is empty. Was it split wrong?"
stop_here $this
}
- git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
+ git stripspace < "$dotest/msg" > "$dotest/msg-clean"
;;
esac
@@ -347,7 +348,7 @@ do
case "$resolved$interactive" in
tt)
# This is used only for interactive view option.
- git-diff-index -p --cached HEAD >"$dotest/patch"
+ git diff-index -p --cached HEAD >"$dotest/patch"
;;
esac
esac
@@ -370,7 +371,7 @@ do
[yY]*) action=yes ;;
[aA]*) action=yes interactive= ;;
[nN]*) action=skip ;;
- [eE]*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
+ [eE]*) git_editor "$dotest/final-commit"
action=again ;;
[vV]*) action=again
LESS=-S ${PAGER:-less} "$dotest/patch" ;;
@@ -399,7 +400,7 @@ do
case "$resolved" in
'')
- git-apply $git_apply_opt $binary --index "$dotest/patch"
+ git apply $git_apply_opt $binary --index "$dotest/patch"
apply_status=$?
;;
t)
@@ -408,11 +409,11 @@ do
# trust what the user has in the index file and the
# working tree.
resolved=
- git-diff-index --quiet --cached HEAD && {
+ git diff-index --quiet --cached HEAD && {
echo "No changes - did you forget to use 'git add'?"
stop_here_user_resolve $this
}
- unmerged=$(git-ls-files -u)
+ unmerged=$(git ls-files -u)
if test -n "$unmerged"
then
echo "You still have unmerged paths in your index"
@@ -420,10 +421,7 @@ do
stop_here_user_resolve $this
fi
apply_status=0
- if test -d "$GIT_DIR/rr-cache"
- then
- git rerere
- fi
+ git rerere
;;
esac
@@ -433,7 +431,7 @@ do
then
# Applying the patch to an earlier tree and merging the
# result may have produced the same tree as ours.
- git-diff-index --quiet --cached HEAD && {
+ git diff-index --quiet --cached HEAD && {
echo No changes -- Patch already applied.
go_next
continue
@@ -453,12 +451,12 @@ do
"$GIT_DIR"/hooks/pre-applypatch || stop_here $this
fi
- tree=$(git-write-tree) &&
+ tree=$(git write-tree) &&
echo Wrote tree $tree &&
- parent=$(git-rev-parse --verify HEAD) &&
- commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
+ parent=$(git rev-parse --verify HEAD) &&
+ commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
echo Committed: $commit &&
- git-update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
+ git update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
stop_here $this
if test -x "$GIT_DIR"/hooks/post-applypatch
diff --git a/git-applymbox.sh b/git-applymbox.sh
deleted file mode 100755
index c18e80ff8c..0000000000
--- a/git-applymbox.sh
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/bin/sh
-##
-## "dotest" is my stupid name for my patch-application script, which
-## I never got around to renaming after I tested it. We're now on the
-## second generation of scripts, still called "dotest".
-##
-## Update: Ryan Anderson finally shamed me into naming this "applymbox".
-##
-## You give it a mbox-format collection of emails, and it will try to
-## apply them to the kernel using "applypatch"
-##
-## The patch application may fail in the middle. In which case:
-## (1) look at .dotest/patch and fix it up to apply
-## (2) re-run applymbox with -c .dotest/msg-number for the current one.
-## Pay a special attention to the commit log message if you do this and
-## use a Signoff_file, because applypatch wants to append the sign-off
-## message to msg-clean every time it is run.
-##
-## git-am is supposed to be the newer and better tool for this job.
-
-USAGE='[-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]'
-. git-sh-setup
-
-git var GIT_COMMITTER_IDENT >/dev/null || exit
-
-keep_subject= query_apply= continue= utf8=-u resume=t
-while case "$#" in 0) break ;; esac
-do
- case "$1" in
- -u) utf8=-u ;;
- -n) utf8=-n ;;
- -k) keep_subject=-k ;;
- -q) query_apply=t ;;
- -c) continue="$2"; resume=f; shift ;;
- -m) fall_back_3way=t ;;
- -*) usage ;;
- *) break ;;
- esac
- shift
-done
-
-case "$continue" in
-'')
- rm -rf .dotest
- mkdir .dotest
- num_msgs=$(git-mailsplit "$1" .dotest) || exit 1
- echo "$num_msgs patch(es) to process."
- shift
-esac
-
-files=$(git-diff-index --cached --name-only HEAD) || exit
-if [ "$files" ]; then
- echo "Dirty index: cannot apply patches (dirty: $files)" >&2
- exit 1
-fi
-
-case "$query_apply" in
-t) touch .dotest/.query_apply
-esac
-case "$fall_back_3way" in
-t) : >.dotest/.3way
-esac
-case "$keep_subject" in
--k) : >.dotest/.keep_subject
-esac
-
-signoff="$1"
-set x .dotest/0*
-shift
-while case "$#" in 0) break;; esac
-do
- i="$1"
- case "$resume,$continue" in
- f,$i) resume=t;;
- f,*) shift
- continue;;
- *)
- git-mailinfo $keep_subject $utf8 \
- .dotest/msg .dotest/patch <$i >.dotest/info || exit 1
- test -s .dotest/patch || {
- echo "Patch is empty. Was it split wrong?"
- exit 1
- }
- git-stripspace < .dotest/msg > .dotest/msg-clean
- ;;
- esac
- while :; # for fixing up and retry
- do
- git-applypatch .dotest/msg-clean .dotest/patch .dotest/info "$signoff"
- case "$?" in
- 0)
- # Remove the cleanly applied one to reduce clutter.
- rm -f .dotest/$i
- ;;
- 2)
- # 2 is a special exit code from applypatch to indicate that
- # the patch wasn't applied, but continue anyway
- ;;
- *)
- ret=$?
- if test -f .dotest/.query_apply
- then
- echo >&2 "* Patch failed."
- echo >&2 "* You could fix it up in your editor and"
- echo >&2 " retry. If you want to do so, say yes here"
- echo >&2 " AFTER fixing .dotest/patch up."
- echo >&2 -n "Retry [y/N]? "
- read yesno
- case "$yesno" in
- [Yy]*)
- continue ;;
- esac
- fi
- exit $ret
- esac
- break
- done
- shift
-done
-# return to pristine
-rm -fr .dotest
diff --git a/git-applypatch.sh b/git-applypatch.sh
deleted file mode 100755
index 8df2aee4c2..0000000000
--- a/git-applypatch.sh
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/bin/sh
-##
-## applypatch takes four file arguments, and uses those to
-## apply the unpacked patch (surprise surprise) that they
-## represent to the current tree.
-##
-## The arguments are:
-## $1 - file with commit message
-## $2 - file with the actual patch
-## $3 - "info" file with Author, email and subject
-## $4 - optional file containing signoff to add
-##
-
-USAGE='<msg> <patch> <info> [<signoff>]'
-. git-sh-setup
-
-case "$#" in 3|4) ;; *) usage ;; esac
-
-final=.dotest/final-commit
-##
-## If this file exists, we ask before applying
-##
-query_apply=.dotest/.query_apply
-
-## We do not munge the first line of the commit message too much
-## if this file exists.
-keep_subject=.dotest/.keep_subject
-
-## We do not attempt the 3-way merge fallback unless this file exists.
-fall_back_3way=.dotest/.3way
-
-MSGFILE=$1
-PATCHFILE=$2
-INFO=$3
-SIGNOFF=$4
-EDIT=${VISUAL:-${EDITOR:-vi}}
-
-export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
-export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
-export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
-export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
-
-if test '' != "$SIGNOFF"
-then
- if test -f "$SIGNOFF"
- then
- SIGNOFF=`cat "$SIGNOFF"` || exit
- elif case "$SIGNOFF" in yes | true | me | please) : ;; *) false ;; esac
- then
- SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
- s/>.*/>/
- s/^/Signed-off-by: /'
- `
- else
- SIGNOFF=
- fi
- if test '' != "$SIGNOFF"
- then
- LAST_SIGNED_OFF_BY=`
- sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
- tail -n 1
- `
- test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
- test '' = "$LAST_SIGNED_OFF_BY" && echo
- echo "$SIGNOFF"
- } >>"$MSGFILE"
- fi
-fi
-
-patch_header=
-test -f "$keep_subject" || patch_header='[PATCH] '
-
-{
- echo "$patch_header$SUBJECT"
- if test -s "$MSGFILE"
- then
- echo
- cat "$MSGFILE"
- fi
-} >"$final"
-
-interactive=yes
-test -f "$query_apply" || interactive=no
-
-while [ "$interactive" = yes ]; do
- echo "Commit Body is:"
- echo "--------------------------"
- cat "$final"
- echo "--------------------------"
- printf "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
- read reply
- case "$reply" in
- y|Y) interactive=no;;
- n|N) exit 2;; # special value to tell dotest to keep going
- e|E) "$EDIT" "$final";;
- a|A) rm -f "$query_apply"
- interactive=no ;;
- esac
-done
-
-if test -x "$GIT_DIR"/hooks/applypatch-msg
-then
- "$GIT_DIR"/hooks/applypatch-msg "$final" || exit
-fi
-
-echo
-echo Applying "'$SUBJECT'"
-echo
-
-git-apply --index "$PATCHFILE" || {
-
- # git-apply exits with status 1 when the patch does not apply,
- # but it die()s with other failures, most notably upon corrupt
- # patch. In the latter case, there is no point to try applying
- # it to another tree and do 3-way merge.
- test $? = 1 || exit 1
-
- test -f "$fall_back_3way" || exit 1
-
- # Here if we know which revision the patch applies to,
- # we create a temporary working tree and index, apply the
- # patch, and attempt 3-way merge with the resulting tree.
-
- O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
- rm -fr .patch-merge-*
-
- if git-apply -z --index-info "$PATCHFILE" \
- >.patch-merge-index-info 2>/dev/null &&
- GIT_INDEX_FILE=.patch-merge-tmp-index \
- git-update-index -z --index-info <.patch-merge-index-info &&
- GIT_INDEX_FILE=.patch-merge-tmp-index \
- git-write-tree >.patch-merge-tmp-base &&
- (
- mkdir .patch-merge-tmp-dir &&
- cd .patch-merge-tmp-dir &&
- GIT_INDEX_FILE="../.patch-merge-tmp-index" \
- GIT_OBJECT_DIRECTORY="$O_OBJECT" \
- git-apply $binary --index
- ) <"$PATCHFILE"
- then
- echo Using index info to reconstruct a base tree...
- mv .patch-merge-tmp-base .patch-merge-base
- mv .patch-merge-tmp-index .patch-merge-index
- else
- (
- N=10
-
- # Otherwise, try nearby trees that can be used to apply the
- # patch.
- git-rev-list --max-count=$N HEAD
-
- # or hoping the patch is against known tags...
- git-ls-remote --tags .
- ) |
- while read base junk
- do
- # Try it if we have it as a tree.
- git-cat-file tree "$base" >/dev/null 2>&1 || continue
-
- rm -fr .patch-merge-tmp-* &&
- mkdir .patch-merge-tmp-dir || break
- (
- cd .patch-merge-tmp-dir &&
- GIT_INDEX_FILE=../.patch-merge-tmp-index &&
- GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
- export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
- git-read-tree "$base" &&
- git-apply --index &&
- mv ../.patch-merge-tmp-index ../.patch-merge-index &&
- echo "$base" >../.patch-merge-base
- ) <"$PATCHFILE" 2>/dev/null && break
- done
- fi
-
- test -f .patch-merge-index &&
- his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
- orig_tree=$(cat .patch-merge-base) &&
- rm -fr .patch-merge-* || exit 1
-
- echo Falling back to patching base and 3-way merge using $orig_tree...
-
- # This is not so wrong. Depending on which base we picked,
- # orig_tree may be wildly different from ours, but his_tree
- # has the same set of wildly different changes in parts the
- # patch did not touch, so resolve ends up canceling them,
- # saying that we reverted all those changes.
-
- if git-merge-resolve $orig_tree -- HEAD $his_tree
- then
- echo Done.
- else
- echo Failed to merge in the changes.
- exit 1
- fi
-}
-
-if test -x "$GIT_DIR"/hooks/pre-applypatch
-then
- "$GIT_DIR"/hooks/pre-applypatch || exit
-fi
-
-tree=$(git-write-tree) || exit 1
-echo Wrote tree $tree
-parent=$(git-rev-parse --verify HEAD) &&
-commit=$(git-commit-tree $tree -p $parent <"$final") || exit 1
-echo Committed: $commit
-git-update-ref -m "applypatch: $SUBJECT" HEAD $commit $parent || exit
-
-if test -x "$GIT_DIR"/hooks/post-applypatch
-then
- "$GIT_DIR"/hooks/post-applypatch
-fi
diff --git a/git-archimport.perl b/git-archimport.perl
index c1e7c1ddcb..b21077206a 100755
--- a/git-archimport.perl
+++ b/git-archimport.perl
@@ -3,19 +3,19 @@
# This tool is copyright (c) 2005, Martin Langhoff.
# It is released under the Gnu Public License, version 2.
#
-# The basic idea is to walk the output of tla abrowse,
-# fetch the changesets and apply them.
+# The basic idea is to walk the output of tla abrowse,
+# fetch the changesets and apply them.
#
=head1 Invocation
- git-archimport [ -h ] [ -v ] [ -o ] [ -a ] [ -f ] [ -T ]
- [ -D depth] [ -t tempdir ] <archive>/<branch> [ <archive>/<branch> ]
+ git-archimport [ -h ] [ -v ] [ -o ] [ -a ] [ -f ] [ -T ]
+ [ -D depth] [ -t tempdir ] <archive>/<branch> [ <archive>/<branch> ]
Imports a project from one or more Arch repositories. It will follow branches
and repositories within the namespaces defined by the <archive/branch>
parameters supplied. If it cannot find the remote branch a merge comes from
-it will just import it as a regular commit. If it can find it, it will mark it
+it will just import it as a regular commit. If it can find it, it will mark it
as a merge whenever possible.
See man (1) git-archimport for more details.
@@ -25,14 +25,14 @@ See man (1) git-archimport for more details.
- create tag objects instead of ref tags
- audit shell-escaping of filenames
- hide our private tags somewhere smarter
- - find a way to make "cat *patches | patch" safe even when patchfiles are missing newlines
+ - find a way to make "cat *patches | patch" safe even when patchfiles are missing newlines
- sort and apply patches by graphing ancestry relations instead of just
relying in dates supplied in the changeset itself.
tla ancestry-graph -m could be helpful here...
=head1 Devel tricks
-Add print in front of the shell commands invoked via backticks.
+Add print in front of the shell commands invoked via backticks.
=head1 Devel Notes
@@ -126,16 +126,16 @@ sub do_abrowse {
my $stage = shift;
while (my ($limit, $level) = each %arch_branches) {
next unless $level == $stage;
-
- open ABROWSE, "$TLA abrowse -fkD --merges $limit |"
+
+ open ABROWSE, "$TLA abrowse -fkD --merges $limit |"
or die "Problems with tla abrowse: $!";
-
+
my %ps = (); # the current one
my $lastseen = '';
-
+
while (<ABROWSE>) {
chomp;
-
+
# first record padded w 8 spaces
if (s/^\s{8}\b//) {
my ($id, $type) = split(m/\s+/, $_, 2);
@@ -147,13 +147,13 @@ sub do_abrowse {
push (@psets, \%last_ps);
$psets{ $last_ps{id} } = \%last_ps;
}
-
+
my $branch = extract_versionname($id);
%ps = ( id => $id, branch => $branch );
if (%last_ps && ($last_ps{branch} eq $branch)) {
$ps{parent_id} = $last_ps{id};
}
-
+
$arch_branches{$branch} = 1;
$lastseen = 'id';
@@ -166,16 +166,16 @@ sub do_abrowse {
$ps{type} = 't';
# read which revision we've tagged when we parse the log
$ps{tag} = $1;
- } else {
+ } else {
warn "Unknown type $type";
}
$arch_branches{$branch} = 1;
$lastseen = 'id';
- } elsif (s/^\s{10}//) {
- # 10 leading spaces or more
+ } elsif (s/^\s{10}//) {
+ # 10 leading spaces or more
# indicate commit metadata
-
+
# date
if ($lastseen eq 'id' && m/^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d)/){
$ps{date} = $1;
@@ -186,12 +186,12 @@ sub do_abrowse {
} elsif ($lastseen eq 'merges' && s/^\s{2}//) {
my $id = $_;
push (@{$ps{merges}}, $id);
-
+
# aggressive branch finding:
if ($opt_D) {
my $branch = extract_versionname($id);
my $repo = extract_reponame($branch);
-
+
if (archive_reachable($repo) &&
!defined $arch_branches{$branch}) {
$arch_branches{$branch} = $stage + 1;
@@ -208,10 +208,10 @@ sub do_abrowse {
if (@psets && $psets[$#psets]{branch} eq $ps{branch}) {
$temp{parent_id} = $psets[$#psets]{id};
}
- push (@psets, \%temp);
+ push (@psets, \%temp);
$psets{ $temp{id} } = \%temp;
- }
-
+ }
+
close ABROWSE or die "$TLA abrowse failed on $limit\n";
}
} # end foreach $root
@@ -253,7 +253,7 @@ unless (-d $git_dir) { # initial import
while (my $file = readdir(DIR)) {
# skip non-interesting-files
next unless -f "$ptag_dir/$file";
-
+
# convert first '--' to '/' from old git-archimport to use
# as an archivename/c--b--v private tag
if ($file !~ m!,!) {
@@ -275,7 +275,7 @@ sub extract_reponame {
my $fq_cvbr = shift; # archivename/[[[[category]branch]version]revision]
return (split(/\//, $fq_cvbr))[0];
}
-
+
sub extract_versionname {
my $name = shift;
$name =~ s/--(?:patch|version(?:fix)?|base)-\d+$//;
@@ -283,7 +283,7 @@ sub extract_versionname {
}
# convert a fully-qualified revision or version to a unique dirname:
-# normalperson@yhbt.net-05/mpd--uclinux--1--patch-2
+# normalperson@yhbt.net-05/mpd--uclinux--1--patch-2
# becomes: normalperson@yhbt.net-05,mpd--uclinux--1
#
# the git notion of a branch is closer to
@@ -339,7 +339,7 @@ sub git_branchname {
sub process_patchset_accurate {
my $ps = shift;
-
+
# switch to that branch if we're not already in that branch:
if (-e "$git_dir/refs/heads/$ps->{branch}") {
system('git-checkout','-f',$ps->{branch}) == 0 or die "$! $?\n";
@@ -348,7 +348,7 @@ sub process_patchset_accurate {
my $rm = safe_pipe_capture('git-ls-files','--others','-z');
rmtree(split(/\0/,$rm)) if $rm;
}
-
+
# Apply the import/changeset/merge into the working tree
my $dir = sync_to_ps($ps);
# read the new log entry:
@@ -361,9 +361,9 @@ sub process_patchset_accurate {
parselog($ps, \@commitlog);
if ($ps->{id} =~ /--base-0$/ && $ps->{id} ne $psets[0]{id}) {
- # this should work when importing continuations
+ # this should work when importing continuations
if ($ps->{tag} && (my $branchpoint = eval { ptag($ps->{tag}) })) {
-
+
# find where we are supposed to branch from
if (! -e "$git_dir/refs/heads/$ps->{branch}") {
system('git-branch',$ps->{branch},$branchpoint) == 0 or die "$! $?\n";
@@ -388,8 +388,8 @@ sub process_patchset_accurate {
}
# allow multiple bases/imports here since Arch supports cherry-picks
# from unrelated trees
- }
-
+ }
+
# update the index with all the changes we got
system('git-diff-files --name-only -z | '.
'git-update-index --remove -z --stdin') == 0 or die "$! $?\n";
@@ -402,7 +402,7 @@ sub process_patchset_accurate {
# does not handle permissions or any renames involving directories
sub process_patchset_fast {
my $ps = shift;
- #
+ #
# create the branch if needed
#
if ($ps->{type} eq 'i' && !$import) {
@@ -417,9 +417,9 @@ sub process_patchset_fast {
# new branch! we need to verify a few things
die "Branch on a non-tag!" unless $ps->{type} eq 't';
my $branchpoint = ptag($ps->{tag});
- die "Tagging from unknown id unsupported: $ps->{tag}"
+ die "Tagging from unknown id unsupported: $ps->{tag}"
unless $branchpoint;
-
+
# find where we are supposed to branch from
if (! -e "$git_dir/refs/heads/$ps->{branch}") {
system('git-branch',$ps->{branch},$branchpoint) == 0 or die "$! $?\n";
@@ -435,13 +435,13 @@ sub process_patchset_fast {
}
system('git-checkout',$ps->{branch}) == 0 or die "$! $?\n";
return 0;
- }
+ }
die $! if $?;
- }
+ }
#
# Apply the import/changeset/merge into the working tree
- #
+ #
if ($ps->{type} eq 'i' || $ps->{type} eq 't') {
apply_import($ps) or die $!;
$stats{import_or_tag}++;
@@ -455,10 +455,10 @@ sub process_patchset_fast {
# prepare update git's index, based on what arch knows
# about the pset, resolve parents, etc
#
-
- my @commitlog = safe_pipe_capture($TLA,'cat-archive-log',$ps->{id});
+
+ my @commitlog = safe_pipe_capture($TLA,'cat-archive-log',$ps->{id});
die "Error in cat-archive-log: $!" if $?;
-
+
parselog($ps,\@commitlog);
# imports don't give us good info
@@ -485,10 +485,10 @@ sub process_patchset_fast {
if (@$ren % 2) {
die "Odd number of entries in rename!?";
}
-
+
while (@$ren) {
my $from = shift @$ren;
- my $to = shift @$ren;
+ my $to = shift @$ren;
unless (-d dirname($to)) {
mkpath(dirname($to)); # will die on err
@@ -529,20 +529,20 @@ if ($opt_f) {
"Things may be a bit slow\n";
*process_patchset = *process_patchset_accurate;
}
-
+
foreach my $ps (@psets) {
# process patchsets
$ps->{branch} = git_branchname($ps->{id});
#
- # ensure we have a clean state
- #
+ # ensure we have a clean state
+ #
if (my $dirty = `git-diff-files`) {
die "Unclean tree when about to process $ps->{id} " .
" - did we fail to commit cleanly before?\n$dirty";
}
die $! if $?;
-
+
#
# skip commits already in repo
#
@@ -559,7 +559,7 @@ foreach my $ps (@psets) {
my $tree = `git-write-tree`;
die "cannot write tree $!" if $?;
chomp $tree;
-
+
#
# Who's your daddy?
#
@@ -570,18 +570,18 @@ foreach my $ps (@psets) {
close HEAD;
chomp $p;
push @par, '-p', $p;
- } else {
+ } else {
if ($ps->{type} eq 's') {
warn "Could not find the right head for the branch $ps->{branch}";
}
}
}
-
+
if ($ps->{merges}) {
push @par, find_parents($ps);
}
- #
+ #
# Commit, tag and clean state
#
$ENV{TZ} = 'GMT';
@@ -592,14 +592,14 @@ foreach my $ps (@psets) {
$ENV{GIT_COMMITTER_EMAIL} = $ps->{email};
$ENV{GIT_COMMITTER_DATE} = $ps->{date};
- my $pid = open2(*READER, *WRITER,'git-commit-tree',$tree,@par)
+ my $pid = open2(*READER, *WRITER,'git-commit-tree',$tree,@par)
or die $!;
print WRITER $ps->{summary},"\n\n";
print WRITER $ps->{message},"\n";
-
+
# make it easy to backtrack and figure out which Arch revision this was:
print WRITER 'git-archimport-id: ',$ps->{id},"\n";
-
+
close WRITER;
my $commitid = <READER>; # read
chomp $commitid;
@@ -611,7 +611,7 @@ foreach my $ps (@psets) {
}
#
# Update the branch
- #
+ #
open HEAD, ">","$git_dir/refs/heads/$ps->{branch}";
print HEAD $commitid;
close HEAD;
@@ -640,7 +640,7 @@ exit 0;
sub sync_to_ps {
my $ps = shift;
my $tree_dir = $tmp.'/'.tree_dirname($ps->{id});
-
+
$opt_v && print "sync_to_ps($ps->{id}) method: ";
if (-d $tree_dir) {
@@ -674,7 +674,7 @@ sub sync_to_ps {
safe_pipe_capture($TLA,'get','--no-pristine',$ps->{id},$tree_dir);
$stats{get_new}++;
}
-
+
# added -I flag to rsync since we're going to fast! AIEEEEE!!!!
system('rsync','-aI','--delete','--exclude',$git_dir,
# '--exclude','.arch-inventory',
@@ -691,15 +691,15 @@ sub apply_import {
mkpath($tmp);
safe_pipe_capture($TLA,'get','-s','--no-pristine',$ps->{id},"$tmp/import");
- die "Cannot get import: $!" if $?;
+ die "Cannot get import: $!" if $?;
system('rsync','-aI','--delete', '--exclude',$git_dir,
'--exclude','.arch-ids','--exclude','{arch}',
"$tmp/import/", './');
die "Cannot rsync import:$!" if $?;
-
+
rmtree("$tmp/import");
die "Cannot remove tempdir: $!" if $?;
-
+
return 1;
}
@@ -712,13 +712,13 @@ sub apply_cset {
# get the changeset
safe_pipe_capture($TLA,'get-changeset',$ps->{id},"$tmp/changeset");
die "Cannot get changeset: $!" if $?;
-
+
# apply patches
if (`find $tmp/changeset/patches -type f -name '*.patch'`) {
# this can be sped up considerably by doing
# (find | xargs cat) | patch
# but that can get mucked up by patches
- # with missing trailing newlines or the standard
+ # with missing trailing newlines or the standard
# 'missing newline' flag in the patch - possibly
# produced with an old/buggy diff.
# slow and safe, we invoke patch once per patchfile
@@ -741,7 +741,7 @@ sub apply_cset {
# bring in new files
system('rsync','-aI','--exclude',$git_dir,
- '--exclude','.arch-ids',
+ '--exclude','.arch-ids',
'--exclude', '{arch}',
"$tmp/changeset/new-files-archive/",'./');
@@ -789,7 +789,7 @@ sub parselog {
removed_files => 1,
removed_directories => 1,
);
-
+
chomp (@$log);
while ($_ = shift @$log) {
if (/^Continuation-of:\s*(.*)/) {
@@ -828,7 +828,7 @@ sub parselog {
}
}
}
-
+
# drop leading empty lines from the log message
while (@$log && $log->[0] eq '') {
shift @$log;
@@ -842,7 +842,7 @@ sub parselog {
$ps->{summary} = $log->[0] . '...';
}
$ps->{message} = join("\n",@$log);
-
+
# skip Arch control files, unescape pika-escaped files
foreach my $k (keys %want_headers) {
next unless (defined $ps->{$k});
@@ -867,7 +867,7 @@ sub parselog {
# write/read a tag
sub tag {
my ($tag, $commit) = @_;
-
+
if ($opt_o) {
$tag =~ s|/|--|g;
} else {
@@ -875,7 +875,7 @@ sub tag {
$patchname =~ s/.*--//;
$tag = git_branchname ($tag) . '--' . $patchname;
}
-
+
if ($commit) {
open(C,">","$git_dir/refs/tags/$tag")
or die "Cannot create tag $tag: $!\n";
@@ -902,8 +902,8 @@ sub ptag {
my ($tag, $commit) = @_;
# don't use subdirs for tags yet, it could screw up other porcelains
- $tag =~ s|/|,|g;
-
+ $tag =~ s|/|,|g;
+
my $tag_file = "$ptag_dir/$tag";
my $tag_branch_dir = dirname($tag_file);
mkpath($tag_branch_dir) unless (-d $tag_branch_dir);
@@ -915,7 +915,7 @@ sub ptag {
or die "Cannot write tag $tag: $!\n";
close(C)
or die "Cannot write tag $tag: $!\n";
- $rptags{$commit} = $tag
+ $rptags{$commit} = $tag
unless $tag =~ m/--base-0$/;
} else { # read
# if the tag isn't there, return 0
@@ -941,7 +941,7 @@ sub find_parents {
# Identify what branches are merging into me
# and whether we are fully merged
# git-merge-base <headsha> <headsha> should tell
- # me what the base of the merge should be
+ # me what the base of the merge should be
#
my $ps = shift;
@@ -963,14 +963,14 @@ sub find_parents {
}
#
- # foreach branch find a merge base and walk it to the
+ # foreach branch find a merge base and walk it to the
# head where we are, collecting the merged patchsets that
# Arch has recorded. Keep that in @have
# Compare that with the commits on the other branch
# between merge-base and the tip of the branch (@need)
# and see if we have a series of consecutive patches
# starting from the merge base. The tip of the series
- # of consecutive patches merged is our new parent for
+ # of consecutive patches merged is our new parent for
# that branch.
#
foreach my $branch (keys %branches) {
@@ -979,13 +979,13 @@ sub find_parents {
next unless -e "$git_dir/refs/heads/$branch";
my $mergebase = `git-merge-base $branch $ps->{branch}`;
- if ($?) {
- # Don't die here, Arch supports one-way cherry-picking
- # between branches with no common base (or any relationship
- # at all beforehand)
- warn "Cannot find merge base for $branch and $ps->{branch}";
- next;
- }
+ if ($?) {
+ # Don't die here, Arch supports one-way cherry-picking
+ # between branches with no common base (or any relationship
+ # at all beforehand)
+ warn "Cannot find merge base for $branch and $ps->{branch}";
+ next;
+ }
chomp $mergebase;
# now walk up to the mergepoint collecting what patches we have
@@ -1010,7 +1010,7 @@ sub find_parents {
# merge what we have with what ancestors have
%have = (%have, %ancestorshave);
- # see what the remote branch has - these are the merges we
+ # see what the remote branch has - these are the merges we
# will want to have in a consecutive series from the mergebase
my $otherbranchtip = git_rev_parse($branch);
my @needraw = `git-rev-list --topo-order $otherbranchtip ^$mergebase`;
@@ -1018,7 +1018,7 @@ sub find_parents {
foreach my $needps (@needraw) { # get the psets
$needps = commitid2pset($needps);
# git-rev-list will also
- # list commits merged in via earlier
+ # list commits merged in via earlier
# merges. we are only interested in commits
# from the branch we're looking at
if ($branch eq $needps->{branch}) {
@@ -1054,7 +1054,7 @@ sub find_parents {
next unless ref $psets{$p}{merges};
my @merges = @{$psets{$p}{merges}};
foreach my $merge (@merges) {
- if ($parents{$merge}) {
+ if ($parents{$merge}) {
delete $parents{$merge};
}
}
@@ -1079,10 +1079,10 @@ sub git_rev_parse {
sub commitid2pset {
my $commitid = shift;
chomp $commitid;
- my $name = $rptags{$commitid}
+ my $name = $rptags{$commitid}
|| die "Cannot find reverse tag mapping for $commitid";
$name =~ s|,|/|;
- my $ps = $psets{$name}
+ my $ps = $psets{$name}
|| (print Dumper(sort keys %psets)) && die "Cannot find patchset for $name";
return $ps;
}
@@ -1112,7 +1112,7 @@ sub archive_reachable {
my $archive = shift;
return 1 if $reachable{$archive};
return 0 if $unreachable{$archive};
-
+
if (system "$TLA whereis-archive $archive >/dev/null") {
if ($opt_a && (system($TLA,'register-archive',
"http://mirrors.sourcecontrol.net/$archive") == 0)) {
@@ -1127,4 +1127,3 @@ sub archive_reachable {
return 1;
}
}
-
diff --git a/git-bisect.sh b/git-bisect.sh
index 1cd456173d..388887a556 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -56,7 +56,7 @@ bisect_start() {
# Verify HEAD. If we were bisecting before this, reset to the
# top-of-line master first!
#
- head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
+ head=$(GIT_DIR="$GIT_DIR" git symbolic-ref HEAD) ||
die "Bad HEAD - I need a symbolic ref"
case "$head" in
refs/heads/bisect)
@@ -99,7 +99,7 @@ bisect_start() {
break
;;
*)
- rev=$(git-rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
+ rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || {
test $has_double_dash -eq 1 &&
die "'$arg' does not appear to be a valid revision"
break
@@ -124,9 +124,9 @@ bisect_bad() {
bisect_autostart
case "$#" in
0)
- rev=$(git-rev-parse --verify HEAD) ;;
+ rev=$(git rev-parse --verify HEAD) ;;
1)
- rev=$(git-rev-parse --verify "$1^{commit}") ;;
+ rev=$(git rev-parse --verify "$1^{commit}") ;;
*)
usage ;;
esac || exit
@@ -138,19 +138,19 @@ bisect_bad() {
bisect_write_bad() {
rev="$1"
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
- echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+ echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
}
bisect_good() {
bisect_autostart
case "$#" in
- 0) revs=$(git-rev-parse --verify HEAD) || exit ;;
- *) revs=$(git-rev-parse --revs-only --no-flags "$@") &&
+ 0) revs=$(git rev-parse --verify HEAD) || exit ;;
+ *) revs=$(git rev-parse --revs-only --no-flags "$@") &&
test '' != "$revs" || die "Bad rev input: $@" ;;
esac
for rev in $revs
do
- rev=$(git-rev-parse --verify "$rev^{commit}") || exit
+ rev=$(git rev-parse --verify "$rev^{commit}") || exit
bisect_write_good "$rev"
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
@@ -161,7 +161,7 @@ bisect_good() {
bisect_write_good() {
rev="$1"
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
- echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+ echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
}
bisect_next_check() {
@@ -211,10 +211,10 @@ bisect_next() {
bisect_autostart
bisect_next_check good
- bad=$(git-rev-parse --verify refs/bisect/bad) &&
+ bad=$(git rev-parse --verify refs/bisect/bad) &&
good=$(git for-each-ref --format='^%(objectname)' \
"refs/bisect/good-*" | tr '[\012]' ' ') &&
- eval="git-rev-list --bisect-vars $good $bad --" &&
+ eval="git rev-list --bisect-vars $good $bad --" &&
eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
eval=$(eval "$eval") &&
eval "$eval" || exit
@@ -225,7 +225,7 @@ bisect_next() {
fi
if [ "$bisect_rev" = "$bad" ]; then
echo "$bisect_rev is first bad commit"
- git-diff-tree --pretty $bisect_rev
+ git diff-tree --pretty $bisect_rev
exit 0
fi
@@ -233,8 +233,8 @@ bisect_next() {
echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect"
git checkout -q new-bisect || exit
mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" &&
- GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect
- git-show-branch "$bisect_rev"
+ GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect
+ git show-branch "$bisect_rev"
}
bisect_visualize() {
@@ -250,7 +250,7 @@ bisect_reset() {
else
branch=master
fi ;;
- 1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
+ 1) git show-ref --verify --quiet -- "refs/heads/$1" || {
echo >&2 "$1 does not seem to be a valid branch"
exit 1
}
@@ -288,12 +288,12 @@ bisect_replay () {
;;
good)
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
- echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+ echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
;;
bad)
echo "$rev" >"$GIT_DIR/refs/bisect/bad"
- echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
+ echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG"
;;
*)
diff --git a/git-checkout.sh b/git-checkout.sh
index 7c5ca3d62f..17f43927aa 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -6,8 +6,8 @@ SUBDIRECTORY_OK=Sometimes
require_work_tree
old_name=HEAD
-old=$(git-rev-parse --verify $old_name 2>/dev/null)
-oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
+old=$(git rev-parse --verify $old_name 2>/dev/null)
+oldbranch=$(git symbolic-ref $old_name 2>/dev/null)
new=
new_name=
force=
@@ -29,9 +29,9 @@ while [ "$#" != "0" ]; do
shift
[ -z "$newbranch" ] &&
die "git checkout: -b needs a branch name"
- git-show-ref --verify --quiet -- "refs/heads/$newbranch" &&
+ git show-ref --verify --quiet -- "refs/heads/$newbranch" &&
die "git checkout: branch $newbranch already exists"
- git-check-ref-format "heads/$newbranch" ||
+ git check-ref-format "heads/$newbranch" ||
die "git checkout: we do not like '$newbranch' as a branch name."
;;
"-l")
@@ -57,20 +57,20 @@ while [ "$#" != "0" ]; do
usage
;;
*)
- if rev=$(git-rev-parse --verify "$arg^0" 2>/dev/null)
+ if rev=$(git rev-parse --verify "$arg^0" 2>/dev/null)
then
if [ -z "$rev" ]; then
echo "unknown flag $arg"
exit 1
fi
new_name="$arg"
- if git-show-ref --verify --quiet -- "refs/heads/$arg"
+ if git show-ref --verify --quiet -- "refs/heads/$arg"
then
- rev=$(git-rev-parse --verify "refs/heads/$arg^0")
+ rev=$(git rev-parse --verify "refs/heads/$arg^0")
branch="$arg"
fi
new="$rev"
- elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
+ elif rev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null)
then
# checking out selected paths from a tree-ish.
new="$rev"
@@ -129,14 +129,14 @@ Did you intend to checkout '$@' which can not be resolved as commit?"
# from a specific tree-ish; note that this is for
# rescuing paths and is never meant to remove what
# is not in the named tree-ish.
- git-ls-tree --full-name -r "$new" "$@" |
- git-update-index --index-info || exit $?
+ git ls-tree --full-name -r "$new" "$@" |
+ git update-index --index-info || exit $?
fi
# Make sure the request is about existing paths.
- git-ls-files --error-unmatch -- "$@" >/dev/null || exit
- git-ls-files -- "$@" |
- git-checkout-index -f -u --stdin
+ git ls-files --error-unmatch -- "$@" >/dev/null || exit
+ git ls-files -- "$@" |
+ git checkout-index -f -u --stdin
exit $?
else
# Make sure we did not fall back on $arg^{tree} codepath
@@ -144,7 +144,7 @@ else
# but switching branches.
if test '' != "$new"
then
- git-rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
+ git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
die "Cannot switch branch to a non-commit."
fi
fi
@@ -200,10 +200,10 @@ fi
if [ "$force" ]
then
- git-read-tree $v --reset -u $new
+ git read-tree $v --reset -u $new
else
- git-update-index --refresh >/dev/null
- merge_error=$(git-read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
+ git update-index --refresh >/dev/null
+ merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
case "$merge" in
'')
echo >&2 "$merge_error"
@@ -211,7 +211,7 @@ else
esac
# Match the index to the working tree, and do a three-way.
- git diff-files --name-only | git update-index --remove --stdin &&
+ git diff-files --name-only | git update-index --remove --stdin &&
work=`git write-tree` &&
git read-tree $v --reset -u $new || exit
@@ -246,7 +246,7 @@ else
(exit $saved_err)
fi
-#
+#
# Switch the HEAD pointer to the new branch if we
# checked out a branch head, and remove any potential
# old MERGE_HEAD's (subsequent commits will clearly not
@@ -254,12 +254,13 @@ fi
#
if [ "$?" -eq 0 ]; then
if [ "$newbranch" ]; then
- git-branch $track $newbranch_log "$newbranch" "$new_name" || exit
+ git branch $track $newbranch_log "$newbranch" "$new_name" || exit
branch="$newbranch"
fi
if test -n "$branch"
then
- GIT_DIR="$GIT_DIR" git-symbolic-ref -m "checkout: moving to $branch" HEAD "refs/heads/$branch"
+ old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
+ GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch"
if test -n "$quiet"
then
true # nothing
@@ -271,15 +272,7 @@ if [ "$?" -eq 0 ]; then
fi
elif test -n "$detached"
then
- # NEEDSWORK: we would want a command to detach the HEAD
- # atomically, instead of this handcrafted command sequence.
- # Perhaps:
- # git update-ref --detach HEAD $new
- # or something like that...
- #
- git-rev-parse HEAD >"$GIT_DIR/HEAD.new" &&
- mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" &&
- git-update-ref -m "checkout: moving to $arg" HEAD "$detached" ||
+ git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
die "Cannot detach HEAD"
if test -n "$detach_warn"
then
diff --git a/git-clean.sh b/git-clean.sh
index 299309d971..a5cfd9f07a 100755
--- a/git-clean.sh
+++ b/git-clean.sh
@@ -20,7 +20,7 @@ require_work_tree
ignored=
ignoredonly=
cleandir=
-disabled="`git-config --bool clean.requireForce`"
+disabled="`git config --bool clean.requireForce`"
rmf="rm -f --"
rmrf="rm -rf --"
rm_refuse="echo Not removing"
@@ -83,7 +83,7 @@ if [ -z "$ignored" ]; then
fi
fi
-git-ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
+git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
while read -r file; do
if [ -d "$file" -a ! -L "$file" ]; then
if [ -z "$cleandir" ]; then
diff --git a/git-clone.sh b/git-clone.sh
index d45618d9af..4c9b1c9710 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -2,7 +2,7 @@
#
# Copyright (c) 2005, Linus Torvalds
# Copyright (c) 2005, Junio C Hamano
-#
+#
# Clone a repository into a different directory that does not yet exist.
# See git-sh-setup why.
@@ -43,7 +43,7 @@ clone_dumb_http () {
clone_tmp="$GIT_DIR/clone-tmp" &&
mkdir -p "$clone_tmp" || exit 1
if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
- "`git-config --bool http.noEPSV`" = true ]; then
+ "`git config --bool http.noEPSV`" = true ]; then
curl_extra_args="${curl_extra_args} --disable-epsv"
fi
http_fetch "$1/info/refs" "$clone_tmp/refs" ||
@@ -72,11 +72,22 @@ Perhaps git-update-server-info needs to be run there?"
rm -fr "$clone_tmp"
http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||
rm -f "$GIT_DIR/REMOTE_HEAD"
+ if test -f "$GIT_DIR/REMOTE_HEAD"; then
+ head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"`
+ case "$head_sha1" in
+ 'ref: refs/'*)
+ ;;
+ *)
+ git-http-fetch $v -a "$head_sha1" "$1" ||
+ rm -f "$GIT_DIR/REMOTE_HEAD"
+ ;;
+ esac
+ fi
}
quiet=
local=no
-use_local=no
+use_local_hardlink=yes
local_shared=no
unset template
no_checkout=
@@ -97,9 +108,13 @@ while
no_checkout=yes ;;
*,--na|*,--nak|*,--nake|*,--naked|\
*,-b|*,--b|*,--ba|*,--bar|*,--bare) bare=yes ;;
- *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
- *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
- local_shared=yes; use_local=yes ;;
+ *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local)
+ use_local_hardlink=yes ;;
+ *,--no-h|*,--no-ha|*,--no-har|*,--no-hard|*,--no-hardl|\
+ *,--no-hardli|*,--no-hardlin|*,--no-hardlink|*,--no-hardlinks)
+ use_local_hardlink=no ;;
+ *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
+ local_shared=yes; ;;
1,--template) usage ;;
*,--template)
shift; template="--template=$1" ;;
@@ -121,7 +136,7 @@ while
*/*)
die "'$2' is not suitable for an origin name"
esac
- git-check-ref-format "heads/$2" ||
+ git check-ref-format "heads/$2" ||
die "'$2' is not suitable for a branch name"
test -z "$origin_override" ||
die "Do not give more than one --origin options."
@@ -176,15 +191,31 @@ dir="$2"
# Try using "humanish" part of source repo if user didn't specify one
[ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
[ -e "$dir" ] && die "destination directory '$dir' already exists."
-mkdir -p "$dir" &&
-D=$(cd "$dir" && pwd) &&
-trap 'err=$?; cd ..; rm -rf "$D"; exit $err' 0
-case "$bare" in
-yes)
- GIT_DIR="$D" ;;
-*)
- GIT_DIR="$D/.git" ;;
-esac && export GIT_DIR && git-init ${template+"$template"} || usage
+[ yes = "$bare" ] && unset GIT_WORK_TREE
+[ -n "$GIT_WORK_TREE" ] && [ -e "$GIT_WORK_TREE" ] &&
+die "working tree '$GIT_WORK_TREE' already exists."
+D=
+W=
+cleanup() {
+ err=$?
+ test -z "$D" && rm -rf "$dir"
+ test -z "$W" && test -n "$GIT_WORK_TREE" && rm -rf "$GIT_WORK_TREE"
+ cd ..
+ test -n "$D" && rm -rf "$D"
+ test -n "$W" && rm -rf "$W"
+ exit $err
+}
+trap cleanup 0
+mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage
+test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" &&
+W=$(cd "$GIT_WORK_TREE" && pwd) && export GIT_WORK_TREE="$W"
+if test yes = "$bare" || test -n "$GIT_WORK_TREE"; then
+ GIT_DIR="$D"
+else
+ GIT_DIR="$D/.git"
+fi &&
+export GIT_DIR &&
+git-init $quiet ${template+"$template"} || usage
if test -n "$reference"
then
@@ -222,34 +253,36 @@ fi
rm -f "$GIT_DIR/CLONE_HEAD"
# We do local magic only when the user tells us to.
-case "$local,$use_local" in
-yes,yes)
+case "$local" in
+yes)
( cd "$repo/objects" ) ||
- die "-l flag seen but repository '$repo' is not local."
-
- case "$local_shared" in
- no)
- # See if we can hardlink and drop "l" if not.
- sample_file=$(cd "$repo" && \
- find objects -type f -print | sed -e 1q)
-
- # objects directory should not be empty since we are cloning!
- test -f "$repo/$sample_file" || exit
+ die "cannot chdir to local '$repo/objects'."
- l=
- if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
- then
- l=l
- fi &&
- rm -f "$GIT_DIR/objects/sample" &&
- cd "$repo" &&
- find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1
- ;;
- yes)
- mkdir -p "$GIT_DIR/objects/info"
- echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates"
- ;;
- esac
+ if test "$local_shared" = yes
+ then
+ mkdir -p "$GIT_DIR/objects/info"
+ echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates"
+ else
+ l= &&
+ if test "$use_local_hardlink" = yes
+ then
+ # See if we can hardlink and drop "l" if not.
+ sample_file=$(cd "$repo" && \
+ find objects -type f -print | sed -e 1q)
+ # objects directory should not be empty because
+ # we are cloning!
+ test -f "$repo/$sample_file" || exit
+ if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
+ then
+ rm -f "$GIT_DIR/objects/sample"
+ l=l
+ else
+ echo >&2 "Warning: -l asked but cannot hardlink to $repo"
+ fi
+ fi &&
+ cd "$repo" &&
+ find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1
+ fi
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
;;
*)
@@ -334,11 +367,15 @@ then
*)
continue ;;
esac
- git-update-ref -m "clone: from $repo" "$destname" "$sha1" ""
+ git update-ref -m "clone: from $repo" "$destname" "$sha1" ""
done < "$GIT_DIR/CLONE_HEAD"
fi
-cd "$D" || exit
+if test -n "$W"; then
+ cd "$W" || exit
+else
+ cd "$D" || exit
+fi
if test -z "$bare" && test -f "$GIT_DIR/REMOTE_HEAD"
then
@@ -377,37 +414,42 @@ then
)
)
+ # Upstream URL
+ git config remote."$origin".url "$repo" &&
+
+ # Set up the mappings to track the remote branches.
+ git config remote."$origin".fetch \
+ "+refs/heads/*:$remote_top/*" '^$' &&
+
# Write out remote.$origin config, and update our "$head_points_at".
case "$head_points_at" in
?*)
# Local default branch
- git-symbolic-ref HEAD "refs/heads/$head_points_at" &&
+ git symbolic-ref HEAD "refs/heads/$head_points_at" &&
# Tracking branch for the primary branch at the remote.
- origin_track="$remote_top/$head_points_at" &&
- git-update-ref HEAD "$head_sha1" &&
+ git update-ref HEAD "$head_sha1" &&
- # Upstream URL
- git-config remote."$origin".url "$repo" &&
-
- # Set up the mappings to track the remote branches.
- git-config remote."$origin".fetch \
- "+refs/heads/*:$remote_top/*" '^$' &&
rm -f "refs/remotes/$origin/HEAD"
- git-symbolic-ref "refs/remotes/$origin/HEAD" \
+ git symbolic-ref "refs/remotes/$origin/HEAD" \
"refs/remotes/$origin/$head_points_at" &&
- git-config branch."$head_points_at".remote "$origin" &&
- git-config branch."$head_points_at".merge "refs/heads/$head_points_at"
+ git config branch."$head_points_at".remote "$origin" &&
+ git config branch."$head_points_at".merge "refs/heads/$head_points_at"
+ ;;
+ '')
+ # Source had detached HEAD pointing nowhere
+ git update-ref --no-deref HEAD "$head_sha1" &&
+ rm -f "refs/remotes/$origin/HEAD"
+ ;;
esac
case "$no_checkout" in
'')
test "z$quiet" = z -a "z$no_progress" = z && v=-v || v=
- git-read-tree -m -u $v HEAD HEAD
+ git read-tree -m -u $v HEAD HEAD
esac
fi
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
trap - 0
-
diff --git a/git-commit.sh b/git-commit.sh
index e8b60f7049..d7e7028c15 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -3,12 +3,12 @@
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2006 Junio C Hamano
-USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
+USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
SUBDIRECTORY_OK=Yes
. git-sh-setup
require_work_tree
-git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
+git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
case "$0" in
*status)
@@ -53,7 +53,7 @@ run_status () {
t) color= ;;
*) color=--nocolor ;;
esac
- git-runstatus ${color} \
+ git runstatus ${color} \
${verbose:+--verbose} \
${amend:+--amend} \
${untracked_files:+--untracked}
@@ -87,6 +87,7 @@ signoff=
force_author=
only_include_assumed=
untracked_files=
+templatefile="`git config commit.template`"
while case "$#" in 0) break;; esac
do
case "$1" in
@@ -189,7 +190,6 @@ $1"
;;
--a|--am|--ame|--amen|--amend)
amend=t
- log_given=t$log_given
use_commit=HEAD
shift
;;
@@ -248,6 +248,13 @@ $1"
signoff=t
shift
;;
+ -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
+ case "$#" in 1) usage ;; esac
+ shift
+ templatefile="$1"
+ no_edit=
+ shift
+ ;;
-q|--q|--qu|--qui|--quie|--quiet)
quiet=t
shift
@@ -290,9 +297,9 @@ esac
case "$log_given" in
tt*)
- die "Only one of -c/-C/-F/--amend can be used." ;;
+ die "Only one of -c/-C/-F can be used." ;;
*tm*|*mt*)
- die "Option -m cannot be combined with -c/-C/-F/--amend." ;;
+ die "Option -m cannot be combined with -c/-C/-F." ;;
esac
case "$#,$also,$only,$amend" in
@@ -321,6 +328,14 @@ t,,[1-9]*)
die "No paths with -i does not make sense." ;;
esac
+if test ! -z "$templatefile" -a -z "$log_given"
+then
+ if test ! -f "$templatefile"
+ then
+ die "Commit template file does not exist."
+ fi
+fi
+
################################################################
# Prepare index to have a tree to be committed
@@ -335,20 +350,20 @@ t,)
cd_to_toplevel &&
GIT_INDEX_FILE="$NEXT_INDEX" &&
export GIT_INDEX_FILE &&
- git-diff-files --name-only -z |
- git-update-index --remove -z --stdin
+ git diff-files --name-only -z |
+ git update-index --remove -z --stdin
) || exit
;;
,t)
save_index &&
- git-ls-files --error-unmatch -- "$@" >/dev/null || exit
+ git ls-files --error-unmatch -- "$@" >/dev/null || exit
- git-diff-files --name-only -z -- "$@" |
+ git diff-files --name-only -z -- "$@" |
(
cd_to_toplevel &&
GIT_INDEX_FILE="$NEXT_INDEX" &&
export GIT_INDEX_FILE &&
- git-update-index --remove -z --stdin
+ git update-index --remove -z --stdin
) || exit
;;
,)
@@ -364,28 +379,28 @@ t,)
refuse_partial "Cannot do a partial commit during a merge."
fi
TMP_INDEX="$GIT_DIR/tmp-index$$"
- commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
+ commit_only=`git ls-files --error-unmatch -- "$@"` || exit
# Build a temporary index and update the real index
# the same way.
if test -z "$initial_commit"
then
GIT_INDEX_FILE="$THIS_INDEX" \
- git-read-tree --index-output="$TMP_INDEX" -i -m HEAD
+ git read-tree --index-output="$TMP_INDEX" -i -m HEAD
else
rm -f "$TMP_INDEX"
fi || exit
printf '%s\n' "$commit_only" |
GIT_INDEX_FILE="$TMP_INDEX" \
- git-update-index --add --remove --stdin &&
+ git update-index --add --remove --stdin &&
save_index &&
printf '%s\n' "$commit_only" |
(
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
- git-update-index --remove --stdin
+ git update-index --remove --stdin
) || exit
;;
esac
@@ -408,12 +423,12 @@ case "$status_only" in
t)
# This will silently fail in a read-only repository, which is
# what we want.
- GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --unmerged --refresh
+ GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
run_status
exit $?
;;
'')
- GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --refresh || exit
+ GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
;;
esac
@@ -454,20 +469,25 @@ then
elif test -f "$GIT_DIR/SQUASH_MSG"
then
cat "$GIT_DIR/SQUASH_MSG"
-fi | git-stripspace >"$GIT_DIR"/COMMIT_EDITMSG
+elif test "$templatefile" != ""
+then
+ cat "$templatefile"
+fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
case "$signoff" in
t)
- need_blank_before_signoff=
+ sign=$(git-var GIT_COMMITTER_IDENT | sed -e '
+ s/>.*/>/
+ s/^/Signed-off-by: /
+ ')
+ blank_before_signoff=
tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
- grep 'Signed-off-by:' >/dev/null || need_blank_before_signoff=yes
- {
- test -z "$need_blank_before_signoff" || echo
- git-var GIT_COMMITTER_IDENT | sed -e '
- s/>.*/>/
- s/^/Signed-off-by: /
- '
- } >>"$GIT_DIR"/COMMIT_EDITMSG
+ grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
+'
+ tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
+ grep "$sign"$ >/dev/null ||
+ printf '%s%s\n' "$blank_before_signoff" "$sign" \
+ >>"$GIT_DIR"/COMMIT_EDITMSG
;;
esac
@@ -483,34 +503,8 @@ fi >>"$GIT_DIR"/COMMIT_EDITMSG
# Author
if test '' != "$use_commit"
then
- pick_author_script='
- /^author /{
- s/'\''/'\''\\'\'\''/g
- h
- s/^author \([^<]*\) <[^>]*> .*$/\1/
- s/'\''/'\''\'\'\''/g
- s/.*/GIT_AUTHOR_NAME='\''&'\''/p
-
- g
- s/^author [^<]* <\([^>]*\)> .*$/\1/
- s/'\''/'\''\'\'\''/g
- s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
-
- g
- s/^author [^<]* <[^>]*> \(.*\)$/\1/
- s/'\''/'\''\'\'\''/g
- s/.*/GIT_AUTHOR_DATE='\''&'\''/p
-
- q
- }
- '
- encoding=$(git config i18n.commitencoding || echo UTF-8)
- set_author_env=`git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
- LANG=C LC_ALL=C sed -ne "$pick_author_script"`
- eval "$set_author_env"
- export GIT_AUTHOR_NAME
- export GIT_AUTHOR_EMAIL
- export GIT_AUTHOR_DATE
+ eval "$(get_author_ident_from_commit "$use_commit")"
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
fi
if test '' != "$force_author"
then
@@ -531,12 +525,12 @@ then
PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
elif test -n "$amend"; then
rloga='commit (amend)'
- PARENTS=$(git-cat-file commit HEAD |
+ PARENTS=$(git cat-file commit HEAD |
sed -n -e '/^$/q' -e 's/^parent /-p /p')
fi
- current="$(git-rev-parse --verify HEAD)"
+ current="$(git rev-parse --verify HEAD)"
else
- if [ -z "$(git-ls-files)" ]; then
+ if [ -z "$(git ls-files)" ]; then
echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
exit 1
fi
@@ -557,7 +551,7 @@ then
} >>"$GIT_DIR"/COMMIT_EDITMSG
else
# we need to check if there is anything to commit
- run_status >/dev/null
+ run_status >/dev/null
fi
if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" -a -z "$amend" ]
then
@@ -568,18 +562,9 @@ fi
case "$no_edit" in
'')
- case "${VISUAL:-$EDITOR},$TERM" in
- ,dumb)
- echo >&2 "Terminal is dumb but no VISUAL nor EDITOR defined."
- echo >&2 "Please supply the commit log message using either"
- echo >&2 "-m or -F option. A boilerplate log message has"
- echo >&2 "been prepared in $GIT_DIR/COMMIT_EDITMSG"
- exit 1
- ;;
- esac
git-var GIT_AUTHOR_IDENT > /dev/null || die
git-var GIT_COMMITTER_IDENT > /dev/null || die
- ${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
+ git_editor "$GIT_DIR/COMMIT_EDITMSG"
;;
esac
@@ -603,23 +588,48 @@ then
else
cat "$GIT_DIR"/COMMIT_EDITMSG
fi |
-git-stripspace >"$GIT_DIR"/COMMIT_MSG
+git stripspace >"$GIT_DIR"/COMMIT_MSG
+
+# Test whether the commit message has any content we didn't supply.
+have_commitmsg=
+grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
+ git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
+
+# Is the commit message totally empty?
+if test -s "$GIT_DIR"/COMMIT_BAREMSG
+then
+ if test "$templatefile" != ""
+ then
+ # Test whether this is just the unaltered template.
+ if cnt=`sed -e '/^#/d' < "$templatefile" |
+ git stripspace |
+ diff "$GIT_DIR"/COMMIT_BAREMSG - |
+ wc -l` &&
+ test 0 -lt $cnt
+ then
+ have_commitmsg=t
+ fi
+ else
+ # No template, so the content in the commit message must
+ # have come from the user.
+ have_commitmsg=t
+ fi
+fi
+
+rm -f "$GIT_DIR"/COMMIT_BAREMSG
-if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
- git-stripspace |
- wc -l` &&
- test 0 -lt $cnt
+if test "$have_commitmsg" = "t"
then
if test -z "$TMP_INDEX"
then
- tree=$(GIT_INDEX_FILE="$USE_INDEX" git-write-tree)
+ tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
else
- tree=$(GIT_INDEX_FILE="$TMP_INDEX" git-write-tree) &&
+ tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
rm -f "$TMP_INDEX"
fi &&
- commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
+ commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
- git-update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
+ git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
if test -f "$NEXT_INDEX"
then
@@ -636,10 +646,7 @@ rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
cd_to_toplevel
-if test -d "$GIT_DIR/rr-cache"
-then
- git-rerere
-fi
+git rerere
if test "$ret" = 0
then
@@ -649,7 +656,7 @@ then
fi
if test -z "$quiet"
then
- commit=`git-diff-tree --always --shortstat --pretty="format:%h: %s"\
+ commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
--summary --root HEAD --`
echo "Created${initial_commit:+ initial} commit $commit"
fi
diff --git a/git-compat-util.h b/git-compat-util.h
index b2ab3f8256..ca0a597a28 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -287,6 +287,32 @@ static inline ssize_t xwrite(int fd, const void *buf, size_t len)
}
}
+static inline int xdup(int fd)
+{
+ int ret = dup(fd);
+ if (ret < 0)
+ die("dup failed: %s", strerror(errno));
+ return ret;
+}
+
+static inline FILE *xfdopen(int fd, const char *mode)
+{
+ FILE *stream = fdopen(fd, mode);
+ if (stream == NULL)
+ die("Out of memory? fdopen failed: %s", strerror(errno));
+ return stream;
+}
+
+static inline int xmkstemp(char *template)
+{
+ int fd;
+
+ fd = mkstemp(template);
+ if (fd < 0)
+ die("Unable to create temporary file: %s", strerror(errno));
+ return fd;
+}
+
static inline size_t xsize_t(off_t len)
{
return (size_t)len;
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index d6ae99b8c0..a33fa8d4c8 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -15,9 +15,9 @@ unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
die "GIT_DIR is not defined or is unreadable";
}
-our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d);
+our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u);
-getopts('hPpvcfam:d:');
+getopts('uhPpvcfam:d:');
$opt_h && usage();
@@ -178,6 +178,10 @@ foreach my $f (@files) {
my %cvsstat;
if (@canstatusfiles) {
+ if ($opt_u) {
+ my @updated = safe_pipe_capture(@cvs, 'update', @canstatusfiles);
+ print @updated;
+ }
my @cvsoutput;
@cvsoutput= safe_pipe_capture(@cvs, 'status', @canstatusfiles);
my $matchcount = 0;
@@ -193,7 +197,7 @@ if (@canstatusfiles) {
# ... validate new files,
foreach my $f (@afiles) {
if (defined ($cvsstat{$f}) and $cvsstat{$f} ne "Unknown") {
- $dirty = 1;
+ $dirty = 1;
warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
warn "Status was: $cvsstat{$f}\n";
}
@@ -277,6 +281,11 @@ if ($opt_c) {
# clean up
unlink(".cvsexportcommit.diff");
+# CVS version 1.11.x and 1.12.x sleeps the wrong way to ensure the timestamp
+# used by CVS and the one set by subsequence file modifications are different.
+# If they are not different CVS will not detect changes.
+sleep(1);
+
sub usage {
print STDERR <<END;
Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 4e6c9c6cc7..ba23eb8eeb 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -29,7 +29,7 @@ use IPC::Open2;
$SIG{'PIPE'}="IGNORE";
$ENV{'TZ'}="UTC";
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r);
my (%conv_author_name, %conv_author_email);
sub usage(;$) {
@@ -40,7 +40,7 @@ Usage: ${\basename $0} # fetch/update GIT from CVS
[-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
[-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
[-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
- [CVS_module]
+ [-r remote] [CVS_module]
END
exit(1);
}
@@ -114,7 +114,7 @@ sub read_repo_config {
}
}
-my $opts = "haivmkuo:d:p:C:z:s:M:P:A:S:L:";
+my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
read_repo_config($opts);
getopts($opts) or usage();
usage if $opt_h;
@@ -134,18 +134,26 @@ if ($opt_d) {
} else {
usage("CVSROOT needs to be set");
}
-$opt_o ||= "origin";
$opt_s ||= "-";
$opt_a ||= 0;
my $git_tree = $opt_C;
$git_tree ||= ".";
+my $remote;
+if (defined $opt_r) {
+ $remote = 'refs/remotes/' . $opt_r;
+ $opt_o ||= "master";
+} else {
+ $opt_o ||= "origin";
+ $remote = 'refs/heads';
+}
+
my $cvs_tree;
if ($#ARGV == 0) {
$cvs_tree = $ARGV[0];
} elsif (-f 'CVS/Repository') {
- open my $f, '<', 'CVS/Repository' or
+ open my $f, '<', 'CVS/Repository' or
die 'Failed to open CVS/Repository';
$cvs_tree = <$f>;
chomp $cvs_tree;
@@ -434,7 +442,7 @@ sub file {
my ($self,$fn,$rev) = @_;
my $res;
- my ($fh, $name) = tempfile('gitcvs.XXXXXX',
+ my ($fh, $name) = tempfile('gitcvs.XXXXXX',
DIR => File::Spec->tmpdir(), UNLINK => 1);
$self->_file($fn,$rev) and $res = $self->_line($fh);
@@ -520,9 +528,9 @@ sub is_sha1 {
sub get_headref ($$) {
my $name = shift;
- my $git_dir = shift;
-
- my $f = "$git_dir/refs/heads/$name";
+ my $git_dir = shift;
+
+ my $f = "$git_dir/$remote/$name";
if (open(my $fh, $f)) {
chomp(my $r = <$fh>);
is_sha1($r) or die "Cannot get head id for $name ($r): $!";
@@ -573,12 +581,12 @@ unless (-d $git_dir) {
# Get the last import timestamps
my $fmt = '($ref, $author) = (%(refname), %(author));';
- open(H, "git-for-each-ref --perl --format='$fmt' refs/heads |") or
+ open(H, "git-for-each-ref --perl --format='$fmt' $remote |") or
die "Cannot run git-for-each-ref: $!\n";
while (defined(my $entry = <H>)) {
my ($ref, $author);
eval($entry) || die "cannot eval refs list: $@";
- my ($head) = ($ref =~ m|^refs/heads/(.*)|);
+ my ($head) = ($ref =~ m|^$remote/(.*)|);
$author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
$branch_date{$head} = $1;
}
@@ -701,9 +709,9 @@ sub commit {
$index{$branch} = tmpnam();
$ENV{GIT_INDEX_FILE} = $index{$branch};
if ($ancestor) {
- system("git-read-tree", $ancestor);
+ system("git-read-tree", "$remote/$ancestor");
} else {
- system("git-read-tree", $branch);
+ system("git-read-tree", "$remote/$branch");
}
die "read-tree failed: $?\n" if $?;
}
@@ -762,40 +770,17 @@ sub commit {
waitpid($pid,0);
die "Error running git-commit-tree: $?\n" if $?;
- system("git-update-ref refs/heads/$branch $cid") == 0
+ system("git-update-ref $remote/$branch $cid") == 0
or die "Cannot write branch $branch for update: $!\n";
if ($tag) {
- my ($in, $out) = ('','');
my ($xtag) = $tag;
$xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
$xtag =~ tr/_/\./ if ( $opt_u );
$xtag =~ s/[\/]/$opt_s/g;
-
- my $pid = open2($in, $out, 'git-mktag');
- print $out "object $cid\n".
- "type commit\n".
- "tag $xtag\n".
- "tagger $author_name <$author_email>\n"
- or die "Cannot create tag object $xtag: $!\n";
- close($out)
- or die "Cannot create tag object $xtag: $!\n";
-
- my $tagobj = <$in>;
- chomp $tagobj;
-
- if ( !close($in) or waitpid($pid, 0) != $pid or
- $? != 0 or $tagobj !~ /^[0123456789abcdef]{40}$/ ) {
- die "Cannot create tag object $xtag: $!\n";
- }
-
-
- open(C,">$git_dir/refs/tags/$xtag")
+
+ system('git-tag', $xtag, $cid) == 0
or die "Cannot create tag $xtag: $!\n";
- print C "$tagobj\n"
- or die "Cannot write tag $xtag: $!\n";
- close(C)
- or die "Cannot write tag $xtag: $!\n";
print "Created tag '$xtag' on '$branch'\n" if $opt_v;
}
@@ -883,12 +868,12 @@ while (<CVS>) {
print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
$ancestor = $opt_o;
}
- if (-f "$git_dir/refs/heads/$branch") {
+ if (-f "$git_dir/$remote/$branch") {
print STDERR "Branch $branch already exists!\n";
$state=11;
next;
}
- unless (open(H,"$git_dir/refs/heads/$ancestor")) {
+ unless (open(H,"$git_dir/$remote/$ancestor")) {
print STDERR "Branch $ancestor does not exist!\n";
$ignorebranch{$branch} = 1;
$state=11;
@@ -896,7 +881,7 @@ while (<CVS>) {
}
chomp(my $id = <H>);
close(H);
- unless (open(H,"> $git_dir/refs/heads/$branch")) {
+ unless (open(H,"> $git_dir/$remote/$branch")) {
print STDERR "Could not create branch $branch: $!\n";
$ignorebranch{$branch} = 1;
$state=11;
@@ -1010,17 +995,19 @@ if ($orig_branch) {
die "Fast-forward update failed: $?\n" if $?;
}
else {
- system(qw(git-merge cvsimport HEAD), "refs/heads/$opt_o");
+ system(qw(git-merge cvsimport HEAD), "$remote/$opt_o");
die "Could not merge $opt_o into the current branch.\n" if $?;
}
} else {
$orig_branch = "master";
print "DONE; creating $orig_branch branch\n" if $opt_v;
- system("git-update-ref", "refs/heads/master", "refs/heads/$opt_o")
+ system("git-update-ref", "refs/heads/master", "$remote/$opt_o")
unless -f "$git_dir/refs/heads/master";
+ system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
+ if ($opt_r && $opt_o ne 'HEAD');
system('git-update-ref', 'HEAD', "$orig_branch");
unless ($opt_i) {
- system('git checkout');
+ system('git checkout -f');
die "checkout failed: $?\n" if $?;
}
}
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 1de517791f..13dbd27a80 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -22,6 +22,9 @@ use bytes;
use Fcntl;
use File::Temp qw/tempdir tempfile/;
use File::Basename;
+use Getopt::Long qw(:config require_order no_ignore_case);
+
+my $VERSION = '@@GIT_VERSION@@';
my $log = GITCVS::log->new();
my $cfg;
@@ -85,19 +88,62 @@ my $methods = {
my $state = { prependdir => '' };
$log->info("--------------- STARTING -----------------");
+my $usage =
+ "Usage: git-cvsserver [options] [pserver|server] [<directory> ...]\n".
+ " --base-path <path> : Prepend to requested CVSROOT\n".
+ " --strict-paths : Don't allow recursing into subdirectories\n".
+ " --export-all : Don't check for gitcvs.enabled in config\n".
+ " --version, -V : Print version information and exit\n".
+ " --help, -h, -H : Print usage information and exit\n".
+ "\n".
+ "<directory> ... is a list of allowed directories. If no directories\n".
+ "are given, all are allowed. This is an additional restriction, gitcvs\n".
+ "access still needs to be enabled by the gitcvs.enabled config option.\n";
+
+my @opts = ( 'help|h|H', 'version|V',
+ 'base-path=s', 'strict-paths', 'export-all' );
+GetOptions( $state, @opts )
+ or die $usage;
+
+if ($state->{version}) {
+ print "git-cvsserver version $VERSION\n";
+ exit;
+}
+if ($state->{help}) {
+ print $usage;
+ exit;
+}
+
my $TEMP_DIR = tempdir( CLEANUP => 1 );
$log->debug("Temporary directory is '$TEMP_DIR'");
+$state->{method} = 'ext';
+if (@ARGV) {
+ if ($ARGV[0] eq 'pserver') {
+ $state->{method} = 'pserver';
+ shift @ARGV;
+ } elsif ($ARGV[0] eq 'server') {
+ shift @ARGV;
+ }
+}
+
+# everything else is a directory
+$state->{allowed_roots} = [ @ARGV ];
+
+# don't export the whole system unless the users requests it
+if ($state->{'export-all'} && !@{$state->{allowed_roots}}) {
+ die "--export-all can only be used together with an explicit whitelist\n";
+}
+
# if we are called with a pserver argument,
# deal with the authentication cat before entering the
# main loop
-$state->{method} = 'ext';
-if (@ARGV && $ARGV[0] eq 'pserver') {
- $state->{method} = 'pserver';
+if ($state->{method} eq 'pserver') {
my $line = <STDIN>; chomp $line;
- unless( $line eq 'BEGIN AUTH REQUEST') {
+ unless( $line =~ /^BEGIN (AUTH|VERIFICATION) REQUEST$/) {
die "E Do not understand $line - expecting BEGIN AUTH REQUEST\n";
}
+ my $request = $1;
$line = <STDIN>; chomp $line;
req_Root('root', $line) # reuse Root
or die "E Invalid root $line \n";
@@ -109,10 +155,11 @@ if (@ARGV && $ARGV[0] eq 'pserver') {
}
$line = <STDIN>; chomp $line; # validate the password?
$line = <STDIN>; chomp $line;
- unless ($line eq 'END AUTH REQUEST') {
- die "E Do not understand $line -- expecting END AUTH REQUEST\n";
+ unless ($line eq "END $request REQUEST") {
+ die "E Do not understand $line -- expecting END $request REQUEST\n";
}
print "I LOVE YOU\n";
+ exit if $request eq 'VERIFICATION'; # cvs login
# and now back to our regular programme...
}
@@ -165,13 +212,53 @@ sub req_Root
my ( $cmd, $data ) = @_;
$log->debug("req_Root : $data");
- $state->{CVSROOT} = $data;
+ unless ($data =~ m#^/#) {
+ print "error 1 Root must be an absolute pathname\n";
+ return 0;
+ }
+
+ my $cvsroot = $state->{'base-path'} || '';
+ $cvsroot =~ s#/+$##;
+ $cvsroot .= $data;
+
+ if ($state->{CVSROOT}
+ && ($state->{CVSROOT} ne $cvsroot)) {
+ print "error 1 Conflicting roots specified\n";
+ return 0;
+ }
+
+ $state->{CVSROOT} = $cvsroot;
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
+
+ if (@{$state->{allowed_roots}}) {
+ my $allowed = 0;
+ foreach my $dir (@{$state->{allowed_roots}}) {
+ next unless $dir =~ m#^/#;
+ $dir =~ s#/+$##;
+ if ($state->{'strict-paths'}) {
+ if ($ENV{GIT_DIR} =~ m#^\Q$dir\E/?$#) {
+ $allowed = 1;
+ last;
+ }
+ } elsif ($ENV{GIT_DIR} =~ m#^\Q$dir\E(/?$|/)#) {
+ $allowed = 1;
+ last;
+ }
+ }
+
+ unless ($allowed) {
+ print "E $ENV{GIT_DIR} does not seem to be a valid GIT repository\n";
+ print "E \n";
+ print "error 1 $ENV{GIT_DIR} is not a valid repository\n";
+ return 0;
+ }
+ }
+
unless (-d $ENV{GIT_DIR} && -e $ENV{GIT_DIR}.'HEAD') {
print "E $ENV{GIT_DIR} does not seem to be a valid GIT repository\n";
- print "E \n";
- print "error 1 $ENV{GIT_DIR} is not a valid repository\n";
+ print "E \n";
+ print "error 1 $ENV{GIT_DIR} is not a valid repository\n";
return 0;
}
@@ -194,7 +281,8 @@ sub req_Root
my $enabled = ($cfg->{gitcvs}{$state->{method}}{enabled}
|| $cfg->{gitcvs}{enabled});
- unless ($enabled && $enabled =~ /^\s*(1|true|yes)\s*$/i) {
+ unless ($state->{'export-all'} ||
+ ($enabled && $enabled =~ /^\s*(1|true|yes)\s*$/i)) {
print "E GITCVS emulation needs to be enabled on this repo\n";
print "E the repo config file needs a [gitcvs] section added, and the parameter 'enabled' set to 1\n";
print "E \n";
@@ -535,8 +623,12 @@ sub req_Modified
my ( $cmd, $data ) = @_;
my $mode = <STDIN>;
+ defined $mode
+ or (print "E end of file reading mode for $data\n"), return;
chomp $mode;
my $size = <STDIN>;
+ defined $size
+ or (print "E end of file reading size of $data\n"), return;
chomp $size;
# Grab config information
@@ -556,7 +648,8 @@ sub req_Modified
$bytesleft -= $blocksize;
}
- close $fh;
+ close $fh
+ or (print "E failed to write temporary, $filename: $!\n"), return;
# Ensure we have something sensible for the file mode
if ( $mode =~ /u=(\w+)/ )
@@ -813,8 +906,13 @@ sub req_update
# projects (heads in this case) to checkout.
#
if ($state->{module} eq '') {
+ my $heads_dir = $state->{CVSROOT} . '/refs/heads';
+ if (!opendir HEADS, $heads_dir) {
+ print "E [server aborted]: Failed to open directory, "
+ . "$heads_dir: $!\nerror\n";
+ return 0;
+ }
print "E cvs update: Updating .\n";
- opendir HEADS, $state->{CVSROOT} . '/refs/heads';
while (my $head = readdir(HEADS)) {
if (-f $state->{CVSROOT} . '/refs/heads/' . $head) {
print "E cvs update: New directory `$head'\n";
@@ -1098,6 +1196,7 @@ sub req_ci
$log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'");
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
+ $ENV{GIT_WORK_TREE} = ".";
$ENV{GIT_INDEX_FILE} = $file_index;
# Remember where the head was at the beginning.
@@ -1623,6 +1722,7 @@ sub req_annotate
$log->info("Temp checkoutdir creation successful, basing annotate session work on '$tmpdir', index file is '$file_index'");
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
+ $ENV{GIT_WORK_TREE} = ".";
$ENV{GIT_INDEX_FILE} = $file_index;
chdir $tmpdir;
@@ -1649,14 +1749,16 @@ sub req_annotate
system("git-read-tree", $lastseenin);
unless ($? == 0)
{
- die "Error running git-read-tree $lastseenin $file_index $!";
+ print "E error running git-read-tree $lastseenin $file_index $!\n";
+ return;
}
$log->info("Created index '$file_index' with commit $lastseenin - exit status $?");
# do a checkout of the file
system('git-checkout-index', '-f', '-u', $filename);
unless ($? == 0) {
- die "Error running git-checkout-index -f -u $filename : $!";
+ print "E error running git-checkout-index -f -u $filename : $!\n";
+ return;
}
$log->info("Annotate $filename");
@@ -1666,7 +1768,11 @@ sub req_annotate
# git-jsannotate telling us about commits we are hiding
# from the client.
- open(ANNOTATEHINTS, ">$tmpdir/.annotate_hints") or die "Error opening > $tmpdir/.annotate_hints $!";
+ my $a_hints = "$tmpdir/.annotate_hints";
+ if (!open(ANNOTATEHINTS, '>', $a_hints)) {
+ print "E failed to open '$a_hints' for writing: $!\n";
+ return;
+ }
for (my $i=0; $i < @$revisions; $i++)
{
print ANNOTATEHINTS $revisions->[$i][2];
@@ -1677,11 +1783,14 @@ sub req_annotate
}
print ANNOTATEHINTS "\n";
- close ANNOTATEHINTS;
+ close ANNOTATEHINTS
+ or (print "E failed to write $a_hints: $!\n"), return;
- my $annotatecmd = 'git-annotate';
- open(ANNOTATE, "-|", $annotatecmd, '-l', '-S', "$tmpdir/.annotate_hints", $filename)
- or die "Error invoking $annotatecmd -l -S $tmpdir/.annotate_hints $filename : $!";
+ my @cmd = (qw(git-annotate -l -S), $a_hints, $filename);
+ if (!open(ANNOTATE, "-|", @cmd)) {
+ print "E error invoking ". join(' ',@cmd) .": $!\n";
+ return;
+ }
my $metadata = {};
print "E Annotations for $filename\n";
print "E ***************\n";
@@ -1725,14 +1834,14 @@ sub req_annotate
# the second is $state->{files} which is everything after it.
sub argsplit
{
- return unless( defined($state->{arguments}) and ref $state->{arguments} eq "ARRAY" );
-
- my $type = shift;
-
$state->{args} = [];
$state->{files} = [];
$state->{opt} = {};
+ return unless( defined($state->{arguments}) and ref $state->{arguments} eq "ARRAY" );
+
+ my $type = shift;
+
if ( defined($type) )
{
my $opt = {};
@@ -1908,12 +2017,12 @@ sub transmitfile
{
open NEWFILE, ">", $targetfile or die("Couldn't open '$targetfile' for writing : $!");
print NEWFILE $_ while ( <$fh> );
- close NEWFILE;
+ close NEWFILE or die("Failed to write '$targetfile': $!");
} else {
print "$size\n";
print while ( <$fh> );
}
- close $fh or die ("Couldn't close filehandle for transmitfile()");
+ close $fh or die ("Couldn't close filehandle for transmitfile(): $!");
} else {
die("Couldn't execute git-cat-file");
}
@@ -2413,17 +2522,14 @@ sub update
if ($parent eq $lastpicked) {
next;
}
- open my $p, 'git-merge-base '. $lastpicked . ' '
- . $parent . '|';
- my @output = (<$p>);
- close $p;
- my $base = join('', @output);
+ my $base = safe_pipe_capture('git-merge-base',
+ $lastpicked, $parent);
chomp $base;
if ($base) {
my @merged;
# print "want to log between $base $parent \n";
open(GITLOG, '-|', 'git-log', "$base..$parent")
- or die "Cannot call git-log: $!";
+ or die "Cannot call git-log: $!";
my $mergedhash;
while (<GITLOG>) {
chomp;
diff --git a/git-fetch.sh b/git-fetch.sh
index 0e05cf1195..c3a200120d 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -61,7 +61,7 @@ do
quiet=--quiet
;;
-v|--verbose)
- verbose=Yes
+ verbose="$verbose"Yes
;;
-k|--k|--ke|--kee|--keep)
keep='-k -k'
@@ -117,20 +117,20 @@ append_fetch_head () {
test -n "$verbose" && flags="$flags$LF-v"
test -n "$force$single_force" && flags="$flags$LF-f"
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
- git-fetch--tool $flags append-fetch-head "$@"
+ git fetch--tool $flags append-fetch-head "$@"
}
# updating the current HEAD with git-fetch in a bare
# repository is always fine.
if test -z "$update_head_ok" && test $(is_bare_repository) = false
then
- orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
+ orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
fi
# Allow --notags from remote.$1.tagopt
case "$tags$no_tags" in
'')
- case "$(git-config --get "remote.$1.tagopt")" in
+ case "$(git config --get "remote.$1.tagopt")" in
--no-tags)
no_tags=t ;;
esac
@@ -146,7 +146,7 @@ if test "$tags"
then
taglist=`IFS=' ' &&
echo "$ls_remote_result" |
- git-show-ref --exclude-existing=refs/tags/ |
+ git show-ref --exclude-existing=refs/tags/ |
while read sha1 name
do
echo ".${name}:${name}"
@@ -163,18 +163,18 @@ fi
fetch_all_at_once () {
- eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
+ eval=$(echo "$1" | git fetch--tool parse-reflist "-")
eval "$eval"
( : subshell because we muck with IFS
IFS=" $LF"
(
if test "$remote" = . ; then
- git-show-ref $rref || echo failed "$remote"
+ git show-ref $rref || echo failed "$remote"
elif test -f "$remote" ; then
test -n "$shallow_depth" &&
die "shallow clone with bundle is not supported"
- git-bundle unbundle "$remote" $rref ||
+ git bundle unbundle "$remote" $rref ||
echo failed "$remote"
else
if test -d "$remote" &&
@@ -190,19 +190,25 @@ fetch_all_at_once () {
# connected to our repository's tips, in which
# case we do not have to do any fetch.
theirs=$(echo "$ls_remote_result" | \
- git-fetch--tool -s pick-rref "$rref" "-") &&
+ git fetch--tool -s pick-rref "$rref" "-") &&
# This will barf when $theirs reach an object that
# we do not have in our repository. Otherwise,
# we already have everything the fetch would bring in.
- git-rev-list --objects $theirs --not --all \
+ git rev-list --objects $theirs --not --all \
>/dev/null 2>/dev/null
then
echo "$ls_remote_result" | \
- git-fetch--tool pick-rref "$rref" "-"
+ git fetch--tool pick-rref "$rref" "-"
else
+ flags=
+ case $verbose in
+ YesYes*)
+ flags="-v"
+ ;;
+ esac
git-fetch-pack --thin $exec $keep $shallow_depth \
- $quiet $no_progress "$remote" $rref ||
+ $quiet $no_progress $flags "$remote" $rref ||
echo failed "$remote"
fi
fi
@@ -212,7 +218,7 @@ fetch_all_at_once () {
test -n "$verbose" && flags="$flags -v"
test -n "$force" && flags="$flags -f"
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
- git-fetch--tool $flags native-store \
+ git fetch--tool $flags native-store \
"$remote" "$remote_nick" "$refs"
)
) || exit
@@ -259,13 +265,13 @@ fetch_per_ref () {
curl_extra_args="-k"
fi
if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
- "`git-config --bool http.noEPSV`" = true ]; then
+ "`git config --bool http.noEPSV`" = true ]; then
noepsv_opt="--disable-epsv"
fi
# Find $remote_name from ls-remote output.
head=$(echo "$ls_remote_result" | \
- git-fetch--tool -s pick-rref "$remote_name" "-")
+ git fetch--tool -s pick-rref "$remote_name" "-")
expr "z$head" : "z$_x40\$" >/dev/null ||
die "No such ref $remote_name at $remote"
echo >&2 "Fetching $remote_name from $remote using $proto"
@@ -277,7 +283,7 @@ fetch_per_ref () {
die "shallow clone with rsync not supported"
TMP_HEAD="$GIT_DIR/TMP_HEAD"
rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
- head=$(git-rev-parse --verify TMP_HEAD)
+ head=$(git rev-parse --verify TMP_HEAD)
rm -f "$TMP_HEAD"
case "$quiet" in '') v=-v ;; *) v= ;; esac
test "$rsync_slurped_objects" || {
@@ -336,10 +342,10 @@ case "$no_tags$tags" in
# using local tracking branch.
taglist=$(IFS=' ' &&
echo "$ls_remote_result" |
- git-show-ref --exclude-existing=refs/tags/ |
+ git show-ref --exclude-existing=refs/tags/ |
while read sha1 name
do
- git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
+ git cat-file -t "$sha1" >/dev/null 2>&1 || continue
echo >&2 "Auto-following $name"
echo ".${name}:${name}"
done)
@@ -359,10 +365,10 @@ case "$orig_head" in
'')
;;
?*)
- curr_head=$(git-rev-parse --verify HEAD 2>/dev/null)
+ curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
if test "$curr_head" != "$orig_head"
then
- git-update-ref \
+ git update-ref \
-m "$GIT_REFLOG_ACTION: Undoing incorrectly fetched HEAD." \
HEAD "$orig_head"
die "Cannot fetch into the current branch."
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
new file mode 100755
index 0000000000..c42e4512cf
--- /dev/null
+++ b/git-filter-branch.sh
@@ -0,0 +1,390 @@
+#!/bin/sh
+#
+# Rewrite revision history
+# Copyright (c) Petr Baudis, 2006
+# Minimal changes to "port" it to core-git (c) Johannes Schindelin, 2007
+#
+# Lets you rewrite the revision history of the current branch, creating
+# a new branch. You can specify a number of filters to modify the commits,
+# files and trees.
+
+USAGE="git-filter-branch [-d TEMPDIR] [FILTERS] DESTBRANCH [REV-RANGE]"
+. git-sh-setup
+
+warn () {
+ echo "$*" >&2
+}
+
+map()
+{
+ # if it was not rewritten, take the original
+ if test -r "$workdir/../map/$1"
+ then
+ cat "$workdir/../map/$1"
+ else
+ echo "$1"
+ fi
+}
+
+# override die(): this version puts in an extra line break, so that
+# the progress is still visible
+
+die()
+{
+ echo >&2
+ echo "$*" >&2
+ exit 1
+}
+
+# When piped a commit, output a script to set the ident of either
+# "author" or "committer
+
+set_ident () {
+ lid="$(echo "$1" | tr "A-Z" "a-z")"
+ uid="$(echo "$1" | tr "a-z" "A-Z")"
+ pick_id_script='
+ /^'$lid' /{
+ s/'\''/'\''\\'\'\''/g
+ h
+ s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
+ s/'\''/'\''\'\'\''/g
+ s/.*/export GIT_'$uid'_NAME='\''&'\''/p
+
+ g
+ s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
+ s/'\''/'\''\'\'\''/g
+ s/.*/export GIT_'$uid'_EMAIL='\''&'\''/p
+
+ g
+ s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
+ s/'\''/'\''\'\'\''/g
+ s/.*/export GIT_'$uid'_DATE='\''&'\''/p
+
+ q
+ }
+ '
+
+ LANG=C LC_ALL=C sed -ne "$pick_id_script"
+ # Ensure non-empty id name.
+ echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\""
+}
+
+tempdir=.git-rewrite
+filter_env=
+filter_tree=
+filter_index=
+filter_parent=
+filter_msg=cat
+filter_commit='git commit-tree "$@"'
+filter_tag_name=
+filter_subdir=
+orig_namespace=refs/original/
+force=
+while case "$#" in 0) usage;; esac
+do
+ case "$1" in
+ --)
+ shift
+ break
+ ;;
+ --force|-f)
+ shift
+ force=t
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ break;
+ esac
+
+ # all switches take one argument
+ ARG="$1"
+ case "$#" in 1) usage ;; esac
+ shift
+ OPTARG="$1"
+ shift
+
+ case "$ARG" in
+ -d)
+ tempdir="$OPTARG"
+ ;;
+ --env-filter)
+ filter_env="$OPTARG"
+ ;;
+ --tree-filter)
+ filter_tree="$OPTARG"
+ ;;
+ --index-filter)
+ filter_index="$OPTARG"
+ ;;
+ --parent-filter)
+ filter_parent="$OPTARG"
+ ;;
+ --msg-filter)
+ filter_msg="$OPTARG"
+ ;;
+ --commit-filter)
+ filter_commit="$OPTARG"
+ ;;
+ --tag-name-filter)
+ filter_tag_name="$OPTARG"
+ ;;
+ --subdirectory-filter)
+ filter_subdir="$OPTARG"
+ ;;
+ --original)
+ orig_namespace="$OPTARG"
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+
+case "$force" in
+t)
+ rm -rf "$tempdir"
+;;
+'')
+ test -d "$tempdir" &&
+ die "$tempdir already exists, please remove it"
+esac
+mkdir -p "$tempdir/t" &&
+tempdir="$(cd "$tempdir"; pwd)" &&
+cd "$tempdir/t" &&
+workdir="$(pwd)" ||
+die ""
+
+# Make sure refs/original is empty
+git for-each-ref > "$tempdir"/backup-refs
+while read sha1 type name
+do
+ case "$force,$name" in
+ ,$orig_namespace*)
+ die "Namespace $orig_namespace not empty"
+ ;;
+ t,$orig_namespace*)
+ git update-ref -d "$name" $sha1
+ ;;
+ esac
+done < "$tempdir"/backup-refs
+
+export GIT_DIR GIT_WORK_TREE=.
+
+# These refs should be updated if their heads were rewritten
+
+git rev-parse --revs-only --symbolic "$@" |
+while read ref
+do
+ # normalize ref
+ case "$ref" in
+ HEAD)
+ ref="$(git symbolic-ref "$ref")"
+ ;;
+ refs/*)
+ ;;
+ *)
+ ref="$(git for-each-ref --format='%(refname)' |
+ grep /"$ref")"
+ esac
+
+ git check-ref-format "$ref" && echo "$ref"
+done > "$tempdir"/heads
+
+test -s "$tempdir"/heads ||
+ die "Which ref do you want to rewrite?"
+
+export GIT_INDEX_FILE="$(pwd)/../index"
+git read-tree || die "Could not seed the index"
+
+ret=0
+
+# map old->new commit ids for rewriting parents
+mkdir ../map || die "Could not create map/ directory"
+
+case "$filter_subdir" in
+"")
+ git rev-list --reverse --topo-order --default HEAD \
+ --parents "$@"
+ ;;
+*)
+ git rev-list --reverse --topo-order --default HEAD \
+ --parents --full-history "$@" -- "$filter_subdir"
+esac > ../revs || die "Could not get the commits"
+commits=$(wc -l <../revs | tr -d " ")
+
+test $commits -eq 0 && die "Found nothing to rewrite"
+
+# Rewrite the commits
+
+i=0
+while read commit parents; do
+ i=$(($i+1))
+ printf "\rRewrite $commit ($i/$commits)"
+
+ case "$filter_subdir" in
+ "")
+ git read-tree -i -m $commit
+ ;;
+ *)
+ git read-tree -i -m $commit:"$filter_subdir"
+ esac || die "Could not initialize the index"
+
+ export GIT_COMMIT=$commit
+ git cat-file commit "$commit" >../commit ||
+ die "Cannot read commit $commit"
+
+ eval "$(set_ident AUTHOR <../commit)" ||
+ die "setting author failed for commit $commit"
+ eval "$(set_ident COMMITTER <../commit)" ||
+ die "setting committer failed for commit $commit"
+ eval "$filter_env" < /dev/null ||
+ die "env filter failed: $filter_env"
+
+ if [ "$filter_tree" ]; then
+ git checkout-index -f -u -a ||
+ die "Could not checkout the index"
+ # files that $commit removed are now still in the working tree;
+ # remove them, else they would be added again
+ git ls-files -z --others | xargs -0 rm -f
+ eval "$filter_tree" < /dev/null ||
+ die "tree filter failed: $filter_tree"
+
+ git diff-index -r $commit | cut -f 2- | tr '\n' '\0' | \
+ xargs -0 git update-index --add --replace --remove
+ git ls-files -z --others | \
+ xargs -0 git update-index --add --replace --remove
+ fi
+
+ eval "$filter_index" < /dev/null ||
+ die "index filter failed: $filter_index"
+
+ parentstr=
+ for parent in $parents; do
+ for reparent in $(map "$parent"); do
+ parentstr="$parentstr -p $reparent"
+ done
+ done
+ if [ "$filter_parent" ]; then
+ parentstr="$(echo "$parentstr" | eval "$filter_parent")" ||
+ die "parent filter failed: $filter_parent"
+ fi
+
+ sed -e '1,/^$/d' <../commit | \
+ eval "$filter_msg" > ../message ||
+ die "msg filter failed: $filter_msg"
+ sh -c "$filter_commit" "git commit-tree" \
+ $(git write-tree) $parentstr < ../message > ../map/$commit
+done <../revs
+
+# In case of a subdirectory filter, it is possible that a specified head
+# is not in the set of rewritten commits, because it was pruned by the
+# revision walker. Fix it by mapping these heads to the next rewritten
+# ancestor(s), i.e. the boundaries in the set of rewritten commits.
+
+# NEEDSWORK: we should sort the unmapped refs topologically first
+while read ref
+do
+ sha1=$(git rev-parse "$ref"^0)
+ test -f "$workdir"/../map/$sha1 && continue
+ # Assign the boundarie(s) in the set of rewritten commits
+ # as the replacement commit(s).
+ # (This would look a bit nicer if --not --stdin worked.)
+ for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") |
+ git rev-list $ref --boundary --stdin |
+ sed -n "s/^-//p")
+ do
+ map $p >> "$workdir"/../map/$sha1
+ done
+done < "$tempdir"/heads
+
+# Finally update the refs
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+count=0
+echo
+while read ref
+do
+ # avoid rewriting a ref twice
+ test -f "$orig_namespace$ref" && continue
+
+ sha1=$(git rev-parse "$ref"^0)
+ rewritten=$(map $sha1)
+
+ test $sha1 = "$rewritten" &&
+ warn "WARNING: Ref '$ref' is unchanged" &&
+ continue
+
+ case "$rewritten" in
+ '')
+ echo "Ref '$ref' was deleted"
+ git update-ref -m "filter-branch: delete" -d "$ref" $sha1 ||
+ die "Could not delete $ref"
+ ;;
+ $_x40)
+ echo "Ref '$ref' was rewritten"
+ git update-ref -m "filter-branch: rewrite" \
+ "$ref" $rewritten $sha1 ||
+ die "Could not rewrite $ref"
+ ;;
+ *)
+ # NEEDSWORK: possibly add -Werror, making this an error
+ warn "WARNING: '$ref' was rewritten into multiple commits:"
+ warn "$rewritten"
+ warn "WARNING: Ref '$ref' points to the first one now."
+ rewritten=$(echo "$rewritten" | head -n 1)
+ git update-ref -m "filter-branch: rewrite to first" \
+ "$ref" $rewritten $sha1 ||
+ die "Could not rewrite $ref"
+ ;;
+ esac
+ git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1
+ count=$(($count+1))
+done < "$tempdir"/heads
+
+# TODO: This should possibly go, with the semantics that all positive given
+# refs are updated, and their original heads stored in refs/original/
+# Filter tags
+
+if [ "$filter_tag_name" ]; then
+ git for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags |
+ while read sha1 type ref; do
+ ref="${ref#refs/tags/}"
+ # XXX: Rewrite tagged trees as well?
+ if [ "$type" != "commit" -a "$type" != "tag" ]; then
+ continue;
+ fi
+
+ if [ "$type" = "tag" ]; then
+ # Dereference to a commit
+ sha1t="$sha1"
+ sha1="$(git rev-parse "$sha1"^{commit} 2>/dev/null)" || continue
+ fi
+
+ [ -f "../map/$sha1" ] || continue
+ new_sha1="$(cat "../map/$sha1")"
+ export GIT_COMMIT="$sha1"
+ new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
+ die "tag name filter failed: $filter_tag_name"
+
+ echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
+
+ if [ "$type" = "tag" ]; then
+ # Warn that we are not rewriting the tag object itself.
+ warn "unreferencing tag object $sha1t"
+ fi
+
+ git update-ref "refs/tags/$new_ref" "$new_sha1" ||
+ die "Could not write tag $new_ref"
+ done
+fi
+
+cd ../..
+rm -rf "$tempdir"
+echo
+test $count -gt 0 && echo "These refs were rewritten:"
+git show-ref | grep ^"$orig_namespace"
+
+exit $ret
diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN
index eee495a986..9770b0bc27 100755
--- a/git-gui/GIT-VERSION-GEN
+++ b/git-gui/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=0.7.GITGUI
+DEF_VER=0.8.GITGUI
LF='
'
diff --git a/git-gui/Makefile b/git-gui/Makefile
index ab550fc6a7..1bac6fed46 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -31,11 +31,35 @@ ifndef INSTALL
INSTALL = install
endif
+INSTALL_D0 = $(INSTALL) -d -m755 # space is required here
+INSTALL_D1 =
+INSTALL_R0 = $(INSTALL) -m644 # space is required here
+INSTALL_R1 =
+INSTALL_X0 = $(INSTALL) -m755 # space is required here
+INSTALL_X1 =
+INSTALL_L0 = rm -f # space is required here
+INSTALL_L1 = && ln # space is required here
+INSTALL_L2 =
+INSTALL_L3 =
+
ifndef V
- QUIET_GEN = @echo ' ' GEN $@;
- QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
- QUIET_INDEX = @echo ' ' INDEX $(dir $@);
+ QUIET = @
+ QUIET_GEN = $(QUIET)echo ' ' GEN $@ &&
+ QUIET_BUILT_IN = $(QUIET)echo ' ' BUILTIN $@ &&
+ QUIET_INDEX = $(QUIET)echo ' ' INDEX $(dir $@) &&
QUIET_2DEVNULL = 2>/dev/null
+
+ INSTALL_D0 = dir=
+ INSTALL_D1 = && echo ' ' DEST $$dir && $(INSTALL) -d -m755 "$$dir"
+ INSTALL_R0 = src=
+ INSTALL_R1 = && echo ' ' INSTALL 644 `basename $$src` && $(INSTALL) -m644 $$src
+ INSTALL_X0 = src=
+ INSTALL_X1 = && echo ' ' INSTALL 755 `basename $$src` && $(INSTALL) -m755 $$src
+
+ INSTALL_L0 = dst=
+ INSTALL_L1 = && src=
+ INSTALL_L2 = && dst=
+ INSTALL_L3 = && echo ' ' 'LINK ' `basename "$$dst"` '->' `basename "$$src"` && rm -f "$$dst" && ln "$$src" "$$dst"
endif
TCL_PATH ?= tclsh
@@ -115,12 +139,12 @@ GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
all:: $(ALL_PROGRAMS) lib/tclIndex
install: all
- $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
- $(INSTALL) git-gui '$(DESTDIR_SQ)$(gitexecdir_SQ)'
- $(foreach p,$(GITGUI_BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
- $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(libdir_SQ)'
- $(INSTALL) -m644 lib/tclIndex '$(DESTDIR_SQ)$(libdir_SQ)'
- $(foreach p,$(ALL_LIBFILES), $(INSTALL) -m644 $p '$(DESTDIR_SQ)$(libdir_SQ)' ;)
+ $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1)
+ $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
+ $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true
+ $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(libdir_SQ)' $(INSTALL_D1)
+ $(QUIET)$(INSTALL_R0)lib/tclIndex $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)'
+ $(QUIET)$(foreach p,$(ALL_LIBFILES), $(INSTALL_R0)$p $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' &&) true
dist-version:
@mkdir -p $(TARDIR)
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index c38aa067ac..671b8873f2 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -44,6 +44,24 @@ if {[catch {package require Tcl 8.4} err]
######################################################################
##
+## enable verbose loading?
+
+if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
+ unset _verbose
+ rename auto_load real__auto_load
+ proc auto_load {name args} {
+ puts stderr "auto_load $name"
+ return [uplevel 1 real__auto_load $name $args]
+ }
+ rename source real__source
+ proc source {name} {
+ puts stderr "source $name"
+ uplevel 1 real__source $name
+ }
+}
+
+######################################################################
+##
## configure our library
set oguilib {@@GITGUI_LIBDIR@@}
@@ -54,26 +72,33 @@ if {$oguirel eq {1}} {
} elseif {[string match @@* $oguirel]} {
set oguilib [file join [file dirname [file normalize $argv0]] lib]
}
+
set idx [file join $oguilib tclIndex]
-catch {
- set fd [open $idx r]
- if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
- set idx [list]
- while {[gets $fd n] >= 0} {
- if {$n ne {} && ![string match #* $n]} {
- lappend idx $n
- }
+if {[catch {set fd [open $idx r]} err]} {
+ catch {wm withdraw .}
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title "git-gui: fatal error" \
+ -message $err
+ exit 1
+}
+if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
+ set idx [list]
+ while {[gets $fd n] >= 0} {
+ if {$n ne {} && ![string match #* $n]} {
+ lappend idx $n
}
- } else {
- set idx {}
}
- close $fd
+} else {
+ set idx {}
}
+close $fd
+
if {$idx ne {}} {
set loaded [list]
foreach p $idx {
if {[lsearch -exact $loaded $p] >= 0} continue
- puts $p
source [file join $oguilib $p]
lappend loaded $p
}
@@ -81,21 +106,7 @@ if {$idx ne {}} {
} else {
set auto_path [concat [list $oguilib] $auto_path]
}
-unset -nocomplain oguilib oguirel idx fd
-
-if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
- unset _verbose
- rename auto_load real__auto_load
- proc auto_load {name args} {
- puts stderr "auto_load $name"
- return [uplevel 1 real__auto_load $name $args]
- }
- rename source real__source
- proc source {name} {
- puts stderr "source $name"
- uplevel 1 real__source $name
- }
-}
+unset -nocomplain oguirel idx fd
######################################################################
##
@@ -106,6 +117,7 @@ set _gitdir {}
set _gitexec {}
set _reponame {}
set _iscygwin {}
+set _search_path {}
proc appname {} {
global _appname
@@ -117,7 +129,7 @@ proc gitdir {args} {
if {$args eq {}} {
return $_gitdir
}
- return [eval [concat [list file join $_gitdir] $args]]
+ return [eval [list file join $_gitdir] $args]
}
proc gitexec {args} {
@@ -126,20 +138,26 @@ proc gitexec {args} {
if {[catch {set _gitexec [git --exec-path]} err]} {
error "Git not installed?\n\n$err"
}
+ if {[is_Cygwin]} {
+ set _gitexec [exec cygpath \
+ --windows \
+ --absolute \
+ $_gitexec]
+ } else {
+ set _gitexec [file normalize $_gitexec]
+ }
}
if {$args eq {}} {
return $_gitexec
}
- return [eval [concat [list file join $_gitexec] $args]]
+ return [eval [list file join $_gitexec] $args]
}
proc reponame {} {
- global _reponame
- return $_reponame
+ return $::_reponame
}
proc is_MacOSX {} {
- global tcl_platform tk_library
if {[tk windowingsystem] eq {aqua}} {
return 1
}
@@ -147,17 +165,16 @@ proc is_MacOSX {} {
}
proc is_Windows {} {
- global tcl_platform
- if {$tcl_platform(platform) eq {windows}} {
+ if {$::tcl_platform(platform) eq {windows}} {
return 1
}
return 0
}
proc is_Cygwin {} {
- global tcl_platform _iscygwin
+ global _iscygwin
if {$_iscygwin eq {}} {
- if {$tcl_platform(platform) eq {windows}} {
+ if {$::tcl_platform(platform) eq {windows}} {
if {[catch {set p [exec cygpath --windir]} err]} {
set _iscygwin 0
} else {
@@ -211,13 +228,22 @@ proc is_config_true {name} {
}
}
+proc get_config {name} {
+ global repo_config
+ if {[catch {set v $repo_config($name)}]} {
+ return {}
+ } else {
+ return $v
+ }
+}
+
proc load_config {include_global} {
global repo_config global_config default_config
array unset global_config
if {$include_global} {
catch {
- set fd_rc [open "| git config --global --list" r]
+ set fd_rc [git_read config --global --list]
while {[gets $fd_rc line] >= 0} {
if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
if {[is_many_config $name]} {
@@ -233,7 +259,7 @@ proc load_config {include_global} {
array unset repo_config
catch {
- set fd_rc [open "| git config --list" r]
+ set fd_rc [git_read config --list]
while {[gets $fd_rc line] >= 0} {
if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
if {[is_many_config $name]} {
@@ -260,8 +286,224 @@ proc load_config {include_global} {
##
## handy utils
+proc _git_cmd {name} {
+ global _git_cmd_path
+
+ if {[catch {set v $_git_cmd_path($name)}]} {
+ switch -- $name {
+ version -
+ --version -
+ --exec-path { return [list $::_git $name] }
+ }
+
+ set p [gitexec git-$name$::_search_exe]
+ if {[file exists $p]} {
+ set v [list $p]
+ } elseif {[is_Windows] && [file exists [gitexec git-$name]]} {
+ # Try to determine what sort of magic will make
+ # git-$name go and do its thing, because native
+ # Tcl on Windows doesn't know it.
+ #
+ set p [gitexec git-$name]
+ set f [open $p r]
+ set s [gets $f]
+ close $f
+
+ switch -glob -- $s {
+ #!*sh { set i sh }
+ #!*perl { set i perl }
+ #!*python { set i python }
+ default { error "git-$name is not supported: $s" }
+ }
+
+ upvar #0 _$i interp
+ if {![info exists interp]} {
+ set interp [_which $i]
+ }
+ if {$interp eq {}} {
+ error "git-$name requires $i (not in PATH)"
+ }
+ set v [list $interp $p]
+ } else {
+ # Assume it is builtin to git somehow and we
+ # aren't actually able to see a file for it.
+ #
+ set v [list $::_git $name]
+ }
+ set _git_cmd_path($name) $v
+ }
+ return $v
+}
+
+proc _which {what} {
+ global env _search_exe _search_path
+
+ if {$_search_path eq {}} {
+ if {[is_Cygwin]} {
+ set _search_path [split [exec cygpath \
+ --windows \
+ --path \
+ --absolute \
+ $env(PATH)] {;}]
+ set _search_exe .exe
+ } elseif {[is_Windows]} {
+ set _search_path [split $env(PATH) {;}]
+ set _search_exe .exe
+ } else {
+ set _search_path [split $env(PATH) :]
+ set _search_exe {}
+ }
+ }
+
+ foreach p $_search_path {
+ set p [file join $p $what$_search_exe]
+ if {[file exists $p]} {
+ return [file normalize $p]
+ }
+ }
+ return {}
+}
+
+proc _lappend_nice {cmd_var} {
+ global _nice
+ upvar $cmd_var cmd
+
+ if {![info exists _nice]} {
+ set _nice [_which nice]
+ }
+ if {$_nice ne {}} {
+ lappend cmd $_nice
+ }
+}
+
proc git {args} {
- return [eval exec git $args]
+ set opt [list exec]
+
+ while {1} {
+ switch -- [lindex $args 0] {
+ --nice {
+ _lappend_nice opt
+ }
+
+ default {
+ break
+ }
+
+ }
+
+ set args [lrange $args 1 end]
+ }
+
+ set cmdp [_git_cmd [lindex $args 0]]
+ set args [lrange $args 1 end]
+
+ return [eval $opt $cmdp $args]
+}
+
+proc _open_stdout_stderr {cmd} {
+ if {[catch {
+ set fd [open $cmd r]
+ } err]} {
+ if { [lindex $cmd end] eq {2>@1}
+ && $err eq {can not find channel named "1"}
+ } {
+ # Older versions of Tcl 8.4 don't have this 2>@1 IO
+ # redirect operator. Fallback to |& cat for those.
+ # The command was not actually started, so its safe
+ # to try to start it a second time.
+ #
+ set fd [open [concat \
+ [lrange $cmd 0 end-1] \
+ [list |& cat] \
+ ] r]
+ } else {
+ error $err
+ }
+ }
+ fconfigure $fd -eofchar {}
+ return $fd
+}
+
+proc git_read {args} {
+ set opt [list |]
+
+ while {1} {
+ switch -- [lindex $args 0] {
+ --nice {
+ _lappend_nice opt
+ }
+
+ --stderr {
+ lappend args 2>@1
+ }
+
+ default {
+ break
+ }
+
+ }
+
+ set args [lrange $args 1 end]
+ }
+
+ set cmdp [_git_cmd [lindex $args 0]]
+ set args [lrange $args 1 end]
+
+ return [_open_stdout_stderr [concat $opt $cmdp $args]]
+}
+
+proc git_write {args} {
+ set opt [list |]
+
+ while {1} {
+ switch -- [lindex $args 0] {
+ --nice {
+ _lappend_nice opt
+ }
+
+ default {
+ break
+ }
+
+ }
+
+ set args [lrange $args 1 end]
+ }
+
+ set cmdp [_git_cmd [lindex $args 0]]
+ set args [lrange $args 1 end]
+
+ return [open [concat $opt $cmdp $args] w]
+}
+
+proc sq {value} {
+ regsub -all ' $value "'\\''" value
+ return "'$value'"
+}
+
+proc load_current_branch {} {
+ global current_branch is_detached
+
+ set fd [open [gitdir HEAD] r]
+ if {[gets $fd ref] < 1} {
+ set ref {}
+ }
+ close $fd
+
+ set pfx {ref: refs/heads/}
+ set len [string length $pfx]
+ if {[string equal -length $len $pfx $ref]} {
+ # We're on a branch. It might not exist. But
+ # HEAD looks good enough to be a branch.
+ #
+ set current_branch [string range $ref $len end]
+ set is_detached 0
+ } else {
+ # Assume this is a detached head.
+ #
+ set current_branch HEAD
+ set is_detached 1
+ }
}
auto_load tk_optionMenu
@@ -275,35 +517,155 @@ proc tk_optionMenu {w varName args} {
######################################################################
##
-## version check
+## find git
-set req_maj 1
-set req_min 5
+set _git [_which git]
+if {$_git eq {}} {
+ catch {wm withdraw .}
+ error_popup "Cannot find git in PATH."
+ exit 1
+}
+
+######################################################################
+##
+## version check
-if {[catch {set v [git --version]} err]} {
+if {[catch {set _git_version [git --version]} err]} {
catch {wm withdraw .}
error_popup "Cannot determine Git version:
$err
-[appname] requires Git $req_maj.$req_min or later."
+[appname] requires Git 1.5.0 or later."
exit 1
}
-if {[regexp {^git version (\d+)\.(\d+)} $v _junk act_maj act_min]} {
- if {$act_maj < $req_maj
- || ($act_maj == $req_maj && $act_min < $req_min)} {
- catch {wm withdraw .}
- error_popup "[appname] requires Git $req_maj.$req_min or later.
+if {![regsub {^git version } $_git_version {} _git_version]} {
+ catch {wm withdraw .}
+ error_popup "Cannot parse Git version string:\n\n$_git_version"
+ exit 1
+}
+
+set _real_git_version $_git_version
+regsub -- {-dirty$} $_git_version {} _git_version
+regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
+regsub {\.rc[0-9]+$} $_git_version {} _git_version
+regsub {\.GIT$} $_git_version {} _git_version
-You are using $v."
+if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
+ catch {wm withdraw .}
+ if {[tk_messageBox \
+ -icon warning \
+ -type yesno \
+ -default no \
+ -title "[appname]: warning" \
+ -message "Git version cannot be determined.
+
+$_git claims it is version '$_real_git_version'.
+
+[appname] requires at least Git 1.5.0 or later.
+
+Assume '$_real_git_version' is version 1.5.0?
+"] eq {yes}} {
+ set _git_version 1.5.0
+ } else {
exit 1
}
-} else {
+}
+unset _real_git_version
+
+proc git-version {args} {
+ global _git_version
+
+ switch [llength $args] {
+ 0 {
+ return $_git_version
+ }
+
+ 2 {
+ set op [lindex $args 0]
+ set vr [lindex $args 1]
+ set cm [package vcompare $_git_version $vr]
+ return [expr $cm $op 0]
+ }
+
+ 4 {
+ set type [lindex $args 0]
+ set name [lindex $args 1]
+ set parm [lindex $args 2]
+ set body [lindex $args 3]
+
+ if {($type ne {proc} && $type ne {method})} {
+ error "Invalid arguments to git-version"
+ }
+ if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
+ error "Last arm of $type $name must be default"
+ }
+
+ foreach {op vr cb} [lrange $body 0 end-2] {
+ if {[git-version $op $vr]} {
+ return [uplevel [list $type $name $parm $cb]]
+ }
+ }
+
+ return [uplevel [list $type $name $parm [lindex $body end]]]
+ }
+
+ default {
+ error "git-version >= x"
+ }
+
+ }
+}
+
+if {[git-version < 1.5]} {
catch {wm withdraw .}
- error_popup "Cannot parse Git version string:\n\n$v"
+ error_popup "[appname] requires Git 1.5.0 or later.
+
+You are using [git-version]:
+
+[git --version]"
exit 1
}
-unset -nocomplain v _junk act_maj act_min req_maj req_min
+
+######################################################################
+##
+## feature option selection
+
+if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
+ unset _junk
+} else {
+ set subcommand gui
+}
+if {$subcommand eq {gui.sh}} {
+ set subcommand gui
+}
+if {$subcommand eq {gui} && [llength $argv] > 0} {
+ set subcommand [lindex $argv 0]
+ set argv [lrange $argv 1 end]
+}
+
+enable_option multicommit
+enable_option branch
+enable_option transport
+disable_option bare
+
+switch -- $subcommand {
+browser -
+blame {
+ enable_option bare
+
+ disable_option multicommit
+ disable_option branch
+ disable_option transport
+}
+citool {
+ enable_option singlecommit
+
+ disable_option multicommit
+ disable_option branch
+ disable_option transport
+}
+}
######################################################################
##
@@ -329,19 +691,24 @@ if {![file isdirectory $_gitdir]} {
error_popup "Git directory not found:\n\n$_gitdir"
exit 1
}
-if {[lindex [file split $_gitdir] end] ne {.git}} {
- catch {wm withdraw .}
- error_popup "Cannot use funny .git directory:\n\n$_gitdir"
- exit 1
+if {![is_enabled bare]} {
+ if {[lindex [file split $_gitdir] end] ne {.git}} {
+ catch {wm withdraw .}
+ error_popup "Cannot use funny .git directory:\n\n$_gitdir"
+ exit 1
+ }
+ if {[catch {cd [file dirname $_gitdir]} err]} {
+ catch {wm withdraw .}
+ error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
+ exit 1
+ }
}
-if {[catch {cd [file dirname $_gitdir]} err]} {
- catch {wm withdraw .}
- error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
- exit 1
+set _reponame [file split [file normalize $_gitdir]]
+if {[lindex $_reponame end] eq {.git}} {
+ set _reponame [lindex $_reponame end-1]
+} else {
+ set _reponame [lindex $_reponame end]
}
-set _reponame [lindex [file split \
- [file normalize [file dirname $_gitdir]]] \
- end]
######################################################################
##
@@ -350,7 +717,6 @@ set _reponame [lindex [file split \
set current_diff_path {}
set current_diff_side {}
set diff_actions [list]
-set ui_status_value {Initializing...}
set HEAD {}
set PARENT {}
@@ -358,6 +724,7 @@ set MERGE_HEAD [list]
set commit_type {}
set empty_tree {}
set current_branch {}
+set is_detached 0
set current_diff_path {}
set selected_commit_type new
@@ -407,15 +774,7 @@ proc repository_state {ctvar hdvar mhvar} {
set mh [list]
- if {[catch {set current_branch [git symbolic-ref HEAD]}]} {
- set current_branch {}
- } else {
- regsub ^refs/((heads|tags|remotes)/)? \
- $current_branch \
- {} \
- current_branch
- }
-
+ load_current_branch
if {[catch {set hd [git rev-parse --verify HEAD]}]} {
set hd {}
set ct initial
@@ -451,7 +810,7 @@ proc PARENT {} {
proc rescan {after {honor_trustmtime 1}} {
global HEAD PARENT MERGE_HEAD commit_type
- global ui_index ui_workdir ui_status_value ui_comm
+ global ui_index ui_workdir ui_comm
global rescan_active file_states
global repo_config
@@ -470,8 +829,9 @@ proc rescan {after {honor_trustmtime 1}} {
array unset file_states
- if {![$ui_comm edit modified]
- || [string trim [$ui_comm get 0.0 end]] eq {}} {
+ if {!$::GITGUI_BCK_exists &&
+ (![$ui_comm edit modified]
+ || [string trim [$ui_comm get 0.0 end]] eq {})} {
if {[string match amend* $commit_type]} {
} elseif {[load_message GITGUI_MSG]} {
} elseif {[load_message MERGE_MSG]} {
@@ -481,22 +841,17 @@ proc rescan {after {honor_trustmtime 1}} {
$ui_comm edit modified false
}
- if {[is_enabled branch]} {
- load_all_heads
- populate_branch_menu
- }
-
if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
rescan_stage2 {} $after
} else {
set rescan_active 1
- set ui_status_value {Refreshing file status...}
- set cmd [list git update-index]
- lappend cmd -q
- lappend cmd --unmerged
- lappend cmd --ignore-missing
- lappend cmd --refresh
- set fd_rf [open "| $cmd" r]
+ ui_status {Refreshing file status...}
+ set fd_rf [git_read update-index \
+ -q \
+ --unmerged \
+ --ignore-missing \
+ --refresh \
+ ]
fconfigure $fd_rf -blocking 0 -translation binary
fileevent $fd_rf readable \
[list rescan_stage2 $fd_rf $after]
@@ -504,7 +859,6 @@ proc rescan {after {honor_trustmtime 1}} {
}
proc rescan_stage2 {fd after} {
- global ui_status_value
global rescan_active buf_rdi buf_rdf buf_rlo
if {$fd ne {}} {
@@ -513,22 +867,25 @@ proc rescan_stage2 {fd after} {
close $fd
}
- set ls_others [list | git ls-files --others -z \
- --exclude-per-directory=.gitignore]
+ set ls_others [list --exclude-per-directory=.gitignore]
set info_exclude [gitdir info exclude]
if {[file readable $info_exclude]} {
lappend ls_others "--exclude-from=$info_exclude"
}
+ set user_exclude [get_config core.excludesfile]
+ if {$user_exclude ne {} && [file readable $user_exclude]} {
+ lappend ls_others "--exclude-from=$user_exclude"
+ }
set buf_rdi {}
set buf_rdf {}
set buf_rlo {}
set rescan_active 3
- set ui_status_value {Scanning for modified files ...}
- set fd_di [open "| git diff-index --cached -z [PARENT]" r]
- set fd_df [open "| git diff-files -z" r]
- set fd_lo [open $ls_others r]
+ ui_status {Scanning for modified files ...}
+ set fd_di [git_read diff-index --cached -z [PARENT]]
+ set fd_df [git_read diff-files -z]
+ set fd_lo [eval git_read ls-files --others -z $ls_others]
fconfigure $fd_di -blocking 0 -translation binary -encoding binary
fconfigure $fd_df -blocking 0 -translation binary -encoding binary
@@ -546,6 +903,7 @@ proc load_message {file} {
if {[catch {set fd [open $f r]}]} {
return 0
}
+ fconfigure $fd -eofchar {}
set content [string trim [read $fd]]
close $fd
regsub -all -line {[ \r\t]+$} $content {} content
@@ -685,6 +1043,14 @@ proc mapdesc {state path} {
return $r
}
+proc ui_status {msg} {
+ $::main_status show $msg
+}
+
+proc ui_ready {{test {}}} {
+ $::main_status show {Ready.} $test
+}
+
proc escape_path {path} {
regsub -all {\\} $path "\\\\" path
regsub -all "\n" $path "\\n" path
@@ -930,32 +1296,6 @@ static unsigned char file_merge_bits[] = {
0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
} -maskdata $filemask
-set file_dir_data {
-#define file_width 18
-#define file_height 18
-static unsigned char file_bits[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
- 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
- 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
- 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-}
-image create bitmap file_dir -background white -foreground blue \
- -data $file_dir_data -maskdata $file_dir_data
-unset file_dir_data
-
-set file_uplevel_data {
-#define up_width 15
-#define up_height 15
-static unsigned char up_bits[] = {
- 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
- 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
- 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
-}
-image create bitmap file_uplevel -background white -foreground red \
- -data $file_uplevel_data -maskdata $file_uplevel_data
-unset file_uplevel_data
-
set ui_index .vpane.files.index.list
set ui_workdir .vpane.files.workdir.list
@@ -1036,28 +1376,18 @@ proc incr_font_size {font {amt 1}} {
set starting_gitk_msg {Starting gitk... please wait...}
proc do_gitk {revs} {
- global env ui_status_value starting_gitk_msg
-
# -- Always start gitk through whatever we were loaded with. This
# lets us bypass using shell process on Windows systems.
#
- set cmd [list [info nameofexecutable]]
- set exe [gitexec gitk]
- lappend cmd $exe
- if {$revs ne {}} {
- append cmd { }
- append cmd $revs
- }
-
+ set exe [file join [file dirname $::_git] gitk]
+ set cmd [list [info nameofexecutable] $exe]
if {! [file exists $exe]} {
error_popup "Unable to start gitk:\n\n$exe does not exist"
} else {
- eval exec $cmd &
- set ui_status_value $starting_gitk_msg
+ eval exec $cmd $revs &
+ ui_status $::starting_gitk_msg
after 10000 {
- if {$ui_status_value eq $starting_gitk_msg} {
- set ui_status_value {Ready.}
- }
+ ui_ready $starting_gitk_msg
}
}
}
@@ -1066,6 +1396,7 @@ set is_quitting 0
proc do_quit {} {
global ui_comm is_quitting repo_config commit_type
+ global GITGUI_BCK_exists GITGUI_BCK_i
if {$is_quitting} return
set is_quitting 1
@@ -1074,18 +1405,30 @@ proc do_quit {} {
# -- Stash our current commit buffer.
#
set save [gitdir GITGUI_MSG]
- set msg [string trim [$ui_comm get 0.0 end]]
- regsub -all -line {[ \r\t]+$} $msg {} msg
- if {(![string match amend* $commit_type]
- || [$ui_comm edit modified])
- && $msg ne {}} {
- catch {
- set fd [open $save w]
- puts -nonewline $fd $msg
- close $fd
- }
+ if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
+ file rename -force [gitdir GITGUI_BCK] $save
+ set GITGUI_BCK_exists 0
} else {
- catch {file delete $save}
+ set msg [string trim [$ui_comm get 0.0 end]]
+ regsub -all -line {[ \r\t]+$} $msg {} msg
+ if {(![string match amend* $commit_type]
+ || [$ui_comm edit modified])
+ && $msg ne {}} {
+ catch {
+ set fd [open $save w]
+ puts -nonewline $fd $msg
+ close $fd
+ }
+ } else {
+ catch {file delete $save}
+ }
+ }
+
+ # -- Remove our editor backup, its not needed.
+ #
+ after cancel $GITGUI_BCK_i
+ if {$GITGUI_BCK_exists} {
+ catch {file delete [gitdir GITGUI_BCK]}
}
# -- Stash our current window geometry into this repository.
@@ -1106,7 +1449,7 @@ proc do_quit {} {
}
proc do_rescan {} {
- rescan {set ui_status_value {Ready.}}
+ rescan ui_ready
}
proc do_commit {} {
@@ -1141,12 +1484,12 @@ proc toggle_or_diff {w x y} {
update_indexinfo \
"Unstaging [short_path $path] from commit" \
[list $path] \
- [concat $after {set ui_status_value {Ready.}}]
+ [concat $after [list ui_ready]]
} elseif {$w eq $ui_workdir} {
update_index \
"Adding [short_path $path]" \
[list $path] \
- [concat $after {set ui_status_value {Ready.}}]
+ [concat $after [list ui_ready]]
}
} else {
show_diff $path $w $lno
@@ -1233,6 +1576,10 @@ foreach class {Button Checkbutton Entry Label
}
unset class
+if {[is_Windows] || [is_MacOSX]} {
+ option add *Menu.tearOff 0
+}
+
if {[is_MacOSX]} {
set M1B M1
set M1T Cmd
@@ -1263,11 +1610,14 @@ proc apply_config {} {
}
}
+set default_config(merge.diffstat) true
set default_config(merge.summary) false
set default_config(merge.verbosity) 2
set default_config(user.name) {}
set default_config(user.email) {}
+set default_config(gui.matchtrackingbranch) false
+set default_config(gui.pruneduringfetch) false
set default_config(gui.trustmtime) false
set default_config(gui.diffcontext) 5
set default_config(gui.newbranchtemplate) {}
@@ -1282,43 +1632,6 @@ apply_config
######################################################################
##
-## feature option selection
-
-if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
- unset _junk
-} else {
- set subcommand gui
-}
-if {$subcommand eq {gui.sh}} {
- set subcommand gui
-}
-if {$subcommand eq {gui} && [llength $argv] > 0} {
- set subcommand [lindex $argv 0]
- set argv [lrange $argv 1 end]
-}
-
-enable_option multicommit
-enable_option branch
-enable_option transport
-
-switch -- $subcommand {
-browser -
-blame {
- disable_option multicommit
- disable_option branch
- disable_option transport
-}
-citool {
- enable_option singlecommit
-
- disable_option multicommit
- disable_option branch
- disable_option transport
-}
-}
-
-######################################################################
-##
## ui construction
set ui_comm {}
@@ -1346,20 +1659,32 @@ if {[is_enabled transport]} {
menu .mbar.repository
.mbar.repository add command \
- -label {Browse Current Branch} \
+ -label {Browse Current Branch's Files} \
-command {browser::new $current_branch}
-trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
+set ui_browse_current [.mbar.repository index last]
+.mbar.repository add command \
+ -label {Browse Branch Files...} \
+ -command browser_open::dialog
.mbar.repository add separator
.mbar.repository add command \
- -label {Visualize Current Branch} \
+ -label {Visualize Current Branch's History} \
-command {do_gitk $current_branch}
-trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
+set ui_visualize_current [.mbar.repository index last]
.mbar.repository add command \
- -label {Visualize All Branches} \
+ -label {Visualize All Branch History} \
-command {do_gitk --all}
.mbar.repository add separator
+proc current_branch_write {args} {
+ global current_branch
+ .mbar.repository entryconf $::ui_browse_current \
+ -label "Browse $current_branch's Files"
+ .mbar.repository entryconf $::ui_visualize_current \
+ -label "Visualize $current_branch's History"
+}
+trace add variable current_branch write current_branch_write
+
if {[is_enabled multicommit]} {
.mbar.repository add command -label {Database Statistics} \
-command do_stats
@@ -1424,13 +1749,24 @@ if {[is_enabled branch]} {
menu .mbar.branch
.mbar.branch add command -label {Create...} \
- -command do_create_branch \
+ -command branch_create::dialog \
-accelerator $M1T-N
lappend disable_on_lock [list .mbar.branch entryconf \
[.mbar.branch index last] -state]
+ .mbar.branch add command -label {Checkout...} \
+ -command branch_checkout::dialog \
+ -accelerator $M1T-O
+ lappend disable_on_lock [list .mbar.branch entryconf \
+ [.mbar.branch index last] -state]
+
+ .mbar.branch add command -label {Rename...} \
+ -command branch_rename::dialog
+ lappend disable_on_lock [list .mbar.branch entryconf \
+ [.mbar.branch index last] -state]
+
.mbar.branch add command -label {Delete...} \
- -command do_delete_branch
+ -command branch_delete::dialog
lappend disable_on_lock [list .mbar.branch entryconf \
[.mbar.branch index last] -state]
@@ -1469,12 +1805,12 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
- .mbar.commit add command -label {Add To Commit} \
+ .mbar.commit add command -label {Stage To Commit} \
-command do_add_selection
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
- .mbar.commit add command -label {Add Existing To Commit} \
+ .mbar.commit add command -label {Stage Changed Files To Commit} \
-command do_add_all \
-accelerator $M1T-I
lappend disable_on_lock \
@@ -1508,14 +1844,14 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
if {[is_enabled branch]} {
menu .mbar.merge
.mbar.merge add command -label {Local Merge...} \
- -command merge::dialog
+ -command merge::dialog \
+ -accelerator $M1T-M
lappend disable_on_lock \
[list .mbar.merge entryconf [.mbar.merge index last] -state]
.mbar.merge add command -label {Abort Merge...} \
-command merge::reset_hard
lappend disable_on_lock \
[list .mbar.merge entryconf [.mbar.merge index last] -state]
-
}
# -- Transport Menu
@@ -1527,6 +1863,8 @@ if {[is_enabled transport]} {
.mbar.push add command -label {Push...} \
-command do_push_anywhere \
-accelerator $M1T-P
+ .mbar.push add command -label {Delete...} \
+ -command remote_branch_delete::dialog
}
if {[is_MacOSX]} {
@@ -1545,34 +1883,6 @@ if {[is_MacOSX]} {
.mbar.edit add separator
.mbar.edit add command -label {Options...} \
-command do_options
-
- # -- Tools Menu
- #
- if {[is_Cygwin] && [file exists /usr/local/miga/lib/gui-miga]} {
- proc do_miga {} {
- global ui_status_value
- if {![lock_index update]} return
- set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
- set miga_fd [open "|$cmd" r]
- fconfigure $miga_fd -blocking 0
- fileevent $miga_fd readable [list miga_done $miga_fd]
- set ui_status_value {Running miga...}
- }
- proc miga_done {fd} {
- read $fd 512
- if {[eof $fd]} {
- close $fd
- unlock_index
- rescan [list set ui_status_value {Ready.}]
- }
- }
- .mbar add cascade -label Tools -menu .mbar.tools
- menu .mbar.tools
- .mbar.tools add command -label "Migrate" \
- -command do_miga
- lappend disable_on_lock \
- [list .mbar.tools entryconf [.mbar.tools index last] -state]
- }
}
# -- Help Menu
@@ -1640,24 +1950,10 @@ proc usage {} {
# -- Not a normal commit type invocation? Do that instead!
#
switch -- $subcommand {
-browser {
- set subcommand_args {rev?}
- switch [llength $argv] {
- 0 {
- set current_branch [git symbolic-ref HEAD]
- regsub ^refs/((heads|tags|remotes)/)? \
- $current_branch {} current_branch
- }
- 1 {
- set current_branch [lindex $argv 0]
- }
- default usage
- }
- browser::new $current_branch
- return
-}
+browser -
blame {
- set subcommand_args {rev? path?}
+ set subcommand_args {rev? path}
+ if {$argv eq {}} usage
set head {}
set path {}
set is_path 0
@@ -1676,22 +1972,52 @@ blame {
} elseif {$head eq {}} {
if {$head ne {}} usage
set head $a
+ set is_path 1
} else {
usage
}
}
unset is_path
+ if {$head ne {} && $path eq {}} {
+ set path $_prefix$head
+ set head {}
+ }
+
if {$head eq {}} {
- set current_branch [git symbolic-ref HEAD]
- regsub ^refs/((heads|tags|remotes)/)? \
- $current_branch {} current_branch
+ load_current_branch
} else {
+ if {[regexp {^[0-9a-f]{1,39}$} $head]} {
+ if {[catch {
+ set head [git rev-parse --verify $head]
+ } err]} {
+ puts stderr $err
+ exit 1
+ }
+ }
set current_branch $head
}
- if {$path eq {}} usage
- blame::new $head $path
+ switch -- $subcommand {
+ browser {
+ if {$head eq {}} {
+ if {$path ne {} && [file isdirectory $path]} {
+ set head $current_branch
+ } else {
+ set head $path
+ set path {}
+ }
+ }
+ browser::new $head $path
+ }
+ blame {
+ if {$head eq {} && ![file exists $path]} {
+ puts stderr "fatal: cannot stat path $path: No such file or directory"
+ exit 1
+ }
+ blame::new $head $path
+ }
+ }
return
}
citool -
@@ -1806,7 +2132,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x
lappend disable_on_lock \
{.vpane.lower.commarea.buttons.rescan conf -state}
-button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
+button .vpane.lower.commarea.buttons.incall -text {Stage Changed} \
-command do_add_all
pack .vpane.lower.commarea.buttons.incall -side top -fill x
lappend disable_on_lock \
@@ -2080,26 +2406,31 @@ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
$ctxm add separator
$ctxm add command -label {Options...} \
-command do_options
-bind_button3 $ui_diff "
- set cursorX %x
- set cursorY %y
- if {\$ui_index eq \$current_diff_side} {
- $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
+proc popup_diff_menu {ctxm x y X Y} {
+ set ::cursorX $x
+ set ::cursorY $y
+ if {$::ui_index eq $::current_diff_side} {
+ $ctxm entryconf $::ui_diff_applyhunk \
+ -state normal \
+ -label {Unstage Hunk From Commit}
+ } elseif {{_O} eq [lindex $::file_states($::current_diff_path) 0]} {
+ $ctxm entryconf $::ui_diff_applyhunk \
+ -state disabled \
+ -label {Stage Hunk For Commit}
} else {
- $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
+ $ctxm entryconf $::ui_diff_applyhunk \
+ -state normal \
+ -label {Stage Hunk For Commit}
}
- tk_popup $ctxm %X %Y
-"
-unset ui_diff_applyhunk
+ tk_popup $ctxm $X $Y
+}
+bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]
# -- Status Bar
#
-label .status -textvariable ui_status_value \
- -anchor w \
- -justify left \
- -borderwidth 1 \
- -relief sunken
+set main_status [::status_bar::new .status]
pack .status -anchor w -side bottom -fill x
+$main_status show {Initializing...}
# -- Load geometry
#
@@ -2150,8 +2481,12 @@ bind $ui_diff <Control-Key-f> {catch {%W yview scroll 1 pages};break}
bind $ui_diff <Button-1> {focus %W}
if {[is_enabled branch]} {
- bind . <$M1B-Key-n> do_create_branch
- bind . <$M1B-Key-N> do_create_branch
+ bind . <$M1B-Key-n> branch_create::dialog
+ bind . <$M1B-Key-N> branch_create::dialog
+ bind . <$M1B-Key-o> branch_checkout::dialog
+ bind . <$M1B-Key-O> branch_checkout::dialog
+ bind . <$M1B-Key-m> merge::dialog
+ bind . <$M1B-Key-M> merge::dialog
}
if {[is_enabled transport]} {
bind . <$M1B-Key-p> do_push_anywhere
@@ -2238,31 +2573,69 @@ user.email settings into your personal
#
if {[is_enabled transport]} {
load_all_remotes
- load_all_heads
- populate_branch_menu
populate_fetch_menu
populate_push_menu
}
-# -- Only suggest a gc run if we are going to stay running.
-#
-if {[is_enabled multicommit]} {
- set object_limit 2000
- if {[is_Windows]} {set object_limit 200}
- regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
- if {$objects_current >= $object_limit} {
- if {[ask_popup \
- "This repository currently has $objects_current loose objects.
+if {[winfo exists $ui_comm]} {
+ set GITGUI_BCK_exists [load_message GITGUI_BCK]
+
+ # -- If both our backup and message files exist use the
+ # newer of the two files to initialize the buffer.
+ #
+ if {$GITGUI_BCK_exists} {
+ set m [gitdir GITGUI_MSG]
+ if {[file isfile $m]} {
+ if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
+ catch {file delete [gitdir GITGUI_MSG]}
+ } else {
+ $ui_comm delete 0.0 end
+ $ui_comm edit reset
+ $ui_comm edit modified false
+ catch {file delete [gitdir GITGUI_BCK]}
+ set GITGUI_BCK_exists 0
+ }
+ }
+ unset m
+ }
-To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
+ proc backup_commit_buffer {} {
+ global ui_comm GITGUI_BCK_exists
-Compress the database now?"] eq yes} {
- do_gc
+ set m [$ui_comm edit modified]
+ if {$m || $GITGUI_BCK_exists} {
+ set msg [string trim [$ui_comm get 0.0 end]]
+ regsub -all -line {[ \r\t]+$} $msg {} msg
+
+ if {$msg eq {}} {
+ if {$GITGUI_BCK_exists} {
+ catch {file delete [gitdir GITGUI_BCK]}
+ set GITGUI_BCK_exists 0
+ }
+ } elseif {$m} {
+ catch {
+ set fd [open [gitdir GITGUI_BCK] w]
+ puts -nonewline $fd $msg
+ close $fd
+ set GITGUI_BCK_exists 1
+ }
+ }
+
+ $ui_comm edit modified false
}
+
+ set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
}
- unset object_limit _junk objects_current
+
+ backup_commit_buffer
}
lock_index begin-read
+if {![winfo ismapped .]} {
+ wm deiconify .
+}
after 1 do_rescan
+if {[is_enabled multicommit]} {
+ after 1000 hint_gc
+}
diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl
index 1d2caac283..96072847a2 100644
--- a/git-gui/lib/blame.tcl
+++ b/git-gui/lib/blame.tcl
@@ -21,7 +21,7 @@ field w_amov ; # text column: annotations + move tracking
field w_asim ; # text column: annotations (simple computation)
field w_file ; # text column: actual file data
field w_cviewer ; # pane showing commit message
-field status ; # text variable bound to status bar
+field status ; # status mega-widget instance
field old_height ; # last known height of $w.file_pane
# Tk UI colors
@@ -33,6 +33,13 @@ variable group_colors {
#ececec
}
+# Switches for original location detection
+#
+variable original_options [list -C -C]
+if {[git-version >= 1.5.3]} {
+ lappend original_options -w ; # ignore indentation changes
+}
+
# Current blame data; cleared/reset on each load
#
field commit ; # input commit to blame
@@ -235,14 +242,7 @@ constructor new {i_commit i_path} {
pack $w.file_pane.cm.sbx -side bottom -fill x
pack $w_cviewer -expand 1 -fill both
- frame $w.status \
- -borderwidth 1 \
- -relief sunken
- label $w.status.l \
- -textvariable @status \
- -anchor w \
- -justify left
- pack $w.status.l -side left
+ set status [::status_bar::new $w.status]
menu $w.ctxm -tearoff 0
$w.ctxm add command \
@@ -304,8 +304,9 @@ constructor new {i_commit i_path} {
set req_w [winfo reqwidth $top]
set req_h [winfo reqheight $top]
+ set scr_h [expr {[winfo screenheight $top] - 100}]
if {$req_w < 600} {set req_w 600}
- if {$req_h < 400} {set req_h 400}
+ if {$req_h < $scr_h} {set req_h $scr_h}
set g "${req_w}x${req_h}"
wm geometry $top $g
update
@@ -352,19 +353,6 @@ method _load {jump} {
set total_lines 0
}
- if {[winfo exists $w.status.c]} {
- $w.status.c coords bar 0 0 0 20
- } else {
- canvas $w.status.c \
- -width 100 \
- -height [expr {int([winfo reqheight $w.status.l] * 0.6)}] \
- -borderwidth 1 \
- -relief groove \
- -highlightt 0
- $w.status.c create rectangle 0 0 0 20 -tags bar -fill navy
- pack $w.status.c -side right
- }
-
if {$history eq {}} {
$w_back conf -state disabled
} else {
@@ -378,13 +366,13 @@ method _load {jump} {
set amov_data [list [list]]
set asim_data [list [list]]
- set status "Loading $commit:[escape_path $path]..."
+ $status show "Reading $commit:[escape_path $path]..."
$w_path conf -text [escape_path $path]
if {$commit eq {}} {
set fd [open $path r]
+ fconfigure $fd -eofchar {}
} else {
- set cmd [list git cat-file blob "$commit:$path"]
- set fd [open "| $cmd" r]
+ set fd [git_read cat-file blob "$commit:$path"]
}
fconfigure $fd -blocking 0 -translation lf -encoding binary
fileevent $fd readable [cb _read_file $fd $jump]
@@ -487,30 +475,28 @@ method _read_file {fd jump} {
} ifdeleted { catch {close $fd} }
method _exec_blame {cur_w cur_d options cur_s} {
- set cmd [list]
- if {![is_Windows] || [is_Cygwin]} {
- lappend cmd nice
- }
- lappend cmd git blame
- set cmd [concat $cmd $options]
- lappend cmd --incremental
+ lappend options --incremental
if {$commit eq {}} {
- lappend cmd --contents $path
+ lappend options --contents $path
} else {
- lappend cmd $commit
+ lappend options $commit
}
- lappend cmd -- $path
- set fd [open "| $cmd" r]
+ lappend options -- $path
+ set fd [eval git_read --nice blame $options]
fconfigure $fd -blocking 0 -translation lf -encoding binary
- fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d $cur_s]
+ fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
set current_fd $fd
set blame_lines 0
- _status $this $cur_s
+
+ $status start \
+ "Loading$cur_s annotations..." \
+ {lines annotated}
}
-method _read_blame {fd cur_w cur_d cur_s} {
+method _read_blame {fd cur_w cur_d} {
upvar #0 $cur_d line_data
variable group_colors
+ variable original_options
if {$fd ne $current_fd} {
catch {close $fd}
@@ -684,30 +670,17 @@ method _read_blame {fd cur_w cur_d cur_s} {
close $fd
if {$cur_w eq $w_asim} {
_exec_blame $this $w_amov @amov_data \
- [list -M -C -C] \
+ $original_options \
{ original location}
} else {
set current_fd {}
- set status {Annotation complete.}
- destroy $w.status.c
+ $status stop {Annotation complete.}
}
} else {
- _status $this $cur_s
+ $status update $blame_lines $total_lines
}
} ifdeleted { catch {close $fd} }
-method _status {cur_s} {
- set have $blame_lines
- set total $total_lines
- set pdone 0
- if {$total} {set pdone [expr {100 * $have / $total}]}
-
- set status [format \
- "Loading%s annotations... %i of %i lines annotated (%2i%%)" \
- $cur_s $have $total $pdone]
- $w.status.c coords bar 0 0 $pdone 20
-}
-
method _click {cur_w pos} {
set lno [lindex [split [$cur_w index $pos] .] 0]
_showcommit $this $cur_w $lno
@@ -788,7 +761,7 @@ method _showcommit {cur_w lno} {
if {[catch {set msg $header($cmit,message)}]} {
set msg {}
catch {
- set fd [open "| git cat-file commit $cmit" r]
+ set fd [git_read cat-file commit $cmit]
fconfigure $fd -encoding binary -translation lf
if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
set enc utf-8
@@ -798,15 +771,20 @@ method _showcommit {cur_w lno} {
set enc [string tolower [string range $line 9 end]]
}
}
- set msg [encoding convertfrom $enc [read $fd]]
- set msg [string trim $msg]
+ set msg [read $fd]
close $fd
- set author_name [encoding convertfrom $enc $author_name]
- set committer_name [encoding convertfrom $enc $committer_name]
-
- set header($cmit,author) $author_name
- set header($cmit,committer) $committer_name
+ set enc [tcl_encoding $enc]
+ if {$enc ne {}} {
+ set msg [encoding convertfrom $enc $msg]
+ set author_name [encoding convertfrom $enc $author_name]
+ set committer_name [encoding convertfrom $enc $committer_name]
+ set header($cmit,author) $author_name
+ set header($cmit,committer) $committer_name
+ set header($cmit,summary) \
+ [encoding convertfrom $enc $header($cmit,summary)]
+ }
+ set msg [string trim $msg]
}
set header($cmit,message) $msg
}
@@ -901,6 +879,11 @@ method _open_tooltip {cur_w} {
set org [lindex $amov_data $lno]
}
+ if {$dat eq {}} {
+ _hide_tooltip $this
+ return
+ }
+
set cmit [lindex $dat 0]
set tooltip_commit [list $cmit]
diff --git a/git-gui/lib/branch.tcl b/git-gui/lib/branch.tcl
index 4f648b2bc7..777eeb79c1 100644
--- a/git-gui/lib/branch.tcl
+++ b/git-gui/lib/branch.tcl
@@ -2,573 +2,37 @@
# Copyright (C) 2006, 2007 Shawn Pearce
proc load_all_heads {} {
- global all_heads
+ global some_heads_tracking
+ set rh refs/heads
+ set rh_len [expr {[string length $rh] + 1}]
set all_heads [list]
- set fd [open "| git for-each-ref --format=%(refname) refs/heads" r]
+ set fd [git_read for-each-ref --format=%(refname) $rh]
while {[gets $fd line] > 0} {
- if {[is_tracking_branch $line]} continue
- if {![regsub ^refs/heads/ $line {} name]} continue
- lappend all_heads $name
+ if {!$some_heads_tracking || ![is_tracking_branch $line]} {
+ lappend all_heads [string range $line $rh_len end]
+ }
}
close $fd
- set all_heads [lsort $all_heads]
+ return [lsort $all_heads]
}
proc load_all_tags {} {
set all_tags [list]
- set fd [open "| git for-each-ref --format=%(refname) refs/tags" r]
+ set fd [git_read for-each-ref \
+ --sort=-taggerdate \
+ --format=%(refname) \
+ refs/tags]
while {[gets $fd line] > 0} {
if {![regsub ^refs/tags/ $line {} name]} continue
lappend all_tags $name
}
close $fd
-
- return [lsort $all_tags]
-}
-
-proc populate_branch_menu {} {
- global all_heads disable_on_lock
-
- set m .mbar.branch
- set last [$m index last]
- for {set i 0} {$i <= $last} {incr i} {
- if {[$m type $i] eq {separator}} {
- $m delete $i last
- set new_dol [list]
- foreach a $disable_on_lock {
- if {[lindex $a 0] ne $m || [lindex $a 2] < $i} {
- lappend new_dol $a
- }
- }
- set disable_on_lock $new_dol
- break
- }
- }
-
- if {$all_heads ne {}} {
- $m add separator
- }
- foreach b $all_heads {
- $m add radiobutton \
- -label $b \
- -command [list switch_branch $b] \
- -variable current_branch \
- -value $b
- lappend disable_on_lock \
- [list $m entryconf [$m index last] -state]
- }
-}
-
-proc do_create_branch_action {w} {
- global all_heads null_sha1 repo_config
- global create_branch_checkout create_branch_revtype
- global create_branch_head create_branch_trackinghead
- global create_branch_name create_branch_revexp
- global create_branch_tag
-
- set newbranch $create_branch_name
- if {$newbranch eq {}
- || $newbranch eq $repo_config(gui.newbranchtemplate)} {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "Please supply a branch name."
- focus $w.desc.name_t
- return
- }
- if {![catch {git show-ref --verify -- "refs/heads/$newbranch"}]} {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "Branch '$newbranch' already exists."
- focus $w.desc.name_t
- return
- }
- if {[catch {git check-ref-format "heads/$newbranch"}]} {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "We do not like '$newbranch' as a branch name."
- focus $w.desc.name_t
- return
- }
-
- set rev {}
- switch -- $create_branch_revtype {
- head {set rev $create_branch_head}
- tracking {set rev $create_branch_trackinghead}
- tag {set rev $create_branch_tag}
- expression {set rev $create_branch_revexp}
- }
- if {[catch {set cmt [git rev-parse --verify "${rev}^0"]}]} {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "Invalid starting revision: $rev"
- return
- }
- if {[catch {
- git update-ref \
- -m "branch: Created from $rev" \
- "refs/heads/$newbranch" \
- $cmt \
- $null_sha1
- } err]} {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "Failed to create '$newbranch'.\n\n$err"
- return
- }
-
- lappend all_heads $newbranch
- set all_heads [lsort $all_heads]
- populate_branch_menu
- destroy $w
- if {$create_branch_checkout} {
- switch_branch $newbranch
- }
+ return $all_tags
}
proc radio_selector {varname value args} {
upvar #0 $varname var
set var $value
}
-
-trace add variable create_branch_head write \
- [list radio_selector create_branch_revtype head]
-trace add variable create_branch_trackinghead write \
- [list radio_selector create_branch_revtype tracking]
-trace add variable create_branch_tag write \
- [list radio_selector create_branch_revtype tag]
-
-trace add variable delete_branch_head write \
- [list radio_selector delete_branch_checktype head]
-trace add variable delete_branch_trackinghead write \
- [list radio_selector delete_branch_checktype tracking]
-
-proc do_create_branch {} {
- global all_heads current_branch repo_config
- global create_branch_checkout create_branch_revtype
- global create_branch_head create_branch_trackinghead
- global create_branch_name create_branch_revexp
- global create_branch_tag
-
- set w .branch_editor
- toplevel $w
- wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
-
- label $w.header -text {Create New Branch} \
- -font font_uibold
- pack $w.header -side top -fill x
-
- frame $w.buttons
- button $w.buttons.create -text Create \
- -default active \
- -command [list do_create_branch_action $w]
- pack $w.buttons.create -side right
- button $w.buttons.cancel -text {Cancel} \
- -command [list destroy $w]
- pack $w.buttons.cancel -side right -padx 5
- pack $w.buttons -side bottom -fill x -pady 10 -padx 10
-
- labelframe $w.desc -text {Branch Description}
- label $w.desc.name_l -text {Name:}
- entry $w.desc.name_t \
- -borderwidth 1 \
- -relief sunken \
- -width 40 \
- -textvariable create_branch_name \
- -validate key \
- -validatecommand {
- if {%d == 1 && [regexp {[~^:?*\[\0- ]} %S]} {return 0}
- return 1
- }
- grid $w.desc.name_l $w.desc.name_t -sticky we -padx {0 5}
- grid columnconfigure $w.desc 1 -weight 1
- pack $w.desc -anchor nw -fill x -pady 5 -padx 5
-
- labelframe $w.from -text {Starting Revision}
- if {$all_heads ne {}} {
- radiobutton $w.from.head_r \
- -text {Local Branch:} \
- -value head \
- -variable create_branch_revtype
- eval tk_optionMenu $w.from.head_m create_branch_head $all_heads
- grid $w.from.head_r $w.from.head_m -sticky w
- }
- set all_trackings [all_tracking_branches]
- if {$all_trackings ne {}} {
- set create_branch_trackinghead [lindex $all_trackings 0]
- radiobutton $w.from.tracking_r \
- -text {Tracking Branch:} \
- -value tracking \
- -variable create_branch_revtype
- eval tk_optionMenu $w.from.tracking_m \
- create_branch_trackinghead \
- $all_trackings
- grid $w.from.tracking_r $w.from.tracking_m -sticky w
- }
- set all_tags [load_all_tags]
- if {$all_tags ne {}} {
- set create_branch_tag [lindex $all_tags 0]
- radiobutton $w.from.tag_r \
- -text {Tag:} \
- -value tag \
- -variable create_branch_revtype
- eval tk_optionMenu $w.from.tag_m create_branch_tag $all_tags
- grid $w.from.tag_r $w.from.tag_m -sticky w
- }
- radiobutton $w.from.exp_r \
- -text {Revision Expression:} \
- -value expression \
- -variable create_branch_revtype
- entry $w.from.exp_t \
- -borderwidth 1 \
- -relief sunken \
- -width 50 \
- -textvariable create_branch_revexp \
- -validate key \
- -validatecommand {
- if {%d == 1 && [regexp {\s} %S]} {return 0}
- if {%d == 1 && [string length %S] > 0} {
- set create_branch_revtype expression
- }
- return 1
- }
- grid $w.from.exp_r $w.from.exp_t -sticky we -padx {0 5}
- grid columnconfigure $w.from 1 -weight 1
- pack $w.from -anchor nw -fill x -pady 5 -padx 5
-
- labelframe $w.postActions -text {Post Creation Actions}
- checkbutton $w.postActions.checkout \
- -text {Checkout after creation} \
- -variable create_branch_checkout
- pack $w.postActions.checkout -anchor nw
- pack $w.postActions -anchor nw -fill x -pady 5 -padx 5
-
- set create_branch_checkout 1
- set create_branch_head $current_branch
- set create_branch_revtype head
- set create_branch_name $repo_config(gui.newbranchtemplate)
- set create_branch_revexp {}
-
- bind $w <Visibility> "
- grab $w
- $w.desc.name_t icursor end
- focus $w.desc.name_t
- "
- bind $w <Key-Escape> "destroy $w"
- bind $w <Key-Return> "do_create_branch_action $w;break"
- wm title $w "[appname] ([reponame]): Create Branch"
- tkwait window $w
-}
-
-proc do_delete_branch_action {w} {
- global all_heads
- global delete_branch_checktype delete_branch_head delete_branch_trackinghead
-
- set check_rev {}
- switch -- $delete_branch_checktype {
- head {set check_rev $delete_branch_head}
- tracking {set check_rev $delete_branch_trackinghead}
- always {set check_rev {:none}}
- }
- if {$check_rev eq {:none}} {
- set check_cmt {}
- } elseif {[catch {set check_cmt [git rev-parse --verify "${check_rev}^0"]}]} {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "Invalid check revision: $check_rev"
- return
- }
-
- set to_delete [list]
- set not_merged [list]
- foreach i [$w.list.l curselection] {
- set b [$w.list.l get $i]
- if {[catch {set o [git rev-parse --verify $b]}]} continue
- if {$check_cmt ne {}} {
- if {$b eq $check_rev} continue
- if {[catch {set m [git merge-base $o $check_cmt]}]} continue
- if {$o ne $m} {
- lappend not_merged $b
- continue
- }
- }
- lappend to_delete [list $b $o]
- }
- if {$not_merged ne {}} {
- set msg "The following branches are not completely merged into $check_rev:
-
- - [join $not_merged "\n - "]"
- tk_messageBox \
- -icon info \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message $msg
- }
- if {$to_delete eq {}} return
- if {$delete_branch_checktype eq {always}} {
- set msg {Recovering deleted branches is difficult.
-
-Delete the selected branches?}
- if {[tk_messageBox \
- -icon warning \
- -type yesno \
- -title [wm title $w] \
- -parent $w \
- -message $msg] ne yes} {
- return
- }
- }
-
- set failed {}
- foreach i $to_delete {
- set b [lindex $i 0]
- set o [lindex $i 1]
- if {[catch {git update-ref -d "refs/heads/$b" $o} err]} {
- append failed " - $b: $err\n"
- } else {
- set x [lsearch -sorted -exact $all_heads $b]
- if {$x >= 0} {
- set all_heads [lreplace $all_heads $x $x]
- }
- }
- }
-
- if {$failed ne {}} {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "Failed to delete branches:\n$failed"
- }
-
- set all_heads [lsort $all_heads]
- populate_branch_menu
- destroy $w
-}
-
-proc do_delete_branch {} {
- global all_heads tracking_branches current_branch
- global delete_branch_checktype delete_branch_head delete_branch_trackinghead
-
- set w .branch_editor
- toplevel $w
- wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
-
- label $w.header -text {Delete Local Branch} \
- -font font_uibold
- pack $w.header -side top -fill x
-
- frame $w.buttons
- button $w.buttons.create -text Delete \
- -command [list do_delete_branch_action $w]
- pack $w.buttons.create -side right
- button $w.buttons.cancel -text {Cancel} \
- -command [list destroy $w]
- pack $w.buttons.cancel -side right -padx 5
- pack $w.buttons -side bottom -fill x -pady 10 -padx 10
-
- labelframe $w.list -text {Local Branches}
- listbox $w.list.l \
- -height 10 \
- -width 70 \
- -selectmode extended \
- -yscrollcommand [list $w.list.sby set]
- foreach h $all_heads {
- if {$h ne $current_branch} {
- $w.list.l insert end $h
- }
- }
- scrollbar $w.list.sby -command [list $w.list.l yview]
- pack $w.list.sby -side right -fill y
- pack $w.list.l -side left -fill both -expand 1
- pack $w.list -fill both -expand 1 -pady 5 -padx 5
-
- labelframe $w.validate -text {Delete Only If}
- radiobutton $w.validate.head_r \
- -text {Merged Into Local Branch:} \
- -value head \
- -variable delete_branch_checktype
- eval tk_optionMenu $w.validate.head_m delete_branch_head $all_heads
- grid $w.validate.head_r $w.validate.head_m -sticky w
- set all_trackings [all_tracking_branches]
- if {$all_trackings ne {}} {
- set delete_branch_trackinghead [lindex $all_trackings 0]
- radiobutton $w.validate.tracking_r \
- -text {Merged Into Tracking Branch:} \
- -value tracking \
- -variable delete_branch_checktype
- eval tk_optionMenu $w.validate.tracking_m \
- delete_branch_trackinghead \
- $all_trackings
- grid $w.validate.tracking_r $w.validate.tracking_m -sticky w
- }
- radiobutton $w.validate.always_r \
- -text {Always (Do not perform merge checks)} \
- -value always \
- -variable delete_branch_checktype
- grid $w.validate.always_r -columnspan 2 -sticky w
- grid columnconfigure $w.validate 1 -weight 1
- pack $w.validate -anchor nw -fill x -pady 5 -padx 5
-
- set delete_branch_head $current_branch
- set delete_branch_checktype head
-
- bind $w <Visibility> "grab $w; focus $w"
- bind $w <Key-Escape> "destroy $w"
- wm title $w "[appname] ([reponame]): Delete Branch"
- tkwait window $w
-}
-
-proc switch_branch {new_branch} {
- global HEAD commit_type current_branch repo_config
-
- if {![lock_index switch]} return
-
- # -- Our in memory state should match the repository.
- #
- repository_state curType curHEAD curMERGE_HEAD
- if {[string match amend* $commit_type]
- && $curType eq {normal}
- && $curHEAD eq $HEAD} {
- } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
- info_popup {Last scanned state does not match repository state.
-
-Another Git program has modified this repository since the last scan. A rescan must be performed before the current branch can be changed.
-
-The rescan will be automatically started now.
-}
- unlock_index
- rescan {set ui_status_value {Ready.}}
- return
- }
-
- # -- Don't do a pointless switch.
- #
- if {$current_branch eq $new_branch} {
- unlock_index
- return
- }
-
- if {$repo_config(gui.trustmtime) eq {true}} {
- switch_branch_stage2 {} $new_branch
- } else {
- set ui_status_value {Refreshing file status...}
- set cmd [list git update-index]
- lappend cmd -q
- lappend cmd --unmerged
- lappend cmd --ignore-missing
- lappend cmd --refresh
- set fd_rf [open "| $cmd" r]
- fconfigure $fd_rf -blocking 0 -translation binary
- fileevent $fd_rf readable \
- [list switch_branch_stage2 $fd_rf $new_branch]
- }
-}
-
-proc switch_branch_stage2 {fd_rf new_branch} {
- global ui_status_value HEAD
-
- if {$fd_rf ne {}} {
- read $fd_rf
- if {![eof $fd_rf]} return
- close $fd_rf
- }
-
- set ui_status_value "Updating working directory to '$new_branch'..."
- set cmd [list git read-tree]
- lappend cmd -m
- lappend cmd -u
- lappend cmd --exclude-per-directory=.gitignore
- lappend cmd $HEAD
- lappend cmd $new_branch
- set fd_rt [open "| $cmd" r]
- fconfigure $fd_rt -blocking 0 -translation binary
- fileevent $fd_rt readable \
- [list switch_branch_readtree_wait $fd_rt $new_branch]
-}
-
-proc switch_branch_readtree_wait {fd_rt new_branch} {
- global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
- global current_branch
- global ui_comm ui_status_value
-
- # -- We never get interesting output on stdout; only stderr.
- #
- read $fd_rt
- fconfigure $fd_rt -blocking 1
- if {![eof $fd_rt]} {
- fconfigure $fd_rt -blocking 0
- return
- }
-
- # -- The working directory wasn't in sync with the index and
- # we'd have to overwrite something to make the switch. A
- # merge is required.
- #
- if {[catch {close $fd_rt} err]} {
- regsub {^fatal: } $err {} err
- warn_popup "File level merge required.
-
-$err
-
-Staying on branch '$current_branch'."
- set ui_status_value "Aborted checkout of '$new_branch' (file level merging is required)."
- unlock_index
- return
- }
-
- # -- Update the symbolic ref. Core git doesn't even check for failure
- # here, it Just Works(tm). If it doesn't we are in some really ugly
- # state that is difficult to recover from within git-gui.
- #
- if {[catch {git symbolic-ref HEAD "refs/heads/$new_branch"} err]} {
- error_popup "Failed to set current branch.
-
-This working directory is only partially switched. We successfully updated your files, but failed to update an internal Git file.
-
-This should not have occurred. [appname] will now close and give up.
-
-$err"
- do_quit
- return
- }
-
- # -- Update our repository state. If we were previously in amend mode
- # we need to toss the current buffer and do a full rescan to update
- # our file lists. If we weren't in amend mode our file lists are
- # accurate and we can avoid the rescan.
- #
- unlock_index
- set selected_commit_type new
- if {[string match amend* $commit_type]} {
- $ui_comm delete 0.0 end
- $ui_comm edit reset
- $ui_comm edit modified false
- rescan {set ui_status_value "Checked out branch '$current_branch'."}
- } else {
- repository_state commit_type HEAD MERGE_HEAD
- set PARENT $HEAD
- set ui_status_value "Checked out branch '$current_branch'."
- }
-}
diff --git a/git-gui/lib/branch_checkout.tcl b/git-gui/lib/branch_checkout.tcl
new file mode 100644
index 0000000000..72c45b4554
--- /dev/null
+++ b/git-gui/lib/branch_checkout.tcl
@@ -0,0 +1,89 @@
+# git-gui branch checkout support
+# Copyright (C) 2007 Shawn Pearce
+
+class branch_checkout {
+
+field w ; # widget path
+field w_rev ; # mega-widget to pick the initial revision
+
+field opt_fetch 1; # refetch tracking branch if used?
+field opt_detach 0; # force a detached head case?
+
+constructor dialog {} {
+ make_toplevel top w
+ wm title $top "[appname] ([reponame]): Checkout Branch"
+ if {$top ne {.}} {
+ wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+ }
+
+ label $w.header -text {Checkout Branch} -font font_uibold
+ pack $w.header -side top -fill x
+
+ frame $w.buttons
+ button $w.buttons.create -text Checkout \
+ -default active \
+ -command [cb _checkout]
+ pack $w.buttons.create -side right
+ button $w.buttons.cancel -text {Cancel} \
+ -command [list destroy $w]
+ pack $w.buttons.cancel -side right -padx 5
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+ set w_rev [::choose_rev::new $w.rev {Revision}]
+ $w_rev bind_listbox <Double-Button-1> [cb _checkout]
+ pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
+
+ labelframe $w.options -text {Options}
+
+ checkbutton $w.options.fetch \
+ -text {Fetch Tracking Branch} \
+ -variable @opt_fetch
+ pack $w.options.fetch -anchor nw
+
+ checkbutton $w.options.detach \
+ -text {Detach From Local Branch} \
+ -variable @opt_detach
+ pack $w.options.detach -anchor nw
+
+ pack $w.options -anchor nw -fill x -pady 5 -padx 5
+
+ bind $w <Visibility> [cb _visible]
+ bind $w <Key-Escape> [list destroy $w]
+ bind $w <Key-Return> [cb _checkout]\;break
+ tkwait window $w
+}
+
+method _checkout {} {
+ set spec [$w_rev get_tracking_branch]
+ if {$spec ne {} && $opt_fetch} {
+ set new {}
+ } elseif {[catch {set new [$w_rev commit_or_die]}]} {
+ return
+ }
+
+ if {$opt_detach} {
+ set ref {}
+ } else {
+ set ref [$w_rev get_local_branch]
+ }
+
+ set co [::checkout_op::new [$w_rev get] $new $ref]
+ $co parent $w
+ $co enable_checkout 1
+ if {$spec ne {} && $opt_fetch} {
+ $co enable_fetch $spec
+ }
+
+ if {[$co run]} {
+ destroy $w
+ } else {
+ $w_rev focus_filter
+ }
+}
+
+method _visible {} {
+ grab $w
+ $w_rev focus_filter
+}
+
+}
diff --git a/git-gui/lib/branch_create.tcl b/git-gui/lib/branch_create.tcl
new file mode 100644
index 0000000000..def615d19d
--- /dev/null
+++ b/git-gui/lib/branch_create.tcl
@@ -0,0 +1,220 @@
+# git-gui branch create support
+# Copyright (C) 2006, 2007 Shawn Pearce
+
+class branch_create {
+
+field w ; # widget path
+field w_rev ; # mega-widget to pick the initial revision
+field w_name ; # new branch name widget
+
+field name {}; # name of the branch the user has chosen
+field name_type user; # type of branch name to use
+
+field opt_merge ff; # type of merge to apply to existing branch
+field opt_checkout 1; # automatically checkout the new branch?
+field opt_fetch 1; # refetch tracking branch if used?
+field reset_ok 0; # did the user agree to reset?
+
+constructor dialog {} {
+ global repo_config
+
+ make_toplevel top w
+ wm title $top "[appname] ([reponame]): Create Branch"
+ if {$top ne {.}} {
+ wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+ }
+
+ label $w.header -text {Create New Branch} -font font_uibold
+ pack $w.header -side top -fill x
+
+ frame $w.buttons
+ button $w.buttons.create -text Create \
+ -default active \
+ -command [cb _create]
+ pack $w.buttons.create -side right
+ button $w.buttons.cancel -text {Cancel} \
+ -command [list destroy $w]
+ pack $w.buttons.cancel -side right -padx 5
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+ labelframe $w.desc -text {Branch Name}
+ radiobutton $w.desc.name_r \
+ -anchor w \
+ -text {Name:} \
+ -value user \
+ -variable @name_type
+ set w_name $w.desc.name_t
+ entry $w_name \
+ -borderwidth 1 \
+ -relief sunken \
+ -width 40 \
+ -textvariable @name \
+ -validate key \
+ -validatecommand [cb _validate %d %S]
+ grid $w.desc.name_r $w_name -sticky we -padx {0 5}
+
+ radiobutton $w.desc.match_r \
+ -anchor w \
+ -text {Match Tracking Branch Name} \
+ -value match \
+ -variable @name_type
+ grid $w.desc.match_r -sticky we -padx {0 5} -columnspan 2
+
+ grid columnconfigure $w.desc 1 -weight 1
+ pack $w.desc -anchor nw -fill x -pady 5 -padx 5
+
+ set w_rev [::choose_rev::new $w.rev {Starting Revision}]
+ pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
+
+ labelframe $w.options -text {Options}
+
+ frame $w.options.merge
+ label $w.options.merge.l -text {Update Existing Branch:}
+ pack $w.options.merge.l -side left
+ radiobutton $w.options.merge.no \
+ -text No \
+ -value none \
+ -variable @opt_merge
+ pack $w.options.merge.no -side left
+ radiobutton $w.options.merge.ff \
+ -text {Fast Forward Only} \
+ -value ff \
+ -variable @opt_merge
+ pack $w.options.merge.ff -side left
+ radiobutton $w.options.merge.reset \
+ -text {Reset} \
+ -value reset \
+ -variable @opt_merge
+ pack $w.options.merge.reset -side left
+ pack $w.options.merge -anchor nw
+
+ checkbutton $w.options.fetch \
+ -text {Fetch Tracking Branch} \
+ -variable @opt_fetch
+ pack $w.options.fetch -anchor nw
+
+ checkbutton $w.options.checkout \
+ -text {Checkout After Creation} \
+ -variable @opt_checkout
+ pack $w.options.checkout -anchor nw
+ pack $w.options -anchor nw -fill x -pady 5 -padx 5
+
+ trace add variable @name_type write [cb _select]
+
+ set name $repo_config(gui.newbranchtemplate)
+ if {[is_config_true gui.matchtrackingbranch]} {
+ set name_type match
+ }
+
+ bind $w <Visibility> [cb _visible]
+ bind $w <Key-Escape> [list destroy $w]
+ bind $w <Key-Return> [cb _create]\;break
+ tkwait window $w
+}
+
+method _create {} {
+ global repo_config
+ global M1B
+
+ set spec [$w_rev get_tracking_branch]
+ switch -- $name_type {
+ user {
+ set newbranch $name
+ }
+ match {
+ if {$spec eq {}} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Please select a tracking branch."
+ return
+ }
+ if {![regsub ^refs/heads/ [lindex $spec 2] {} newbranch]} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Tracking branch [$w get] is not a branch in the remote repository."
+ return
+ }
+ }
+ }
+
+ if {$newbranch eq {}
+ || $newbranch eq $repo_config(gui.newbranchtemplate)} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Please supply a branch name."
+ focus $w_name
+ return
+ }
+
+ if {[catch {git check-ref-format "heads/$newbranch"}]} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "'$newbranch' is not an acceptable branch name."
+ focus $w_name
+ return
+ }
+
+ if {$spec ne {} && $opt_fetch} {
+ set new {}
+ } elseif {[catch {set new [$w_rev commit_or_die]}]} {
+ return
+ }
+
+ set co [::checkout_op::new \
+ [$w_rev get] \
+ $new \
+ refs/heads/$newbranch]
+ $co parent $w
+ $co enable_create 1
+ $co enable_merge $opt_merge
+ $co enable_checkout $opt_checkout
+ if {$spec ne {} && $opt_fetch} {
+ $co enable_fetch $spec
+ }
+
+ if {[$co run]} {
+ destroy $w
+ } else {
+ focus $w_name
+ }
+}
+
+method _validate {d S} {
+ if {$d == 1} {
+ if {[regexp {[~^:?*\[\0- ]} $S]} {
+ return 0
+ }
+ if {[string length $S] > 0} {
+ set name_type user
+ }
+ }
+ return 1
+}
+
+method _select {args} {
+ if {$name_type eq {match}} {
+ $w_rev pick_tracking_branch
+ }
+}
+
+method _visible {} {
+ grab $w
+ if {$name_type eq {user}} {
+ $w_name icursor end
+ focus $w_name
+ }
+}
+
+}
diff --git a/git-gui/lib/branch_delete.tcl b/git-gui/lib/branch_delete.tcl
new file mode 100644
index 0000000000..c7573c6c72
--- /dev/null
+++ b/git-gui/lib/branch_delete.tcl
@@ -0,0 +1,149 @@
+# git-gui branch delete support
+# Copyright (C) 2007 Shawn Pearce
+
+class branch_delete {
+
+field w ; # widget path
+field w_heads ; # listbox of local head names
+field w_check ; # revision picker for merge test
+field w_delete ; # delete button
+
+constructor dialog {} {
+ global current_branch
+
+ make_toplevel top w
+ wm title $top "[appname] ([reponame]): Delete Branch"
+ if {$top ne {.}} {
+ wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+ }
+
+ label $w.header -text {Delete Local Branch} -font font_uibold
+ pack $w.header -side top -fill x
+
+ frame $w.buttons
+ set w_delete $w.buttons.delete
+ button $w_delete \
+ -text Delete \
+ -default active \
+ -state disabled \
+ -command [cb _delete]
+ pack $w_delete -side right
+ button $w.buttons.cancel \
+ -text {Cancel} \
+ -command [list destroy $w]
+ pack $w.buttons.cancel -side right -padx 5
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+ labelframe $w.list -text {Local Branches}
+ set w_heads $w.list.l
+ listbox $w_heads \
+ -height 10 \
+ -width 70 \
+ -selectmode extended \
+ -exportselection false \
+ -yscrollcommand [list $w.list.sby set]
+ scrollbar $w.list.sby -command [list $w.list.l yview]
+ pack $w.list.sby -side right -fill y
+ pack $w.list.l -side left -fill both -expand 1
+ pack $w.list -fill both -expand 1 -pady 5 -padx 5
+
+ set w_check [choose_rev::new \
+ $w.check \
+ {Delete Only If Merged Into} \
+ ]
+ $w_check none {Always (Do not perform merge test.)}
+ pack $w.check -anchor nw -fill x -pady 5 -padx 5
+
+ foreach h [load_all_heads] {
+ if {$h ne $current_branch} {
+ $w_heads insert end $h
+ }
+ }
+
+ bind $w_heads <<ListboxSelect>> [cb _select]
+ bind $w <Visibility> "
+ grab $w
+ focus $w
+ "
+ bind $w <Key-Escape> [list destroy $w]
+ bind $w <Key-Return> [cb _delete]\;break
+ tkwait window $w
+}
+
+method _select {} {
+ if {[$w_heads curselection] eq {}} {
+ $w_delete configure -state disabled
+ } else {
+ $w_delete configure -state normal
+ }
+}
+
+method _delete {} {
+ if {[catch {set check_cmt [$w_check commit_or_die]}]} {
+ return
+ }
+
+ set to_delete [list]
+ set not_merged [list]
+ foreach i [$w_heads curselection] {
+ set b [$w_heads get $i]
+ if {[catch {
+ set o [git rev-parse --verify "refs/heads/$b"]
+ }]} continue
+ if {$check_cmt ne {}} {
+ if {[catch {set m [git merge-base $o $check_cmt]}]} continue
+ if {$o ne $m} {
+ lappend not_merged $b
+ continue
+ }
+ }
+ lappend to_delete [list $b $o]
+ }
+ if {$not_merged ne {}} {
+ set msg "The following branches are not completely merged into [$w_check get]:
+
+ - [join $not_merged "\n - "]"
+ tk_messageBox \
+ -icon info \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message $msg
+ }
+ if {$to_delete eq {}} return
+ if {$check_cmt eq {}} {
+ set msg {Recovering deleted branches is difficult.
+
+Delete the selected branches?}
+ if {[tk_messageBox \
+ -icon warning \
+ -type yesno \
+ -title [wm title $w] \
+ -parent $w \
+ -message $msg] ne yes} {
+ return
+ }
+ }
+
+ set failed {}
+ foreach i $to_delete {
+ set b [lindex $i 0]
+ set o [lindex $i 1]
+ if {[catch {git update-ref -d "refs/heads/$b" $o} err]} {
+ append failed " - $b: $err\n"
+ }
+ }
+
+ if {$failed ne {}} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Failed to delete branches:\n$failed"
+ }
+
+ destroy $w
+}
+
+}
diff --git a/git-gui/lib/branch_rename.tcl b/git-gui/lib/branch_rename.tcl
new file mode 100644
index 0000000000..1cadc31d20
--- /dev/null
+++ b/git-gui/lib/branch_rename.tcl
@@ -0,0 +1,128 @@
+# git-gui branch rename support
+# Copyright (C) 2007 Shawn Pearce
+
+class branch_rename {
+
+field w
+field oldname
+field newname
+
+constructor dialog {} {
+ global current_branch
+
+ make_toplevel top w
+ wm title $top "[appname] ([reponame]): Rename Branch"
+ if {$top ne {.}} {
+ wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+ }
+
+ set oldname $current_branch
+ set newname [get_config gui.newbranchtemplate]
+
+ label $w.header -text {Rename Branch} -font font_uibold
+ pack $w.header -side top -fill x
+
+ frame $w.buttons
+ button $w.buttons.rename -text Rename \
+ -default active \
+ -command [cb _rename]
+ pack $w.buttons.rename -side right
+ button $w.buttons.cancel -text {Cancel} \
+ -command [list destroy $w]
+ pack $w.buttons.cancel -side right -padx 5
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+ frame $w.rename
+ label $w.rename.oldname_l -text {Branch:}
+ eval tk_optionMenu $w.rename.oldname_m @oldname [load_all_heads]
+
+ label $w.rename.newname_l -text {New Name:}
+ entry $w.rename.newname_t \
+ -borderwidth 1 \
+ -relief sunken \
+ -width 40 \
+ -textvariable @newname \
+ -validate key \
+ -validatecommand {
+ if {%d == 1 && [regexp {[~^:?*\[\0- ]} %S]} {return 0}
+ return 1
+ }
+
+ grid $w.rename.oldname_l $w.rename.oldname_m -sticky w -padx {0 5}
+ grid $w.rename.newname_l $w.rename.newname_t -sticky we -padx {0 5}
+ grid columnconfigure $w.rename 1 -weight 1
+ pack $w.rename -anchor nw -fill x -pady 5 -padx 5
+
+ bind $w <Key-Return> [cb _rename]
+ bind $w <Key-Escape> [list destroy $w]
+ bind $w <Visibility> "
+ grab $w
+ $w.rename.newname_t icursor end
+ focus $w.rename.newname_t
+ "
+ tkwait window $w
+}
+
+method _rename {} {
+ global current_branch
+
+ if {$oldname eq {}} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Please select a branch to rename."
+ focus $w.rename.oldname_m
+ return
+ }
+ if {$newname eq {}
+ || $newname eq [get_config gui.newbranchtemplate]} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Please supply a branch name."
+ focus $w.rename.newname_t
+ return
+ }
+ if {![catch {git show-ref --verify -- "refs/heads/$newname"}]} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Branch '$newname' already exists."
+ focus $w.rename.newname_t
+ return
+ }
+ if {[catch {git check-ref-format "heads/$newname"}]} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "We do not like '$newname' as a branch name."
+ focus $w.rename.newname_t
+ return
+ }
+
+ if {[catch {git branch -m $oldname $newname} err]} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Failed to rename '$oldname'.\n\n$err"
+ return
+ }
+
+ if {$current_branch eq $oldname} {
+ set current_branch $newname
+ }
+
+ destroy $w
+}
+
+}
diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl
index e612247c9e..888db3c889 100644
--- a/git-gui/lib/browser.tcl
+++ b/git-gui/lib/browser.tcl
@@ -3,6 +3,13 @@
class browser {
+image create photo ::browser::img_parent -data {R0lGODlhEAAQAIUAAPwCBBxSHBxOHMTSzNzu3KzCtBRGHCSKFIzCjLzSxBQ2FAxGHDzCLCyeHBQ+FHSmfAwuFBxKLDSCNMzizISyjJzOnDSyLAw+FAQSDAQeDBxWJAwmDAQOBKzWrDymNAQaDAQODAwaDDyKTFSyXFTGTEy6TAQCBAQKDAwiFBQyHAwSFAwmHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ1QIBwSCwaj0hiQCBICpcDQsFgGAaIguhhi0gohIsrQEDYMhiNrRfgeAQC5fMCAolIDhD2hFI5WC4YRBkaBxsOE2l/RxsHHA4dHmkfRyAbIQ4iIyQlB5NFGCAACiakpSZEJyinTgAcKSesACorgU4mJ6uxR35BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
+image create photo ::browser::img_rblob -data {R0lGODlhEAAQAIUAAPwCBFxaXNze3Ly2rJSWjPz+/Ozq7GxqbJyanPT29HRydMzOzDQyNIyKjERCROTi3Pz69PTy7Pzy7PTu5Ozm3LyqlJyWlJSSjJSOhOzi1LyulPz27PTq3PTm1OzezLyqjIyKhJSKfOzaxPz29OzizLyidIyGdIyCdOTOpLymhOzavOTStMTCtMS+rMS6pMSynMSulLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaQQIAQECgajcNkQMBkDgKEQFK4LFgLhkMBIVUKroWEYlEgMLxbBKLQUBwc52HgAQ4LBo049atWQyIPA3pEdFcQEhMUFYNVagQWFxgZGoxfYRsTHB0eH5UJCJAYICEinUoPIxIcHCQkIiIllQYEGCEhJicoKYwPmiQeKisrKLFKLCwtLi8wHyUlMYwM0tPUDH5BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
+image create photo ::browser::img_xblob -data {R0lGODlhEAAQAIYAAPwCBFRWVFxaXNza3OTi3Nze3Ly2tJyanPz+/Ozq7GxubNzSxMzOzMTGxHRybDQyNLy+vHRydHx6fKSipISChIyKjGxqbERCRCwuLLy6vGRiZExKTCQiJAwKDLSytLy2rJSSlHx+fDw6PKyqrBQWFPTu5Ozm3LyulLS2tCQmJAQCBPTq3Ozi1MSynCwqLAQGBOTazOzizOzezLyqjBweHNzSvOzaxKyurHRuZNzOtLymhDw+PIyCdOzWvOTOpLyidNzKtOTStLyifMTCtMS+rLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfZgACCAAEChYeGg4oCAwQFjgYBBwGKggEECJkICQoIkwADCwwNDY2mDA4Lng8QDhESsLARExQVDhYXGBkWExIaGw8cHR4SCQQfFQ8eFgUgIQEiwiMSBMYfGB4atwEXDyQd0wQlJicPKAHoFyIpJCoeDgMrLC0YKBsX6i4kL+4OMDEyZijr5oLGNxUqUCioEcPGDAwjPNyI6MEDChQjcOSwsUDHgw07RIgI4KCkAgs8cvTw8eOBogAxQtXIASTISiEuBwUYMoRIixYnZggpUgTDywdIkWJIitRPIAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
+image create photo ::browser::img_tree -data {R0lGODlhEAAQAIYAAPwCBAQCBExKTBwWHMzKzOzq7ERCRExGTCwqLARqnAQ+ZHR2dKyqrNTOzHx2fCQiJMTi9NTu9HzC3AxmnAQ+XPTm7Dy67DymzITC3IzG5AxypHRydKymrMzOzOzu7BweHByy9AyGtFyy1IzG3NTu/ARupFRSVByazBR6rAyGvFyuzJTK3MTm9BR+tAxWhHS61MTi7Pz+/IymvCxulBRelAx2rHS63Pz6/PTy9PTu9Nza3ISitBRupFSixNTS1CxqnDQyNMzGzOTi5MTCxMTGxGxubGxqbLy2vLSutGRiZLy6vLSytKyurDQuNFxaXKSipDw6PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfDgACCAAECg4eIAAMEBQYHCImDBgkKCwwNBQIBBw4Bhw8QERITFJYEFQUFnoIPFhcYoRkaFBscHR4Ggh8gIRciEiMQJBkltCa6JyUoKSkXKhIrLCQYuQAPLS4TEyUhKb0qLzDVAjEFMjMuNBMoNcw21QY3ODkFOjs82RM1PfDzFRU3fOggcM7Fj2pAgggRokOHDx9DhhAZUqQaISBGhjwMEvEIkiIHEgUAkgSJkiNLmFSMJChAEydPGBSBwvJQgAc0/QQCACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
+image create photo ::browser::img_symlink -data {R0lGODlhEAAQAIQAAPwCBCwqLLSytLy+vERGRFRWVDQ2NKSmpAQCBKyurMTGxISChJyanHR2dIyKjGxubHRydGRmZIyOjFxeXHx6fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAVbICACwWieY1CibCCsrBkMb0zchSEcNYskCtqBBzshFkOGQFk0IRqOxqPBODRHCMhCQKteRc9FI/KQWGOIyFYgkDC+gPR4snCcfRGKOIKIgSMQE31+f4OEYCZ+IQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
+image create photo ::browser::img_unknown -data {R0lGODlhEAAQAIUAAPwCBFxaXIyKjNTW1Nze3LS2tJyanER2RGS+VPz+/PTu5GxqbPz69BQ6BCxeLFSqRPT29HRydMzOzDQyNERmPKSypCRWHIyKhERCRDyGPKz2nESiLBxGHCyCHGxubPz6/PTy7Ozi1Ly2rKSipOzm3LyqlKSWhCRyFOzizLymhNTKtNzOvOzaxOTStPz27OzWvOTOpLSupLyedMS+rMS6pMSulLyqjLymfLyifAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAamQIAQECgajcOkYEBoDgoBQyAJOCCuiENCsWBIh9aGw9F4HCARiXciRDQoBUnlYRlcIgsMG5CxXAgMGhscBRAEBRd7AB0eBBoIgxUfICEiikSPgyMMIAokJZcBkBybJgomIaBJAZoMpyCmqkMBFCcVCrgKKAwpoSorKqchKCwtvasIFBIhLiYvLzDHsxQNMcMKLDAwMqEz3jQ1NTY3ONyrE+jp6hN+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
+
field w
field browser_commit
field browser_path
@@ -13,13 +20,13 @@ field browser_busy 1
field ls_buf {}; # Buffered record output from ls-tree
-constructor new {commit} {
+constructor new {commit {path {}}} {
global cursor_ptr M1B
make_toplevel top w
wm title $top "[appname] ([reponame]): File Browser"
set browser_commit $commit
- set browser_path $browser_commit:
+ set browser_path $browser_commit:$path
label $w.path \
-textvariable @browser_path \
@@ -73,7 +80,11 @@ constructor new {commit} {
bind $w_list <Visibility> [list focus $w_list]
set w $w_list
- _ls $this $browser_commit
+ if {$path ne {}} {
+ _ls $this $browser_commit:$path $path
+ } else {
+ _ls $this $browser_commit $path
+ }
return $this
}
@@ -173,15 +184,14 @@ method _ls {tree_id {name {}}} {
$w image create end \
-align center -padx 5 -pady 1 \
-name icon0 \
- -image file_uplevel
+ -image ::browser::img_parent
$w insert end {[Up To Parent]}
lappend browser_files parent
}
lappend browser_stack [list $tree_id $name]
$w conf -state disabled
- set cmd [list git ls-tree -z $tree_id]
- set fd [open "| $cmd" r]
+ set fd [git_read ls-tree -z $tree_id]
fconfigure $fd -blocking 0 -translation binary -encoding binary
fileevent $fd readable [cb _read $fd]
}
@@ -204,14 +214,21 @@ method _read {fd} {
switch -- $type {
blob {
- set image file_mod
+ scan [lindex $info 0] %o mode
+ if {$mode == 0120000} {
+ set image ::browser::img_symlink
+ } elseif {($mode & 0100) != 0} {
+ set image ::browser::img_xblob
+ } else {
+ set image ::browser::img_rblob
+ }
}
tree {
- set image file_dir
+ set image ::browser::img_tree
append path /
}
default {
- set image file_question
+ set image ::browser::img_unknown
}
}
@@ -240,3 +257,56 @@ method _read {fd} {
}
}
+
+class browser_open {
+
+field w ; # widget path
+field w_rev ; # mega-widget to pick the initial revision
+
+constructor dialog {} {
+ make_toplevel top w
+ wm title $top "[appname] ([reponame]): Browse Branch Files"
+ if {$top ne {.}} {
+ wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+ }
+
+ label $w.header \
+ -text {Browse Branch Files} \
+ -font font_uibold
+ pack $w.header -side top -fill x
+
+ frame $w.buttons
+ button $w.buttons.browse -text Browse \
+ -default active \
+ -command [cb _open]
+ pack $w.buttons.browse -side right
+ button $w.buttons.cancel -text {Cancel} \
+ -command [list destroy $w]
+ pack $w.buttons.cancel -side right -padx 5
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+ set w_rev [::choose_rev::new $w.rev {Revision}]
+ $w_rev bind_listbox <Double-Button-1> [cb _open]
+ pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
+
+ bind $w <Visibility> [cb _visible]
+ bind $w <Key-Escape> [list destroy $w]
+ bind $w <Key-Return> [cb _open]\;break
+ tkwait window $w
+}
+
+method _open {} {
+ if {[catch {$w_rev commit_or_die} err]} {
+ return
+ }
+ set name [$w_rev get]
+ destroy $w
+ browser::new $name
+}
+
+method _visible {} {
+ grab $w
+ $w_rev focus_filter
+}
+
+}
diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl
new file mode 100644
index 0000000000..170f737f61
--- /dev/null
+++ b/git-gui/lib/checkout_op.tcl
@@ -0,0 +1,588 @@
+# git-gui commit checkout support
+# Copyright (C) 2007 Shawn Pearce
+
+class checkout_op {
+
+field w {}; # our window (if we have one)
+field w_cons {}; # embedded console window object
+
+field new_expr ; # expression the user saw/thinks this is
+field new_hash ; # commit SHA-1 we are switching to
+field new_ref ; # ref we are updating/creating
+
+field parent_w .; # window that started us
+field merge_type none; # type of merge to apply to existing branch
+field merge_base {}; # merge base if we have another ref involved
+field fetch_spec {}; # refetch tracking branch if used?
+field checkout 1; # actually checkout the branch?
+field create 0; # create the branch if it doesn't exist?
+
+field reset_ok 0; # did the user agree to reset?
+field fetch_ok 0; # did the fetch succeed?
+
+field readtree_d {}; # buffered output from read-tree
+field update_old {}; # was the update-ref call deferred?
+field reflog_msg {}; # log message for the update-ref call
+
+constructor new {expr hash {ref {}}} {
+ set new_expr $expr
+ set new_hash $hash
+ set new_ref $ref
+
+ return $this
+}
+
+method parent {path} {
+ set parent_w [winfo toplevel $path]
+}
+
+method enable_merge {type} {
+ set merge_type $type
+}
+
+method enable_fetch {spec} {
+ set fetch_spec $spec
+}
+
+method enable_checkout {co} {
+ set checkout $co
+}
+
+method enable_create {co} {
+ set create $co
+}
+
+method run {} {
+ if {$fetch_spec ne {}} {
+ global M1B
+
+ # We were asked to refresh a single tracking branch
+ # before we get to work. We should do that before we
+ # consider any ref updating.
+ #
+ set fetch_ok 0
+ set l_trck [lindex $fetch_spec 0]
+ set remote [lindex $fetch_spec 1]
+ set r_head [lindex $fetch_spec 2]
+ regsub ^refs/heads/ $r_head {} r_name
+
+ set cmd [list git fetch $remote]
+ if {$l_trck ne {}} {
+ lappend cmd +$r_head:$l_trck
+ } else {
+ lappend cmd $r_head
+ }
+
+ _toplevel $this {Refreshing Tracking Branch}
+ set w_cons [::console::embed \
+ $w.console \
+ "Fetching $r_name from $remote"]
+ pack $w.console -fill both -expand 1
+ $w_cons exec $cmd [cb _finish_fetch]
+
+ bind $w <$M1B-Key-w> break
+ bind $w <$M1B-Key-W> break
+ bind $w <Visibility> "
+ [list grab $w]
+ [list focus $w]
+ "
+ wm protocol $w WM_DELETE_WINDOW [cb _noop]
+ tkwait window $w
+
+ if {!$fetch_ok} {
+ delete_this
+ return 0
+ }
+ }
+
+ if {$new_ref ne {}} {
+ # If we have a ref we need to update it before we can
+ # proceed with a checkout (if one was enabled).
+ #
+ if {![_update_ref $this]} {
+ delete_this
+ return 0
+ }
+ }
+
+ if {$checkout} {
+ _checkout $this
+ return 1
+ }
+
+ delete_this
+ return 1
+}
+
+method _noop {} {}
+
+method _finish_fetch {ok} {
+ if {$ok} {
+ set l_trck [lindex $fetch_spec 0]
+ if {$l_trck eq {}} {
+ set l_trck FETCH_HEAD
+ }
+ if {[catch {set new_hash [git rev-parse --verify "$l_trck^0"]} err]} {
+ set ok 0
+ $w_cons insert "fatal: Cannot resolve $l_trck"
+ $w_cons insert $err
+ }
+ }
+
+ $w_cons done $ok
+ set w_cons {}
+ wm protocol $w WM_DELETE_WINDOW {}
+
+ if {$ok} {
+ destroy $w
+ set w {}
+ } else {
+ button $w.close -text Close -command [list destroy $w]
+ pack $w.close -side bottom -anchor e -padx 10 -pady 10
+ }
+
+ set fetch_ok $ok
+}
+
+method _update_ref {} {
+ global null_sha1 current_branch
+
+ set ref $new_ref
+ set new $new_hash
+
+ set is_current 0
+ set rh refs/heads/
+ set rn [string length $rh]
+ if {[string equal -length $rn $rh $ref]} {
+ set newbranch [string range $ref $rn end]
+ if {$current_branch eq $newbranch} {
+ set is_current 1
+ }
+ } else {
+ set newbranch $ref
+ }
+
+ if {[catch {set cur [git rev-parse --verify "$ref^0"]}]} {
+ # Assume it does not exist, and that is what the error was.
+ #
+ if {!$create} {
+ _error $this "Branch '$newbranch' does not exist."
+ return 0
+ }
+
+ set reflog_msg "branch: Created from $new_expr"
+ set cur $null_sha1
+ } elseif {$create && $merge_type eq {none}} {
+ # We were told to create it, but not do a merge.
+ # Bad. Name shouldn't have existed.
+ #
+ _error $this "Branch '$newbranch' already exists."
+ return 0
+ } elseif {!$create && $merge_type eq {none}} {
+ # We aren't creating, it exists and we don't merge.
+ # We are probably just a simple branch switch.
+ # Use whatever value we just read.
+ #
+ set new $cur
+ set new_hash $cur
+ } elseif {$new eq $cur} {
+ # No merge would be required, don't compute anything.
+ #
+ } else {
+ catch {set merge_base [git merge-base $new $cur]}
+ if {$merge_base eq $cur} {
+ # The current branch is older.
+ #
+ set reflog_msg "merge $new_expr: Fast-forward"
+ } else {
+ switch -- $merge_type {
+ ff {
+ if {$merge_base eq $new} {
+ # The current branch is actually newer.
+ #
+ set new $cur
+ set new_hash $cur
+ } else {
+ _error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required."
+ return 0
+ }
+ }
+ reset {
+ # The current branch will lose things.
+ #
+ if {[_confirm_reset $this $cur]} {
+ set reflog_msg "reset $new_expr"
+ } else {
+ return 0
+ }
+ }
+ default {
+ _error $this "Merge strategy '$merge_type' not supported."
+ return 0
+ }
+ }
+ }
+ }
+
+ if {$new ne $cur} {
+ if {$is_current} {
+ # No so fast. We should defer this in case
+ # we cannot update the working directory.
+ #
+ set update_old $cur
+ return 1
+ }
+
+ if {[catch {
+ git update-ref -m $reflog_msg $ref $new $cur
+ } err]} {
+ _error $this "Failed to update '$newbranch'.\n\n$err"
+ return 0
+ }
+ }
+
+ return 1
+}
+
+method _checkout {} {
+ if {[lock_index checkout_op]} {
+ after idle [cb _start_checkout]
+ } else {
+ _error $this "Staging area (index) is already locked."
+ delete_this
+ }
+}
+
+method _start_checkout {} {
+ global HEAD commit_type
+
+ # -- Our in memory state should match the repository.
+ #
+ repository_state curType curHEAD curMERGE_HEAD
+ if {[string match amend* $commit_type]
+ && $curType eq {normal}
+ && $curHEAD eq $HEAD} {
+ } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
+ info_popup {Last scanned state does not match repository state.
+
+Another Git program has modified this repository since the last scan. A rescan must be performed before the current branch can be changed.
+
+The rescan will be automatically started now.
+}
+ unlock_index
+ rescan ui_ready
+ delete_this
+ return
+ }
+
+ if {$curHEAD eq $new_hash} {
+ _after_readtree $this
+ } elseif {[is_config_true gui.trustmtime]} {
+ _readtree $this
+ } else {
+ ui_status {Refreshing file status...}
+ set fd [git_read update-index \
+ -q \
+ --unmerged \
+ --ignore-missing \
+ --refresh \
+ ]
+ fconfigure $fd -blocking 0 -translation binary
+ fileevent $fd readable [cb _refresh_wait $fd]
+ }
+}
+
+method _refresh_wait {fd} {
+ read $fd
+ if {[eof $fd]} {
+ close $fd
+ _readtree $this
+ }
+}
+
+method _name {} {
+ if {$new_ref eq {}} {
+ return [string range $new_hash 0 7]
+ }
+
+ set rh refs/heads/
+ set rn [string length $rh]
+ if {[string equal -length $rn $rh $new_ref]} {
+ return [string range $new_ref $rn end]
+ } else {
+ return $new_ref
+ }
+}
+
+method _readtree {} {
+ global HEAD
+
+ set readtree_d {}
+ $::main_status start \
+ "Updating working directory to '[_name $this]'..." \
+ {files checked out}
+
+ set fd [git_read --stderr read-tree \
+ -m \
+ -u \
+ -v \
+ --exclude-per-directory=.gitignore \
+ $HEAD \
+ $new_hash \
+ ]
+ fconfigure $fd -blocking 0 -translation binary
+ fileevent $fd readable [cb _readtree_wait $fd]
+}
+
+method _readtree_wait {fd} {
+ global current_branch
+
+ set buf [read $fd]
+ $::main_status update_meter $buf
+ append readtree_d $buf
+
+ fconfigure $fd -blocking 1
+ if {![eof $fd]} {
+ fconfigure $fd -blocking 0
+ return
+ }
+
+ if {[catch {close $fd}]} {
+ set err $readtree_d
+ regsub {^fatal: } $err {} err
+ $::main_status stop "Aborted checkout of '[_name $this]' (file level merging is required)."
+ warn_popup "File level merge required.
+
+$err
+
+Staying on branch '$current_branch'."
+ unlock_index
+ delete_this
+ return
+ }
+
+ $::main_status stop
+ _after_readtree $this
+}
+
+method _after_readtree {} {
+ global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
+ global current_branch is_detached
+ global ui_comm
+
+ set name [_name $this]
+ set log "checkout: moving"
+ if {!$is_detached} {
+ append log " from $current_branch"
+ }
+
+ # -- Move/create HEAD as a symbolic ref. Core git does not
+ # even check for failure here, it Just Works(tm). If it
+ # doesn't we are in some really ugly state that is difficult
+ # to recover from within git-gui.
+ #
+ set rh refs/heads/
+ set rn [string length $rh]
+ if {[string equal -length $rn $rh $new_ref]} {
+ set new_branch [string range $new_ref $rn end]
+ if {$is_detached || $current_branch ne $new_branch} {
+ append log " to $new_branch"
+ if {[catch {
+ git symbolic-ref -m $log HEAD $new_ref
+ } err]} {
+ _fatal $this $err
+ }
+ set current_branch $new_branch
+ set is_detached 0
+ }
+ } else {
+ if {$new_hash ne $HEAD} {
+ append log " to $new_expr"
+ if {[catch {
+ _detach_HEAD $log $new_hash
+ } err]} {
+ _fatal $this $err
+ }
+ }
+ set current_branch HEAD
+ set is_detached 1
+ }
+
+ # -- We had to defer updating the branch itself until we
+ # knew the working directory would update. So now we
+ # need to finish that work. If it fails we're in big
+ # trouble.
+ #
+ if {$update_old ne {}} {
+ if {[catch {
+ git update-ref \
+ -m $reflog_msg \
+ $new_ref \
+ $new_hash \
+ $update_old
+ } err]} {
+ _fatal $this $err
+ }
+ }
+
+ if {$is_detached} {
+ info_popup "You are no longer on a local branch.
+
+If you wanted to be on a branch, create one now starting from 'This Detached Checkout'."
+ }
+
+ # -- Update our repository state. If we were previously in
+ # amend mode we need to toss the current buffer and do a
+ # full rescan to update our file lists. If we weren't in
+ # amend mode our file lists are accurate and we can avoid
+ # the rescan.
+ #
+ unlock_index
+ set selected_commit_type new
+ if {[string match amend* $commit_type]} {
+ $ui_comm delete 0.0 end
+ $ui_comm edit reset
+ $ui_comm edit modified false
+ rescan [list ui_status "Checked out '$name'."]
+ } else {
+ repository_state commit_type HEAD MERGE_HEAD
+ set PARENT $HEAD
+ ui_status "Checked out '$name'."
+ }
+ delete_this
+}
+
+git-version proc _detach_HEAD {log new} {
+ >= 1.5.3 {
+ git update-ref --no-deref -m $log HEAD $new
+ }
+ default {
+ set p [gitdir HEAD]
+ file delete $p
+ set fd [open $p w]
+ fconfigure $fd -translation lf -encoding utf-8
+ puts $fd $new
+ close $fd
+ }
+}
+
+method _confirm_reset {cur} {
+ set reset_ok 0
+ set name [_name $this]
+ set gitk [list do_gitk [list $cur ^$new_hash]]
+
+ _toplevel $this {Confirm Branch Reset}
+ pack [label $w.msg1 \
+ -anchor w \
+ -justify left \
+ -text "Resetting '$name' to $new_expr will lose the following commits:" \
+ ] -anchor w
+
+ set list $w.list.l
+ frame $w.list
+ text $list \
+ -font font_diff \
+ -width 80 \
+ -height 10 \
+ -wrap none \
+ -xscrollcommand [list $w.list.sbx set] \
+ -yscrollcommand [list $w.list.sby set]
+ scrollbar $w.list.sbx -orient h -command [list $list xview]
+ scrollbar $w.list.sby -orient v -command [list $list yview]
+ pack $w.list.sbx -fill x -side bottom
+ pack $w.list.sby -fill y -side right
+ pack $list -fill both -expand 1
+ pack $w.list -fill both -expand 1 -padx 5 -pady 5
+
+ pack [label $w.msg2 \
+ -anchor w \
+ -justify left \
+ -text {Recovering lost commits may not be easy.} \
+ ]
+ pack [label $w.msg3 \
+ -anchor w \
+ -justify left \
+ -text "Reset '$name'?" \
+ ]
+
+ frame $w.buttons
+ button $w.buttons.visualize \
+ -text Visualize \
+ -command $gitk
+ pack $w.buttons.visualize -side left
+ button $w.buttons.reset \
+ -text Reset \
+ -command "
+ set @reset_ok 1
+ destroy $w
+ "
+ pack $w.buttons.reset -side right
+ button $w.buttons.cancel \
+ -default active \
+ -text Cancel \
+ -command [list destroy $w]
+ pack $w.buttons.cancel -side right -padx 5
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+ set fd [git_read rev-list --pretty=oneline $cur ^$new_hash]
+ while {[gets $fd line] > 0} {
+ set abbr [string range $line 0 7]
+ set subj [string range $line 41 end]
+ $list insert end "$abbr $subj\n"
+ }
+ close $fd
+ $list configure -state disabled
+
+ bind $w <Key-v> $gitk
+ bind $w <Visibility> "
+ grab $w
+ focus $w.buttons.cancel
+ "
+ bind $w <Key-Return> [list destroy $w]
+ bind $w <Key-Escape> [list destroy $w]
+ tkwait window $w
+ return $reset_ok
+}
+
+method _error {msg} {
+ if {[winfo ismapped $parent_w]} {
+ set p $parent_w
+ } else {
+ set p .
+ }
+
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $p] \
+ -parent $p \
+ -message $msg
+}
+
+method _toplevel {title} {
+ regsub -all {::} $this {__} w
+ set w .$w
+
+ if {[winfo ismapped $parent_w]} {
+ set p $parent_w
+ } else {
+ set p .
+ }
+
+ toplevel $w
+ wm title $w $title
+ wm geometry $w "+[winfo rootx $p]+[winfo rooty $p]"
+}
+
+method _fatal {err} {
+ error_popup "Failed to set current branch.
+
+This working directory is only partially switched. We successfully updated your files, but failed to update an internal Git file.
+
+This should not have occurred. [appname] will now close and give up.
+
+$err"
+ exit 1
+}
+
+}
diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl
new file mode 100644
index 0000000000..ec064b3e13
--- /dev/null
+++ b/git-gui/lib/choose_rev.tcl
@@ -0,0 +1,627 @@
+# git-gui revision chooser
+# Copyright (C) 2006, 2007 Shawn Pearce
+
+class choose_rev {
+
+image create photo ::choose_rev::img_find -data {R0lGODlhEAAQAIYAAPwCBCQmJDw+PBQSFAQCBMza3NTm5MTW1HyChOT29Ozq7MTq7Kze5Kzm7Oz6/NTy9Iza5GzGzKzS1Nzy9Nz29Kzq9HTGzHTK1Lza3AwKDLzu9JTi7HTW5GTCzITO1Mzq7Hza5FTK1ESyvHzKzKzW3DQyNDyqtDw6PIzW5HzGzAT+/Dw+RKyurNTOzMTGxMS+tJSGdATCxHRydLSqpLymnLSijBweHERCRNze3Pz69PTy9Oze1OTSxOTGrMSqlLy+vPTu5OzSvMymjNTGvNS+tMy2pMyunMSefAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAe4gACCAAECA4OIiAIEBQYHBAKJgwIICQoLDA0IkZIECQ4PCxARCwSSAxITFA8VEBYXGBmJAQYLGhUbHB0eH7KIGRIMEBAgISIjJKaIJQQLFxERIialkieUGigpKRoIBCqJKyyLBwvJAioEyoICLS4v6QQwMQQyLuqLli8zNDU2BCf1lN3AkUPHDh49fAQAAEnGD1MCCALZEaSHkIUMBQS8wWMIkSJGhBzBmFEGgRsBUqpMiSgdAD+BAAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
+
+field w ; # our megawidget path
+field w_list ; # list of currently filtered specs
+field w_filter ; # filter entry for $w_list
+
+field c_expr {}; # current revision expression
+field filter ; # current filter string
+field revtype head; # type of revision chosen
+field cur_specs [list]; # list of specs for $revtype
+field spec_head ; # list of all head specs
+field spec_trck ; # list of all tracking branch specs
+field spec_tag ; # list of all tag specs
+field tip_data ; # array of tip commit info by refname
+field log_last ; # array of reflog date by refname
+
+field tooltip_wm {} ; # Current tooltip toplevel, if open
+field tooltip_t {} ; # Text widget in $tooltip_wm
+field tooltip_timer {} ; # Current timer event for our tooltip
+
+proc new {path {title {}}} {
+ return [_new $path 0 $title]
+}
+
+proc new_unmerged {path {title {}}} {
+ return [_new $path 1 $title]
+}
+
+constructor _new {path unmerged_only title} {
+ global current_branch is_detached
+
+ if {![info exists ::all_remotes]} {
+ load_all_remotes
+ }
+
+ set w $path
+
+ if {$title ne {}} {
+ labelframe $w -text $title
+ } else {
+ frame $w
+ }
+ bind $w <Destroy> [cb _delete %W]
+
+ if {$is_detached} {
+ radiobutton $w.detachedhead_r \
+ -anchor w \
+ -text {This Detached Checkout} \
+ -value HEAD \
+ -variable @revtype
+ grid $w.detachedhead_r -sticky we -padx {0 5} -columnspan 2
+ }
+
+ radiobutton $w.expr_r \
+ -text {Revision Expression:} \
+ -value expr \
+ -variable @revtype
+ entry $w.expr_t \
+ -borderwidth 1 \
+ -relief sunken \
+ -width 50 \
+ -textvariable @c_expr \
+ -validate key \
+ -validatecommand [cb _validate %d %S]
+ grid $w.expr_r $w.expr_t -sticky we -padx {0 5}
+
+ frame $w.types
+ radiobutton $w.types.head_r \
+ -text {Local Branch} \
+ -value head \
+ -variable @revtype
+ pack $w.types.head_r -side left
+ radiobutton $w.types.trck_r \
+ -text {Tracking Branch} \
+ -value trck \
+ -variable @revtype
+ pack $w.types.trck_r -side left
+ radiobutton $w.types.tag_r \
+ -text {Tag} \
+ -value tag \
+ -variable @revtype
+ pack $w.types.tag_r -side left
+ set w_filter $w.types.filter
+ entry $w_filter \
+ -borderwidth 1 \
+ -relief sunken \
+ -width 12 \
+ -textvariable @filter \
+ -validate key \
+ -validatecommand [cb _filter %P]
+ pack $w_filter -side right
+ pack [label $w.types.filter_icon \
+ -image ::choose_rev::img_find \
+ ] -side right
+ grid $w.types -sticky we -padx {0 5} -columnspan 2
+
+ frame $w.list
+ set w_list $w.list.l
+ listbox $w_list \
+ -font font_diff \
+ -width 50 \
+ -height 10 \
+ -selectmode browse \
+ -exportselection false \
+ -xscrollcommand [cb _sb_set $w.list.sbx h] \
+ -yscrollcommand [cb _sb_set $w.list.sby v]
+ pack $w_list -fill both -expand 1
+ grid $w.list -sticky nswe -padx {20 5} -columnspan 2
+ bind $w_list <Any-Motion> [cb _show_tooltip @%x,%y]
+ bind $w_list <Any-Enter> [cb _hide_tooltip]
+ bind $w_list <Any-Leave> [cb _hide_tooltip]
+ bind $w_list <Destroy> [cb _hide_tooltip]
+
+ grid columnconfigure $w 1 -weight 1
+ if {$is_detached} {
+ grid rowconfigure $w 3 -weight 1
+ } else {
+ grid rowconfigure $w 2 -weight 1
+ }
+
+ trace add variable @revtype write [cb _select]
+ bind $w_filter <Key-Return> [list focus $w_list]\;break
+ bind $w_filter <Key-Down> [list focus $w_list]
+
+ set fmt list
+ append fmt { %(refname)}
+ append fmt { [list}
+ append fmt { %(objecttype)}
+ append fmt { %(objectname)}
+ append fmt { [concat %(taggername) %(authorname)]}
+ append fmt { [concat %(taggerdate) %(authordate)]}
+ append fmt { %(subject)}
+ append fmt {] [list}
+ append fmt { %(*objecttype)}
+ append fmt { %(*objectname)}
+ append fmt { %(*authorname)}
+ append fmt { %(*authordate)}
+ append fmt { %(*subject)}
+ append fmt {]}
+ set all_refn [list]
+ set fr_fd [git_read for-each-ref \
+ --tcl \
+ --sort=-taggerdate \
+ --format=$fmt \
+ refs/heads \
+ refs/remotes \
+ refs/tags \
+ ]
+ fconfigure $fr_fd -translation lf -encoding utf-8
+ while {[gets $fr_fd line] > 0} {
+ set line [eval $line]
+ if {[lindex $line 1 0] eq {tag}} {
+ if {[lindex $line 2 0] eq {commit}} {
+ set sha1 [lindex $line 2 1]
+ } else {
+ continue
+ }
+ } elseif {[lindex $line 1 0] eq {commit}} {
+ set sha1 [lindex $line 1 1]
+ } else {
+ continue
+ }
+ set refn [lindex $line 0]
+ set tip_data($refn) [lrange $line 1 end]
+ lappend cmt_refn($sha1) $refn
+ lappend all_refn $refn
+ }
+ close $fr_fd
+
+ if {$unmerged_only} {
+ set fr_fd [git_read rev-list --all ^$::HEAD]
+ while {[gets $fr_fd sha1] > 0} {
+ if {[catch {set rlst $cmt_refn($sha1)}]} continue
+ foreach refn $rlst {
+ set inc($refn) 1
+ }
+ }
+ close $fr_fd
+ } else {
+ foreach refn $all_refn {
+ set inc($refn) 1
+ }
+ }
+
+ set spec_head [list]
+ foreach name [load_all_heads] {
+ set refn refs/heads/$name
+ if {[info exists inc($refn)]} {
+ lappend spec_head [list $name $refn]
+ }
+ }
+
+ set spec_trck [list]
+ foreach spec [all_tracking_branches] {
+ set refn [lindex $spec 0]
+ if {[info exists inc($refn)]} {
+ regsub ^refs/(heads|remotes)/ $refn {} name
+ lappend spec_trck [concat $name $spec]
+ }
+ }
+
+ set spec_tag [list]
+ foreach name [load_all_tags] {
+ set refn refs/tags/$name
+ if {[info exists inc($refn)]} {
+ lappend spec_tag [list $name $refn]
+ }
+ }
+
+ if {$is_detached} { set revtype HEAD
+ } elseif {[llength $spec_head] > 0} { set revtype head
+ } elseif {[llength $spec_trck] > 0} { set revtype trck
+ } elseif {[llength $spec_tag ] > 0} { set revtype tag
+ } else { set revtype expr
+ }
+
+ if {$revtype eq {head} && $current_branch ne {}} {
+ set i 0
+ foreach spec $spec_head {
+ if {[lindex $spec 0] eq $current_branch} {
+ $w_list selection clear 0 end
+ $w_list selection set $i
+ break
+ }
+ incr i
+ }
+ }
+
+ return $this
+}
+
+method none {text} {
+ if {![winfo exists $w.none_r]} {
+ radiobutton $w.none_r \
+ -anchor w \
+ -value none \
+ -variable @revtype
+ grid $w.none_r -sticky we -padx {0 5} -columnspan 2
+ }
+ $w.none_r configure -text $text
+}
+
+method get {} {
+ switch -- $revtype {
+ head -
+ trck -
+ tag {
+ set i [$w_list curselection]
+ if {$i ne {}} {
+ return [lindex $cur_specs $i 0]
+ } else {
+ return {}
+ }
+ }
+
+ HEAD { return HEAD }
+ expr { return $c_expr }
+ none { return {} }
+ default { error "unknown type of revision" }
+ }
+}
+
+method pick_tracking_branch {} {
+ set revtype trck
+}
+
+method focus_filter {} {
+ if {[$w_filter cget -state] eq {normal}} {
+ focus $w_filter
+ }
+}
+
+method bind_listbox {event script} {
+ bind $w_list $event $script
+}
+
+method get_local_branch {} {
+ if {$revtype eq {head}} {
+ return [_expr $this]
+ } else {
+ return {}
+ }
+}
+
+method get_tracking_branch {} {
+ set i [$w_list curselection]
+ if {$i eq {} || $revtype ne {trck}} {
+ return {}
+ }
+ return [lrange [lindex $cur_specs $i] 1 end]
+}
+
+method get_commit {} {
+ set e [_expr $this]
+ if {$e eq {}} {
+ return {}
+ }
+ return [git rev-parse --verify "$e^0"]
+}
+
+method commit_or_die {} {
+ if {[catch {set new [get_commit $this]} err]} {
+
+ # Cleanup the not-so-friendly error from rev-parse.
+ #
+ regsub {^fatal:\s*} $err {} err
+ if {$err eq {Needed a single revision}} {
+ set err {}
+ }
+
+ set top [winfo toplevel $w]
+ set msg "Invalid revision: [get $this]\n\n$err"
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $top] \
+ -parent $top \
+ -message $msg
+ error $msg
+ }
+ return $new
+}
+
+method _expr {} {
+ switch -- $revtype {
+ head -
+ trck -
+ tag {
+ set i [$w_list curselection]
+ if {$i ne {}} {
+ return [lindex $cur_specs $i 1]
+ } else {
+ error "No revision selected."
+ }
+ }
+
+ expr {
+ if {$c_expr ne {}} {
+ return $c_expr
+ } else {
+ error "Revision expression is empty."
+ }
+ }
+ HEAD { return HEAD }
+ none { return {} }
+ default { error "unknown type of revision" }
+ }
+}
+
+method _validate {d S} {
+ if {$d == 1} {
+ if {[regexp {\s} $S]} {
+ return 0
+ }
+ if {[string length $S] > 0} {
+ set revtype expr
+ }
+ }
+ return 1
+}
+
+method _filter {P} {
+ if {[regexp {\s} $P]} {
+ return 0
+ }
+ _rebuild $this $P
+ return 1
+}
+
+method _select {args} {
+ _rebuild $this $filter
+ focus_filter $this
+}
+
+method _rebuild {pat} {
+ set ste normal
+ switch -- $revtype {
+ head { set new $spec_head }
+ trck { set new $spec_trck }
+ tag { set new $spec_tag }
+ expr -
+ HEAD -
+ none {
+ set new [list]
+ set ste disabled
+ }
+ }
+
+ if {[$w_list cget -state] eq {disabled}} {
+ $w_list configure -state normal
+ }
+ $w_list delete 0 end
+
+ if {$pat ne {}} {
+ set pat *${pat}*
+ }
+ set cur_specs [list]
+ foreach spec $new {
+ set txt [lindex $spec 0]
+ if {$pat eq {} || [string match $pat $txt]} {
+ lappend cur_specs $spec
+ $w_list insert end $txt
+ }
+ }
+ if {$cur_specs ne {}} {
+ $w_list selection clear 0 end
+ $w_list selection set 0
+ }
+
+ if {[$w_filter cget -state] ne $ste} {
+ $w_list configure -state $ste
+ $w_filter configure -state $ste
+ }
+}
+
+method _delete {current} {
+ if {$current eq $w} {
+ delete_this
+ }
+}
+
+method _sb_set {sb orient first last} {
+ set old_focus [focus -lastfor $w]
+
+ if {$first == 0 && $last == 1} {
+ if {[winfo exists $sb]} {
+ destroy $sb
+ if {$old_focus ne {}} {
+ update
+ focus $old_focus
+ }
+ }
+ return
+ }
+
+ if {![winfo exists $sb]} {
+ if {$orient eq {h}} {
+ scrollbar $sb -orient h -command [list $w_list xview]
+ pack $sb -fill x -side bottom -before $w_list
+ } else {
+ scrollbar $sb -orient v -command [list $w_list yview]
+ pack $sb -fill y -side right -before $w_list
+ }
+ if {$old_focus ne {}} {
+ update
+ focus $old_focus
+ }
+ }
+ $sb set $first $last
+}
+
+method _show_tooltip {pos} {
+ if {$tooltip_wm ne {}} {
+ _open_tooltip $this
+ } elseif {$tooltip_timer eq {}} {
+ set tooltip_timer [after 1000 [cb _open_tooltip]]
+ }
+}
+
+method _open_tooltip {} {
+ global remote_url
+
+ set tooltip_timer {}
+ set pos_x [winfo pointerx $w_list]
+ set pos_y [winfo pointery $w_list]
+ if {[winfo containing $pos_x $pos_y] ne $w_list} {
+ _hide_tooltip $this
+ return
+ }
+
+ set pos @[join [list \
+ [expr {$pos_x - [winfo rootx $w_list]}] \
+ [expr {$pos_y - [winfo rooty $w_list]}]] ,]
+ set lno [$w_list index $pos]
+ if {$lno eq {}} {
+ _hide_tooltip $this
+ return
+ }
+
+ set spec [lindex $cur_specs $lno]
+ set refn [lindex $spec 1]
+ if {$refn eq {}} {
+ _hide_tooltip $this
+ return
+ }
+
+ if {$tooltip_wm eq {}} {
+ set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1]
+ wm overrideredirect $tooltip_wm 1
+ wm transient $tooltip_wm [winfo toplevel $w_list]
+ set tooltip_t $tooltip_wm.label
+ text $tooltip_t \
+ -takefocus 0 \
+ -highlightthickness 0 \
+ -relief flat \
+ -borderwidth 0 \
+ -wrap none \
+ -background lightyellow \
+ -foreground black
+ $tooltip_t tag conf section_header -font font_uibold
+ bind $tooltip_wm <Escape> [cb _hide_tooltip]
+ pack $tooltip_t
+ } else {
+ $tooltip_t conf -state normal
+ $tooltip_t delete 0.0 end
+ }
+
+ set data $tip_data($refn)
+ if {[lindex $data 0 0] eq {tag}} {
+ set tag [lindex $data 0]
+ if {[lindex $data 1 0] eq {commit}} {
+ set cmit [lindex $data 1]
+ } else {
+ set cmit {}
+ }
+ } elseif {[lindex $data 0 0] eq {commit}} {
+ set tag {}
+ set cmit [lindex $data 0]
+ }
+
+ $tooltip_t insert end [lindex $spec 0]
+ set last [_reflog_last $this [lindex $spec 1]]
+ if {$last ne {}} {
+ $tooltip_t insert end "\n"
+ $tooltip_t insert end "updated"
+ $tooltip_t insert end " $last"
+ }
+ $tooltip_t insert end "\n"
+
+ if {$tag ne {}} {
+ $tooltip_t insert end "\n"
+ $tooltip_t insert end "tag" section_header
+ $tooltip_t insert end " [lindex $tag 1]\n"
+ $tooltip_t insert end [lindex $tag 2]
+ $tooltip_t insert end " ([lindex $tag 3])\n"
+ $tooltip_t insert end [lindex $tag 4]
+ $tooltip_t insert end "\n"
+ }
+
+ if {$cmit ne {}} {
+ $tooltip_t insert end "\n"
+ $tooltip_t insert end "commit" section_header
+ $tooltip_t insert end " [lindex $cmit 1]\n"
+ $tooltip_t insert end [lindex $cmit 2]
+ $tooltip_t insert end " ([lindex $cmit 3])\n"
+ $tooltip_t insert end [lindex $cmit 4]
+ }
+
+ if {[llength $spec] > 2} {
+ $tooltip_t insert end "\n"
+ $tooltip_t insert end "remote" section_header
+ $tooltip_t insert end " [lindex $spec 2]\n"
+ $tooltip_t insert end "url"
+ $tooltip_t insert end " $remote_url([lindex $spec 2])\n"
+ $tooltip_t insert end "branch"
+ $tooltip_t insert end " [lindex $spec 3]"
+ }
+
+ $tooltip_t conf -state disabled
+ _position_tooltip $this
+}
+
+method _reflog_last {name} {
+ if {[info exists reflog_last($name)]} {
+ return reflog_last($name)
+ }
+
+ set last {}
+ if {[catch {set last [file mtime [gitdir $name]]}]
+ && ![catch {set g [open [gitdir logs $name] r]}]} {
+ fconfigure $g -translation binary
+ while {[gets $g line] >= 0} {
+ if {[regexp {> ([1-9][0-9]*) } $line line when]} {
+ set last $when
+ }
+ }
+ close $g
+ }
+
+ if {$last ne {}} {
+ set last [clock format $last -format {%a %b %e %H:%M:%S %Y}]
+ }
+ set reflog_last($name) $last
+ return $last
+}
+
+method _position_tooltip {} {
+ set max_h [lindex [split [$tooltip_t index end] .] 0]
+ set max_w 0
+ for {set i 1} {$i <= $max_h} {incr i} {
+ set c [lindex [split [$tooltip_t index "$i.0 lineend"] .] 1]
+ if {$c > $max_w} {set max_w $c}
+ }
+ $tooltip_t conf -width $max_w -height $max_h
+
+ set req_w [winfo reqwidth $tooltip_t]
+ set req_h [winfo reqheight $tooltip_t]
+ set pos_x [expr {[winfo pointerx .] + 5}]
+ set pos_y [expr {[winfo pointery .] + 10}]
+
+ set g "${req_w}x${req_h}"
+ if {$pos_x >= 0} {append g +}
+ append g $pos_x
+ if {$pos_y >= 0} {append g +}
+ append g $pos_y
+
+ wm geometry $tooltip_wm $g
+ raise $tooltip_wm
+}
+
+method _hide_tooltip {} {
+ if {$tooltip_wm ne {}} {
+ destroy $tooltip_wm
+ set tooltip_wm {}
+ }
+ if {$tooltip_timer ne {}} {
+ after cancel $tooltip_timer
+ set tooltip_timer {}
+ }
+}
+
+}
diff --git a/git-gui/lib/class.tcl b/git-gui/lib/class.tcl
index 9d298d0dcc..24e8cecea4 100644
--- a/git-gui/lib/class.tcl
+++ b/git-gui/lib/class.tcl
@@ -5,7 +5,7 @@ proc class {class body} {
if {[namespace exists $class]} {
error "class $class already declared"
}
- namespace eval $class {
+ namespace eval $class "
variable __nextid 0
variable __sealed 0
variable __field_list {}
@@ -13,10 +13,9 @@ proc class {class body} {
proc cb {name args} {
upvar this this
- set args [linsert $args 0 $name $this]
- return [uplevel [list namespace code $args]]
+ concat \[list ${class}::\$name \$this\] \$args
}
- }
+ "
namespace eval $class $body
}
@@ -51,15 +50,16 @@ proc constructor {name params body} {
set mbodyc {}
append mbodyc {set this } $class
- append mbodyc {::__o[incr } $class {::__nextid]} \;
- append mbodyc {namespace eval $this {}} \;
+ append mbodyc {::__o[incr } $class {::__nextid]::__d} \;
+ append mbodyc {create_this } $class \;
+ append mbodyc {set __this [namespace qualifiers $this]} \;
if {$__field_list ne {}} {
append mbodyc {upvar #0}
foreach n $__field_list {
set n [lindex $n 0]
- append mbodyc { ${this}::} $n { } $n
- regsub -all @$n\\M $body "\${this}::$n" body
+ append mbodyc { ${__this}::} $n { } $n
+ regsub -all @$n\\M $body "\${__this}::$n" body
}
append mbodyc \;
foreach n $__field_list {
@@ -80,10 +80,12 @@ proc method {name params body {deleted {}} {del_body {}}} {
set params [linsert $params 0 this]
set mbodyc {}
+ append mbodyc {set __this [namespace qualifiers $this]} \;
+
switch $deleted {
{} {}
ifdeleted {
- append mbodyc {if {![namespace exists $this]} }
+ append mbodyc {if {![namespace exists $__this]} }
append mbodyc \{ $del_body \; return \} \;
}
default {
@@ -98,10 +100,12 @@ proc method {name params body {deleted {}} {del_body {}}} {
if { [regexp -all -- $n\\M $body] == 1
&& [regexp -all -- \\\$$n\\M $body] == 1
&& [regexp -all -- \\\$$n\\( $body] == 0} {
- regsub -all \\\$$n\\M $body "\[set \${this}::$n\]" body
+ regsub -all \
+ \\\$$n\\M $body \
+ "\[set \${__this}::$n\]" body
} else {
- append decl { ${this}::} $n { } $n
- regsub -all @$n\\M $body "\${this}::$n" body
+ append decl { ${__this}::} $n { } $n
+ regsub -all @$n\\M $body "\${__this}::$n" body
}
}
}
@@ -112,11 +116,21 @@ proc method {name params body {deleted {}} {del_body {}}} {
namespace eval $class [list proc $name $params $mbodyc]
}
+proc create_this {class} {
+ upvar this this
+ namespace eval [namespace qualifiers $this] [list proc \
+ [namespace tail $this] \
+ [list name args] \
+ "eval \[list ${class}::\$name $this\] \$args" \
+ ]
+}
+
proc delete_this {{t {}}} {
if {$t eq {}} {
upvar this this
set t $this
}
+ set t [namespace qualifiers $t]
if {[namespace exists $t]} {namespace delete $t}
}
diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl
index e139f4da2b..f857a2ff5b 100644
--- a/git-gui/lib/commit.tcl
+++ b/git-gui/lib/commit.tcl
@@ -25,7 +25,7 @@ You are currently in the middle of a merge that has not been fully completed. Y
set msg {}
set parents [list]
if {[catch {
- set fd [open "| git cat-file commit $curHEAD" r]
+ set fd [git_read cat-file commit $curHEAD]
fconfigure $fd -encoding binary -translation lf
if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
set enc utf-8
@@ -37,9 +37,14 @@ You are currently in the middle of a merge that has not been fully completed. Y
set enc [string tolower [string range $line 9 end]]
}
}
- set msg [encoding convertfrom $enc [read $fd]]
- set msg [string trim $msg]
+ set msg [read $fd]
close $fd
+
+ set enc [tcl_encoding $enc]
+ if {$enc ne {}} {
+ set msg [encoding convertfrom $enc $msg]
+ }
+ set msg [string trim $msg]
} err]} {
error_popup "Error loading commit data for amend:\n\n$err"
return
@@ -58,7 +63,7 @@ You are currently in the middle of a merge that has not been fully completed. Y
$ui_comm insert end $msg
$ui_comm edit reset
$ui_comm edit modified false
- rescan {set ui_status_value {Ready.}}
+ rescan ui_ready
}
set GIT_COMMITTER_IDENT {}
@@ -108,12 +113,12 @@ proc create_new_commit {} {
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
- rescan {set ui_status_value {Ready.}}
+ rescan ui_ready
}
proc commit_tree {} {
global HEAD commit_type file_states ui_comm repo_config
- global ui_status_value pch_error
+ global pch_error
if {[committer_ident] eq {}} return
if {![lock_index update]} return
@@ -132,7 +137,7 @@ Another Git program has modified this repository since the last scan. A rescan
The rescan will be automatically started now.
}
unlock_index
- rescan {set ui_status_value {Ready.}}
+ rescan ui_ready
return
}
@@ -148,7 +153,7 @@ The rescan will be automatically started now.
U? {
error_popup "Unmerged files cannot be committed.
-File [short_path $path] has merge conflicts. You must resolve them and add the file before committing.
+File [short_path $path] has merge conflicts. You must resolve them and stage the file before committing.
"
unlock_index
return
@@ -164,7 +169,7 @@ File [short_path $path] cannot be committed by this program.
if {!$files_ready && ![string match *merge $curType]} {
info_popup {No changes to commit.
-You must add at least 1 file before you can commit.
+You must stage at least 1 file before you can commit.
}
unlock_index
return
@@ -206,22 +211,22 @@ A good commit message has the following format:
return
}
- set ui_status_value {Calling pre-commit hook...}
+ ui_status {Calling pre-commit hook...}
set pch_error {}
set fd_ph [open "| $pchook" r]
- fconfigure $fd_ph -blocking 0 -translation binary
+ fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
fileevent $fd_ph readable \
[list commit_prehook_wait $fd_ph $curHEAD $msg]
}
proc commit_prehook_wait {fd_ph curHEAD msg} {
- global pch_error ui_status_value
+ global pch_error
append pch_error [read $fd_ph]
fconfigure $fd_ph -blocking 1
if {[eof $fd_ph]} {
if {[catch {close $fd_ph}]} {
- set ui_status_value {Commit declined by pre-commit hook.}
+ ui_status {Commit declined by pre-commit hook.}
hook_failed_popup pre-commit $pch_error
unlock_index
} else {
@@ -234,25 +239,23 @@ proc commit_prehook_wait {fd_ph curHEAD msg} {
}
proc commit_writetree {curHEAD msg} {
- global ui_status_value
-
- set ui_status_value {Committing changes...}
- set fd_wt [open "| git write-tree" r]
+ ui_status {Committing changes...}
+ set fd_wt [git_read write-tree]
fileevent $fd_wt readable \
[list commit_committree $fd_wt $curHEAD $msg]
}
proc commit_committree {fd_wt curHEAD msg} {
global HEAD PARENT MERGE_HEAD commit_type
- global all_heads current_branch
- global ui_status_value ui_comm selected_commit_type
+ global current_branch
+ global ui_comm selected_commit_type
global file_states selected_paths rescan_active
global repo_config
gets $fd_wt tree_id
if {$tree_id eq {} || [catch {close $fd_wt} err]} {
error_popup "write-tree failed:\n\n$err"
- set ui_status_value {Commit failed.}
+ ui_status {Commit failed.}
unlock_index
return
}
@@ -260,7 +263,7 @@ proc commit_committree {fd_wt curHEAD msg} {
# -- Verify this wasn't an empty change.
#
if {$commit_type eq {normal}} {
- set fd_ot [open "| git cat-file commit $PARENT" r]
+ set fd_ot [git_read cat-file commit $PARENT]
fconfigure $fd_ot -encoding binary -translation lf
set old_tree [gets $fd_ot]
close $fd_ot
@@ -280,7 +283,7 @@ No files were modified by this commit and it was not a merge commit.
A rescan will be automatically started now.
}
unlock_index
- rescan {set ui_status_value {No changes to commit.}}
+ rescan {ui_status {No changes to commit.}}
return
}
}
@@ -289,11 +292,18 @@ A rescan will be automatically started now.
#
set msg_p [gitdir COMMIT_EDITMSG]
set msg_wt [open $msg_p w]
+ fconfigure $msg_wt -translation lf
if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
set enc utf-8
}
- fconfigure $msg_wt -encoding binary -translation binary
- puts -nonewline $msg_wt [encoding convertto $enc $msg]
+ set use_enc [tcl_encoding $enc]
+ if {$use_enc ne {}} {
+ fconfigure $msg_wt -encoding $use_enc
+ } else {
+ puts stderr "warning: Tcl does not support encoding '$enc'."
+ fconfigure $msg_wt -encoding utf-8
+ }
+ puts -nonewline $msg_wt $msg
close $msg_wt
# -- Create the commit.
@@ -305,7 +315,7 @@ A rescan will be automatically started now.
lappend cmd <$msg_p
if {[catch {set cmt_id [eval git $cmd]} err]} {
error_popup "commit-tree failed:\n\n$err"
- set ui_status_value {Commit failed.}
+ ui_status {Commit failed.}
unlock_index
return
}
@@ -327,7 +337,7 @@ A rescan will be automatically started now.
git update-ref -m $reflogm HEAD $cmt_id $curHEAD
} err]} {
error_popup "update-ref failed:\n\n$err"
- set ui_status_value {Commit failed.}
+ ui_status {Commit failed.}
unlock_index
return
}
@@ -342,7 +352,12 @@ A rescan will be automatically started now.
# -- Let rerere do its thing.
#
- if {[file isdirectory [gitdir rr-cache]]} {
+ if {[get_config rerere.enabled] eq {}} {
+ set rerere [file isdirectory [gitdir rr-cache]]
+ } else {
+ set rerere [is_config_true rerere.enabled]
+ }
+ if {$rerere} {
catch {git rerere}
}
@@ -364,17 +379,13 @@ A rescan will be automatically started now.
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
+ if {$::GITGUI_BCK_exists} {
+ catch {file delete [gitdir GITGUI_BCK]}
+ set ::GITGUI_BCK_exists 0
+ }
if {[is_enabled singlecommit]} do_quit
- # -- Make sure our current branch exists.
- #
- if {$commit_type eq {initial}} {
- lappend all_heads $current_branch
- set all_heads [lsort -unique $all_heads]
- populate_branch_menu
- }
-
# -- Update in memory status
#
set selected_commit_type new
@@ -416,6 +427,5 @@ A rescan will be automatically started now.
display_all_files
unlock_index
reshow_diff
- set ui_status_value \
- "Created commit [string range $cmt_id 0 7]: $subject"
+ ui_status "Created commit [string range $cmt_id 0 7]: $subject"
}
diff --git a/git-gui/lib/console.tcl b/git-gui/lib/console.tcl
index 34de5d4859..6f718fbac3 100644
--- a/git-gui/lib/console.tcl
+++ b/git-gui/lib/console.tcl
@@ -7,6 +7,7 @@ field t_short
field t_long
field w
field console_cr
+field is_toplevel 1; # are we our own window?
constructor new {short_title long_title} {
set t_short $short_title
@@ -15,10 +16,25 @@ constructor new {short_title long_title} {
return $this
}
+constructor embed {path title} {
+ set t_short {}
+ set t_long $title
+ set w $path
+ set is_toplevel 0
+ _init $this
+ return $this
+}
+
method _init {} {
global M1B
- make_toplevel top w -autodelete 0
- wm title $top "[appname] ([reponame]): $t_short"
+
+ if {$is_toplevel} {
+ make_toplevel top w -autodelete 0
+ wm title $top "[appname] ([reponame]): $t_short"
+ } else {
+ frame $w
+ }
+
set console_cr 1.0
frame $w.m
@@ -61,31 +77,26 @@ method _init {} {
$w.m.t tag remove sel 0.0 end
"
- button $w.ok -text {Close} \
- -state disabled \
- -command "destroy $w"
- pack $w.ok -side bottom -anchor e -pady 10 -padx 10
+ if {$is_toplevel} {
+ button $w.ok -text {Close} \
+ -state disabled \
+ -command [list destroy $w]
+ pack $w.ok -side bottom -anchor e -pady 10 -padx 10
+ bind $w <Visibility> [list focus $w]
+ }
bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y"
bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
- bind $w <Visibility> "focus $w"
}
method exec {cmd {after {}}} {
- # -- Cygwin's Tcl tosses the enviroment when we exec our child.
- # But most users need that so we have to relogin. :-(
- #
- if {[is_Cygwin]} {
- set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"]
+ if {[lindex $cmd 0] eq {git}} {
+ set fd_f [eval git_read --stderr [lrange $cmd 1 end]]
+ } else {
+ lappend cmd 2>@1
+ set fd_f [_open_stdout_stderr $cmd]
}
-
- # -- Tcl won't let us redirect both stdout and stderr to
- # the same pipe. So pass it through cat...
- #
- set cmd [concat | $cmd |& cat]
-
- set fd_f [open $cmd r]
fconfigure $fd_f -blocking 0 -translation binary
fileevent $fd_f readable [cb _read $fd_f $after]
}
@@ -159,20 +170,32 @@ method chain {cmdlist {ok 1}} {
}
}
+method insert {txt} {
+ if {![winfo exists $w.m.t]} {_init $this}
+ $w.m.t conf -state normal
+ $w.m.t insert end "$txt\n"
+ set console_cr [$w.m.t index {end -1c}]
+ $w.m.t conf -state disabled
+}
+
method done {ok} {
if {$ok} {
if {[winfo exists $w.m.s]} {
$w.m.s conf -background green -text {Success}
- $w.ok conf -state normal
- focus $w.ok
+ if {$is_toplevel} {
+ $w.ok conf -state normal
+ focus $w.ok
+ }
}
} else {
if {![winfo exists $w.m.s]} {
_init $this
}
$w.m.s conf -background red -text {Error: Command Failed}
- $w.ok conf -state normal
- focus $w.ok
+ if {$is_toplevel} {
+ $w.ok conf -state normal
+ focus $w.ok
+ }
}
delete_this
}
diff --git a/git-gui/lib/database.tcl b/git-gui/lib/database.tcl
index 43e4a289bb..0657cc2245 100644
--- a/git-gui/lib/database.tcl
+++ b/git-gui/lib/database.tcl
@@ -2,7 +2,7 @@
# Copyright (C) 2006, 2007 Shawn Pearce
proc do_stats {} {
- set fd [open "| git count-objects -v" r]
+ set fd [git_read count-objects -v]
while {[gets $fd line] > 0} {
if {[regexp {^([^:]+): (\d+)$} $line _ name value]} {
set stats($name) $value
@@ -87,3 +87,30 @@ proc do_fsck_objects {} {
lappend cmd --strict
console::exec $w $cmd
}
+
+proc hint_gc {} {
+ set object_limit 8
+ if {[is_Windows]} {
+ set object_limit 1
+ }
+
+ set objects_current [llength [glob \
+ -directory [gitdir objects 42] \
+ -nocomplain \
+ -tails \
+ -- \
+ *]]
+
+ if {$objects_current >= $object_limit} {
+ set objects_current [expr {$objects_current * 256}]
+ set object_limit [expr {$object_limit * 256}]
+ if {[ask_popup \
+ "This repository currently has approximately $objects_current loose objects.
+
+To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
+
+Compress the database now?"] eq yes} {
+ do_gc
+ }
+ }
+}
diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl
index 29436b50cb..e09e1257e1 100644
--- a/git-gui/lib/diff.tcl
+++ b/git-gui/lib/diff.tcl
@@ -17,7 +17,7 @@ proc clear_diff {} {
}
proc reshow_diff {} {
- global ui_status_value file_states file_lists
+ global file_states file_lists
global current_diff_path current_diff_side
set p $current_diff_path
@@ -49,13 +49,13 @@ A rescan will be automatically started to find other files which may have the sa
clear_diff
display_file $path __
- rescan {set ui_status_value {Ready.}} 0
+ rescan ui_ready 0
}
proc show_diff {path w {lno {}}} {
global file_states file_lists
global is_3way_diff diff_active repo_config
- global ui_diff ui_status_value ui_index ui_workdir
+ global ui_diff ui_index ui_workdir
global current_diff_path current_diff_side current_diff_header
if {$diff_active || ![lock_index read]} return
@@ -78,7 +78,7 @@ proc show_diff {path w {lno {}}} {
set current_diff_path $path
set current_diff_side $w
set current_diff_header {}
- set ui_status_value "Loading diff of [escape_path $path]..."
+ ui_status "Loading diff of [escape_path $path]..."
# - Git won't give us the diff, there's nothing to compare to!
#
@@ -86,13 +86,14 @@ proc show_diff {path w {lno {}}} {
set max_sz [expr {128 * 1024}]
if {[catch {
set fd [open $path r]
+ fconfigure $fd -eofchar {}
set content [read $fd $max_sz]
close $fd
set sz [file size $path]
} err ]} {
set diff_active 0
unlock_index
- set ui_status_value "Unable to display [escape_path $path]"
+ ui_status "Unable to display [escape_path $path]"
error_popup "Error loading file:\n\n$err"
return
}
@@ -127,11 +128,11 @@ proc show_diff {path w {lno {}}} {
$ui_diff conf -state disabled
set diff_active 0
unlock_index
- set ui_status_value {Ready.}
+ ui_ready
return
}
- set cmd [list | git]
+ set cmd [list]
if {$w eq $ui_index} {
lappend cmd diff-index
lappend cmd --cached
@@ -154,10 +155,10 @@ proc show_diff {path w {lno {}}} {
lappend cmd --
lappend cmd $path
- if {[catch {set fd [open $cmd r]} err]} {
+ if {[catch {set fd [eval git_read --nice $cmd]} err]} {
set diff_active 0
unlock_index
- set ui_status_value "Unable to display [escape_path $path]"
+ ui_status "Unable to display [escape_path $path]"
error_popup "Error loading diff:\n\n$err"
return
}
@@ -170,7 +171,7 @@ proc show_diff {path w {lno {}}} {
}
proc read_diff {fd} {
- global ui_diff ui_status_value diff_active
+ global ui_diff diff_active
global is_3way_diff current_diff_header
$ui_diff conf -state normal
@@ -256,7 +257,7 @@ proc read_diff {fd} {
close $fd
set diff_active 0
unlock_index
- set ui_status_value {Ready.}
+ ui_ready
if {[$ui_diff index end] eq {2.0}} {
handle_empty_diff
@@ -271,7 +272,7 @@ proc apply_hunk {x y} {
if {$current_diff_path eq {} || $current_diff_header eq {}} return
if {![lock_index apply_hunk]} return
- set apply_cmd {git apply --cached --whitespace=nowarn}
+ set apply_cmd {apply --cached --whitespace=nowarn}
set mi [lindex $file_states($current_diff_path) 0]
if {$current_diff_side eq $ui_index} {
set mode unstage
@@ -301,7 +302,7 @@ proc apply_hunk {x y} {
}
if {[catch {
- set p [open "| $apply_cmd" w]
+ set p [eval git_write $apply_cmd]
fconfigure $p -translation binary -encoding binary
puts -nonewline $p $current_diff_header
puts -nonewline $p [$ui_diff get $s_lno $e_lno]
diff --git a/git-gui/lib/encoding.tcl b/git-gui/lib/encoding.tcl
new file mode 100644
index 0000000000..7f06b0d47f
--- /dev/null
+++ b/git-gui/lib/encoding.tcl
@@ -0,0 +1,276 @@
+# git-gui encoding support
+# Copyright (C) 2005 Paul Mackerras <paulus@samba.org>
+# (Copied from gitk, commit fd8ccbec4f0161)
+
+# This list of encoding names and aliases is distilled from
+# http://www.iana.org/assignments/character-sets.
+# Not all of them are supported by Tcl.
+set encoding_aliases {
+ { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
+ ISO646-US US-ASCII us IBM367 cp367 csASCII }
+ { ISO-10646-UTF-1 csISO10646UTF1 }
+ { ISO_646.basic:1983 ref csISO646basic1983 }
+ { INVARIANT csINVARIANT }
+ { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
+ { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
+ { NATS-SEFI iso-ir-8-1 csNATSSEFI }
+ { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
+ { NATS-DANO iso-ir-9-1 csNATSDANO }
+ { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
+ { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
+ { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
+ { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
+ { ISO-2022-KR csISO2022KR }
+ { EUC-KR csEUCKR }
+ { ISO-2022-JP csISO2022JP }
+ { ISO-2022-JP-2 csISO2022JP2 }
+ { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
+ csISO13JISC6220jp }
+ { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
+ { IT iso-ir-15 ISO646-IT csISO15Italian }
+ { PT iso-ir-16 ISO646-PT csISO16Portuguese }
+ { ES iso-ir-17 ISO646-ES csISO17Spanish }
+ { greek7-old iso-ir-18 csISO18Greek7Old }
+ { latin-greek iso-ir-19 csISO19LatinGreek }
+ { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
+ { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
+ { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
+ { ISO_5427 iso-ir-37 csISO5427Cyrillic }
+ { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
+ { BS_viewdata iso-ir-47 csISO47BSViewdata }
+ { INIS iso-ir-49 csISO49INIS }
+ { INIS-8 iso-ir-50 csISO50INIS8 }
+ { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
+ { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
+ { ISO_5428:1980 iso-ir-55 csISO5428Greek }
+ { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
+ { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
+ { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
+ csISO60Norwegian1 }
+ { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
+ { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
+ { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
+ { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
+ { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
+ { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
+ { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
+ { greek7 iso-ir-88 csISO88Greek7 }
+ { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
+ { iso-ir-90 csISO90 }
+ { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
+ { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
+ csISO92JISC62991984b }
+ { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
+ { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
+ { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
+ csISO95JIS62291984handadd }
+ { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
+ { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
+ { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
+ { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
+ CP819 csISOLatin1 }
+ { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
+ { T.61-7bit iso-ir-102 csISO102T617bit }
+ { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
+ { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
+ { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
+ { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
+ { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
+ { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
+ { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
+ { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
+ arabic csISOLatinArabic }
+ { ISO_8859-6-E csISO88596E ISO-8859-6-E }
+ { ISO_8859-6-I csISO88596I ISO-8859-6-I }
+ { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
+ greek greek8 csISOLatinGreek }
+ { T.101-G2 iso-ir-128 csISO128T101G2 }
+ { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
+ csISOLatinHebrew }
+ { ISO_8859-8-E csISO88598E ISO-8859-8-E }
+ { ISO_8859-8-I csISO88598I ISO-8859-8-I }
+ { CSN_369103 iso-ir-139 csISO139CSN369103 }
+ { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
+ { ISO_6937-2-add iso-ir-142 csISOTextComm }
+ { IEC_P27-1 iso-ir-143 csISO143IECP271 }
+ { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
+ csISOLatinCyrillic }
+ { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
+ { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
+ { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
+ { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
+ { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
+ { ISO_6937-2-25 iso-ir-152 csISO6937Add }
+ { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
+ { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
+ { ISO_10367-box iso-ir-155 csISO10367Box }
+ { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
+ { latin-lap lap iso-ir-158 csISO158Lap }
+ { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
+ { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
+ { us-dk csUSDK }
+ { dk-us csDKUS }
+ { JIS_X0201 X0201 csHalfWidthKatakana }
+ { KSC5636 ISO646-KR csKSC5636 }
+ { ISO-10646-UCS-2 csUnicode }
+ { ISO-10646-UCS-4 csUCS4 }
+ { DEC-MCS dec csDECMCS }
+ { hp-roman8 roman8 r8 csHPRoman8 }
+ { macintosh mac csMacintosh }
+ { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
+ csIBM037 }
+ { IBM038 EBCDIC-INT cp038 csIBM038 }
+ { IBM273 CP273 csIBM273 }
+ { IBM274 EBCDIC-BE CP274 csIBM274 }
+ { IBM275 EBCDIC-BR cp275 csIBM275 }
+ { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
+ { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
+ { IBM280 CP280 ebcdic-cp-it csIBM280 }
+ { IBM281 EBCDIC-JP-E cp281 csIBM281 }
+ { IBM284 CP284 ebcdic-cp-es csIBM284 }
+ { IBM285 CP285 ebcdic-cp-gb csIBM285 }
+ { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
+ { IBM297 cp297 ebcdic-cp-fr csIBM297 }
+ { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
+ { IBM423 cp423 ebcdic-cp-gr csIBM423 }
+ { IBM424 cp424 ebcdic-cp-he csIBM424 }
+ { IBM437 cp437 437 csPC8CodePage437 }
+ { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
+ { IBM775 cp775 csPC775Baltic }
+ { IBM850 cp850 850 csPC850Multilingual }
+ { IBM851 cp851 851 csIBM851 }
+ { IBM852 cp852 852 csPCp852 }
+ { IBM855 cp855 855 csIBM855 }
+ { IBM857 cp857 857 csIBM857 }
+ { IBM860 cp860 860 csIBM860 }
+ { IBM861 cp861 861 cp-is csIBM861 }
+ { IBM862 cp862 862 csPC862LatinHebrew }
+ { IBM863 cp863 863 csIBM863 }
+ { IBM864 cp864 csIBM864 }
+ { IBM865 cp865 865 csIBM865 }
+ { IBM866 cp866 866 csIBM866 }
+ { IBM868 CP868 cp-ar csIBM868 }
+ { IBM869 cp869 869 cp-gr csIBM869 }
+ { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
+ { IBM871 CP871 ebcdic-cp-is csIBM871 }
+ { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
+ { IBM891 cp891 csIBM891 }
+ { IBM903 cp903 csIBM903 }
+ { IBM904 cp904 904 csIBBM904 }
+ { IBM905 CP905 ebcdic-cp-tr csIBM905 }
+ { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
+ { IBM1026 CP1026 csIBM1026 }
+ { EBCDIC-AT-DE csIBMEBCDICATDE }
+ { EBCDIC-AT-DE-A csEBCDICATDEA }
+ { EBCDIC-CA-FR csEBCDICCAFR }
+ { EBCDIC-DK-NO csEBCDICDKNO }
+ { EBCDIC-DK-NO-A csEBCDICDKNOA }
+ { EBCDIC-FI-SE csEBCDICFISE }
+ { EBCDIC-FI-SE-A csEBCDICFISEA }
+ { EBCDIC-FR csEBCDICFR }
+ { EBCDIC-IT csEBCDICIT }
+ { EBCDIC-PT csEBCDICPT }
+ { EBCDIC-ES csEBCDICES }
+ { EBCDIC-ES-A csEBCDICESA }
+ { EBCDIC-ES-S csEBCDICESS }
+ { EBCDIC-UK csEBCDICUK }
+ { EBCDIC-US csEBCDICUS }
+ { UNKNOWN-8BIT csUnknown8BiT }
+ { MNEMONIC csMnemonic }
+ { MNEM csMnem }
+ { VISCII csVISCII }
+ { VIQR csVIQR }
+ { KOI8-R csKOI8R }
+ { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
+ { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
+ { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
+ { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
+ { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
+ { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
+ { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
+ { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
+ { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
+ { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
+ { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
+ { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
+ { IBM1047 IBM-1047 }
+ { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
+ { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
+ { UNICODE-1-1 csUnicode11 }
+ { CESU-8 csCESU-8 }
+ { BOCU-1 csBOCU-1 }
+ { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
+ { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
+ l8 }
+ { ISO-8859-15 ISO_8859-15 Latin-9 }
+ { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
+ { GBK CP936 MS936 windows-936 }
+ { JIS_Encoding csJISEncoding }
+ { Shift_JIS MS_Kanji csShiftJIS }
+ { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
+ EUC-JP }
+ { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
+ { ISO-10646-UCS-Basic csUnicodeASCII }
+ { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
+ { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
+ { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
+ { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
+ { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
+ { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
+ { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
+ { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
+ { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
+ { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
+ { Adobe-Standard-Encoding csAdobeStandardEncoding }
+ { Ventura-US csVenturaUS }
+ { Ventura-International csVenturaInternational }
+ { PC8-Danish-Norwegian csPC8DanishNorwegian }
+ { PC8-Turkish csPC8Turkish }
+ { IBM-Symbols csIBMSymbols }
+ { IBM-Thai csIBMThai }
+ { HP-Legal csHPLegal }
+ { HP-Pi-font csHPPiFont }
+ { HP-Math8 csHPMath8 }
+ { Adobe-Symbol-Encoding csHPPSMath }
+ { HP-DeskTop csHPDesktop }
+ { Ventura-Math csVenturaMath }
+ { Microsoft-Publishing csMicrosoftPublishing }
+ { Windows-31J csWindows31J }
+ { GB2312 csGB2312 }
+ { Big5 csBig5 }
+}
+
+proc tcl_encoding {enc} {
+ global encoding_aliases
+ set names [encoding names]
+ set lcnames [string tolower $names]
+ set enc [string tolower $enc]
+ set i [lsearch -exact $lcnames $enc]
+ if {$i < 0} {
+ # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
+ if {[regsub {^iso[-_]} $enc iso encx]} {
+ set i [lsearch -exact $lcnames $encx]
+ }
+ }
+ if {$i < 0} {
+ foreach l $encoding_aliases {
+ set ll [string tolower $l]
+ if {[lsearch -exact $ll $enc] < 0} continue
+ # look through the aliases for one that tcl knows about
+ foreach e $ll {
+ set i [lsearch -exact $lcnames $e]
+ if {$i < 0} {
+ if {[regsub {^iso[-_]} $e iso ex]} {
+ set i [lsearch -exact $lcnames $ex]
+ }
+ }
+ if {$i >= 0} break
+ }
+ break
+ }
+ }
+ if {$i >= 0} {
+ return [lindex $names $i]
+ }
+ return {}
+}
diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl
index d0253ae2ff..16a22187b2 100644
--- a/git-gui/lib/error.tcl
+++ b/git-gui/lib/error.tcl
@@ -51,12 +51,15 @@ proc ask_popup {msg} {
if {[reponame] ne {}} {
append title " ([reponame])"
}
- return [tk_messageBox \
- -parent . \
+ set cmd [list tk_messageBox \
-icon question \
-type yesno \
-title $title \
-message $msg]
+ if {[winfo ismapped .]} {
+ lappend cmd -parent .
+ }
+ eval $cmd
}
proc hook_failed_popup {hook msg} {
diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl
index 42742850ee..f47f9290c8 100644
--- a/git-gui/lib/index.tcl
+++ b/git-gui/lib/index.tcl
@@ -2,7 +2,7 @@
# Copyright (C) 2006, 2007 Shawn Pearce
proc update_indexinfo {msg pathList after} {
- global update_index_cp ui_status_value
+ global update_index_cp
if {![lock_index update]} return
@@ -12,12 +12,12 @@ proc update_indexinfo {msg pathList after} {
set batch [expr {int($totalCnt * .01) + 1}]
if {$batch > 25} {set batch 25}
- set ui_status_value [format \
+ ui_status [format \
"$msg... %i/%i files (%.2f%%)" \
$update_index_cp \
$totalCnt \
0.0]
- set fd [open "| git update-index -z --index-info" w]
+ set fd [git_write update-index -z --index-info]
fconfigure $fd \
-blocking 0 \
-buffering full \
@@ -36,7 +36,7 @@ proc update_indexinfo {msg pathList after} {
}
proc write_update_indexinfo {fd pathList totalCnt batch msg after} {
- global update_index_cp ui_status_value
+ global update_index_cp
global file_states current_diff_path
if {$update_index_cp >= $totalCnt} {
@@ -67,7 +67,7 @@ proc write_update_indexinfo {fd pathList totalCnt batch msg after} {
display_file $path $new
}
- set ui_status_value [format \
+ ui_status [format \
"$msg... %i/%i files (%.2f%%)" \
$update_index_cp \
$totalCnt \
@@ -75,7 +75,7 @@ proc write_update_indexinfo {fd pathList totalCnt batch msg after} {
}
proc update_index {msg pathList after} {
- global update_index_cp ui_status_value
+ global update_index_cp
if {![lock_index update]} return
@@ -85,12 +85,12 @@ proc update_index {msg pathList after} {
set batch [expr {int($totalCnt * .01) + 1}]
if {$batch > 25} {set batch 25}
- set ui_status_value [format \
+ ui_status [format \
"$msg... %i/%i files (%.2f%%)" \
$update_index_cp \
$totalCnt \
0.0]
- set fd [open "| git update-index --add --remove -z --stdin" w]
+ set fd [git_write update-index --add --remove -z --stdin]
fconfigure $fd \
-blocking 0 \
-buffering full \
@@ -109,7 +109,7 @@ proc update_index {msg pathList after} {
}
proc write_update_index {fd pathList totalCnt batch msg after} {
- global update_index_cp ui_status_value
+ global update_index_cp
global file_states current_diff_path
if {$update_index_cp >= $totalCnt} {
@@ -144,7 +144,7 @@ proc write_update_index {fd pathList totalCnt batch msg after} {
display_file $path $new
}
- set ui_status_value [format \
+ ui_status [format \
"$msg... %i/%i files (%.2f%%)" \
$update_index_cp \
$totalCnt \
@@ -152,7 +152,7 @@ proc write_update_index {fd pathList totalCnt batch msg after} {
}
proc checkout_index {msg pathList after} {
- global update_index_cp ui_status_value
+ global update_index_cp
if {![lock_index update]} return
@@ -162,18 +162,18 @@ proc checkout_index {msg pathList after} {
set batch [expr {int($totalCnt * .01) + 1}]
if {$batch > 25} {set batch 25}
- set ui_status_value [format \
+ ui_status [format \
"$msg... %i/%i files (%.2f%%)" \
$update_index_cp \
$totalCnt \
0.0]
- set cmd [list git checkout-index]
- lappend cmd --index
- lappend cmd --quiet
- lappend cmd --force
- lappend cmd -z
- lappend cmd --stdin
- set fd [open "| $cmd " w]
+ set fd [git_write checkout-index \
+ --index \
+ --quiet \
+ --force \
+ -z \
+ --stdin \
+ ]
fconfigure $fd \
-blocking 0 \
-buffering full \
@@ -192,7 +192,7 @@ proc checkout_index {msg pathList after} {
}
proc write_checkout_index {fd pathList totalCnt batch msg after} {
- global update_index_cp ui_status_value
+ global update_index_cp
global file_states current_diff_path
if {$update_index_cp >= $totalCnt} {
@@ -217,7 +217,7 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} {
}
}
- set ui_status_value [format \
+ ui_status [format \
"$msg... %i/%i files (%.2f%%)" \
$update_index_cp \
$totalCnt \
@@ -249,7 +249,7 @@ proc unstage_helper {txt paths} {
update_indexinfo \
$txt \
$pathList \
- [concat $after {set ui_status_value {Ready.}}]
+ [concat $after [list ui_ready]]
}
}
@@ -293,7 +293,7 @@ proc add_helper {txt paths} {
update_index \
$txt \
$pathList \
- [concat $after {set ui_status_value {Ready to commit.}}]
+ [concat $after {ui_status {Ready to commit.}}]
}
}
@@ -360,7 +360,7 @@ proc revert_helper {txt paths} {
"[appname] ([reponame])" \
"Revert changes in $s?
-Any unadded changes will be permanently lost by the revert." \
+Any unstaged changes will be permanently lost by the revert." \
question \
1 \
{Do Nothing} \
@@ -370,7 +370,7 @@ Any unadded changes will be permanently lost by the revert." \
checkout_index \
$txt \
$pathList \
- [concat $after {set ui_status_value {Ready.}}]
+ [concat $after [list ui_ready]]
} else {
unlock_index
}
diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl
index 889182f545..5de0d82b14 100644
--- a/git-gui/lib/merge.tcl
+++ b/git-gui/lib/merge.tcl
@@ -1,9 +1,12 @@
# git-gui branch merge support
# Copyright (C) 2006, 2007 Shawn Pearce
-namespace eval merge {
+class merge {
+
+field w ; # top level window
+field w_rev ; # mega-widget to pick the revision to merge
-proc _can_merge {} {
+method _can_merge {} {
global HEAD commit_type file_states
if {[string match amend* $commit_type]} {
@@ -28,7 +31,7 @@ Another Git program has modified this repository since the last scan. A rescan
The rescan will be automatically started now.
}
unlock_index
- rescan {set ui_status_value {Ready.}}
+ rescan ui_ready
return 0
}
@@ -42,7 +45,7 @@ The rescan will be automatically started now.
File [short_path $path] has merge conflicts.
-You must resolve them, add the file, and commit to complete the current merge. Only then can you begin another merge.
+You must resolve them, stage the file, and commit to complete the current merge. Only then can you begin another merge.
"
unlock_index
return 0
@@ -63,145 +66,93 @@ You should complete the current commit before starting a merge. Doing so will h
return 1
}
-proc _refs {w list} {
- set r {}
- foreach i [$w.source.l curselection] {
- lappend r [lindex [lindex $list $i] 0]
+method _rev {} {
+ if {[catch {$w_rev commit_or_die}]} {
+ return {}
}
- return $r
+ return [$w_rev get]
}
-proc _visualize {w list} {
- set revs [_refs $w $list]
- if {$revs eq {}} return
- lappend revs --not HEAD
- do_gitk $revs
+method _visualize {} {
+ set rev [_rev $this]
+ if {$rev ne {}} {
+ do_gitk [list $rev --not HEAD]
+ }
}
-proc _start {w list} {
- global HEAD ui_status_value current_branch
+method _start {} {
+ global HEAD current_branch remote_url
- set cmd [list git merge]
- set names [_refs $w $list]
- set revcnt [llength $names]
- append cmd { } $names
-
- if {$revcnt == 0} {
+ set name [_rev $this]
+ if {$name eq {}} {
return
- } elseif {$revcnt == 1} {
- set unit branch
- } elseif {$revcnt <= 15} {
- set unit branches
-
- if {[tk_dialog \
- $w.confirm_octopus \
- [wm title $w] \
- "Use octopus merge strategy?
-
-You are merging $revcnt branches at once. This requires using the octopus merge driver, which may not succeed if there are file-level conflicts.
-" \
- question \
- 0 \
- {Cancel} \
- {Use octopus} \
- ] != 1} return
- } else {
- tk_messageBox \
- -icon error \
- -type ok \
- -title [wm title $w] \
- -parent $w \
- -message "Too many branches selected.
+ }
-You have requested to merge $revcnt branches in an octopus merge. This exceeds Git's internal limit of 15 branches per merge.
+ set spec [$w_rev get_tracking_branch]
+ set cmit [$w_rev get_commit]
-Please select fewer branches. To merge more than 15 branches, merge the branches in batches.
-"
- return
+ set fh [open [gitdir FETCH_HEAD] w]
+ fconfigure $fh -translation lf
+ if {$spec eq {}} {
+ set remote .
+ set branch $name
+ set stitle $branch
+ } else {
+ set remote $remote_url([lindex $spec 1])
+ if {[regexp {^[^:@]*@[^:]*:/} $remote]} {
+ regsub {^[^:@]*@} $remote {} remote
+ }
+ set branch [lindex $spec 2]
+ set stitle "$branch of $remote"
}
-
- set msg "Merging $current_branch, [join $names {, }]"
- set ui_status_value "$msg..."
- set cons [console::new "Merge" $msg]
- console::exec $cons $cmd \
- [namespace code [list _finish $revcnt $cons]]
+ regsub ^refs/heads/ $branch {} branch
+ puts $fh "$cmit\t\tbranch '$branch' of $remote"
+ close $fh
+
+ set cmd [list git]
+ lappend cmd merge
+ lappend cmd --strategy=recursive
+ lappend cmd [git fmt-merge-msg <[gitdir FETCH_HEAD]]
+ lappend cmd HEAD
+ lappend cmd $cmit
+
+ set msg "Merging $current_branch and $stitle"
+ ui_status "$msg..."
+ set cons [console::new "Merge" "merge $stitle"]
+ console::exec $cons $cmd [cb _finish $cons]
wm protocol $w WM_DELETE_WINDOW {}
destroy $w
}
-proc _finish {revcnt w ok} {
- console::done $w $ok
+method _finish {cons ok} {
+ console::done $cons $ok
if {$ok} {
set msg {Merge completed successfully.}
} else {
- if {$revcnt != 1} {
- info_popup "Octopus merge failed.
-
-Your merge of $revcnt branches has failed.
-
-There are file-level conflicts between the branches which must be resolved manually.
-
-The working directory will now be reset.
-
-You can attempt this merge again by merging only one branch at a time." $w
-
- set fd [open "| git read-tree --reset -u HEAD" r]
- fconfigure $fd -blocking 0 -translation binary
- fileevent $fd readable \
- [namespace code [list _reset_wait $fd]]
- set ui_status_value {Aborting... please wait...}
- return
- }
-
set msg {Merge failed. Conflict resolution is required.}
}
unlock_index
- rescan [list set ui_status_value $msg]
+ rescan [list ui_status $msg]
+ delete_this
}
-proc dialog {} {
+constructor dialog {} {
global current_branch
global M1B
- if {![_can_merge]} return
-
- set fmt {list %(objectname) %(*objectname) %(refname) %(subject)}
- set cmd [list git for-each-ref --tcl --format=$fmt]
- lappend cmd refs/heads
- lappend cmd refs/remotes
- lappend cmd refs/tags
- set fr_fd [open "| $cmd" r]
- fconfigure $fr_fd -translation binary
- while {[gets $fr_fd line] > 0} {
- set line [eval $line]
- set ref [lindex $line 2]
- regsub ^refs/(heads|remotes|tags)/ $ref {} ref
- set subj($ref) [lindex $line 3]
- lappend sha1([lindex $line 0]) $ref
- if {[lindex $line 1] ne {}} {
- lappend sha1([lindex $line 1]) $ref
- }
- }
- close $fr_fd
-
- set to_show {}
- set fr_fd [open "| git rev-list --all --not HEAD"]
- while {[gets $fr_fd line] > 0} {
- if {[catch {set ref $sha1($line)}]} continue
- foreach n $ref {
- lappend to_show [list $n $line]
- }
+ if {![_can_merge $this]} {
+ delete_this
+ return
}
- close $fr_fd
- set to_show [lsort -unique $to_show]
- set w .merge_setup
- toplevel $w
- wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
+ make_toplevel top w
+ wm title $top "[appname] ([reponame]): Merge"
+ if {$top ne {.}} {
+ wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+ }
- set _visualize [namespace code [list _visualize $w $to_show]]
- set _start [namespace code [list _start $w $to_show]]
+ set _start [cb _start]
label $w.header \
-text "Merge Into $current_branch" \
@@ -209,55 +160,51 @@ proc dialog {} {
pack $w.header -side top -fill x
frame $w.buttons
- button $w.buttons.visualize -text Visualize -command $_visualize
+ button $w.buttons.visualize \
+ -text Visualize \
+ -command [cb _visualize]
pack $w.buttons.visualize -side left
- button $w.buttons.create -text Merge -command $_start
- pack $w.buttons.create -side right
+ button $w.buttons.merge \
+ -text Merge \
+ -command $_start
+ pack $w.buttons.merge -side right
button $w.buttons.cancel \
-text {Cancel} \
- -command "unlock_index;destroy $w"
+ -command [cb _cancel]
pack $w.buttons.cancel -side right -padx 5
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
- labelframe $w.source -text {Source Branches}
- listbox $w.source.l \
- -height 10 \
- -width 70 \
- -font font_diff \
- -selectmode extended \
- -yscrollcommand [list $w.source.sby set]
- scrollbar $w.source.sby -command [list $w.source.l yview]
- pack $w.source.sby -side right -fill y
- pack $w.source.l -side left -fill both -expand 1
- pack $w.source -fill both -expand 1 -pady 5 -padx 5
-
- foreach ref $to_show {
- set n [lindex $ref 0]
- if {[string length $n] > 20} {
- set n "[string range $n 0 16]..."
- }
- $w.source.l insert end [format {%s %-20s %s} \
- [string range [lindex $ref 1] 0 5] \
- $n \
- $subj([lindex $ref 0])]
- }
-
- bind $w.source.l <Key-K> [list event generate %W <Shift-Key-Up>]
- bind $w.source.l <Key-J> [list event generate %W <Shift-Key-Down>]
- bind $w.source.l <Key-k> [list event generate %W <Key-Up>]
- bind $w.source.l <Key-j> [list event generate %W <Key-Down>]
- bind $w.source.l <Key-h> [list event generate %W <Key-Left>]
- bind $w.source.l <Key-l> [list event generate %W <Key-Right>]
- bind $w.source.l <Key-v> $_visualize
+ set w_rev [::choose_rev::new_unmerged $w.rev {Revision To Merge}]
+ pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
bind $w <$M1B-Key-Return> $_start
- bind $w <Visibility> "grab $w; focus $w.source.l"
- bind $w <Key-Escape> "unlock_index;destroy $w"
- wm protocol $w WM_DELETE_WINDOW "unlock_index;destroy $w"
- wm title $w "[appname] ([reponame]): Merge"
+ bind $w <Key-Return> $_start
+ bind $w <Key-Escape> [cb _cancel]
+ wm protocol $w WM_DELETE_WINDOW [cb _cancel]
+
+ bind $w.buttons.merge <Visibility> [cb _visible]
tkwait window $w
}
+method _visible {} {
+ grab $w
+ if {[is_config_true gui.matchtrackingbranch]} {
+ $w_rev pick_tracking_branch
+ }
+ $w_rev focus_filter
+}
+
+method _cancel {} {
+ wm protocol $w WM_DELETE_WINDOW {}
+ unlock_index
+ destroy $w
+ delete_this
+}
+
+}
+
+namespace eval merge {
+
proc reset_hard {} {
global HEAD commit_type file_states
@@ -272,20 +219,24 @@ You must finish amending this commit.
if {![lock_index abort]} return
if {[string match *merge* $commit_type]} {
- set op merge
+ set op_question "Abort merge?
+
+Aborting the current merge will cause *ALL* uncommitted changes to be lost.
+
+Continue with aborting the current merge?"
} else {
- set op commit
- }
+ set op_question "Reset changes?
- if {[ask_popup "Abort $op?
+Resetting the changes will cause *ALL* uncommitted changes to be lost.
-Aborting the current $op will cause *ALL* uncommitted changes to be lost.
+Continue with resetting the current changes?"
+ }
-Continue with aborting the current $op?"] eq {yes}} {
- set fd [open "| git read-tree --reset -u HEAD" r]
+ if {[ask_popup $op_question] eq {yes}} {
+ set fd [git_read --stderr read-tree --reset -u -v HEAD]
fconfigure $fd -blocking 0 -translation binary
fileevent $fd readable [namespace code [list _reset_wait $fd]]
- set ui_status_value {Aborting... please wait...}
+ $::main_status start {Aborting} {files reset}
} else {
unlock_index
}
@@ -294,9 +245,12 @@ Continue with aborting the current $op?"] eq {yes}} {
proc _reset_wait {fd} {
global ui_comm
- read $fd
+ $::main_status update_meter [read $fd]
+
+ fconfigure $fd -blocking 1
if {[eof $fd]} {
- close $fd
+ set fail [catch {close $fd} err]
+ $::main_status stop
unlock_index
$ui_comm delete 0.0 end
@@ -308,7 +262,12 @@ proc _reset_wait {fd} {
catch {file delete [gitdir MERGE_MSG]}
catch {file delete [gitdir GITGUI_MSG]}
- rescan {set ui_status_value {Abort completed. Ready.}}
+ if {$fail} {
+ warn_popup "Abort failed.\n\n$err"
+ }
+ rescan {ui_status {Abort completed. Ready.}}
+ } else {
+ fconfigure $fd -blocking 0
}
}
diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl
index b29e14e64d..aa9f783afd 100644
--- a/git-gui/lib/option.tcl
+++ b/git-gui/lib/option.tcl
@@ -55,7 +55,7 @@ proc save_config {} {
}
proc do_about {} {
- global appvers copyright
+ global appvers copyright oguilib
global tcl_patchLevel tk_patchLevel
set w .about_dialog
@@ -94,6 +94,11 @@ $copyright" \
append v ", Tk version $tk_patchLevel"
}
+ set d {}
+ append d "git wrapper: $::_git\n"
+ append d "git exec dir: [gitexec]\n"
+ append d "git-gui lib: $oguilib"
+
label $w.vers \
-text $v \
-padx 5 -pady 5 \
@@ -103,6 +108,15 @@ $copyright" \
-relief solid
pack $w.vers -side top -fill x -padx 5 -pady 5
+ label $w.dirs \
+ -text $d \
+ -padx 5 -pady 5 \
+ -justify left \
+ -anchor w \
+ -borderwidth 1 \
+ -relief solid
+ pack $w.dirs -side top -fill x -padx 5 -pady 5
+
menu $w.ctxm -tearoff 0
$w.ctxm add command \
-label {Copy} \
@@ -174,8 +188,11 @@ proc do_options {} {
{b merge.summary {Summarize Merge Commits}}
{i-1..5 merge.verbosity {Merge Verbosity}}
+ {b merge.diffstat {Show Diffstat After Merge}}
{b gui.trustmtime {Trust File Modification Timestamps}}
+ {b gui.pruneduringfetch {Prune Tracking Branches During Fetch}}
+ {b gui.matchtrackingbranch {Match Tracking Branches}}
{i-0..99 gui.diffcontext {Number of Diff Context Lines}}
{t gui.newbranchtemplate {New Branch Name Template}}
} {
diff --git a/git-gui/lib/remote.tcl b/git-gui/lib/remote.tcl
index 99f353ed7d..cf9b9d5829 100644
--- a/git-gui/lib/remote.tcl
+++ b/git-gui/lib/remote.tcl
@@ -1,14 +1,13 @@
# git-gui remote management
# Copyright (C) 2006, 2007 Shawn Pearce
+set some_heads_tracking 0; # assume not
+
proc is_tracking_branch {name} {
global tracking_branches
-
- if {![catch {set info $tracking_branches($name)}]} {
- return 1
- }
- foreach t [array names tracking_branches] {
- if {[string match {*/\*} $t] && [string match $t $name]} {
+ foreach spec $tracking_branches {
+ set t [lindex $spec 0]
+ if {$t eq $name || [string match $t $name]} {
return 1
}
}
@@ -18,36 +17,54 @@ proc is_tracking_branch {name} {
proc all_tracking_branches {} {
global tracking_branches
- set all_trackings {}
- set cmd {}
- foreach name [array names tracking_branches] {
- if {[regsub {/\*$} $name {} name]} {
- lappend cmd $name
+ set all [list]
+ set pat [list]
+ set cmd [list]
+
+ foreach spec $tracking_branches {
+ set dst [lindex $spec 0]
+ if {[string range $dst end-1 end] eq {/*}} {
+ lappend pat $spec
+ lappend cmd [string range $dst 0 end-2]
} else {
- regsub ^refs/(heads|remotes)/ $name {} name
- lappend all_trackings $name
+ lappend all $spec
}
}
- if {$cmd ne {}} {
- set fd [open "| git for-each-ref --format=%(refname) $cmd" r]
- while {[gets $fd name] > 0} {
- regsub ^refs/(heads|remotes)/ $name {} name
- lappend all_trackings $name
+ if {$pat ne {}} {
+ set fd [eval git_read for-each-ref --format=%(refname) $cmd]
+ while {[gets $fd n] > 0} {
+ foreach spec $pat {
+ set dst [string range [lindex $spec 0] 0 end-2]
+ set len [string length $dst]
+ if {[string equal -length $len $dst $n]} {
+ set src [string range [lindex $spec 2] 0 end-2]
+ set spec [list \
+ $n \
+ [lindex $spec 1] \
+ $src[string range $n $len end] \
+ ]
+ lappend all $spec
+ }
+ }
}
close $fd
}
- return [lsort -unique $all_trackings]
+ return [lsort -index 0 -unique $all]
}
proc load_all_remotes {} {
global repo_config
- global all_remotes tracking_branches
+ global all_remotes tracking_branches some_heads_tracking
+ global remote_url
+ set some_heads_tracking 0
set all_remotes [list]
- array unset tracking_branches
+ set trck [list]
+ set rh_str refs/heads/
+ set rh_len [string length $rh_str]
set rm_dir [gitdir remotes]
if {[file isdirectory $rm_dir]} {
set all_remotes [glob \
@@ -60,12 +77,25 @@ proc load_all_remotes {} {
catch {
set fd [open [file join $rm_dir $name] r]
while {[gets $fd line] >= 0} {
+ if {[regexp {^URL:[ ]*(.+)$} $line line url]} {
+ set remote_url($name) $url
+ continue
+ }
if {![regexp {^Pull:[ ]*([^:]+):(.+)$} \
$line line src dst]} continue
- if {![regexp ^refs/ $dst]} {
- set dst "refs/heads/$dst"
+ if {[string index $src 0] eq {+}} {
+ set src [string range $src 1 end]
+ }
+ if {![string equal -length 5 refs/ $src]} {
+ set src $rh_str$src
}
- set tracking_branches($dst) [list $name $src]
+ if {![string equal -length 5 refs/ $dst]} {
+ set dst $rh_str$dst
+ }
+ if {[string equal -length $rh_len $rh_str $dst]} {
+ set some_heads_tracking 1
+ }
+ lappend trck [list $dst $name $src]
}
close $fd
}
@@ -75,19 +105,30 @@ proc load_all_remotes {} {
foreach line [array names repo_config remote.*.url] {
if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue
lappend all_remotes $name
+ set remote_url($name) $repo_config(remote.$name.url)
if {[catch {set fl $repo_config(remote.$name.fetch)}]} {
set fl {}
}
foreach line $fl {
if {![regexp {^([^:]+):(.+)$} $line line src dst]} continue
- if {![regexp ^refs/ $dst]} {
- set dst "refs/heads/$dst"
+ if {[string index $src 0] eq {+}} {
+ set src [string range $src 1 end]
+ }
+ if {![string equal -length 5 refs/ $src]} {
+ set src $rh_str$src
+ }
+ if {![string equal -length 5 refs/ $dst]} {
+ set dst $rh_str$dst
}
- set tracking_branches($dst) [list $name $src]
+ if {[string equal -length $rh_len $rh_str $dst]} {
+ set some_heads_tracking 1
+ }
+ lappend trck [list $dst $name $src]
}
}
+ set tracking_branches [lsort -index 0 -unique $trck]
set all_remotes [lsort -unique $all_remotes]
}
@@ -95,6 +136,7 @@ proc populate_fetch_menu {} {
global all_remotes repo_config
set m .mbar.fetch
+ set prune_list [list]
foreach r $all_remotes {
set enable 0
if {![catch {set a $repo_config(remote.$r.url)}]} {
@@ -115,11 +157,21 @@ proc populate_fetch_menu {} {
}
if {$enable} {
+ lappend prune_list $r
$m add command \
-label "Fetch from $r..." \
-command [list fetch_from $r]
}
}
+
+ if {$prune_list ne {}} {
+ $m add separator
+ }
+ foreach r $prune_list {
+ $m add command \
+ -label "Prune from $r..." \
+ -command [list prune_from $r]
+ }
}
proc populate_push_menu {} {
diff --git a/git-gui/lib/remote_branch_delete.tcl b/git-gui/lib/remote_branch_delete.tcl
new file mode 100644
index 0000000000..c88a360db5
--- /dev/null
+++ b/git-gui/lib/remote_branch_delete.tcl
@@ -0,0 +1,347 @@
+# git-gui remote branch deleting support
+# Copyright (C) 2007 Shawn Pearce
+
+class remote_branch_delete {
+
+field w
+field head_m
+
+field urltype {url}
+field remote {}
+field url {}
+
+field checktype {head}
+field check_head {}
+
+field status {}
+field idle_id {}
+field full_list {}
+field head_list {}
+field active_ls {}
+field head_cache
+field full_cache
+field cached
+
+constructor dialog {} {
+ global all_remotes M1B
+
+ make_toplevel top w
+ wm title $top "[appname] ([reponame]): Delete Remote Branch"
+ if {$top ne {.}} {
+ wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+ }
+
+ label $w.header -text {Delete Remote Branch} -font font_uibold
+ pack $w.header -side top -fill x
+
+ frame $w.buttons
+ button $w.buttons.delete -text Delete \
+ -default active \
+ -command [cb _delete]
+ pack $w.buttons.delete -side right
+ button $w.buttons.cancel -text {Cancel} \
+ -command [list destroy $w]
+ pack $w.buttons.cancel -side right -padx 5
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+ labelframe $w.dest -text {From Repository}
+ if {$all_remotes ne {}} {
+ radiobutton $w.dest.remote_r \
+ -text {Remote:} \
+ -value remote \
+ -variable @urltype
+ eval tk_optionMenu $w.dest.remote_m @remote $all_remotes
+ grid $w.dest.remote_r $w.dest.remote_m -sticky w
+ if {[lsearch -sorted -exact $all_remotes origin] != -1} {
+ set remote origin
+ } else {
+ set remote [lindex $all_remotes 0]
+ }
+ set urltype remote
+ trace add variable @remote write [cb _write_remote]
+ } else {
+ set urltype url
+ }
+ radiobutton $w.dest.url_r \
+ -text {Arbitrary URL:} \
+ -value url \
+ -variable @urltype
+ entry $w.dest.url_t \
+ -borderwidth 1 \
+ -relief sunken \
+ -width 50 \
+ -textvariable @url \
+ -validate key \
+ -validatecommand {
+ if {%d == 1 && [regexp {\s} %S]} {return 0}
+ return 1
+ }
+ trace add variable @url write [cb _write_url]
+ grid $w.dest.url_r $w.dest.url_t -sticky we -padx {0 5}
+ grid columnconfigure $w.dest 1 -weight 1
+ pack $w.dest -anchor nw -fill x -pady 5 -padx 5
+
+ labelframe $w.heads -text {Branches}
+ listbox $w.heads.l \
+ -height 10 \
+ -width 70 \
+ -listvariable @head_list \
+ -selectmode extended \
+ -yscrollcommand [list $w.heads.sby set]
+ scrollbar $w.heads.sby -command [list $w.heads.l yview]
+
+ frame $w.heads.footer
+ label $w.heads.footer.status \
+ -textvariable @status \
+ -anchor w \
+ -justify left
+ button $w.heads.footer.rescan \
+ -text {Rescan} \
+ -command [cb _rescan]
+ pack $w.heads.footer.status -side left -fill x
+ pack $w.heads.footer.rescan -side right
+
+ pack $w.heads.footer -side bottom -fill x
+ pack $w.heads.sby -side right -fill y
+ pack $w.heads.l -side left -fill both -expand 1
+ pack $w.heads -fill both -expand 1 -pady 5 -padx 5
+
+ labelframe $w.validate -text {Delete Only If}
+ radiobutton $w.validate.head_r \
+ -text {Merged Into:} \
+ -value head \
+ -variable @checktype
+ set head_m [tk_optionMenu $w.validate.head_m @check_head {}]
+ trace add variable @head_list write [cb _write_head_list]
+ trace add variable @check_head write [cb _write_check_head]
+ grid $w.validate.head_r $w.validate.head_m -sticky w
+ radiobutton $w.validate.always_r \
+ -text {Always (Do not perform merge checks)} \
+ -value always \
+ -variable @checktype
+ grid $w.validate.always_r -columnspan 2 -sticky w
+ grid columnconfigure $w.validate 1 -weight 1
+ pack $w.validate -anchor nw -fill x -pady 5 -padx 5
+
+ trace add variable @urltype write [cb _write_urltype]
+ _rescan $this
+
+ bind $w <Key-F5> [cb _rescan]
+ bind $w <$M1B-Key-r> [cb _rescan]
+ bind $w <$M1B-Key-R> [cb _rescan]
+ bind $w <Key-Return> [cb _delete]
+ bind $w <Key-Escape> [list destroy $w]
+ return $w
+}
+
+method _delete {} {
+ switch $urltype {
+ remote {set uri $remote}
+ url {set uri $url}
+ }
+
+ set cache $urltype:$uri
+ set crev {}
+ if {$checktype eq {head}} {
+ if {$check_head eq {}} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "A branch is required for 'Merged Into'."
+ return
+ }
+ set crev $full_cache("$cache\nrefs/heads/$check_head")
+ }
+
+ set not_merged [list]
+ set need_fetch 0
+ set have_selection 0
+ set push_cmd [list git push]
+ lappend push_cmd -v
+ lappend push_cmd $uri
+
+ foreach i [$w.heads.l curselection] {
+ set ref [lindex $full_list $i]
+ if {$crev ne {}} {
+ set obj $full_cache("$cache\n$ref")
+ if {[catch {set m [git merge-base $obj $crev]}]} {
+ set need_fetch 1
+ set m {}
+ }
+ if {$obj ne $m} {
+ lappend not_merged [lindex $head_list $i]
+ continue
+ }
+ }
+
+ lappend push_cmd :$ref
+ set have_selection 1
+ }
+
+ if {$not_merged ne {}} {
+ set msg "The following branches are not completely merged into $check_head:
+
+ - [join $not_merged "\n - "]"
+
+ if {$need_fetch} {
+ append msg "
+
+One or more of the merge tests failed because you have not fetched the necessary commits. Try fetching from $uri first."
+ }
+
+ tk_messageBox \
+ -icon info \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message $msg
+ if {!$have_selection} return
+ }
+
+ if {!$have_selection} {
+ tk_messageBox \
+ -icon error \
+ -type ok \
+ -title [wm title $w] \
+ -parent $w \
+ -message "Please select one or more branches to delete."
+ return
+ }
+
+ if {[tk_messageBox \
+ -icon warning \
+ -type yesno \
+ -title [wm title $w] \
+ -parent $w \
+ -message {Recovering deleted branches is difficult.
+
+Delete the selected branches?}] ne yes} {
+ return
+ }
+
+ destroy $w
+
+ set cons [console::new \
+ "push $uri" \
+ "Deleting branches from $uri"]
+ console::exec $cons $push_cmd
+}
+
+method _rescan {{force 1}} {
+ switch $urltype {
+ remote {set uri $remote}
+ url {set uri $url}
+ }
+
+ if {$force} {
+ unset -nocomplain cached($urltype:$uri)
+ }
+
+ if {$idle_id ne {}} {
+ after cancel $idle_id
+ set idle_id {}
+ }
+
+ _load $this $urltype:$uri $uri
+}
+
+method _write_remote {args} { set urltype remote }
+method _write_url {args} { set urltype url }
+method _write_check_head {args} { set checktype head }
+
+method _write_head_list {args} {
+ $head_m delete 0 end
+ foreach abr $head_list {
+ $head_m insert end radiobutton \
+ -label $abr \
+ -value $abr \
+ -variable @check_head
+ }
+ if {[lsearch -exact -sorted $head_list $check_head] < 0} {
+ set check_head {}
+ }
+}
+
+method _write_urltype {args} {
+ if {$urltype eq {url}} {
+ if {$idle_id ne {}} {
+ after cancel $idle_id
+ }
+ _load $this none: {}
+ set idle_id [after 1000 [cb _rescan 0]]
+ } else {
+ _rescan $this 0
+ }
+}
+
+method _load {cache uri} {
+ if {$active_ls ne {}} {
+ catch {close $active_ls}
+ }
+
+ if {$uri eq {}} {
+ $w.heads.l conf -state disabled
+ set head_list [list]
+ set full_list [list]
+ set status {No repository selected.}
+ return
+ }
+
+ if {[catch {set x $cached($cache)}]} {
+ set status "Scanning $uri..."
+ $w.heads.l conf -state disabled
+ set head_list [list]
+ set full_list [list]
+ set head_cache($cache) [list]
+ set full_cache($cache) [list]
+ set active_ls [git_read ls-remote $uri]
+ fconfigure $active_ls \
+ -blocking 0 \
+ -translation lf \
+ -encoding utf-8
+ fileevent $active_ls readable [cb _read $cache $active_ls]
+ } else {
+ set status {}
+ set full_list $full_cache($cache)
+ set head_list $head_cache($cache)
+ $w.heads.l conf -state normal
+ }
+}
+
+method _read {cache fd} {
+ if {$fd ne $active_ls} {
+ catch {close $fd}
+ return
+ }
+
+ while {[gets $fd line] >= 0} {
+ if {[string match {*^{}} $line]} continue
+ if {[regexp {^([0-9a-f]{40}) (.*)$} $line _junk obj ref]} {
+ if {[regsub ^refs/heads/ $ref {} abr]} {
+ lappend head_list $abr
+ lappend head_cache($cache) $abr
+ lappend full_list $ref
+ lappend full_cache($cache) $ref
+ set full_cache("$cache\n$ref") $obj
+ }
+ }
+ }
+
+ if {[eof $fd]} {
+ if {[catch {close $fd} err]} {
+ set status $err
+ set head_list [list]
+ set full_list [list]
+ } else {
+ set status {}
+ set cached($cache) 1
+ $w.heads.l conf -state normal
+ }
+ }
+} ifdeleted {
+ catch {close $fd}
+}
+
+}
diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl
index a0a1b7dddd..c36be2f3cd 100644
--- a/git-gui/lib/shortcut.tcl
+++ b/git-gui/lib/shortcut.tcl
@@ -13,10 +13,11 @@ proc do_windows_shortcut {} {
set fn ${fn}.bat
}
if {[catch {
+ set ge [file normalize [file dirname $::_git]]
set fd [open $fn w]
puts $fd "@ECHO Entering [reponame]"
puts $fd "@ECHO Starting git-gui... please wait..."
- puts $fd "@SET PATH=[file normalize [gitexec]];%PATH%"
+ puts $fd "@SET PATH=$ge;%PATH%"
puts $fd "@SET GIT_DIR=[file normalize [gitdir]]"
puts -nonewline $fd "@\"[info nameofexecutable]\""
puts $fd " \"[file normalize $argv0]\""
@@ -53,7 +54,7 @@ proc do_cygwin_shortcut {} {
set sh [exec cygpath \
--windows \
--absolute \
- /bin/sh]
+ /bin/sh.exe]
set me [exec cygpath \
--unix \
--absolute \
@@ -62,18 +63,12 @@ proc do_cygwin_shortcut {} {
--unix \
--absolute \
[gitdir]]
- set gw [exec cygpath \
- --windows \
- --absolute \
- [file dirname [gitdir]]]
- regsub -all ' $me "'\\''" me
- regsub -all ' $gd "'\\''" gd
- puts $fd "@ECHO Entering $gw"
+ puts $fd "@ECHO Entering [reponame]"
puts $fd "@ECHO Starting git-gui... please wait..."
puts -nonewline $fd "@\"$sh\" --login -c \""
- puts -nonewline $fd "GIT_DIR='$gd'"
- puts -nonewline $fd " '$me'"
- puts $fd "&\""
+ puts -nonewline $fd "GIT_DIR=[sq $gd]"
+ puts -nonewline $fd " [sq $me]"
+ puts $fd " &\""
close $fd
} err]} {
error_popup "Cannot write script:\n\n$err"
@@ -90,6 +85,9 @@ proc do_macosx_app {} {
-initialdir [file join $env(HOME) Desktop] \
-initialfile "Git [reponame].app"]
if {$fn != {}} {
+ if {[file extension $fn] ne {.app}} {
+ set fn ${fn}.app
+ }
if {[catch {
set Contents [file join $fn Contents]
set MacOS [file join $Contents MacOS]
@@ -123,20 +121,27 @@ proc do_macosx_app {} {
close $fd
set fd [open $exe w]
- set gd [file normalize [gitdir]]
- set ep [file normalize [gitexec]]
- regsub -all ' $gd "'\\''" gd
- regsub -all ' $ep "'\\''" ep
puts $fd "#!/bin/sh"
- foreach name [array names env] {
- if {[string match GIT_* $name]} {
- regsub -all ' $env($name) "'\\''" v
- puts $fd "export $name='$v'"
+ foreach name [lsort [array names env]] {
+ set value $env($name)
+ switch -- $name {
+ GIT_DIR { set value [file normalize [gitdir]] }
+ }
+
+ switch -glob -- $name {
+ SSH_* -
+ GIT_* {
+ puts $fd "if test \"z\$$name\" = z; then"
+ puts $fd " export $name=[sq $value]"
+ puts $fd "fi &&"
+ }
}
}
- puts $fd "export PATH='$ep':\$PATH"
- puts $fd "export GIT_DIR='$gd'"
- puts $fd "exec [file normalize $argv0]"
+ puts $fd "export PATH=[sq [file dirname $::_git]]:\$PATH &&"
+ puts $fd "cd [sq [file normalize [pwd]]] &&"
+ puts $fd "exec \\"
+ puts $fd " [sq [info nameofexecutable]] \\"
+ puts $fd " [sq [file normalize $argv0]]"
close $fd
file attributes $exe -permissions u+x,g+x,o+x
diff --git a/git-gui/lib/status_bar.tcl b/git-gui/lib/status_bar.tcl
new file mode 100644
index 0000000000..72a8fe1fd3
--- /dev/null
+++ b/git-gui/lib/status_bar.tcl
@@ -0,0 +1,96 @@
+# git-gui status bar mega-widget
+# Copyright (C) 2007 Shawn Pearce
+
+class status_bar {
+
+field w ; # our own window path
+field w_l ; # text widget we draw messages into
+field w_c ; # canvas we draw a progress bar into
+field status {}; # single line of text we show
+field prefix {}; # text we format into status
+field units {}; # unit of progress
+field meter {}; # current core git progress meter (if active)
+
+constructor new {path} {
+ set w $path
+ set w_l $w.l
+ set w_c $w.c
+
+ frame $w \
+ -borderwidth 1 \
+ -relief sunken
+ label $w_l \
+ -textvariable @status \
+ -anchor w \
+ -justify left
+ pack $w_l -side left
+
+ bind $w <Destroy> [cb _delete %W]
+ return $this
+}
+
+method start {msg uds} {
+ if {[winfo exists $w_c]} {
+ $w_c coords bar 0 0 0 20
+ } else {
+ canvas $w_c \
+ -width 100 \
+ -height [expr {int([winfo reqheight $w_l] * 0.6)}] \
+ -borderwidth 1 \
+ -relief groove \
+ -highlightt 0
+ $w_c create rectangle 0 0 0 20 -tags bar -fill navy
+ pack $w_c -side right
+ }
+
+ set status $msg
+ set prefix $msg
+ set units $uds
+ set meter {}
+}
+
+method update {have total} {
+ set pdone 0
+ if {$total > 0} {
+ set pdone [expr {100 * $have / $total}]
+ }
+
+ set status [format "%s ... %i of %i %s (%2i%%)" \
+ $prefix $have $total $units $pdone]
+ $w_c coords bar 0 0 $pdone 20
+}
+
+method update_meter {buf} {
+ append meter $buf
+ set r [string last "\r" $meter]
+ if {$r == -1} {
+ return
+ }
+
+ set prior [string range $meter 0 $r]
+ set meter [string range $meter [expr {$r + 1}] end]
+ if {[regexp "\\((\\d+)/(\\d+)\\)\\s+done\r\$" $prior _j a b]} {
+ update $this $a $b
+ }
+}
+
+method stop {{msg {}}} {
+ destroy $w_c
+ if {$msg ne {}} {
+ set status $msg
+ }
+}
+
+method show {msg {test {}}} {
+ if {$test eq {} || $status eq $test} {
+ set status $msg
+ }
+}
+
+method _delete {current} {
+ if {$current eq $w} {
+ delete_this
+ }
+}
+
+}
diff --git a/git-gui/lib/transport.tcl b/git-gui/lib/transport.tcl
index c0e7d20fce..3a22bd40d4 100644
--- a/git-gui/lib/transport.tcl
+++ b/git-gui/lib/transport.tcl
@@ -5,9 +5,19 @@ proc fetch_from {remote} {
set w [console::new \
"fetch $remote" \
"Fetching new changes from $remote"]
- set cmd [list git fetch]
- lappend cmd $remote
- console::exec $w $cmd
+ set cmds [list]
+ lappend cmds [list exec git fetch $remote]
+ if {[is_config_true gui.pruneduringfetch]} {
+ lappend cmds [list exec git remote prune $remote]
+ }
+ console::chain $w $cmds
+}
+
+proc prune_from {remote} {
+ set w [console::new \
+ "remote prune $remote" \
+ "Pruning tracking branches deleted from $remote"]
+ console::exec $w [list git remote prune $remote]
}
proc push_to {remote} {
@@ -64,7 +74,7 @@ trace add variable push_remote write \
[list radio_selector push_urltype remote]
proc do_push_anywhere {} {
- global all_heads all_remotes current_branch
+ global all_remotes current_branch
global push_urltype push_remote push_url push_thin push_tags
set w .push_setup
@@ -91,7 +101,7 @@ proc do_push_anywhere {} {
-width 70 \
-selectmode extended \
-yscrollcommand [list $w.source.sby set]
- foreach h $all_heads {
+ foreach h [load_all_heads] {
$w.source.l insert end $h
if {$h eq $current_branch} {
$w.source.l select set end
diff --git a/git-instaweb.sh b/git-instaweb.sh
index cbc7418e35..b79c6b6a42 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -8,13 +8,7 @@ USAGE='[--start] [--stop] [--restart]
. git-sh-setup
-case "$GIT_DIR" in
-/*)
- fqgitdir="$GIT_DIR" ;;
-*)
- fqgitdir="$PWD/$GIT_DIR" ;;
-esac
-
+fqgitdir="$GIT_DIR"
local="`git config --bool --get instaweb.local`"
httpd="`git config --get instaweb.httpd`"
browser="`git config --get instaweb.browser`"
diff --git a/git-lost-found.sh b/git-lost-found.sh
index 58570dff13..c0b00e0fd1 100755
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
@@ -17,10 +17,10 @@ while read dangling type sha1
do
case "$dangling" in
dangling)
- if git-rev-parse --verify "$sha1^0" >/dev/null 2>/dev/null
+ if git rev-parse --verify "$sha1^0" >/dev/null 2>/dev/null
then
dir="$laf/commit"
- git-show-branch "$sha1"
+ git show-branch "$sha1"
else
dir="$laf/other"
fi
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index a6ed99a7c5..b7e5d04584 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -58,7 +58,7 @@ http://* | https://* | ftp://* )
curl_extra_args="-k"
fi
if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
- "`git-config --bool http.noEPSV`" = true ]; then
+ "`git config --bool http.noEPSV`" = true ]; then
curl_extra_args="${curl_extra_args} --disable-epsv"
fi
curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" ||
@@ -82,7 +82,7 @@ rsync://* )
(cd $tmpdir && find refs -type f) |
while read path
do
- cat "$tmpdir/$path" | tr -d '\012'
+ tr -d '\012' <"$tmpdir/$path"
echo " $path"
done &&
rm -fr $tmpdir
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index eb3f473d5a..645e1147dc 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -45,7 +45,7 @@ esac
# MRT is the current "merge result tree"
MRC=$head MSG= PARENT="-p $head"
-MRT=$(git-write-tree)
+MRT=$(git write-tree)
CNT=1 ;# counting our head
NON_FF_MERGE=0
OCTOPUS_FAILURE=0
@@ -61,7 +61,7 @@ do
exit 2
esac
- common=$(git-merge-base --all $MRC $SHA1) ||
+ common=$(git merge-base --all $MRC $SHA1) ||
die "Unable to find common commit with $SHA1"
case "$LF$common$LF" in
@@ -82,22 +82,22 @@ do
# We still need to count this as part of the parent set.
echo "Fast forwarding to: $SHA1"
- git-read-tree -u -m $head $SHA1 || exit
- MRC=$SHA1 MRT=$(git-write-tree)
+ git read-tree -u -m $head $SHA1 || exit
+ MRC=$SHA1 MRT=$(git write-tree)
continue
fi
NON_FF_MERGE=1
echo "Trying simple merge with $SHA1"
- git-read-tree -u -m --aggressive $common $MRT $SHA1 || exit 2
- next=$(git-write-tree 2>/dev/null)
+ git read-tree -u -m --aggressive $common $MRT $SHA1 || exit 2
+ next=$(git write-tree 2>/dev/null)
if test $? -ne 0
then
echo "Simple merge did not work, trying automatic merge."
git-merge-index -o git-merge-one-file -a ||
OCTOPUS_FAILURE=1
- next=$(git-write-tree 2>/dev/null)
+ next=$(git write-tree 2>/dev/null)
fi
# We have merged the other branch successfully. Ideally
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index 7d62d7902c..1e7727d276 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -13,7 +13,7 @@
# $7 - file in branch2 mode (or empty)
#
# Handle some trivial cases.. The _really_ trivial cases have
-# been handled already by git-read-tree, but that one doesn't
+# been handled already by git read-tree, but that one doesn't
# do any merges that might change the tree layout.
case "${1:-.}${2:-.}${3:-.}" in
@@ -27,14 +27,15 @@ case "${1:-.}${2:-.}${3:-.}" in
# read-tree checked that index matches HEAD already,
# so we know we do not have this path tracked.
# there may be an unrelated working tree file here,
- # which we should just leave unmolested.
- exit 0
+ # which we should just leave unmolested. Make sure
+ # we do not have it in the index, though.
+ exec git update-index --remove -- "$4"
fi
if test -f "$4"; then
rm -f -- "$4" &&
rmdir -p "$(expr "z$4" : 'z\(.*\)/')" 2>/dev/null || :
fi &&
- exec git-update-index --remove -- "$4"
+ exec git update-index --remove -- "$4"
;;
#
@@ -42,7 +43,8 @@ case "${1:-.}${2:-.}${3:-.}" in
#
".$2.")
# the other side did not add and we added so there is nothing
- # to be done.
+ # to be done, except making the path merged.
+ exec git update-index --add --cacheinfo "$6" "$2" "$4"
;;
"..$3")
echo "Adding $4"
@@ -50,8 +52,8 @@ case "${1:-.}${2:-.}${3:-.}" in
echo "ERROR: untracked $4 is overwritten by the merge."
exit 1
}
- git-update-index --add --cacheinfo "$6$7" "$2$3" "$4" &&
- exec git-checkout-index -u -f -- "$4"
+ git update-index --add --cacheinfo "$7" "$3" "$4" &&
+ exec git checkout-index -u -f -- "$4"
;;
#
@@ -64,8 +66,8 @@ case "${1:-.}${2:-.}${3:-.}" in
exit 1
fi
echo "Adding $4"
- git-update-index --add --cacheinfo "$6" "$2" "$4" &&
- exec git-checkout-index -u -f -- "$4"
+ git update-index --add --cacheinfo "$6" "$2" "$4" &&
+ exec git checkout-index -u -f -- "$4"
;;
#
@@ -84,11 +86,11 @@ case "${1:-.}${2:-.}${3:-.}" in
case "$1" in
'')
echo "Added $4 in both, but differently."
- # This extracts OUR file in $orig, and uses git-apply to
+ # This extracts OUR file in $orig, and uses git apply to
# remove lines that are unique to ours.
orig=`git-unpack-file $2`
sz0=`wc -c <"$orig"`
- diff -u -La/$orig -Lb/$orig $orig $src2 | git-apply --no-add
+ diff -u -La/$orig -Lb/$orig $orig $src2 | git apply --no-add
sz1=`wc -c <"$orig"`
# If we do not have enough common material, it is not
@@ -104,12 +106,12 @@ case "${1:-.}${2:-.}${3:-.}" in
# Be careful for funny filename such as "-L" in "$4", which
# would confuse "merge" greatly.
src1=`git-unpack-file $2`
- git-merge-file "$src1" "$orig" "$src2"
+ git merge-file "$src1" "$orig" "$src2"
ret=$?
# Create the working tree file, using "our tree" version from the
# index, and then store the result of the merge.
- git-checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4"
+ git checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4"
rm -f -- "$orig" "$src1" "$src2"
if [ "$6" != "$7" ]; then
@@ -124,7 +126,7 @@ case "${1:-.}${2:-.}${3:-.}" in
echo "ERROR: Merge conflict in $4"
exit 1
fi
- exec git-update-index -- "$4"
+ exec git update-index -- "$4"
;;
*)
diff --git a/git-merge-ours.sh b/git-merge-ours.sh
index 2b6a5c0d10..c81a790aa6 100755
--- a/git-merge-ours.sh
+++ b/git-merge-ours.sh
@@ -9,6 +9,6 @@
# because the current index is what we will be committing as the
# merge result.
-git-diff-index --quiet --cached HEAD || exit 2
+git diff-index --quiet --cached HEAD || exit 2
exit 0
diff --git a/git-merge-resolve.sh b/git-merge-resolve.sh
index 75e1de49ac..bb19da2cc1 100755
--- a/git-merge-resolve.sh
+++ b/git-merge-resolve.sh
@@ -37,10 +37,10 @@ then
exit 2
fi
-git-update-index --refresh 2>/dev/null
-git-read-tree -u -m --aggressive $bases $head $remotes || exit 2
+git update-index --refresh 2>/dev/null
+git read-tree -u -m --aggressive $bases $head $remotes || exit 2
echo "Trying simple merge."
-if result_tree=$(git-write-tree 2>/dev/null)
+if result_tree=$(git write-tree 2>/dev/null)
then
exit 0
else
diff --git a/git-merge-stupid.sh b/git-merge-stupid.sh
index 4faecb933d..4b1e595363 100755
--- a/git-merge-stupid.sh
+++ b/git-merge-stupid.sh
@@ -40,10 +40,10 @@ case "$bases" in
for c in $bases
do
rm -f $G
- GIT_INDEX_FILE=$G git-read-tree -m $c $head $remotes \
+ GIT_INDEX_FILE=$G git read-tree -m $c $head $remotes \
2>/dev/null || continue
# Count the paths that are unmerged.
- cnt=`GIT_INDEX_FILE=$G git-ls-files --unmerged | wc -l`
+ cnt=`GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l`
if test $best_cnt -le 0 -o $cnt -le $best_cnt
then
best=$c
@@ -63,10 +63,10 @@ case "$bases" in
;;
esac
-git-update-index --refresh 2>/dev/null
-git-read-tree -u -m $common $head $remotes || exit 2
+git update-index --refresh 2>/dev/null
+git read-tree -u -m $common $head $remotes || exit 2
echo "Trying simple merge."
-if result_tree=$(git-write-tree 2>/dev/null)
+if result_tree=$(git write-tree 2>/dev/null)
then
exit 0
else
diff --git a/git-merge.sh b/git-merge.sh
index b2f8a2acd3..5ccf28251d 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
+USAGE='[-n] [--summary] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
SUBDIRECTORY_OK=Yes
. git-sh-setup
@@ -31,7 +31,7 @@ dropsave() {
savestate() {
# Stash away any local modifications.
- git-diff-index -z --name-only $head |
+ git diff-index -z --name-only $head |
cpio -0 -o >"$GIT_DIR/MERGE_SAVE"
}
@@ -40,7 +40,7 @@ restorestate() {
then
git reset --hard $head >/dev/null
cpio -iuv <"$GIT_DIR/MERGE_SAVE"
- git-update-index --refresh >/dev/null
+ git update-index --refresh >/dev/null
fi
}
@@ -57,7 +57,7 @@ finish_up_to_date () {
squash_message () {
echo Squashed commit of the following:
echo
- git-log --no-merges ^"$head" $remote
+ git log --no-merges ^"$head" $remote
}
finish () {
@@ -79,7 +79,7 @@ finish () {
echo "No merge message -- not updating HEAD"
;;
*)
- git-update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
+ git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
;;
esac
;;
@@ -88,24 +88,24 @@ finish () {
'')
;;
?*)
- case "$no_summary" in
- '')
- git-diff-tree --stat --summary -M "$head" "$1"
- ;;
- esac
+ if test "$show_diffstat" = t
+ then
+ # We want color (if set), but no pager
+ GIT_PAGER='' git diff --stat --summary -M "$head" "$1"
+ fi
;;
esac
}
merge_name () {
remote="$1"
- rh=$(git-rev-parse --verify "$remote^0" 2>/dev/null) || return
- bh=$(git-show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
+ rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return
+ bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
if test "$rh" = "$bh"
then
echo "$rh branch '$remote' of ."
elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
- git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
+ git show-ref -q --verify "refs/heads/$truname" 2>/dev/null
then
echo "$rh branch '$truname' (early part) of ."
elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
@@ -125,7 +125,9 @@ do
case "$1" in
-n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
--no-summa|--no-summar|--no-summary)
- no_summary=t ;;
+ show_diffstat=false ;;
+ --summary)
+ show_diffstat=t ;;
--sq|--squ|--squa|--squas|--squash)
squash=t no_commit=t ;;
--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
@@ -167,6 +169,11 @@ do
shift
done
+if test -z "$show_diffstat"; then
+ test "$(git config --bool merge.diffstat)" = false && show_diffstat=false
+ test -z "$show_diffstat" && show_diffstat=t
+fi
+
# This could be traditional "merge <msg> HEAD <commit>..." and the
# way we can tell it is to see if the second token is HEAD, but some
# people might have misused the interface and used a committish that
@@ -174,15 +181,15 @@ done
# have "-m" so it is an additional safety measure to check for it.
if test -z "$have_message" &&
- second_token=$(git-rev-parse --verify "$2^0" 2>/dev/null) &&
- head_commit=$(git-rev-parse --verify "HEAD" 2>/dev/null) &&
+ second_token=$(git rev-parse --verify "$2^0" 2>/dev/null) &&
+ head_commit=$(git rev-parse --verify "HEAD" 2>/dev/null) &&
test "$second_token" = "$head_commit"
then
merge_msg="$1"
shift
head_arg="$1"
shift
-elif ! git-rev-parse --verify HEAD >/dev/null 2>&1
+elif ! git rev-parse --verify HEAD >/dev/null 2>&1
then
# If the merged head is a valid one there is no reason to
# forbid "git merge" into a branch yet to be born. We do
@@ -196,8 +203,8 @@ then
rh=$(git rev-parse --verify "$1^0") ||
die "$1 - not something we can merge"
- git-update-ref -m "initial pull" HEAD "$rh" "" &&
- git-read-tree --reset -u HEAD
+ git update-ref -m "initial pull" HEAD "$rh" "" &&
+ git read-tree --reset -u HEAD
exit
else
@@ -212,11 +219,11 @@ else
merge_name=$(for remote
do
merge_name "$remote"
- done | git-fmt-merge-msg
+ done | git fmt-merge-msg
)
merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
fi
-head=$(git-rev-parse --verify "$head_arg"^0) || usage
+head=$(git rev-parse --verify "$head_arg"^0) || usage
# All the rest are remote heads
test "$#" = 0 && usage ;# we need at least one remote head.
@@ -225,7 +232,7 @@ set_reflog_action "merge $*"
remoteheads=
for remote
do
- remotehead=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
+ remotehead=$(git rev-parse --verify "$remote"^0 2>/dev/null) ||
die "$remote - not something we can merge"
remoteheads="${remoteheads}$remotehead "
eval GITHEAD_$remotehead='"$remote"'
@@ -237,7 +244,7 @@ case "$use_strategies" in
'')
case "$#" in
1)
- var="`git-config --get pull.twohead`"
+ var="`git config --get pull.twohead`"
if test -n "$var"
then
use_strategies="$var"
@@ -245,7 +252,7 @@ case "$use_strategies" in
use_strategies="$default_twohead_strategies"
fi ;;
*)
- var="`git-config --get pull.octopus`"
+ var="`git config --get pull.octopus`"
if test -n "$var"
then
use_strategies="$var"
@@ -271,10 +278,10 @@ done
case "$#" in
1)
- common=$(git-merge-base --all $head "$@")
+ common=$(git merge-base --all $head "$@")
;;
*)
- common=$(git-show-branch --merge-base $head "$@")
+ common=$(git show-branch --merge-base $head "$@")
;;
esac
echo "$head" >"$GIT_DIR/ORIG_HEAD"
@@ -294,15 +301,15 @@ f,*)
;;
?,1,"$head",*)
# Again the most common case of merging one remote.
- echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
- git-update-index --refresh 2>/dev/null
+ echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)"
+ git update-index --refresh 2>/dev/null
msg="Fast forward"
if test -n "$have_message"
then
msg="$msg (no commit created; -m option ignored)"
fi
- new_head=$(git-rev-parse --verify "$1^0") &&
- git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
+ new_head=$(git rev-parse --verify "$1^0") &&
+ git read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
finish "$new_head" "$msg" || exit
dropsave
exit 0
@@ -314,7 +321,7 @@ f,*)
?,1,*,)
# We are not doing octopus, not fast forward, and have only
# one common.
- git-update-index --refresh 2>/dev/null
+ git update-index --refresh 2>/dev/null
case " $use_strategies " in
*' recursive '*|*' recur '*)
: run merge later
@@ -323,13 +330,13 @@ f,*)
# See if it is really trivial.
git var GIT_COMMITTER_IDENT >/dev/null || exit
echo "Trying really trivial in-index merge..."
- if git-read-tree --trivial -m -u -v $common $head "$1" &&
- result_tree=$(git-write-tree)
+ if git read-tree --trivial -m -u -v $common $head "$1" &&
+ result_tree=$(git write-tree)
then
echo "Wonderful."
result_commit=$(
printf '%s\n' "$merge_msg" |
- git-commit-tree $result_tree -p HEAD -p "$1"
+ git commit-tree $result_tree -p HEAD -p "$1"
) || exit
finish "$result_commit" "In-index merge"
dropsave
@@ -343,7 +350,7 @@ f,*)
up_to_date=t
for remote
do
- common_one=$(git-merge-base --all $head $remote)
+ common_one=$(git merge-base --all $head $remote)
if test "$common_one" != "$remote"
then
up_to_date=f
@@ -412,8 +419,8 @@ do
if test "$exit" -eq 1
then
cnt=`{
- git-diff-files --name-only
- git-ls-files --unmerged
+ git diff-files --name-only
+ git ls-files --unmerged
} | wc -l`
if test $best_cnt -le 0 -o $cnt -le $best_cnt
then
@@ -425,15 +432,15 @@ do
}
# Automerge succeeded.
- result_tree=$(git-write-tree) && break
+ result_tree=$(git write-tree) && break
done
# If we have a resulting tree, that means the strategy module
# auto resolved the merge cleanly.
if test '' != "$result_tree"
then
- parents=$(git-show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
- result_commit=$(printf '%s\n' "$merge_msg" | git-commit-tree $result_tree $parents) || exit
+ parents=$(git show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
+ result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
finish "$result_commit" "Merge made by $wt_strategy."
dropsave
exit 0
@@ -489,9 +496,6 @@ Conflicts:
sed -e 's/^[^ ]* / /' |
uniq
} >>"$GIT_DIR/MERGE_MSG"
- if test -d "$GIT_DIR/rr-cache"
- then
- git-rerere
- fi
+ git rerere
die "Automatic merge failed; fix conflicts and then commit the result."
fi
diff --git a/git-mergetool.sh b/git-mergetool.sh
index e62351bcba..47a80553ad 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -5,7 +5,7 @@
# Copyright (c) 2006 Theodore Y. Ts'o
#
# This file is licensed under the GPL v2, or a later version
-# at the discretion of Junio C Hammano.
+# at the discretion of Junio C Hamano.
#
USAGE='[--tool=tool] [file to merge] ...'
@@ -65,14 +65,14 @@ resolve_symlink_merge () {
read ans
case "$ans" in
[lL]*)
- git-checkout-index -f --stage=2 -- "$path"
- git-add -- "$path"
+ git checkout-index -f --stage=2 -- "$path"
+ git add -- "$path"
cleanup_temp_files --save-backup
return
;;
[rR]*)
- git-checkout-index -f --stage=3 -- "$path"
- git-add -- "$path"
+ git checkout-index -f --stage=3 -- "$path"
+ git add -- "$path"
cleanup_temp_files --save-backup
return
;;
@@ -93,12 +93,12 @@ resolve_deleted_merge () {
read ans
case "$ans" in
[mMcC]*)
- git-add -- "$path"
+ git add -- "$path"
cleanup_temp_files --save-backup
return
;;
[dD]*)
- git-rm -- "$path" > /dev/null
+ git rm -- "$path" > /dev/null
cleanup_temp_files
return
;;
@@ -140,7 +140,7 @@ remove_backup () {
merge_file () {
path="$1"
- f=`git-ls-files -u -- "$path"`
+ f=`git ls-files -u -- "$path"`
if test -z "$f" ; then
if test ! -f "$path" ; then
echo "$path: file not found"
@@ -215,6 +215,12 @@ merge_file () {
check_unchanged
save_backup
;;
+ gvimdiff)
+ touch "$BACKUP"
+ gvimdiff -f -- "$LOCAL" "$path" "$REMOTE"
+ check_unchanged
+ save_backup
+ ;;
xxdiff)
touch "$BACKUP"
if base_present ; then
@@ -291,9 +297,9 @@ do
done
if test -z "$merge_tool"; then
- merge_tool=`git-config merge.tool`
+ merge_tool=`git config merge.tool`
case "$merge_tool" in
- kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | "")
+ kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | "")
;; # happy
*)
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
@@ -304,28 +310,42 @@ if test -z "$merge_tool"; then
fi
if test -z "$merge_tool" ; then
- if type kdiff3 >/dev/null 2>&1 && test -n "$DISPLAY"; then
- merge_tool="kdiff3";
- elif type tkdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
- merge_tool=tkdiff
- elif type xxdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
- merge_tool=xxdiff
- elif type meld >/dev/null 2>&1 && test -n "$DISPLAY"; then
- merge_tool=meld
- elif type opendiff >/dev/null 2>&1; then
- merge_tool=opendiff
- elif type emacs >/dev/null 2>&1; then
- merge_tool=emerge
- elif type vimdiff >/dev/null 2>&1; then
- merge_tool=vimdiff
- else
+ if test -n "$DISPLAY"; then
+ merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
+ if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
+ merge_tool_candidates="meld $merge_tool_candidates"
+ fi
+ if test "$KDE_FULL_SESSION" = "true"; then
+ merge_tool_candidates="kdiff3 $merge_tool_candidates"
+ fi
+ fi
+ if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
+ merge_tool_candidates="$merge_tool_candidates emerge"
+ fi
+ if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
+ merge_tool_candidates="$merge_tool_candidates vimdiff"
+ fi
+ merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
+ echo "merge tool candidates: $merge_tool_candidates"
+ for i in $merge_tool_candidates; do
+ if test $i = emerge ; then
+ cmd=emacs
+ else
+ cmd=$i
+ fi
+ if type $cmd > /dev/null 2>&1; then
+ merge_tool=$i
+ break
+ fi
+ done
+ if test -z "$merge_tool" ; then
echo "No available merge resolution programs available."
exit 1
fi
fi
case "$merge_tool" in
- kdiff3|tkdiff|meld|xxdiff|vimdiff|opendiff)
+ kdiff3|tkdiff|meld|xxdiff|vimdiff|gvimdiff|opendiff)
if ! type "$merge_tool" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available"
exit 1
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 0506b12cb2..695a4094bb 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -2,7 +2,7 @@
# git-ls-remote could be called from outside a git managed repository;
# this would fail in that case and would issue an error message.
-GIT_DIR=$(git-rev-parse --git-dir 2>/dev/null) || :;
+GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) || :;
get_data_source () {
case "$1" in
@@ -13,7 +13,7 @@ get_data_source () {
echo self
;;
*)
- if test "$(git-config --get "remote.$1.url")"
+ if test "$(git config --get "remote.$1.url")"
then
echo config
elif test -f "$GIT_DIR/remotes/$1"
@@ -38,7 +38,7 @@ get_remote_url () {
echo "$1"
;;
config)
- git-config --get "remote.$1.url"
+ git config --get "remote.$1.url"
;;
remotes)
sed -ne '/^URL: */{
@@ -55,8 +55,8 @@ get_remote_url () {
}
get_default_remote () {
- curr_branch=$(git-symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
- origin=$(git-config --get "branch.$curr_branch.remote")
+ curr_branch=$(git symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
+ origin=$(git config --get "branch.$curr_branch.remote")
echo ${origin:-origin}
}
@@ -66,7 +66,7 @@ get_remote_default_refs_for_push () {
'' | branches | self)
;; # no default push mapping, just send matching refs.
config)
- git-config --get-all "remote.$1.push" ;;
+ git config --get-all "remote.$1.push" ;;
remotes)
sed -ne '/^Push: */{
s///p
@@ -107,9 +107,9 @@ canon_refs_list_for_fetch () {
shift
if test "$remote" = "$(get_default_remote)"
then
- curr_branch=$(git-symbolic-ref -q HEAD | \
+ curr_branch=$(git symbolic-ref -q HEAD | \
sed -e 's|^refs/heads/||')
- merge_branches=$(git-config \
+ merge_branches=$(git config \
--get-all "branch.${curr_branch}.merge")
fi
if test -z "$merge_branches" && test $is_explicit != explicit
@@ -156,7 +156,7 @@ canon_refs_list_for_fetch () {
if local_ref_name=$(expr "z$local" : 'zrefs/\(.*\)')
then
- git-check-ref-format "$local_ref_name" ||
+ git check-ref-format "$local_ref_name" ||
die "* refusing to create funny ref '$local_ref_name' locally"
fi
echo "${dot_prefix}${force}${remote}:${local}"
@@ -171,11 +171,11 @@ get_remote_default_refs_for_fetch () {
echo "HEAD:" ;;
self)
canon_refs_list_for_fetch -d "$1" \
- $(git-for-each-ref --format='%(refname):')
+ $(git for-each-ref --format='%(refname):')
;;
config)
canon_refs_list_for_fetch -d "$1" \
- $(git-config --get-all "remote.$1.fetch") ;;
+ $(git config --get-all "remote.$1.fetch") ;;
branches)
remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1")
case "$remote_branch" in '') remote_branch=master ;; esac
@@ -254,7 +254,7 @@ get_uploadpack () {
data_source=$(get_data_source "$1")
case "$data_source" in
config)
- uplp=$(git-config --get "remote.$1.uploadpack")
+ uplp=$(git config --get "remote.$1.uploadpack")
echo ${uplp:-git-upload-pack}
;;
*)
diff --git a/git-pull.sh b/git-pull.sh
index a3665d7751..5e96d1f228 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -22,6 +22,9 @@ do
-n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
--no-summa|--no-summar|--no-summary)
no_summary=-n ;;
+ --summary)
+ no_summary=$1
+ ;;
--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
no_commit=--no-commit ;;
--sq|--squ|--squa|--squas|--squash)
@@ -51,10 +54,10 @@ do
shift
done
-orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
+orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
git-fetch --update-head-ok "$@" || exit 1
-curr_head=$(git-rev-parse --verify HEAD 2>/dev/null)
+curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
if test "$curr_head" != "$orig_head"
then
# The fetch involved updating the current branch.
@@ -66,8 +69,8 @@ then
echo >&2 "Warning: fetch updated the current branch head."
echo >&2 "Warning: fast forwarding your working tree from"
echo >&2 "Warning: commit $orig_head."
- git-update-index --refresh 2>/dev/null
- git-read-tree -u -m "$orig_head" "$curr_head" ||
+ git update-index --refresh 2>/dev/null
+ git read-tree -u -m "$orig_head" "$curr_head" ||
die 'Cannot fast-forward your working tree.
After making sure that you saved anything precious from
$ git diff '$orig_head'
@@ -83,7 +86,7 @@ merge_head=$(sed -e '/ not-for-merge /d' \
case "$merge_head" in
'')
- curr_branch=$(git-symbolic-ref -q HEAD)
+ curr_branch=$(git symbolic-ref -q HEAD)
case $? in
0) ;;
1) echo >&2 "You are not currently on a branch; you must explicitly"
@@ -110,11 +113,11 @@ esac
if test -z "$orig_head"
then
- git-update-ref -m "initial pull" HEAD $merge_head "" &&
- git-read-tree --reset -u HEAD || exit 1
+ git update-ref -m "initial pull" HEAD $merge_head "" &&
+ git read-tree --reset -u HEAD || exit 1
exit
fi
-merge_name=$(git-fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
+merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
exec git-merge $no_summary $no_commit $squash $strategy_args \
"$merge_name" HEAD $merge_head
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index a7a6757dd8..9de54d19fb 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -67,13 +67,14 @@ tmp_info="$tmp_dir/info"
# Find the intial commit
-commit=$(git-rev-parse HEAD)
+commit=$(git rev-parse HEAD)
mkdir $tmp_dir || exit 2
-for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
+for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do
echo $patch_name
- (cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
- test -s .dotest/patch || {
+ git mailinfo "$tmp_msg" "$tmp_patch" \
+ <"$QUILT_PATCHES/$patch_name" >"$tmp_info" || exit 3
+ test -s "$tmp_patch" || {
echo "Patch is empty. Was it split wrong?"
exit 1
}
@@ -113,10 +114,10 @@ for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
fi
if [ -z "$dry_run" ] ; then
- git-apply --index -C1 "$tmp_patch" &&
- tree=$(git-write-tree) &&
- commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
- git-update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
+ git apply --index -C1 "$tmp_patch" &&
+ tree=$(git write-tree) &&
+ commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
+ git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
fi
done
rm -rf $tmp_dir || exit 5
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
new file mode 100755
index 0000000000..bdec462609
--- /dev/null
+++ b/git-rebase--interactive.sh
@@ -0,0 +1,483 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+
+# SHORT DESCRIPTION
+#
+# This script makes it easy to fix up commits in the middle of a series,
+# and rearrange commits.
+#
+# The original idea comes from Eric W. Biederman, in
+# http://article.gmane.org/gmane.comp.version-control.git/22407
+
+USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
+ [--onto <branch>] <upstream> [<branch>])'
+
+. git-sh-setup
+require_work_tree
+
+DOTEST="$GIT_DIR/.dotest-merge"
+TODO="$DOTEST"/git-rebase-todo
+DONE="$DOTEST"/done
+MSG="$DOTEST"/message
+SQUASH_MSG="$DOTEST"/message-squash
+REWRITTEN="$DOTEST"/rewritten
+PRESERVE_MERGES=
+STRATEGY=
+VERBOSE=
+test -d "$REWRITTEN" && PRESERVE_MERGES=t
+test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
+test -f "$DOTEST"/verbose && VERBOSE=t
+
+warn () {
+ echo "$*" >&2
+}
+
+output () {
+ case "$VERBOSE" in
+ '')
+ "$@" > "$DOTEST"/output 2>&1
+ status=$?
+ test $status != 0 &&
+ cat "$DOTEST"/output
+ return $status
+ ;;
+ *)
+ "$@"
+ esac
+}
+
+require_clean_work_tree () {
+ # test if working tree is dirty
+ git rev-parse --verify HEAD > /dev/null &&
+ git update-index --refresh &&
+ git diff-files --quiet &&
+ git diff-index --cached --quiet HEAD ||
+ die "Working tree is dirty"
+}
+
+ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION"
+
+comment_for_reflog () {
+ case "$ORIG_REFLOG_ACTION" in
+ ''|rebase*)
+ GIT_REFLOG_ACTION="rebase -i ($1)"
+ export GIT_REFLOG_ACTION
+ esac
+}
+
+mark_action_done () {
+ sed -e 1q < "$TODO" >> "$DONE"
+ sed -e 1d < "$TODO" >> "$TODO".new
+ mv -f "$TODO".new "$TODO"
+ count=$(($(wc -l < "$DONE")))
+ total=$(($count+$(wc -l < "$TODO")))
+ printf "Rebasing (%d/%d)\r" $count $total
+ test -z "$VERBOSE" || echo
+}
+
+make_patch () {
+ parent_sha1=$(git rev-parse --verify "$1"^ 2> /dev/null)
+ git diff "$parent_sha1".."$1" > "$DOTEST"/patch
+}
+
+die_with_patch () {
+ test -f "$DOTEST"/message ||
+ git cat-file commit $sha1 | sed "1,/^$/d" > "$DOTEST"/message
+ test -f "$DOTEST"/author-script ||
+ get_author_ident_from_commit $sha1 > "$DOTEST"/author-script
+ make_patch "$1"
+ die "$2"
+}
+
+die_abort () {
+ rm -rf "$DOTEST"
+ die "$1"
+}
+
+pick_one () {
+ case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
+ output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
+ test -d "$REWRITTEN" &&
+ pick_one_preserving_merges "$@" && return
+ parent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)
+ current_sha1=$(git rev-parse --verify HEAD)
+ if test $current_sha1 = $parent_sha1; then
+ output git reset --hard $sha1
+ test "a$1" = a-n && output git reset --soft $current_sha1
+ sha1=$(git rev-parse --short $sha1)
+ output warn Fast forward to $sha1
+ else
+ output git cherry-pick $STRATEGY "$@"
+ fi
+}
+
+pick_one_preserving_merges () {
+ case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
+ sha1=$(git rev-parse $sha1)
+
+ if test -f "$DOTEST"/current-commit
+ then
+ current_commit=$(cat "$DOTEST"/current-commit) &&
+ git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
+ rm "$DOTEST"/current-commit ||
+ die "Cannot write current commit's replacement sha1"
+ fi
+
+ # rewrite parents; if none were rewritten, we can fast-forward.
+ fast_forward=t
+ preserve=t
+ new_parents=
+ for p in $(git rev-list --parents -1 $sha1 | cut -d\ -f2-)
+ do
+ if test -f "$REWRITTEN"/$p
+ then
+ preserve=f
+ new_p=$(cat "$REWRITTEN"/$p)
+ test $p != $new_p && fast_forward=f
+ case "$new_parents" in
+ *$new_p*)
+ ;; # do nothing; that parent is already there
+ *)
+ new_parents="$new_parents $new_p"
+ esac
+ fi
+ done
+ case $fast_forward in
+ t)
+ output warn "Fast forward to $sha1"
+ test $preserve=f && echo $sha1 > "$REWRITTEN"/$sha1
+ ;;
+ f)
+ test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
+
+ first_parent=$(expr "$new_parents" : " \([^ ]*\)")
+ # detach HEAD to current parent
+ output git checkout $first_parent 2> /dev/null ||
+ die "Cannot move HEAD to $first_parent"
+
+ echo $sha1 > "$DOTEST"/current-commit
+ case "$new_parents" in
+ \ *\ *)
+ # redo merge
+ author_script=$(get_author_ident_from_commit $sha1)
+ eval "$author_script"
+ msg="$(git cat-file commit $sha1 | \
+ sed -e '1,/^$/d' -e "s/[\"\\]/\\\\&/g")"
+ # NEEDSWORK: give rerere a chance
+ if ! output git merge $STRATEGY -m "$msg" $new_parents
+ then
+ echo "$msg" > "$GIT_DIR"/MERGE_MSG
+ die Error redoing merge $sha1
+ fi
+ ;;
+ *)
+ output git cherry-pick $STRATEGY "$@" ||
+ die_with_patch $sha1 "Could not pick $sha1"
+ esac
+ esac
+}
+
+nth_string () {
+ case "$1" in
+ *1[0-9]|*[04-9]) echo "$1"th;;
+ *1) echo "$1"st;;
+ *2) echo "$1"nd;;
+ *3) echo "$1"rd;;
+ esac
+}
+
+make_squash_message () {
+ if test -f "$SQUASH_MSG"; then
+ COUNT=$(($(sed -n "s/^# This is [^0-9]*\([0-9]\+\).*/\1/p" \
+ < "$SQUASH_MSG" | tail -n 1)+1))
+ echo "# This is a combination of $COUNT commits."
+ sed -n "2,\$p" < "$SQUASH_MSG"
+ else
+ COUNT=2
+ echo "# This is a combination of two commits."
+ echo "# The first commit's message is:"
+ echo
+ git cat-file commit HEAD | sed -e '1,/^$/d'
+ echo
+ fi
+ echo "# This is the $(nth_string $COUNT) commit message:"
+ echo
+ git cat-file commit $1 | sed -e '1,/^$/d'
+}
+
+peek_next_command () {
+ sed -n "1s/ .*$//p" < "$TODO"
+}
+
+do_next () {
+ test -f "$DOTEST"/message && rm "$DOTEST"/message
+ test -f "$DOTEST"/author-script && rm "$DOTEST"/author-script
+ read command sha1 rest < "$TODO"
+ case "$command" in
+ \#|'')
+ mark_action_done
+ ;;
+ pick)
+ comment_for_reflog pick
+
+ mark_action_done
+ pick_one $sha1 ||
+ die_with_patch $sha1 "Could not apply $sha1... $rest"
+ ;;
+ edit)
+ comment_for_reflog edit
+
+ mark_action_done
+ pick_one $sha1 ||
+ die_with_patch $sha1 "Could not apply $sha1... $rest"
+ make_patch $sha1
+ warn
+ warn "You can amend the commit now, with"
+ warn
+ warn " git commit --amend"
+ warn
+ exit 0
+ ;;
+ squash)
+ comment_for_reflog squash
+
+ test -z "$(grep -ve '^$' -e '^#' < $DONE)" &&
+ die "Cannot 'squash' without a previous commit"
+
+ mark_action_done
+ make_squash_message $sha1 > "$MSG"
+ case "$(peek_next_command)" in
+ squash)
+ EDIT_COMMIT=
+ USE_OUTPUT=output
+ cp "$MSG" "$SQUASH_MSG"
+ ;;
+ *)
+ EDIT_COMMIT=-e
+ USE_OUTPUT=
+ test -f "$SQUASH_MSG" && rm "$SQUASH_MSG"
+ esac
+
+ failed=f
+ output git reset --soft HEAD^
+ pick_one -n $sha1 || failed=t
+ author_script=$(get_author_ident_from_commit $sha1)
+ echo "$author_script" > "$DOTEST"/author-script
+ case $failed in
+ f)
+ # This is like --amend, but with a different message
+ eval "$author_script"
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
+ $USE_OUTPUT git commit -F "$MSG" $EDIT_COMMIT
+ ;;
+ t)
+ cp "$MSG" "$GIT_DIR"/MERGE_MSG
+ warn
+ warn "Could not apply $sha1... $rest"
+ die_with_patch $sha1 ""
+ esac
+ ;;
+ *)
+ warn "Unknown command: $command $sha1 $rest"
+ die_with_patch $sha1 "Please fix this in the file $TODO."
+ esac
+ test -s "$TODO" && return
+
+ comment_for_reflog finish &&
+ HEADNAME=$(cat "$DOTEST"/head-name) &&
+ OLDHEAD=$(cat "$DOTEST"/head) &&
+ SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
+ if test -d "$REWRITTEN"
+ then
+ test -f "$DOTEST"/current-commit &&
+ current_commit=$(cat "$DOTEST"/current-commit) &&
+ git rev-parse HEAD > "$REWRITTEN"/$current_commit
+ NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD)
+ else
+ NEWHEAD=$(git rev-parse HEAD)
+ fi &&
+ message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
+ git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
+ git symbolic-ref HEAD $HEADNAME && {
+ test ! -f "$DOTEST"/verbose ||
+ git diff --stat $(cat "$DOTEST"/head)..HEAD
+ } &&
+ rm -rf "$DOTEST" &&
+ warn "Successfully rebased and updated $HEADNAME."
+
+ exit
+}
+
+do_rest () {
+ while :
+ do
+ do_next
+ done
+}
+
+while case $# in 0) break ;; esac
+do
+ case "$1" in
+ --continue)
+ comment_for_reflog continue
+
+ test -d "$DOTEST" || die "No interactive rebase running"
+
+ # commit if necessary
+ git rev-parse --verify HEAD > /dev/null &&
+ git update-index --refresh &&
+ git diff-files --quiet &&
+ ! git diff-index --cached --quiet HEAD &&
+ . "$DOTEST"/author-script &&
+ export GIT_AUTHOR_NAME GIT_AUTHOR_NAME GIT_AUTHOR_DATE &&
+ git commit -F "$DOTEST"/message -e
+
+ require_clean_work_tree
+ do_rest
+ ;;
+ --abort)
+ comment_for_reflog abort
+
+ test -d "$DOTEST" || die "No interactive rebase running"
+
+ HEADNAME=$(cat "$DOTEST"/head-name)
+ HEAD=$(cat "$DOTEST"/head)
+ git symbolic-ref HEAD $HEADNAME &&
+ output git reset --hard $HEAD &&
+ rm -rf "$DOTEST"
+ exit
+ ;;
+ --skip)
+ comment_for_reflog skip
+
+ test -d "$DOTEST" || die "No interactive rebase running"
+
+ output git reset --hard && do_rest
+ ;;
+ -s|--strategy)
+ shift
+ case "$#,$1" in
+ *,*=*)
+ STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;;
+ 1,*)
+ usage ;;
+ *)
+ STRATEGY="-s $2"
+ shift ;;
+ esac
+ ;;
+ --merge)
+ # we use merge anyway
+ ;;
+ -C*)
+ die "Interactive rebase uses merge, so $1 does not make sense"
+ ;;
+ -v|--verbose)
+ VERBOSE=t
+ ;;
+ -p|--preserve-merges)
+ PRESERVE_MERGES=t
+ ;;
+ -i|--interactive)
+ # yeah, we know
+ ;;
+ ''|-h)
+ usage
+ ;;
+ *)
+ test -d "$DOTEST" &&
+ die "Interactive rebase already started"
+
+ git var GIT_COMMITTER_IDENT >/dev/null ||
+ die "You need to set your committer info first"
+
+ comment_for_reflog start
+
+ ONTO=
+ case "$1" in
+ --onto)
+ ONTO=$(git rev-parse --verify "$2") ||
+ die "Does not point to a valid commit: $2"
+ shift; shift
+ ;;
+ esac
+
+ require_clean_work_tree
+
+ mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
+ if test ! -z "$2"
+ then
+ output git show-ref --verify --quiet "refs/heads/$2" ||
+ die "Invalid branchname: $2"
+ output git checkout "$2" ||
+ die "Could not checkout $2"
+ fi
+
+ HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
+ UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
+
+ test -z "$ONTO" && ONTO=$UPSTREAM
+
+ : > "$DOTEST"/interactive || die "Could not mark as interactive"
+ git symbolic-ref HEAD > "$DOTEST"/head-name ||
+ die "Could not get HEAD"
+
+ echo $HEAD > "$DOTEST"/head
+ echo $UPSTREAM > "$DOTEST"/upstream
+ echo $ONTO > "$DOTEST"/onto
+ test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
+ test t = "$VERBOSE" && : > "$DOTEST"/verbose
+ if test t = "$PRESERVE_MERGES"
+ then
+ # $REWRITTEN contains files for each commit that is
+ # reachable by at least one merge base of $HEAD and
+ # $UPSTREAM. They are not necessarily rewritten, but
+ # their children might be.
+ # This ensures that commits on merged, but otherwise
+ # unrelated side branches are left alone. (Think "X"
+ # in the man page's example.)
+ mkdir "$REWRITTEN" &&
+ for c in $(git merge-base --all $HEAD $UPSTREAM)
+ do
+ echo $ONTO > "$REWRITTEN"/$c ||
+ die "Could not init rewritten commits"
+ done
+ MERGES_OPTION=
+ else
+ MERGES_OPTION=--no-merges
+ fi
+
+ SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
+ SHORTHEAD=$(git rev-parse --short $HEAD)
+ SHORTONTO=$(git rev-parse --short $ONTO)
+ cat > "$TODO" << EOF
+# Rebasing $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
+#
+# Commands:
+# pick = use commit
+# edit = use commit, but stop for amending
+# squash = use commit, but meld into previous commit
+#
+# If you remove a line here THAT COMMIT WILL BE LOST.
+#
+EOF
+ git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
+ --abbrev=7 --reverse --left-right --cherry-pick \
+ $UPSTREAM...$HEAD | \
+ sed -n "s/^>/pick /p" >> "$TODO"
+
+ test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
+ die_abort "Nothing to do"
+
+ cp "$TODO" "$TODO".backup
+ git_editor "$TODO" ||
+ die "Could not execute editor"
+
+ test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
+ die_abort "Nothing to do"
+
+ output git checkout $ONTO && do_rest
+ esac
+ shift
+done
diff --git a/git-rebase.sh b/git-rebase.sh
index 2dc2c4fe9b..cbafa14ed5 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano.
#
-USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'
+USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]'
LONG_USAGE='git-rebase replaces <branch> with a new branch of the
same name. When the --onto option is provided the new branch starts
out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -51,15 +51,15 @@ continue_merge () {
test -n "$prev_head" || die "prev_head must be defined"
test -d "$dotest" || die "$dotest directory does not exist"
- unmerged=$(git-ls-files -u)
+ unmerged=$(git ls-files -u)
if test -n "$unmerged"
then
echo "You still have unmerged paths in your index"
- echo "did you forget update-index?"
+ echo "did you forget to use git add?"
die "$RESOLVEMSG"
fi
- if ! git-diff-index --quiet HEAD
+ if ! git diff-index --quiet HEAD
then
if ! git-commit -C "`cat $dotest/current`"
then
@@ -71,10 +71,10 @@ continue_merge () {
else
printf "Already applied: %0${prec}d" $msgnum
fi
- echo ' '`git-rev-list --pretty=oneline -1 HEAD | \
+ echo ' '`git rev-list --pretty=oneline -1 HEAD | \
sed 's/^[a-f0-9]\+ //'`
- prev_head=`git-rev-parse HEAD^0`
+ prev_head=`git rev-parse HEAD^0`
# save the resulting commit so we can read-tree on it later
echo "$prev_head" > "$dotest/prev_head"
@@ -86,8 +86,8 @@ continue_merge () {
call_merge () {
cmt="$(cat $dotest/cmt.$1)"
echo "$cmt" > "$dotest/current"
- hd=$(git-rev-parse --verify HEAD)
- cmt_name=$(git-symbolic-ref HEAD)
+ hd=$(git rev-parse --verify HEAD)
+ cmt_name=$(git symbolic-ref HEAD)
msgnum=$(cat $dotest/msgnum)
end=$(cat $dotest/end)
eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
@@ -101,7 +101,7 @@ call_merge () {
return
;;
1)
- test -d "$GIT_DIR/rr-cache" && git-rerere
+ git rerere
die "$RESOLVEMSG"
;;
2)
@@ -120,13 +120,23 @@ finish_rb_merge () {
echo "All done."
}
+is_interactive () {
+ test -f "$dotest"/interactive ||
+ while case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac
+ do
+ shift
+ done && test -n "$1"
+}
+
+is_interactive "$@" && exec git-rebase--interactive "$@"
+
while case "$#" in 0) break ;; esac
do
case "$1" in
--continue)
- git-diff-files --quiet || {
+ git diff-files --quiet || {
echo "You must edit all merge conflicts and then"
- echo "mark them as resolved using git update-index"
+ echo "mark them as resolved using git add"
exit 1
}
if test -d "$dotest"
@@ -150,10 +160,7 @@ do
--skip)
if test -d "$dotest"
then
- if test -d "$GIT_DIR/rr-cache"
- then
- git-rerere clear
- fi
+ git rerere clear
prev_head="`cat $dotest/prev_head`"
end="`cat $dotest/end`"
msgnum="`cat $dotest/msgnum`"
@@ -171,10 +178,7 @@ do
exit
;;
--abort)
- if test -d "$GIT_DIR/rr-cache"
- then
- git-rerere clear
- fi
+ git rerere clear
if test -d "$dotest"
then
rm -r "$dotest"
@@ -249,8 +253,8 @@ else
fi
# The tree must be really really clean.
-git-update-index --refresh || exit
-diff=$(git-diff-index --cached --name-status -r HEAD)
+git update-index --refresh || exit
+diff=$(git diff-index --cached --name-status -r HEAD)
case "$diff" in
?*) echo "cannot rebase: your index is not up-to-date"
echo "$diff"
@@ -265,7 +269,7 @@ upstream=`git rev-parse --verify "${upstream_name}^0"` ||
# Make sure the branch to rebase onto is valid.
onto_name=${newbase-"$upstream_name"}
-onto=$(git-rev-parse --verify "${onto_name}^0") || exit
+onto=$(git rev-parse --verify "${onto_name}^0") || exit
# If a hook exists, give it a chance to interrupt
if test -x "$GIT_DIR/hooks/pre-rebase"
@@ -291,14 +295,16 @@ case "$#" in
fi
;;
esac
-branch=$(git-rev-parse --verify "${branch_name}^0") || exit
+branch=$(git rev-parse --verify "${branch_name}^0") || exit
# Now we are rebasing commits $upstream..$branch on top of $onto
-# Check if we are already based on $onto, but this should be
-# done only when upstream and onto are the same.
-mb=$(git-merge-base "$onto" "$branch")
-if test "$upstream" = "$onto" && test "$mb" = "$onto"
+# Check if we are already based on $onto with linear history,
+# but this should be done only when upstream and onto are the same.
+mb=$(git merge-base "$onto" "$branch")
+if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
+ # linear history?
+ ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null
then
echo >&2 "Current branch $branch_name is up to date."
exit 0
@@ -307,7 +313,8 @@ fi
if test -n "$verbose"
then
echo "Changes from $mb to $onto:"
- git-diff-tree --stat --summary "$mb" "$onto"
+ # We want color (if set), but no pager
+ GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
fi
# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
@@ -324,7 +331,7 @@ fi
if test -z "$do_merge"
then
- git-format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
+ git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG"
exit $?
fi
@@ -335,11 +342,11 @@ fi
mkdir -p "$dotest"
echo "$onto" > "$dotest/onto"
echo "$onto_name" > "$dotest/onto_name"
-prev_head=`git-rev-parse HEAD^0`
+prev_head=`git rev-parse HEAD^0`
echo "$prev_head" > "$dotest/prev_head"
msgnum=0
-for cmt in `git-rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
+for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
do
msgnum=$(($msgnum + 1))
echo "$cmt" > "$dotest/cmt.$msgnum"
diff --git a/git-remote.perl b/git-remote.perl
index 5763799127..01cf480221 100755
--- a/git-remote.perl
+++ b/git-remote.perl
@@ -258,6 +258,7 @@ sub show_remote {
if ($info->{'PUSH'}) {
my @pushed = map {
s|^refs/heads/||;
+ s|^\+refs/heads/|+|;
s|:refs/heads/|:|;
$_;
} @{$info->{'PUSH'}};
@@ -318,9 +319,21 @@ sub add_usage {
exit(1);
}
+local $VERBOSE = 0;
+@ARGV = grep {
+ if ($_ eq '-v' or $_ eq '--verbose') {
+ $VERBOSE=1;
+ 0
+ } else {
+ 1
+ }
+} @ARGV;
+
if (!@ARGV) {
for (sort keys %$remote) {
- print "$_\n";
+ print "$_";
+ print "\t$remote->{$_}->{URL}" if $VERBOSE;
+ print "\n";
}
}
elsif ($ARGV[0] eq 'show') {
diff --git a/git-repack.sh b/git-repack.sh
index ddfa8b44a1..156c5e8f4a 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -3,12 +3,12 @@
# Copyright (c) 2005 Linus Torvalds
#
-USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]'
+USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--max-pack-size=N] [--window=N] [--window-memory=N] [--depth=N]'
SUBDIRECTORY_OK='Yes'
. git-sh-setup
no_update_info= all_into_one= remove_redundant=
-local= quiet= no_reuse_delta= extra=
+local= quiet= no_reuse= extra=
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -16,9 +16,11 @@ do
-a) all_into_one=t ;;
-d) remove_redundant=t ;;
-q) quiet=-q ;;
- -f) no_reuse_delta=--no-reuse-delta ;;
+ -f) no_reuse=--no-reuse-object ;;
-l) local=--local ;;
+ --max-pack-size=*) extra="$extra $1" ;;
--window=*) extra="$extra $1" ;;
+ --window-memory=*) extra="$extra $1" ;;
--depth=*) extra="$extra $1" ;;
*) usage ;;
esac
@@ -35,7 +37,7 @@ true)
esac
PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
-PACKTMP="$GIT_DIR/.tmp-$$-pack"
+PACKTMP="$GIT_OBJECT_DIRECTORY/.tmp-$$-pack"
rm -f "$PACKTMP"-*
trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
@@ -61,12 +63,16 @@ case ",$all_into_one," in
;;
esac
-args="$args $local $quiet $no_reuse_delta$extra"
-name=$(git-pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
+args="$args $local $quiet $no_reuse$extra"
+names=$(git pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
exit 1
-if [ -z "$name" ]; then
- echo Nothing new to pack.
-else
+if [ -z "$names" ]; then
+ if test -z "$quiet"; then
+ echo Nothing new to pack.
+ fi
+fi
+for name in $names ; do
+ fullbases="$fullbases pack-$name"
chmod a-w "$PACKTMP-$name.pack"
chmod a-w "$PACKTMP-$name.idx"
if test "$quiet" != '-q'; then
@@ -92,7 +98,7 @@ else
exit 1
}
rm -f "$PACKDIR/old-pack-$name.pack" "$PACKDIR/old-pack-$name.idx"
-fi
+done
if test "$remove_redundant" = t
then
@@ -103,14 +109,14 @@ then
( cd "$PACKDIR" &&
for e in $existing
do
- case "$e" in
- pack-$name) ;;
+ case " $fullbases " in
+ *" $e "*) ;;
*) rm -f "$e.pack" "$e.idx" "$e.keep" ;;
esac
done
)
fi
- git-prune-packed $quiet
+ git prune-packed $quiet
fi
case "$no_update_info" in
diff --git a/git-request-pull.sh b/git-request-pull.sh
index ba577d4ce1..a992430679 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -18,8 +18,8 @@ head=${3-HEAD}
[ "$base" ] || usage
[ "$url" ] || usage
-baserev=`git-rev-parse --verify "$base"^0` &&
-headrev=`git-rev-parse --verify "$head"^0` || exit
+baserev=`git rev-parse --verify "$base"^0` &&
+headrev=`git rev-parse --verify "$head"^0` || exit
merge_base=`git merge-base $baserev $headrev` ||
die "fatal: No commits in common between $base and $head"
diff --git a/git-reset.sh b/git-reset.sh
index a172d7ce25..1dc606fbd3 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -24,7 +24,7 @@ do
usage
;;
*)
- rev=$(git-rev-parse --verify "$1") || exit
+ rev=$(git rev-parse --verify "$1") || exit
shift
break
;;
@@ -33,7 +33,7 @@ do
done
: ${rev=HEAD}
-rev=$(git-rev-parse --verify $rev^0) || exit
+rev=$(git rev-parse --verify $rev^0) || exit
# Skip -- in "git reset HEAD -- foo" and "git reset -- foo".
case "$1" in --) shift ;; esac
@@ -46,7 +46,7 @@ then
test "$reset_type" = "--mixed" ||
die "Cannot do partial $reset_type reset."
- git-diff-index --cached $rev -- "$@" |
+ git diff-index --cached $rev -- "$@" |
sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z] \(.*\)$/\1 \2 \3/' |
git update-index --add --remove --index-info || exit
git update-index --refresh
@@ -66,22 +66,22 @@ fi
if test "$reset_type" = "--soft"
then
if test -f "$GIT_DIR/MERGE_HEAD" ||
- test "" != "$(git-ls-files --unmerged)"
+ test "" != "$(git ls-files --unmerged)"
then
die "Cannot do a soft reset in the middle of a merge."
fi
else
- git-read-tree -v --reset $update "$rev" || exit
+ git read-tree -v --reset $update "$rev" || exit
fi
# Any resets update HEAD to the head being switched to.
-if orig=$(git-rev-parse --verify HEAD 2>/dev/null)
+if orig=$(git rev-parse --verify HEAD 2>/dev/null)
then
echo "$orig" >"$GIT_DIR/ORIG_HEAD"
else
rm -f "$GIT_DIR/ORIG_HEAD"
fi
-git-update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev"
+git update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev"
update_ref_status=$?
case "$reset_type" in
@@ -96,7 +96,7 @@ case "$reset_type" in
;; # Nothing else to do
--mixed )
# Report what has not been updated.
- git-update-index --refresh
+ git update-index --refresh
;;
esac
diff --git a/git-send-email.perl b/git-send-email.perl
index 7c0c90bd21..69559b289a 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -49,8 +49,8 @@ Options:
--bcc Specify a list of email addresses that should be Bcc:
on all the emails.
- --compose Use \$EDITOR to edit an introductory message for the
- patch series.
+ --compose Use \$GIT_EDITOR, core.editor, \$EDITOR, or \$VISUAL to edit
+ an introductory message for the patch series.
--subject Specify the initial "Subject:" line.
Only necessary if --compose is also set. If --compose
@@ -64,15 +64,17 @@ Options:
email sent, rather than to the first email sent.
Defaults to on.
- --no-signed-off-cc Suppress the automatic addition of email addresses
- that appear in Signed-off-by: or Cc: lines to the cc:
- list. Note: Using this option is not recommended.
+ --signed-off-cc Automatically add email addresses that appear in
+ Signed-off-by: or Cc: lines to the cc: list. Defaults to on.
--smtp-server If set, specifies the outgoing SMTP server to use.
Defaults to localhost.
--suppress-from Suppress sending emails to yourself if your address
- appears in a From: line.
+ appears in a From: line. Defaults to off.
+
+ --thread Specify that the "In-Reply-To:" header should be set on all
+ emails. Defaults to on.
--quiet Make git-send-email less verbose. One line per email
should be all that is output.
@@ -135,11 +137,8 @@ my $compose_filename = ".msg.$$";
# Variables we fill in automatically, or via prompting:
my (@to,@cc,@initial_cc,@bcclist,@xh,
- $initial_reply_to,$initial_subject,@files,$from,$compose,$time);
+ $initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
-# Behavior modification variables
-my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
- $dry_run) = (1, 0, 0, 0, 0);
my $smtp_server;
my $envelope_sender;
@@ -154,9 +153,22 @@ if ($@) {
$term = new FakeTerm "$@: going non-interactive";
}
-my $def_chain = $repo->config_bool('sendemail.chainreplyto');
-if (defined $def_chain and not $def_chain) {
- $chain_reply_to = 0;
+# Behavior modification variables
+my ($quiet, $dry_run) = (0, 0);
+
+# Variables with corresponding config settings
+my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc);
+
+my %config_settings = (
+ "thread" => [\$thread, 1],
+ "chainreplyto" => [\$chain_reply_to, 1],
+ "suppressfrom" => [\$suppress_from, 0],
+ "signedoffcc" => [\$signed_off_cc, 1],
+);
+
+foreach my $setting (keys %config_settings) {
+ my $config = $repo->config_bool("sendemail.$setting");
+ ${$config_settings{$setting}->[0]} = (defined $config) ? $config : $config_settings{$setting}->[1];
}
@bcclist = $repo->config('sendemail.bcc');
@@ -167,7 +179,7 @@ if (!@bcclist or !$bcclist[0]) {
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:
-my $rc = GetOptions("from=s" => \$from,
+my $rc = GetOptions("sender|from=s" => \$sender,
"in-reply-to=s" => \$initial_reply_to,
"subject=s" => \$initial_subject,
"to=s" => \@to,
@@ -177,10 +189,11 @@ my $rc = GetOptions("from=s" => \$from,
"smtp-server=s" => \$smtp_server,
"compose" => \$compose,
"quiet" => \$quiet,
- "suppress-from" => \$suppress_from,
- "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
+ "suppress-from!" => \$suppress_from,
+ "signed-off-cc|signed-off-by-cc!" => \$signed_off_cc,
"dry-run" => \$dry_run,
"envelope-sender=s" => \$envelope_sender,
+ "thread!" => \$thread,
);
unless ($rc) {
@@ -203,8 +216,8 @@ foreach my $entry (@bcclist) {
# Now, let's fill any that aren't set in with defaults:
-my ($author) = $repo->ident_person('author');
-my ($committer) = $repo->ident_person('committer');
+my ($repoauthor) = $repo->ident_person('author');
+my ($repocommitter) = $repo->ident_person('committer');
my %aliases;
my @alias_files = $repo->config('sendemail.aliasesfile');
@@ -224,7 +237,7 @@ my %parse_alias = (
$aliases{$1} = [ split(/\s+/, $2) ];
}}},
pine => sub { my $fh = shift; while (<$fh>) {
- if (/^(\S+)\s+(.*)$/) {
+ if (/^(\S+)\t.*\t(.*)$/) {
$aliases{$1} = [ split(/\s*,\s*/, $2) ];
}}},
gnus => sub { my $fh = shift; while (<$fh>) {
@@ -241,15 +254,17 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
}
}
+($sender) = expand_aliases($sender) if defined $sender;
+
my $prompting = 0;
-if (!defined $from) {
- $from = $author || $committer;
+if (!defined $sender) {
+ $sender = $repoauthor || $repocommitter;
do {
- $_ = $term->readline("Who should the emails appear to be from? [$from] ");
+ $_ = $term->readline("Who should the emails appear to be from? [$sender] ");
} while (!defined $_);
- $from = $_ if ($_);
- print "Emails will be sent from: ", $from, "\n";
+ $sender = $_ if ($_);
+ print "Emails will be sent from: ", $sender, "\n";
$prompting++;
}
@@ -274,7 +289,7 @@ sub expand_aliases {
}
@to = expand_aliases(@to);
-@to = (map { sanitize_address_rfc822($_) } @to);
+@to = (map { sanitize_address($_) } @to);
@initial_cc = expand_aliases(@initial_cc);
@bcclist = expand_aliases(@bcclist);
@@ -287,7 +302,7 @@ if (!defined $initial_subject && $compose) {
$prompting++;
}
-if (!defined $initial_reply_to && $prompting) {
+if ($thread && !defined $initial_reply_to && $prompting) {
do {
$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ",
$initial_reply_to);
@@ -315,7 +330,7 @@ if ($compose) {
# effort to have it be unique
open(C,">",$compose_filename)
or die "Failed to open for writing $compose_filename: $!";
- print C "From $from # This line is ignored.\n";
+ print C "From $sender # This line is ignored.\n";
printf C "Subject: %s\n\n", $initial_subject;
printf C <<EOT;
GIT: Please enter your email below.
@@ -326,8 +341,7 @@ GIT: for the patch you are writing.
EOT
close(C);
- my $editor = $ENV{EDITOR};
- $editor = 'vi' unless defined $editor;
+ my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
system($editor, $compose_filename);
open(C2,">",$compose_filename . ".final")
@@ -394,6 +408,7 @@ sub extract_valid_address {
# check for a local address:
return $address if ($address =~ /^($local_part_regexp)$/);
+ $address =~ s/^\s*<(.*)>\s*$/$1/;
if ($have_email_valid) {
return scalar Email::Valid->address($address);
} else {
@@ -412,13 +427,21 @@ sub extract_valid_address {
# 1 second since the last time we were called.
# We'll setup a template for the message id, using the "from" address:
-my $message_id_from = extract_valid_address($from);
-my $message_id_template = "<%s-git-send-email-$message_id_from>";
sub make_message_id
{
my $date = time;
my $pseudo_rand = int (rand(4200));
+ my $du_part;
+ for ($sender, $repocommitter, $repoauthor) {
+ $du_part = extract_valid_address(sanitize_address($_));
+ last if (defined $du_part and $du_part ne '');
+ }
+ if (not defined $du_part or $du_part eq '') {
+ use Sys::Hostname qw();
+ $du_part = 'user@' . Sys::Hostname::hostname();
+ }
+ my $message_id_template = "<%s-git-send-email-$du_part>";
$message_id = sprintf $message_id_template, "$date$pseudo_rand";
#print "new message id = $message_id\n"; # Was useful for debugging
}
@@ -436,22 +459,41 @@ sub unquote_rfc2047 {
return "$_";
}
-# If an address contains a . in the name portion, the name must be quoted.
-sub sanitize_address_rfc822
+# use the simplest quoting being able to handle the recipient
+sub sanitize_address
{
my ($recipient) = @_;
- my ($recipient_name) = ($recipient =~ /^(.*?)\s+</);
- if ($recipient_name && $recipient_name =~ /\./ && $recipient_name !~ /^".*"$/) {
- my ($name, $addr) = ($recipient =~ /^(.*?)(\s+<.*)/);
- $recipient = "\"$name\"$addr";
+ my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
+
+ if (not $recipient_name) {
+ return "$recipient";
}
- return $recipient;
+
+ # if recipient_name is already quoted, do nothing
+ if ($recipient_name =~ /^(".*"|=\?utf-8\?q\?.*\?=)$/) {
+ return $recipient;
+ }
+
+ # rfc2047 is needed if a non-ascii char is included
+ if ($recipient_name =~ /[^[:ascii:]]/) {
+ $recipient_name =~ s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
+ $recipient_name =~ s/(.*)/=\?utf-8\?q\?$1\?=/;
+ }
+
+ # double quotes are needed if specials or CTLs are included
+ elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
+ $recipient_name =~ s/(["\\\r])/\\$1/;
+ $recipient_name = "\"$recipient_name\"";
+ }
+
+ return "$recipient_name $recipient_addr";
+
}
sub send_message
{
my @recipients = unique_email_list(@to);
- @cc = (map { sanitize_address_rfc822($_) } @cc);
+ @cc = (map { sanitize_address($_) } @cc);
my $to = join (",\n\t", @recipients);
@recipients = unique_email_list(@recipients,@cc,@bcclist);
@recipients = (map { extract_valid_address($_) } @recipients);
@@ -466,15 +508,17 @@ sub send_message
if ($cc ne '') {
$ccline = "\nCc: $cc";
}
- $from = sanitize_address_rfc822($from);
- my $header = "From: $from
+ my $sanitized_sender = sanitize_address($sender);
+ make_message_id();
+
+ my $header = "From: $sanitized_sender
To: $to${ccline}
Subject: $subject
Date: $date
Message-Id: $message_id
X-Mailer: git-send-email $gitversion
";
- if ($reply_to) {
+ if ($thread && $reply_to) {
$header .= "In-Reply-To: $reply_to\n";
$header .= "References: $references\n";
@@ -484,7 +528,7 @@ X-Mailer: git-send-email $gitversion
}
my @sendmail_parameters = ('-i', @recipients);
- my $raw_from = $from;
+ my $raw_from = $sanitized_sender;
$raw_from = $envelope_sender if (defined $envelope_sender);
$raw_from = extract_valid_address($raw_from);
unshift (@sendmail_parameters,
@@ -521,7 +565,7 @@ X-Mailer: git-send-email $gitversion
} else {
print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
}
- print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
+ print "From: $sanitized_sender\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
if ($smtp) {
print "Result: ", $smtp->code, ' ',
($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
@@ -533,13 +577,12 @@ X-Mailer: git-send-email $gitversion
$reply_to = $initial_reply_to;
$references = $initial_reply_to || '';
-make_message_id();
$subject = $initial_subject;
foreach my $t (@files) {
open(F,"<",$t) or die "can't open file $t";
- my $author_not_sender = undef;
+ my $author = undef;
@cc = @initial_cc;
@xh = ();
my $input_format = undef;
@@ -561,12 +604,11 @@ foreach my $t (@files) {
$subject = $1;
} elsif (/^(Cc|From):\s+(.*)$/) {
- if (unquote_rfc2047($2) eq $from) {
- $from = $2;
+ if (unquote_rfc2047($2) eq $sender) {
next if ($suppress_from);
}
elsif ($1 eq 'From') {
- $author_not_sender = $2;
+ $author = unquote_rfc2047($2);
}
printf("(mbox) Adding cc: %s from line '%s'\n",
$2, $_) unless $quiet;
@@ -600,7 +642,7 @@ foreach my $t (@files) {
}
} else {
$message .= $_;
- if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) {
+ if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) {
my $c = $2;
chomp $c;
push @cc, $c;
@@ -610,9 +652,8 @@ foreach my $t (@files) {
}
}
close F;
- if (defined $author_not_sender) {
- $author_not_sender = unquote_rfc2047($author_not_sender);
- $message = "From: $author_not_sender\n\n$message";
+ if (defined $author) {
+ $message = "From: $author\n\n$message";
}
@@ -627,7 +668,6 @@ foreach my $t (@files) {
$references = "$message_id";
}
}
- make_message_id();
}
if ($compose) {
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index f24c7f2d23..185c5c6c95 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -28,16 +28,28 @@ set_reflog_action() {
fi
}
-is_bare_repository () {
- git-config --bool --get core.bare ||
- case "$GIT_DIR" in
- .git | */.git) echo false ;;
- *) echo true ;;
+git_editor() {
+ : "${GIT_EDITOR:=$(git config core.editor)}"
+ : "${GIT_EDITOR:=${VISUAL:-${EDITOR}}}"
+ case "$GIT_EDITOR,$TERM" in
+ ,dumb)
+ echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL,"
+ echo >&2 "or EDITOR. Tried to fall back to vi but terminal is dumb."
+ echo >&2 "Please set one of these variables to an appropriate"
+ echo >&2 "editor or run $0 with options that will not cause an"
+ echo >&2 "editor to be invoked (e.g., -m or -F for git-commit)."
+ exit 1
+ ;;
esac
+ eval "${GIT_EDITOR:=vi}" '"$@"'
+}
+
+is_bare_repository () {
+ git rev-parse --is-bare-repository
}
cd_to_toplevel () {
- cdup=$(git-rev-parse --show-cdup)
+ cdup=$(git rev-parse --show-cdup)
if test ! -z "$cdup"
then
cd "$cdup" || {
@@ -48,11 +60,37 @@ cd_to_toplevel () {
}
require_work_tree () {
- test $(is_bare_repository) = false &&
- test $(git-rev-parse --is-inside-git-dir) = false ||
+ test $(git rev-parse --is-inside-work-tree) = true ||
die "fatal: $0 cannot be used without a working tree."
}
+get_author_ident_from_commit () {
+ pick_author_script='
+ /^author /{
+ s/'\''/'\''\\'\'\''/g
+ h
+ s/^author \([^<]*\) <[^>]*> .*$/\1/
+ s/'\''/'\''\'\'\''/g
+ s/.*/GIT_AUTHOR_NAME='\''&'\''/p
+
+ g
+ s/^author [^<]* <\([^>]*\)> .*$/\1/
+ s/'\''/'\''\'\'\''/g
+ s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
+
+ g
+ s/^author [^<]* <[^>]*> \(.*\)$/\1/
+ s/'\''/'\''\'\'\''/g
+ s/.*/GIT_AUTHOR_DATE='\''&'\''/p
+
+ q
+ }
+ '
+ encoding=$(git config i18n.commitencoding || echo UTF-8)
+ git show -s --pretty=raw --encoding="$encoding" "$1" |
+ LANG=C LC_ALL=C sed -ne "$pick_author_script"
+}
+
if [ -z "$LONG_USAGE" ]
then
LONG_USAGE="Usage: $0 $USAGE"
@@ -72,12 +110,22 @@ esac
if [ -z "$SUBDIRECTORY_OK" ]
then
: ${GIT_DIR=.git}
- GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || {
+ GIT_DIR=$(GIT_DIR="$GIT_DIR" git rev-parse --git-dir) || {
exit=$?
echo >&2 "You need to run this command from the toplevel of the working tree."
exit $exit
}
else
- GIT_DIR=$(git-rev-parse --git-dir) || exit
+ GIT_DIR=$(git rev-parse --git-dir) || {
+ exit=$?
+ echo >&2 "Failed to find a valid git directory."
+ exit $exit
+ }
fi
+
+test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || {
+ echo >&2 "Unable to determine absolute path of git directory"
+ exit 1
+}
+
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
diff --git a/git-stash.sh b/git-stash.sh
new file mode 100755
index 0000000000..30425ce6df
--- /dev/null
+++ b/git-stash.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+# Copyright (c) 2007, Nanako Shiraishi
+
+USAGE='[ | list | show | apply | clear]'
+
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+require_work_tree
+cd_to_toplevel
+
+TMP="$GIT_DIR/.git-stash.$$"
+trap 'rm -f "$TMP-*"' 0
+
+ref_stash=refs/stash
+
+no_changes () {
+ git diff-index --quiet --cached HEAD &&
+ git diff-files --quiet
+}
+
+clear_stash () {
+ if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
+ then
+ git update-ref -d refs/stash $current
+ fi
+}
+
+save_stash () {
+ stash_msg="$1"
+
+ if no_changes
+ then
+ echo >&2 'No local changes to save'
+ exit 0
+ fi
+ test -f "$GIT_DIR/logs/$ref_stash" ||
+ clear_stash || die "Cannot initialize stash"
+
+ # Make sure the reflog for stash is kept.
+ : >>"$GIT_DIR/logs/$ref_stash"
+
+ # state of the base commit
+ if b_commit=$(git rev-parse --verify HEAD)
+ then
+ head=$(git log --abbrev-commit --pretty=oneline -n 1 HEAD)
+ else
+ die "You do not have the initial commit yet"
+ fi
+
+ if branch=$(git symbolic-ref -q HEAD)
+ then
+ branch=${branch#refs/heads/}
+ else
+ branch='(no branch)'
+ fi
+ msg=$(printf '%s: %s' "$branch" "$head")
+
+ # state of the index
+ i_tree=$(git write-tree) &&
+ i_commit=$(printf 'index on %s' "$msg" |
+ git commit-tree $i_tree -p $b_commit) ||
+ die "Cannot save the current index state"
+
+ # state of the working tree
+ w_tree=$( (
+ rm -f "$TMP-index" &&
+ cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
+ GIT_INDEX_FILE="$TMP-index" &&
+ export GIT_INDEX_FILE &&
+ git read-tree -m $i_tree &&
+ git add -u &&
+ git write-tree &&
+ rm -f "$TMP-index"
+ ) ) ||
+ die "Cannot save the current worktree state"
+
+ # create the stash
+ if test -z "$stash_msg"
+ then
+ stash_msg=$(printf 'WIP on %s' "$msg")
+ else
+ stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
+ fi
+ w_commit=$(printf '%s\n' "$stash_msg" |
+ git commit-tree $w_tree -p $b_commit -p $i_commit) ||
+ die "Cannot record working tree state"
+
+ git update-ref -m "$stash_msg" $ref_stash $w_commit ||
+ die "Cannot save the current status"
+ printf >&2 'Saved "%s"\n' "$stash_msg"
+}
+
+have_stash () {
+ git rev-parse --verify $ref_stash >/dev/null 2>&1
+}
+
+list_stash () {
+ have_stash || return 0
+ git log --pretty=oneline -g "$@" $ref_stash |
+ sed -n -e 's/^[.0-9a-f]* refs\///p'
+}
+
+show_stash () {
+ flags=$(git rev-parse --no-revs --flags "$@")
+ if test -z "$flags"
+ then
+ flags=--stat
+ fi
+ s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@")
+
+ w_commit=$(git rev-parse --verify "$s") &&
+ b_commit=$(git rev-parse --verify "$s^") &&
+ git diff $flags $b_commit $w_commit
+}
+
+apply_stash () {
+ git diff-files --quiet ||
+ die 'Cannot restore on top of a dirty state'
+
+ unstash_index=
+ case "$1" in
+ --index)
+ unstash_index=t
+ shift
+ esac
+
+ # current index state
+ c_tree=$(git write-tree) ||
+ die 'Cannot apply a stash in the middle of a merge'
+
+ # stash records the work tree, and is a merge between the
+ # base commit (first parent) and the index tree (second parent).
+ s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
+ w_tree=$(git rev-parse --verify "$s:") &&
+ b_tree=$(git rev-parse --verify "$s^1:") &&
+ i_tree=$(git rev-parse --verify "$s^2:") ||
+ die "$*: no valid stashed state found"
+
+ unstashed_index_tree=
+ if test -n "$unstash_index" && test "$b_tree" != "$i_tree"
+ then
+ git diff --binary $s^2^..$s^2 | git apply --cached
+ test $? -ne 0 &&
+ die 'Conflicts in index. Try without --index.'
+ unstashed_index_tree=$(git-write-tree) ||
+ die 'Could not save index tree'
+ git reset
+ fi
+
+ eval "
+ GITHEAD_$w_tree='Stashed changes' &&
+ GITHEAD_$c_tree='Updated upstream' &&
+ GITHEAD_$b_tree='Version stash was based on' &&
+ export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
+ "
+
+ if git-merge-recursive $b_tree -- $c_tree $w_tree
+ then
+ # No conflict
+ if test -n "$unstashed_index_tree"
+ then
+ git read-tree "$unstashed_index_tree"
+ else
+ a="$TMP-added" &&
+ git diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
+ git read-tree --reset $c_tree &&
+ git update-index --add --stdin <"$a" ||
+ die "Cannot unstage modified files"
+ rm -f "$a"
+ fi
+ git status || :
+ else
+ # Merge conflict; keep the exit status from merge-recursive
+ status=$?
+ if test -n "$unstash_index"
+ then
+ echo >&2 'Index was not unstashed.'
+ fi
+ exit $status
+ fi
+}
+
+# Main command set
+case "$1" in
+list)
+ shift
+ if test $# = 0
+ then
+ set x -n 10
+ shift
+ fi
+ list_stash "$@"
+ ;;
+show)
+ shift
+ show_stash "$@"
+ ;;
+apply)
+ shift
+ apply_stash "$@"
+ ;;
+clear)
+ clear_stash
+ ;;
+help | usage)
+ usage
+ ;;
+*)
+ if test $# -gt 0 && test "$1" = save
+ then
+ shift
+ fi
+ save_stash "$*" && git-reset --hard
+ ;;
+esac
diff --git a/git-submodule.sh b/git-submodule.sh
new file mode 100755
index 0000000000..2cfeaddbc2
--- /dev/null
+++ b/git-submodule.sh
@@ -0,0 +1,322 @@
+#!/bin/sh
+#
+# git-submodules.sh: add, init, update or list git submodules
+#
+# Copyright (c) 2007 Lars Hjemli
+
+USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
+. git-sh-setup
+require_work_tree
+
+add=
+branch=
+init=
+update=
+status=
+quiet=
+cached=
+
+#
+# print stuff on stdout unless -q was specified
+#
+say()
+{
+ if test -z "$quiet"
+ then
+ echo "$@"
+ fi
+}
+
+# NEEDSWORK: identical function exists in get_repo_base in clone.sh
+get_repo_base() {
+ (
+ cd "`/bin/pwd`" &&
+ cd "$1" || cd "$1.git" &&
+ {
+ cd .git
+ pwd
+ }
+ ) 2>/dev/null
+}
+
+#
+# Map submodule path to submodule name
+#
+# $1 = path
+#
+module_name()
+{
+ # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
+ re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g')
+ name=$( GIT_CONFIG=.gitmodules \
+ git config --get-regexp '^submodule\..*\.path$' |
+ sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
+ test -z "$name" &&
+ die "No submodule mapping found in .gitmodules for path '$path'"
+ echo "$name"
+}
+
+#
+# Clone a submodule
+#
+# Prior to calling, modules_update checks that a possibly existing
+# path is not a git repository.
+# Likewise, module_add checks that path does not exist at all,
+# since it is the location of a new submodule.
+#
+module_clone()
+{
+ path=$1
+ url=$2
+
+ # If there already is a directory at the submodule path,
+ # expect it to be empty (since that is the default checkout
+ # action) and try to remove it.
+ # Note: if $path is a symlink to a directory the test will
+ # succeed but the rmdir will fail. We might want to fix this.
+ if test -d "$path"
+ then
+ rmdir "$path" 2>/dev/null ||
+ die "Directory '$path' exist, but is neither empty nor a git repository"
+ fi
+
+ test -e "$path" &&
+ die "A file already exist at path '$path'"
+
+ git-clone -n "$url" "$path" ||
+ die "Clone of '$url' into submodule path '$path' failed"
+}
+
+#
+# Add a new submodule to the working tree, .gitmodules and the index
+#
+# $@ = repo [path]
+#
+# optional branch is stored in global branch variable
+#
+module_add()
+{
+ repo=$1
+ path=$2
+
+ if test -z "$repo"; then
+ usage
+ fi
+
+ # Turn the source into an absolute path if
+ # it is local
+ if base=$(get_repo_base "$repo"); then
+ repo="$base"
+ fi
+
+ # Guess path from repo if not specified or strip trailing slashes
+ if test -z "$path"; then
+ path=$(echo "$repo" | sed -e 's|/*$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
+ else
+ path=$(echo "$path" | sed -e 's|/*$||')
+ fi
+
+ test -e "$path" &&
+ die "'$path' already exists"
+
+ git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
+ die "'$path' already exists in the index"
+
+ module_clone "$path" "$repo" || exit
+ (unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
+ die "Unable to checkout submodule '$path'"
+ git add "$path" ||
+ die "Failed to add submodule '$path'"
+
+ GIT_CONFIG=.gitmodules git config submodule."$path".path "$path" &&
+ GIT_CONFIG=.gitmodules git config submodule."$path".url "$repo" &&
+ git add .gitmodules ||
+ die "Failed to register submodule '$path'"
+}
+
+#
+# Register submodules in .git/config
+#
+# $@ = requested paths (default to all)
+#
+modules_init()
+{
+ git ls-files --stage -- "$@" | grep -e '^160000 ' |
+ while read mode sha1 stage path
+ do
+ # Skip already registered paths
+ name=$(module_name "$path") || exit
+ url=$(git config submodule."$name".url)
+ test -z "$url" || continue
+
+ url=$(GIT_CONFIG=.gitmodules git config submodule."$name".url)
+ test -z "$url" &&
+ die "No url found for submodule path '$path' in .gitmodules"
+
+ git config submodule."$name".url "$url" ||
+ die "Failed to register url for submodule path '$path'"
+
+ say "Submodule '$name' ($url) registered for path '$path'"
+ done
+}
+
+#
+# Update each submodule path to correct revision, using clone and checkout as needed
+#
+# $@ = requested paths (default to all)
+#
+modules_update()
+{
+ git ls-files --stage -- "$@" | grep -e '^160000 ' |
+ while read mode sha1 stage path
+ do
+ name=$(module_name "$path") || exit
+ url=$(git config submodule."$name".url)
+ if test -z "$url"
+ then
+ # Only mention uninitialized submodules when its
+ # path have been specified
+ test "$#" != "0" &&
+ say "Submodule path '$path' not initialized"
+ continue
+ fi
+
+ if ! test -d "$path"/.git
+ then
+ module_clone "$path" "$url" || exit
+ subsha1=
+ else
+ subsha1=$(unset GIT_DIR && cd "$path" &&
+ git rev-parse --verify HEAD) ||
+ die "Unable to find current revision in submodule path '$path'"
+ fi
+
+ if test "$subsha1" != "$sha1"
+ then
+ (unset GIT_DIR && cd "$path" && git-fetch &&
+ git-checkout -q "$sha1") ||
+ die "Unable to checkout '$sha1' in submodule path '$path'"
+
+ say "Submodule path '$path': checked out '$sha1'"
+ fi
+ done
+}
+
+set_name_rev () {
+ revname=$( (
+ unset GIT_DIR &&
+ cd "$1" && {
+ git describe "$2" 2>/dev/null ||
+ git describe --tags "$2" 2>/dev/null ||
+ git describe --contains --tags "$2"
+ }
+ ) )
+ test -z "$revname" || revname=" ($revname)"
+}
+
+#
+# List all submodules, prefixed with:
+# - submodule not initialized
+# + different revision checked out
+#
+# If --cached was specified the revision in the index will be printed
+# instead of the currently checked out revision.
+#
+# $@ = requested paths (default to all)
+#
+modules_list()
+{
+ git ls-files --stage -- "$@" | grep -e '^160000 ' |
+ while read mode sha1 stage path
+ do
+ name=$(module_name "$path") || exit
+ url=$(git config submodule."$name".url)
+ if test -z "url" || ! test -d "$path"/.git
+ then
+ say "-$sha1 $path"
+ continue;
+ fi
+ set_name_rev "$path" "$sha1"
+ if git diff-files --quiet -- "$path"
+ then
+ say " $sha1 $path$revname"
+ else
+ if test -z "$cached"
+ then
+ sha1=$(unset GIT_DIR && cd "$path" && git rev-parse --verify HEAD)
+ set_name_rev "$path" "$sha1"
+ fi
+ say "+$sha1 $path$revname"
+ fi
+ done
+}
+
+while case "$#" in 0) break ;; esac
+do
+ case "$1" in
+ add)
+ add=1
+ ;;
+ init)
+ init=1
+ ;;
+ update)
+ update=1
+ ;;
+ status)
+ status=1
+ ;;
+ -q|--quiet)
+ quiet=1
+ ;;
+ -b|--branch)
+ case "$2" in
+ '')
+ usage
+ ;;
+ esac
+ branch="$2"; shift
+ ;;
+ --cached)
+ cached=1
+ ;;
+ --)
+ break
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+
+case "$add,$branch" in
+1,*)
+ ;;
+,)
+ ;;
+,*)
+ usage
+ ;;
+esac
+
+case "$add,$init,$update,$status,$cached" in
+1,,,,)
+ module_add "$@"
+ ;;
+,1,,,)
+ modules_init "$@"
+ ;;
+,,1,,)
+ modules_update "$@"
+ ;;
+,,,1,*)
+ modules_list "$@"
+ ;;
+*)
+ usage
+ ;;
+esac
diff --git a/git-svn.perl b/git-svn.perl
index e35006142a..d162114e26 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -38,14 +38,16 @@ use IPC::Open3;
use Git;
BEGIN {
- my $s;
+ # import functions from Git into our packages, en masse
+ no strict 'refs';
foreach (qw/command command_oneline command_noisy command_output_pipe
command_input_pipe command_close_pipe/) {
- $s .= "*SVN::Git::Editor::$_ = *SVN::Git::Fetcher::$_ = ".
- "*Git::SVN::Migration::$_ = ".
- "*Git::SVN::Log::$_ = *Git::SVN::$_ = *$_ = *Git::$_; ";
+ for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher
+ Git::SVN::Migration Git::SVN::Log Git::SVN),
+ __PACKAGE__) {
+ *{"${package}::$_"} = \&{"Git::$_"};
+ }
}
- eval $s;
}
my ($SVN);
@@ -372,16 +374,9 @@ sub cmd_dcommit {
die "Unable to determine upstream SVN information from ",
"$head history\n";
}
- my $c = $refs[-1];
my $last_rev;
- foreach my $d (@refs) {
- if (!verify_ref("$d~1")) {
- fatal "Commit $d\n",
- "has no parent commit, and therefore ",
- "nothing to diff against.\n",
- "You should be working from a repository ",
- "originally created by git-svn\n";
- }
+ my ($linear_refs, $parents) = linearize_history($gs, \@refs);
+ foreach my $d (@$linear_refs) {
unless (defined $last_rev) {
(undef, $last_rev, undef) = cmt_metadata("$d~1");
unless (defined $last_rev) {
@@ -403,6 +398,9 @@ sub cmd_dcommit {
svn_path => '');
if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) {
print "No changes\n$d~1 == $d\n";
+ } elsif ($parents->{$d} && @{$parents->{$d}}) {
+ $gs->{inject_parents_dcommit}->{$last_rev} =
+ $parents->{$d};
}
}
}
@@ -594,8 +592,7 @@ sub post_fetch_checkout {
my $index = $ENV{GIT_INDEX_FILE} || "$ENV{GIT_DIR}/index";
return if -f $index;
- chomp(my $bare = `git config --bool --get core.bare`);
- return if $bare eq 'true';
+ return if command_oneline(qw/rev-parse --is-inside-work-tree/) eq 'false';
return if command_oneline(qw/rev-parse --is-inside-git-dir/) eq 'true';
command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
print STDERR "Checked out HEAD:\n ",
@@ -743,7 +740,7 @@ sub load_authors {
my $log = $cmd eq 'log';
while (<$authors>) {
chomp;
- next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
+ next unless /^(.+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
my ($user, $name, $email) = ($1, $2, $3);
if ($log) {
$Git::SVN::Log::rusers{"$name <$email>"} = $user;
@@ -785,12 +782,12 @@ sub read_repo_config {
sub extract_metadata {
my $id = shift or return (undef, undef, undef);
- my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+)
+ my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+)
\s([a-f\d\-]+)$/x);
if (!defined $rev || !$uuid || !$url) {
# some of the original repositories I made had
# identifiers like this:
- ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/);
+ ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/);
}
return ($url, $rev, $uuid);
}
@@ -802,25 +799,87 @@ sub cmt_metadata {
sub working_head_info {
my ($head, $refs) = @_;
- my ($fh, $ctx) = command_output_pipe('rev-list', $head);
- while (my $hash = <$fh>) {
- chomp($hash);
- my ($url, $rev, $uuid) = cmt_metadata($hash);
+ my ($fh, $ctx) = command_output_pipe('log', $head);
+ my $hash;
+ my %max;
+ while (<$fh>) {
+ if ( m{^commit ($::sha1)$} ) {
+ unshift @$refs, $hash if $hash and $refs;
+ $hash = $1;
+ next;
+ }
+ next unless s{^\s*(git-svn-id:)}{$1};
+ my ($url, $rev, $uuid) = extract_metadata($_);
if (defined $url && defined $rev) {
+ next if $max{$url} and $max{$url} < $rev;
if (my $gs = Git::SVN->find_by_url($url)) {
my $c = $gs->rev_db_get($rev);
if ($c && $c eq $hash) {
close $fh; # break the pipe
return ($url, $rev, $uuid, $gs);
+ } else {
+ $max{$url} ||= $gs->rev_db_max;
}
}
}
- unshift @$refs, $hash if $refs;
}
command_close_pipe($fh, $ctx);
(undef, undef, undef, undef);
}
+sub read_commit_parents {
+ my ($parents, $c) = @_;
+ my ($fh, $ctx) = command_output_pipe(qw/cat-file commit/, $c);
+ while (<$fh>) {
+ chomp;
+ last if '';
+ /^parent ($sha1)/ or next;
+ push @{$parents->{$c}}, $1;
+ }
+ close $fh; # break the pipe
+}
+
+sub linearize_history {
+ my ($gs, $refs) = @_;
+ my %parents;
+ foreach my $c (@$refs) {
+ read_commit_parents(\%parents, $c);
+ }
+
+ my @linear_refs;
+ my %skip = ();
+ my $last_svn_commit = $gs->last_commit;
+ foreach my $c (reverse @$refs) {
+ next if $c eq $last_svn_commit;
+ last if $skip{$c};
+
+ unshift @linear_refs, $c;
+ $skip{$c} = 1;
+
+ # we only want the first parent to diff against for linear
+ # history, we save the rest to inject when we finalize the
+ # svn commit
+ my $fp_a = verify_ref("$c~1");
+ my $fp_b = shift @{$parents{$c}} if $parents{$c};
+ if (!$fp_a || !$fp_b) {
+ die "Commit $c\n",
+ "has no parent commit, and therefore ",
+ "nothing to diff against.\n",
+ "You should be working from a repository ",
+ "originally created by git-svn\n";
+ }
+ if ($fp_a ne $fp_b) {
+ die "$c~1 = $fp_a, however parsing commit $c ",
+ "revealed that:\n$c~1 = $fp_b\nBUG!\n";
+ }
+
+ foreach my $p (@{$parents{$c}}) {
+ $skip{$p} = 1;
+ }
+ }
+ (\@linear_refs, \%parents);
+}
+
package Git::SVN;
use strict;
use warnings;
@@ -846,26 +905,26 @@ BEGIN {
# some options are read globally, but can be overridden locally
# per [svn-remote "..."] section. Command-line options will *NOT*
# override options set in an [svn-remote "..."] section
- my $e;
- foreach (qw/follow_parent no_metadata use_svm_props
- use_svnsync_props/) {
- my $key = $_;
+ no strict 'refs';
+ for my $option (qw/follow_parent no_metadata use_svm_props
+ use_svnsync_props/) {
+ my $key = $option;
$key =~ tr/_//d;
- $e .= "sub $_ {
- my (\$self) = \@_;
- return \$self->{-$_} if exists \$self->{-$_};
- my \$k = \"svn-remote.\$self->{repo_id}\.$key\";
- eval { command_oneline(qw/config --get/, \$k) };
- if (\$@) {
- \$self->{-$_} = \$Git::SVN::_$_;
+ my $prop = "-$option";
+ *$option = sub {
+ my ($self) = @_;
+ return $self->{$prop} if exists $self->{$prop};
+ my $k = "svn-remote.$self->{repo_id}.$key";
+ eval { command_oneline(qw/config --get/, $k) };
+ if ($@) {
+ $self->{$prop} = ${"Git::SVN::_$option"};
} else {
- my \$v = command_oneline(qw/config --bool/,\$k);
- \$self->{-$_} = \$v eq 'false' ? 0 : 1;
+ my $v = command_oneline(qw/config --bool/,$k);
+ $self->{$prop} = $v eq 'false' ? 0 : 1;
}
- return \$self->{-$_} }\n";
+ return $self->{$prop};
+ }
}
- $e .= "1;\n";
- eval $e or die $@;
}
my %LOCKFILES;
@@ -879,8 +938,8 @@ sub resolve_local_globs {
foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
next unless m#^refs/remotes/$ref->{regex}$#;
my $p = $1;
- my $pathname = $path->full_path($p);
- my $refname = $ref->full_path($p);
+ my $pathname = desanitize_refname($path->full_path($p));
+ my $refname = desanitize_refname($ref->full_path($p));
if (my $existing = $fetch->{$pathname}) {
if ($existing ne $refname) {
die "Refspec conflict:\n",
@@ -967,7 +1026,9 @@ sub read_all_remotes {
my $r = {};
foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
if (m!^(.+)\.fetch=\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!) {
- $r->{$1}->{fetch}->{$2} = $3;
+ my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
+ $local_ref =~ s{^/}{};
+ $r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
$r->{$1}->{url} = $2;
} elsif (m!^(.+)\.(branches|tags)=
@@ -1087,6 +1148,7 @@ sub init_remote_config {
unless ($no_write) {
command_noisy('config',
"svn-remote.$self->{repo_id}.url", $url);
+ $self->{path} =~ s{^/}{};
command_noisy('config', '--add',
"svn-remote.$self->{repo_id}.fetch",
"$self->{path}:".$self->refname);
@@ -1177,7 +1239,40 @@ sub new {
$self;
}
-sub refname { "refs/remotes/$_[0]->{ref_id}" }
+sub refname {
+ my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
+
+ # It cannot end with a slash /, we'll throw up on this because
+ # SVN can't have directories with a slash in their name, either:
+ if ($refname =~ m{/$}) {
+ die "ref: '$refname' ends with a trailing slash, this is ",
+ "not permitted by git nor Subversion\n";
+ }
+
+ # It cannot have ASCII control character space, tilde ~, caret ^,
+ # colon :, question-mark ?, asterisk *, space, or open bracket [
+ # anywhere.
+ #
+ # Additionally, % must be escaped because it is used for escaping
+ # and we want our escaped refname to be reversible
+ $refname =~ s{([ \%~\^:\?\*\[\t])}{uc sprintf('%%%02x',ord($1))}eg;
+
+ # no slash-separated component can begin with a dot .
+ # /.* becomes /%2E*
+ $refname =~ s{/\.}{/%2E}g;
+
+ # It cannot have two consecutive dots .. anywhere
+ # .. becomes %2E%2E
+ $refname =~ s{\.\.}{%2E%2E}g;
+
+ return $refname;
+}
+
+sub desanitize_refname {
+ my ($refname) = @_;
+ $refname =~ s{%(?:([0-9A-F]{2}))}{chr hex($1)}eg;
+ return $refname;
+}
sub svm_uuid {
my ($self) = @_;
@@ -1457,7 +1552,7 @@ sub tmp_config {
my (@args) = @_;
my $old_def_config = "$ENV{GIT_DIR}/svn/config";
my $config = "$ENV{GIT_DIR}/svn/.metadata";
- if (-e $old_def_config && ! -e $config) {
+ if (! -f $config && -f $old_def_config) {
rename $old_def_config, $config or
die "Failed rename $old_def_config => $config: $!\n";
}
@@ -1541,6 +1636,11 @@ sub get_commit_parents {
if (my $cur = ::verify_ref($self->refname.'^0')) {
push @tmp, $cur;
}
+ if (my $ipd = $self->{inject_parents_dcommit}) {
+ if (my $commit = delete $ipd->{$log_entry->{revision}}) {
+ push @tmp, @$commit;
+ }
+ }
push @tmp, $_ foreach (@{$log_entry->{parents}}, @tmp);
while (my $p = shift @tmp) {
next if $seen{$p};
@@ -1964,16 +2064,19 @@ sub rebuild {
return;
}
print "Rebuilding $db_path ...\n";
- my ($rev_list, $ctx) = command_output_pipe("rev-list", $self->refname);
+ my ($log, $ctx) = command_output_pipe("log", $self->refname);
my $latest;
my $full_url = $self->full_url;
remove_username($full_url);
my $svn_uuid;
- while (<$rev_list>) {
- chomp;
- my $c = $_;
- die "Non-SHA1: $c\n" unless $c =~ /^$::sha1$/o;
- my ($url, $rev, $uuid) = ::cmt_metadata($c);
+ my $c;
+ while (<$log>) {
+ if ( m{^commit ($::sha1)$} ) {
+ $c = $1;
+ next;
+ }
+ next unless s{^\s*(git-svn-id:)}{$1};
+ my ($url, $rev, $uuid) = ::extract_metadata($_);
remove_username($url);
# ignore merges (from set-tree)
@@ -1991,7 +2094,7 @@ sub rebuild {
$self->rev_db_set($rev, $c);
print "r$rev = $c\n";
}
- command_close_pipe($rev_list, $ctx);
+ command_close_pipe($log, $ctx);
print "Done rebuilding $db_path\n";
}
@@ -2654,6 +2757,9 @@ sub repo_path {
sub url_path {
my ($self, $path) = @_;
+ if ($self->{url} =~ m#^https?://#) {
+ $path =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
+ }
$self->{url} . '/' . $self->repo_path($path);
}
@@ -2899,17 +3005,17 @@ my ($can_do_switch, %ignored_err, $RA);
BEGIN {
# enforce temporary pool usage for some simple functions
- my $e;
- foreach (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) {
- $e .= "sub $_ {
- my \$self = shift;
- my \$pool = SVN::Pool->new;
- my \@ret = \$self->SUPER::$_(\@_,\$pool);
- \$pool->clear;
- wantarray ? \@ret : \$ret[0]; }\n";
+ no strict 'refs';
+ for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) {
+ my $SUPER = "SUPER::$f";
+ *$f = sub {
+ my $self = shift;
+ my $pool = SVN::Pool->new;
+ my @ret = $self->$SUPER(@_,$pool);
+ $pool->clear;
+ wantarray ? @ret : $ret[0];
+ };
}
-
- eval "$e; 1;" or die $@;
}
sub new {
@@ -2923,6 +3029,7 @@ sub new {
SVN::Client::get_ssl_server_trust_file_provider(),
SVN::Client::get_simple_prompt_provider(
\&Git::SVN::Prompt::simple, 2),
+ SVN::Client::get_ssl_client_cert_file_provider(),
SVN::Client::get_ssl_client_cert_prompt_provider(
\&Git::SVN::Prompt::ssl_client_cert, 2),
SVN::Client::get_ssl_client_cert_pw_prompt_provider(
@@ -2934,6 +3041,7 @@ sub new {
\&Git::SVN::Prompt::username, 2),
]);
my $config = SVN::Core::config_get_config($config_dir);
+ $RA = undef;
my $self = SVN::Ra->new(url => $url, auth => $baton,
config => $config,
pool => SVN::Pool->new,
@@ -3072,11 +3180,8 @@ sub gs_do_switch {
$editor->{git_commit_ok};
}
-sub gs_fetch_loop_common {
- my ($self, $base, $head, $gsv, $globs) = @_;
- return if ($base > $head);
- my $inc = $_log_window_size;
- my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
+sub longest_common_path {
+ my ($gsv, $globs) = @_;
my %common;
my $common_max = scalar @$gsv;
@@ -3108,6 +3213,15 @@ sub gs_fetch_loop_common {
last;
}
}
+ $longest_path;
+}
+
+sub gs_fetch_loop_common {
+ my ($self, $base, $head, $gsv, $globs) = @_;
+ return if ($base > $head);
+ my $inc = $_log_window_size;
+ my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
+ my $longest_path = longest_common_path($gsv, $globs);
while (1) {
my %revs;
my $err;
@@ -3387,11 +3501,17 @@ sub log_use_color {
sub git_svn_log_cmd {
my ($r_min, $r_max, @args) = @_;
my $head = 'HEAD';
+ my (@files, @log_opts);
foreach my $x (@args) {
- last if $x eq '--';
- next unless ::verify_ref("$x^0");
- $head = $x;
- last;
+ if ($x eq '--' || @files) {
+ push @files, $x;
+ } else {
+ if (::verify_ref("$x^0")) {
+ $head = $x;
+ } else {
+ push @log_opts, $x;
+ }
+ }
}
my ($url, $rev, $uuid, $gs) = ::working_head_info($head);
@@ -3401,13 +3521,13 @@ sub git_svn_log_cmd {
push @cmd, '-r' unless $non_recursive;
push @cmd, qw/--raw --name-status/ if $verbose;
push @cmd, '--color' if log_use_color();
- return @cmd unless defined $r_max;
- if ($r_max == $r_min) {
+ push @cmd, @log_opts;
+ if (defined $r_max && $r_max == $r_min) {
push @cmd, '--max-count=1';
if (my $c = $gs->rev_db_get($r_max)) {
push @cmd, $c;
}
- } else {
+ } elsif (defined $r_max) {
my ($c_min, $c_max);
$c_max = $gs->rev_db_get($r_max);
$c_min = $gs->rev_db_get($r_min);
@@ -3423,7 +3543,7 @@ sub git_svn_log_cmd {
push @cmd, $c_min;
}
}
- return @cmd;
+ return (@cmd, @files);
}
# adapted from pager.c
@@ -3588,7 +3708,7 @@ sub cmd_show_log {
}
config_pager();
- @args = (git_svn_log_cmd($r_min, $r_max, @args), @args);
+ @args = git_svn_log_cmd($r_min, $r_max, @args);
my $log = command_output_pipe(@args);
run_pager();
my (@k, $c, $d, $stat);
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 3af8c7e110..8c17fb5ae2 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -49,7 +49,7 @@ getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:R:uv") or usage();
usage if $opt_h;
my $tag_name = $opt_t || "tags";
-my $trunk_name = $opt_T || "trunk";
+my $trunk_name = defined $opt_T ? $opt_T : "trunk";
my $branch_name = $opt_b || "branches";
my $project_name = $opt_P || "";
$project_name = "/" . $project_name if ($project_name);
@@ -542,7 +542,7 @@ sub copy_path($$$$$$$$) {
if ($node_kind eq $SVN::Node::dir) {
$srcpath =~ s#/*$#/#;
}
-
+
my $pid = open my $f,'-|';
die $! unless defined $pid;
if (!$pid) {
@@ -560,7 +560,7 @@ sub copy_path($$$$$$$$) {
} else {
$p = $path;
}
- push(@$new,[$mode,$sha1,$p]);
+ push(@$new,[$mode,$sha1,$p]);
}
close($f) or
print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n";
@@ -867,34 +867,14 @@ sub commit {
or die "Cannot write branch $dest for update: $!\n";
}
- if($tag) {
- my($in, $out) = ('','');
+ if ($tag) {
$last_rev = "-" if %$changed_paths;
# the tag was 'complex', i.e. did not refer to a "real" revision
$dest =~ tr/_/\./ if $opt_u;
- $branch = $dest;
-
- my $pid = open2($in, $out, 'git-mktag');
- print $out ("object $cid\n".
- "type commit\n".
- "tag $dest\n".
- "tagger $committer_name <$committer_email> 0 +0000\n") and
- close($out)
- or die "Cannot create tag object $dest: $!\n";
-
- my $tagobj = <$in>;
- chomp $tagobj;
-
- if ( !close($in) or waitpid($pid, 0) != $pid or
- $? != 0 or $tagobj !~ /^[0123456789abcdef]{40}$/ ) {
- die "Cannot create tag object $dest: $!\n";
- }
- open(C,">$git_dir/refs/tags/$dest") and
- print C ("$tagobj\n") and
- close(C)
- or die "Cannot create tag $branch: $!\n";
+ system('git-tag', $dest, $cid) == 0
+ or die "Cannot create tag $dest: $!\n";
print "Created tag '$dest' on '$branch'\n" if $opt_v;
}
diff --git a/git.c b/git.c
index 29b55a1604..cab0e7227d 100644
--- a/git.c
+++ b/git.c
@@ -4,7 +4,7 @@
#include "quote.h"
const char git_usage_string[] =
- "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
+ "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
static void prepend_to_path(const char *dir, int len)
{
@@ -28,7 +28,7 @@ static void prepend_to_path(const char *dir, int len)
free(path);
}
-static int handle_options(const char*** argv, int* argc)
+static int handle_options(const char*** argv, int* argc, int* envchanged)
{
int handled = 0;
@@ -64,14 +64,34 @@ static int handle_options(const char*** argv, int* argc)
usage(git_usage_string);
}
setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
(*argv)++;
(*argc)--;
handled++;
} else if (!prefixcmp(cmd, "--git-dir=")) {
setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--work-tree")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No directory given for --work-tree.\n" );
+ usage(git_usage_string);
+ }
+ setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (!prefixcmp(cmd, "--work-tree=")) {
+ setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
+ if (envchanged)
+ *envchanged = 1;
} else if (!strcmp(cmd, "--bare")) {
static char git_dir[PATH_MAX+1];
setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 1);
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(git_usage_string);
@@ -150,7 +170,7 @@ static int split_cmdline(char *cmdline, const char ***argv)
static int handle_alias(int *argcp, const char ***argv)
{
- int nongit = 0, ret = 0, saved_errno = errno;
+ int nongit = 0, envchanged = 0, ret = 0, saved_errno = errno;
const char *subdir;
int count, option_count;
const char** new_argv;
@@ -161,6 +181,21 @@ static int handle_alias(int *argcp, const char ***argv)
git_config(git_alias_config);
if (alias_string) {
if (alias_string[0] == '!') {
+ if (*argcp > 1) {
+ int i, sz = PATH_MAX;
+ char *s = xmalloc(sz), *new_alias = s;
+
+ add_to_string(&s, &sz, alias_string, 0);
+ free(alias_string);
+ alias_string = new_alias;
+ for (i = 1; i < *argcp &&
+ !add_to_string(&s, &sz, " ", 0) &&
+ !add_to_string(&s, &sz, (*argv)[i], 1)
+ ; i++)
+ ; /* do nothing */
+ if (!sz)
+ die("Too many or long arguments");
+ }
trace_printf("trace: alias to shell cmd: %s => %s\n",
alias_command, alias_string + 1);
ret = system(alias_string + 1);
@@ -171,7 +206,11 @@ static int handle_alias(int *argcp, const char ***argv)
alias_string + 1, alias_command);
}
count = split_cmdline(alias_string, &new_argv);
- option_count = handle_options(&new_argv, &count);
+ option_count = handle_options(&new_argv, &count, &envchanged);
+ if (envchanged)
+ die("alias '%s' changes environment variables\n"
+ "You can use '!git' in the alias to do this.",
+ alias_command);
memmove(new_argv - option_count, new_argv,
count * sizeof(char *));
new_argv -= option_count;
@@ -214,34 +253,79 @@ const char git_version_string[] = GIT_VERSION;
* require working tree to be present -- anything uses this needs
* RUN_SETUP for reading from the configuration file.
*/
-#define NOT_BARE (1<<2)
+#define NEED_WORK_TREE (1<<2)
-static void handle_internal_command(int argc, const char **argv, char **envp)
+struct cmd_struct {
+ const char *cmd;
+ int (*fn)(int, const char **, const char *);
+ int option;
+};
+
+static int run_command(struct cmd_struct *p, int argc, const char **argv)
+{
+ int status;
+ struct stat st;
+ const char *prefix;
+
+ prefix = NULL;
+ if (p->option & RUN_SETUP)
+ prefix = setup_git_directory();
+ if (p->option & USE_PAGER)
+ setup_pager();
+ if (p->option & NEED_WORK_TREE) {
+ const char *work_tree = get_git_work_tree();
+ const char *git_dir = get_git_dir();
+ if (!is_absolute_path(git_dir))
+ set_git_dir(make_absolute_path(git_dir));
+ if (!work_tree || chdir(work_tree))
+ die("%s must be run in a work tree", p->cmd);
+ }
+ trace_argv_printf(argv, argc, "trace: built-in: git");
+
+ status = p->fn(argc, argv, prefix);
+ if (status)
+ return status;
+
+ /* Somebody closed stdout? */
+ if (fstat(fileno(stdout), &st))
+ return 0;
+ /* Ignore write errors for pipes and sockets.. */
+ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
+ return 0;
+
+ /* Check for ENOSPC and EIO errors.. */
+ if (fflush(stdout))
+ die("write failure on standard output: %s", strerror(errno));
+ if (ferror(stdout))
+ die("unknown write failure on standard output");
+ if (fclose(stdout))
+ die("close failed on standard output: %s", strerror(errno));
+ return 0;
+}
+
+static void handle_internal_command(int argc, const char **argv)
{
const char *cmd = argv[0];
- static struct cmd_struct {
- const char *cmd;
- int (*fn)(int, const char **, const char *);
- int option;
- } commands[] = {
- { "add", cmd_add, RUN_SETUP | NOT_BARE },
- { "annotate", cmd_annotate, RUN_SETUP | USE_PAGER },
+ static struct cmd_struct commands[] = {
+ { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+ { "annotate", cmd_annotate, RUN_SETUP },
{ "apply", cmd_apply },
{ "archive", cmd_archive },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP },
{ "bundle", cmd_bundle },
{ "cat-file", cmd_cat_file, RUN_SETUP },
- { "checkout-index", cmd_checkout_index, RUN_SETUP },
+ { "checkout-index", cmd_checkout_index,
+ RUN_SETUP | NEED_WORK_TREE},
{ "check-ref-format", cmd_check_ref_format },
- { "check-attr", cmd_check_attr, RUN_SETUP | NOT_BARE },
+ { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
{ "cherry", cmd_cherry, RUN_SETUP },
- { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NOT_BARE },
+ { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "config", cmd_config },
{ "count-objects", cmd_count_objects, RUN_SETUP },
{ "describe", cmd_describe, RUN_SETUP },
- { "diff", cmd_diff, USE_PAGER },
+ { "diff", cmd_diff },
{ "diff-files", cmd_diff_files },
{ "diff-index", cmd_diff_index, RUN_SETUP },
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
@@ -264,10 +348,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "mailsplit", cmd_mailsplit },
{ "merge-base", cmd_merge_base, RUN_SETUP },
{ "merge-file", cmd_merge_file },
- { "mv", cmd_mv, RUN_SETUP | NOT_BARE },
+ { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
{ "name-rev", cmd_name_rev, RUN_SETUP },
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
- { "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER },
+ { "pickaxe", cmd_blame, RUN_SETUP },
{ "prune", cmd_prune, RUN_SETUP },
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
{ "push", cmd_push, RUN_SETUP },
@@ -277,19 +361,21 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "rerere", cmd_rerere, RUN_SETUP },
{ "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP },
- { "revert", cmd_revert, RUN_SETUP | NOT_BARE },
- { "rm", cmd_rm, RUN_SETUP | NOT_BARE },
- { "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
+ { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
+ { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE },
+ { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
{ "stripspace", cmd_stripspace },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
+ { "tag", cmd_tag, RUN_SETUP },
{ "tar-tree", cmd_tar_tree },
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
{ "update-index", cmd_update_index, RUN_SETUP },
{ "update-ref", cmd_update_ref, RUN_SETUP },
{ "upload-archive", cmd_upload_archive },
+ { "verify-tag", cmd_verify_tag, RUN_SETUP },
{ "version", cmd_version },
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
{ "write-tree", cmd_write_tree, RUN_SETUP },
@@ -307,25 +393,13 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
for (i = 0; i < ARRAY_SIZE(commands); i++) {
struct cmd_struct *p = commands+i;
- const char *prefix;
if (strcmp(p->cmd, cmd))
continue;
-
- prefix = NULL;
- if (p->option & RUN_SETUP)
- prefix = setup_git_directory();
- if (p->option & USE_PAGER)
- setup_pager();
- if ((p->option & NOT_BARE) &&
- (is_bare_repository() || is_inside_git_dir()))
- die("%s must be run in a work tree", cmd);
- trace_argv_printf(argv, argc, "trace: built-in: git");
-
- exit(p->fn(argc, argv, prefix));
+ exit(run_command(p, argc, argv));
}
}
-int main(int argc, const char **argv, char **envp)
+int main(int argc, const char **argv)
{
const char *cmd = argv[0] ? argv[0] : "git-help";
char *slash = strrchr(cmd, '/');
@@ -358,14 +432,14 @@ int main(int argc, const char **argv, char **envp)
if (!prefixcmp(cmd, "git-")) {
cmd += 4;
argv[0] = cmd;
- handle_internal_command(argc, argv, envp);
+ handle_internal_command(argc, argv);
die("cannot handle %s internally", cmd);
}
/* Look for flags.. */
argv++;
argc--;
- handle_options(&argv, &argc);
+ handle_options(&argv, &argc, NULL);
if (argc > 0) {
if (!prefixcmp(argv[0], "--"))
argv[0] += 2;
@@ -377,11 +451,11 @@ int main(int argc, const char **argv, char **envp)
cmd = argv[0];
/*
- * We search for git commands in the following order:
- * - git_exec_path()
- * - the path of the "git" command if we could find it
- * in $0
- * - the regular PATH.
+ * We execute external git command via execv_git_cmd(),
+ * which looks at "--exec-path" option, GIT_EXEC_PATH
+ * environment, and $(gitexecdir) in Makefile while built,
+ * in this order. For scripted commands, we prepend
+ * the value of the exec_path variable to the PATH.
*/
if (exec_path)
prepend_to_path(exec_path, strlen(exec_path));
@@ -390,7 +464,7 @@ int main(int argc, const char **argv, char **envp)
while (1) {
/* See if it's an internal command */
- handle_internal_command(argc, argv, envp);
+ handle_internal_command(argc, argv);
/* .. then try the external ones */
execv_git_cmd(argv);
diff --git a/git.spec.in b/git.spec.in
index 3a45eb8761..fe7b3d8215 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -12,7 +12,7 @@ URL: http://kernel.org/pub/software/scm/git/
Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz
BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, git-gui, git-p4, perl-Git
+Requires: git-core, git-svn, git-cvs, git-arch, git-email, gitk, git-gui, perl-Git
%description
Git is a fast, scalable, distributed revision control system with an
@@ -53,17 +53,10 @@ Requires: git-core = %{version}-%{release}, tla
%description arch
Git tools for importing Arch repositories.
-%package p4
-Summary: Git tools for importing Perforce repositories
-Group: Development/Tools
-Requires: git-core = %{version}-%{release}, python
-%description p4
-Git tools for importing Perforce repositories.
-
%package email
Summary: Git tools for sending email
Group: Development/Tools
-Requires: git-core = %{version}-%{release}
+Requires: git-core = %{version}-%{release}
%description email
Git tools for sending email.
@@ -95,14 +88,14 @@ Perl interface to Git
%setup -q
%build
-make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" WITH_P4IMPORT=YesPlease \
+make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" \
ETC_GITCONFIG=/etc/gitconfig \
- prefix=%{_prefix} PYTHON_PATH=%{python_path} all %{!?_without_docs: doc}
+ prefix=%{_prefix} all %{!?_without_docs: doc}
%install
rm -rf $RPM_BUILD_ROOT
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
- WITH_P4IMPORT=YesPlease prefix=%{_prefix} mandir=%{_mandir} \
+ prefix=%{_prefix} mandir=%{_mandir} \
ETC_GITCONFIG=/etc/gitconfig \
PYTHON_PATH=%{python_path} \
INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
@@ -110,10 +103,10 @@ find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
-(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "p4import|archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files
(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
%if %{!?_without_docs:1}0
-(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "p4import|archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk|git-gui|git-citool" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
%else
rm -rf $RPM_BUILD_ROOT%{_mandir}
%endif
@@ -145,13 +138,6 @@ rm -rf $RPM_BUILD_ROOT
%{!?_without_docs: %{_mandir}/man1/git-archimport.1*}
%{!?_without_docs: %doc Documentation/git-archimport.html }
-%files p4
-%defattr(-,root,root)
-%doc Documentation/git-p4import.txt
-%{_bindir}/git-p4import
-%{!?_without_docs: %{_mandir}/man1/git-p4import.1*}
-%{!?_without_docs: %doc Documentation/git-p4import.html }
-
%files email
%defattr(-,root,root)
%doc Documentation/*email*.txt
@@ -164,11 +150,10 @@ rm -rf $RPM_BUILD_ROOT
%{_bindir}/git-gui
%{_bindir}/git-citool
%{_datadir}/git-gui/
-# Not Yet...
-# %{!?_without_docs: %{_mandir}/man1/git-gui.1}
-# %{!?_without_docs: %doc Documentation/git-gui.html}
-# %{!?_without_docs: %{_mandir}/man1/git-citool.1}
-# %{!?_without_docs: %doc Documentation/git-citool.html}
+%{!?_without_docs: %{_mandir}/man1/git-gui.1*}
+%{!?_without_docs: %doc Documentation/git-gui.html}
+%{!?_without_docs: %{_mandir}/man1/git-citool.1*}
+%{!?_without_docs: %doc Documentation/git-citool.html}
%files -n gitk
%defattr(-,root,root)
@@ -188,6 +173,15 @@ rm -rf $RPM_BUILD_ROOT
%{!?_without_docs: %doc Documentation/technical}
%changelog
+* Sun Jul 15 2007 Sean Estabrooks <seanlkml@sympatico.ca>
+- Removed p4import.
+
+* Tue Jun 26 2007 Quy Tonthat <qtonthat@gmail.com>
+- Fixed problems looking for wrong manpages.
+
+* Thu Jun 21 2007 Shawn O. Pearce <spearce@spearce.org>
+- Added documentation files for git-gui
+
* Tue May 13 2007 Quy Tonthat <qtonthat@gmail.com>
- Added lib files for git-gui
- Added Documentation/technical (As needed by Git Users Manual)
diff --git a/gitk b/gitk
index a57e84cef7..57617d58b0 100755
--- a/gitk
+++ b/gitk
@@ -16,36 +16,96 @@ proc gitdir {} {
}
}
+# A simple scheduler for compute-intensive stuff.
+# The aim is to make sure that event handlers for GUI actions can
+# run at least every 50-100 ms. Unfortunately fileevent handlers are
+# run before X event handlers, so reading from a fast source can
+# make the GUI completely unresponsive.
+proc run args {
+ global isonrunq runq
+
+ set script $args
+ if {[info exists isonrunq($script)]} return
+ if {$runq eq {}} {
+ after idle dorunq
+ }
+ lappend runq [list {} $script]
+ set isonrunq($script) 1
+}
+
+proc filerun {fd script} {
+ fileevent $fd readable [list filereadable $fd $script]
+}
+
+proc filereadable {fd script} {
+ global runq
+
+ fileevent $fd readable {}
+ if {$runq eq {}} {
+ after idle dorunq
+ }
+ lappend runq [list $fd $script]
+}
+
+proc dorunq {} {
+ global isonrunq runq
+
+ set tstart [clock clicks -milliseconds]
+ set t0 $tstart
+ while {$runq ne {}} {
+ set fd [lindex $runq 0 0]
+ set script [lindex $runq 0 1]
+ set repeat [eval $script]
+ set t1 [clock clicks -milliseconds]
+ set t [expr {$t1 - $t0}]
+ set runq [lrange $runq 1 end]
+ if {$repeat ne {} && $repeat} {
+ if {$fd eq {} || $repeat == 2} {
+ # script returns 1 if it wants to be readded
+ # file readers return 2 if they could do more straight away
+ lappend runq [list $fd $script]
+ } else {
+ fileevent $fd readable [list filereadable $fd $script]
+ }
+ } elseif {$fd eq {}} {
+ unset isonrunq($script)
+ }
+ set t0 $t1
+ if {$t1 - $tstart >= 80} break
+ }
+ if {$runq ne {}} {
+ after idle dorunq
+ }
+}
+
+# Start off a git rev-list process and arrange to read its output
proc start_rev_list {view} {
- global startmsecs nextupdate
+ global startmsecs
global commfd leftover tclencoding datemode
global viewargs viewfiles commitidx
+ global lookingforhead showlocalchanges
set startmsecs [clock clicks -milliseconds]
- set nextupdate [expr {$startmsecs + 100}]
set commitidx($view) 0
- set args $viewargs($view)
- if {$viewfiles($view) ne {}} {
- set args [concat $args "--" $viewfiles($view)]
- }
set order "--topo-order"
if {$datemode} {
set order "--date-order"
}
if {[catch {
- set fd [open [concat | git rev-list --header $order \
- --parents --boundary --default HEAD $args] r]
+ set fd [open [concat | git log -z --pretty=raw $order --parents \
+ --boundary $viewargs($view) "--" $viewfiles($view)] r]
} err]} {
- puts stderr "Error executing git rev-list: $err"
+ error_popup "Error executing git rev-list: $err"
exit 1
}
set commfd($view) $fd
set leftover($view) {}
- fconfigure $fd -blocking 0 -translation lf
+ set lookingforhead $showlocalchanges
+ fconfigure $fd -blocking 0 -translation lf -eofchar {}
if {$tclencoding != {}} {
fconfigure $fd -encoding $tclencoding
}
- fileevent $fd readable [list getcommitlines $fd $view]
+ filerun $fd [list getcommitlines $fd $view]
nowbusy $view
}
@@ -72,15 +132,21 @@ proc getcommits {} {
}
proc getcommitlines {fd view} {
- global commitlisted nextupdate
+ global commitlisted
global leftover commfd
global displayorder commitidx commitrow commitdata
- global parentlist childlist children curview hlview
- global vparentlist vchildlist vdisporder vcmitlisted
+ global parentlist children curview hlview
+ global vparentlist vdisporder vcmitlisted
set stuff [read $fd 500000]
+ # git log doesn't terminate the last commit with a null...
+ if {$stuff == {} && $leftover($view) ne {} && [eof $fd]} {
+ set stuff "\0"
+ }
if {$stuff == {}} {
- if {![eof $fd]} return
+ if {![eof $fd]} {
+ return 1
+ }
global viewname
unset commfd($view)
notbusy $view
@@ -105,9 +171,9 @@ proc getcommitlines {fd view} {
error_popup $err
}
if {$view == $curview} {
- after idle finishcommits
+ run chewcommits $view
}
- return
+ return 0
}
set start 0
set gotsome 0
@@ -128,10 +194,14 @@ proc getcommitlines {fd view} {
set j [string first "\n" $cmit]
set ok 0
set listed 1
- if {$j >= 0} {
- set ids [string range $cmit 0 [expr {$j - 1}]]
- if {[string range $ids 0 0] == "-"} {
- set listed 0
+ if {$j >= 0 && [string match "commit *" $cmit]} {
+ set ids [string range $cmit 7 [expr {$j - 1}]]
+ if {[string match {[-<>]*} $ids]} {
+ switch -- [string index $ids 0] {
+ "-" {set listed 0}
+ "<" {set listed 2}
+ ">" {set listed 3}
+ }
set ids [string range $ids 1 end]
}
set ok 1
@@ -147,7 +217,7 @@ proc getcommitlines {fd view} {
if {[string length $shortcmit] > 80} {
set shortcmit "[string range $shortcmit 0 80]..."
}
- error_popup "Can't parse git rev-list output: {$shortcmit}"
+ error_popup "Can't parse git log output: {$shortcmit}"
exit 1
}
set id [lindex $ids 0]
@@ -171,41 +241,52 @@ proc getcommitlines {fd view} {
incr commitidx($view)
if {$view == $curview} {
lappend parentlist $olds
- lappend childlist $children($view,$id)
lappend displayorder $id
lappend commitlisted $listed
} else {
lappend vparentlist($view) $olds
- lappend vchildlist($view) $children($view,$id)
lappend vdisporder($view) $id
lappend vcmitlisted($view) $listed
}
set gotsome 1
}
if {$gotsome} {
- if {$view == $curview} {
- while {[layoutmore $nextupdate]} doupdate
- } elseif {[info exists hlview] && $view == $hlview} {
- vhighlightmore
- }
- }
- if {[clock clicks -milliseconds] >= $nextupdate} {
- doupdate
+ run chewcommits $view
}
+ return 2
}
-proc doupdate {} {
- global commfd nextupdate numcommits
+proc chewcommits {view} {
+ global curview hlview commfd
+ global selectedline pending_select
+
+ set more 0
+ if {$view == $curview} {
+ set allread [expr {![info exists commfd($view)]}]
+ set tlimit [expr {[clock clicks -milliseconds] + 50}]
+ set more [layoutmore $tlimit $allread]
+ if {$allread && !$more} {
+ global displayorder commitidx phase
+ global numcommits startmsecs
- foreach v [array names commfd] {
- fileevent $commfd($v) readable {}
+ if {[info exists pending_select]} {
+ set row [first_real_row]
+ selectline $row 1
+ }
+ if {$commitidx($curview) > 0} {
+ #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
+ #puts "overall $ms ms for $numcommits commits"
+ } else {
+ show_status "No commits selected"
+ }
+ notbusy layout
+ set phase {}
+ }
}
- update
- set nextupdate [expr {[clock clicks -milliseconds] + 100}]
- foreach v [array names commfd] {
- set fd $commfd($v)
- fileevent $fd readable [list getcommitlines $fd $v]
+ if {[info exists hlview] && $view == $hlview} {
+ vhighlightmore
}
+ return $more
}
proc readcommit {id} {
@@ -215,7 +296,7 @@ proc readcommit {id} {
proc updatecommits {} {
global viewdata curview phase displayorder
- global children commitrow selectedline thickerline
+ global children commitrow selectedline thickerline showneartags
if {$phase ne {}} {
stop_rev_list
@@ -230,8 +311,11 @@ proc updatecommits {} {
catch {unset selectedline}
catch {unset thickerline}
catch {unset viewdata($n)}
- discardallcommits
readrefs
+ changedrefs
+ if {$showneartags} {
+ getallcommits
+ }
showview $n
}
@@ -263,12 +347,16 @@ proc parsecommit {id contents listed} {
}
}
set headline {}
- # take the first line of the comment as the headline
- set i [string first "\n" $comment]
+ # take the first non-blank line of the comment as the headline
+ set headline [string trimleft $comment]
+ set i [string first "\n" $headline]
if {$i >= 0} {
- set headline [string trim [string range $comment 0 $i]]
- } else {
- set headline $comment
+ set headline [string range $headline 0 $i]
+ }
+ set headline [string trimright $headline]
+ set i [string first "\r" $headline]
+ if {$i >= 0} {
+ set headline [string trimright [string range $headline 0 $i]]
}
if {!$listed} {
# git rev-list indents the comment by 4 spaces;
@@ -303,60 +391,93 @@ proc getcommit {id} {
}
proc readrefs {} {
- global tagids idtags headids idheads tagcontents
- global otherrefids idotherrefs mainhead
+ global tagids idtags headids idheads tagobjid
+ global otherrefids idotherrefs mainhead mainheadid
foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
catch {unset $v}
}
- set refd [open [list | git show-ref] r]
- while {0 <= [set n [gets $refd line]]} {
- if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
- match id path]} {
- continue
- }
- if {[regexp {^remotes/.*/HEAD$} $path match]} {
- continue
- }
- if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
- set type others
- set name $path
- }
- if {[regexp {^remotes/} $path match]} {
- set type heads
- }
- if {$type == "tags"} {
- set tagids($name) $id
- lappend idtags($id) $name
- set obj {}
- set type {}
- set tag {}
- catch {
- set commit [exec git rev-parse "$id^0"]
- if {$commit != $id} {
- set tagids($name) $commit
- lappend idtags($commit) $name
- }
- }
- catch {
- set tagcontents($name) [exec git cat-file tag $id]
+ set refd [open [list | git show-ref -d] r]
+ while {[gets $refd line] >= 0} {
+ if {[string index $line 40] ne " "} continue
+ set id [string range $line 0 39]
+ set ref [string range $line 41 end]
+ if {![string match "refs/*" $ref]} continue
+ set name [string range $ref 5 end]
+ if {[string match "remotes/*" $name]} {
+ if {![string match "*/HEAD" $name]} {
+ set headids($name) $id
+ lappend idheads($id) $name
}
- } elseif { $type == "heads" } {
+ } elseif {[string match "heads/*" $name]} {
+ set name [string range $name 6 end]
set headids($name) $id
lappend idheads($id) $name
+ } elseif {[string match "tags/*" $name]} {
+ # this lets refs/tags/foo^{} overwrite refs/tags/foo,
+ # which is what we want since the former is the commit ID
+ set name [string range $name 5 end]
+ if {[string match "*^{}" $name]} {
+ set name [string range $name 0 end-3]
+ } else {
+ set tagobjid($name) $id
+ }
+ set tagids($name) $id
+ lappend idtags($id) $name
} else {
set otherrefids($name) $id
lappend idotherrefs($id) $name
}
}
- close $refd
+ catch {close $refd}
set mainhead {}
+ set mainheadid {}
catch {
set thehead [exec git symbolic-ref HEAD]
if {[string match "refs/heads/*" $thehead]} {
set mainhead [string range $thehead 11 end]
+ if {[info exists headids($mainhead)]} {
+ set mainheadid $headids($mainhead)
+ }
+ }
+ }
+}
+
+# skip over fake commits
+proc first_real_row {} {
+ global nullid nullid2 displayorder numcommits
+
+ for {set row 0} {$row < $numcommits} {incr row} {
+ set id [lindex $displayorder $row]
+ if {$id ne $nullid && $id ne $nullid2} {
+ break
}
}
+ return $row
+}
+
+# update things for a head moved to a child of its previous location
+proc movehead {id name} {
+ global headids idheads
+
+ removehead $headids($name) $name
+ set headids($name) $id
+ lappend idheads($id) $name
+}
+
+# update things when a head has been removed
+proc removehead {id name} {
+ global headids idheads
+
+ if {$idheads($id) eq $name} {
+ unset idheads($id)
+ } else {
+ set i [lsearch -exact $idheads($id) $name]
+ if {$i >= 0} {
+ set idheads($id) [lreplace $idheads($id) $i $i]
+ }
+ }
+ unset headids($name)
}
proc show_error {w top msg} {
@@ -395,14 +516,14 @@ proc confirm_popup msg {
proc makewindow {} {
global canv canv2 canv3 linespc charspc ctext cflist
- global textfont mainfont uifont
+ global textfont mainfont uifont tabstop
global findtype findtypemenu findloc findstring fstring geometry
global entries sha1entry sha1string sha1but
global maincursor textcursor curtextcursor
- global rowctxmenu mergemax wrapcomment
+ global rowctxmenu fakerowmenu mergemax wrapcomment
global highlight_files gdttype
global searchstring sstring
- global bgcolor fgcolor bglist fglist diffcolors
+ global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
global headctxmenu
menu .bar
@@ -457,15 +578,18 @@ proc makewindow {} {
set cscroll .tf.histframe.csb
set canv .tf.histframe.pwclist.canv
canvas $canv \
+ -selectbackground $selectbgcolor \
-background $bgcolor -bd 0 \
-yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
.tf.histframe.pwclist add $canv
set canv2 .tf.histframe.pwclist.canv2
canvas $canv2 \
+ -selectbackground $selectbgcolor \
-background $bgcolor -bd 0 -yscrollincr $linespc
.tf.histframe.pwclist add $canv2
set canv3 .tf.histframe.pwclist.canv3
canvas $canv3 \
+ -selectbackground $selectbgcolor \
-background $bgcolor -bd 0 -yscrollincr $linespc
.tf.histframe.pwclist add $canv3
eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
@@ -612,6 +736,7 @@ proc makewindow {} {
pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
set ctext .bleft.ctext
text $ctext -background $bgcolor -foreground $fgcolor \
+ -tabs "[expr {$tabstop * $charspc}]" \
-state disabled -font $textfont \
-yscrollcommand scrolltext -wrap none
scrollbar .bleft.sb -command "$ctext yview"
@@ -666,6 +791,7 @@ proc makewindow {} {
set cflist .bright.cfiles
set indent [font measure $mainfont "nn"]
text $cflist \
+ -selectbackground $selectbgcolor \
-background $bgcolor -foreground $fgcolor \
-font $mainfont \
-tabs [list $indent [expr {2 * $indent}]] \
@@ -689,12 +815,23 @@ proc makewindow {} {
wm geometry . "$geometry(main)"
}
+ if {[tk windowingsystem] eq {aqua}} {
+ set M1B M1
+ } else {
+ set M1B Control
+ }
+
bind .pwbottom <Configure> {resizecdetpanes %W %w}
pack .ctop -fill both -expand 1
bindall <1> {selcanvline %W %x %y}
#bindall <B1-Motion> {selcanvline %W %x %y}
- bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
- bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
+ if {[tk windowingsystem] == "win32"} {
+ bind . <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D }
+ bind $ctext <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D ; break }
+ } else {
+ bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
+ bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
+ }
bindall <2> "canvscan mark %W %x %y"
bindall <B2-Motion> "canvscan dragto %W %x %y"
bindkey <Home> selfirstline
@@ -707,12 +844,12 @@ proc makewindow {} {
bindkey <Key-Left> "goback"
bind . <Key-Prior> "selnextpage -1"
bind . <Key-Next> "selnextpage 1"
- bind . <Control-Home> "allcanvs yview moveto 0.0"
- bind . <Control-End> "allcanvs yview moveto 1.0"
- bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
- bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
- bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
- bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
+ bind . <$M1B-Home> "allcanvs yview moveto 0.0"
+ bind . <$M1B-End> "allcanvs yview moveto 1.0"
+ bind . <$M1B-Key-Up> "allcanvs yview scroll -1 units"
+ bind . <$M1B-Key-Down> "allcanvs yview scroll 1 units"
+ bind . <$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
+ bind . <$M1B-Key-Next> "allcanvs yview scroll 1 pages"
bindkey <Key-Delete> "$ctext yview scroll -1 pages"
bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
bindkey <Key-space> "$ctext yview scroll 1 pages"
@@ -732,15 +869,15 @@ proc makewindow {} {
bindkey ? findprev
bindkey f nextfile
bindkey <F5> updatecommits
- bind . <Control-q> doquit
- bind . <Control-f> dofind
- bind . <Control-g> {findnext 0}
- bind . <Control-r> dosearchback
- bind . <Control-s> dosearch
- bind . <Control-equal> {incrfont 1}
- bind . <Control-KP_Add> {incrfont 1}
- bind . <Control-minus> {incrfont -1}
- bind . <Control-KP_Subtract> {incrfont -1}
+ bind . <$M1B-q> doquit
+ bind . <$M1B-f> dofind
+ bind . <$M1B-g> {findnext 0}
+ bind . <$M1B-r> dosearchback
+ bind . <$M1B-s> dosearch
+ bind . <$M1B-equal> {incrfont 1}
+ bind . <$M1B-KP_Add> {incrfont 1}
+ bind . <$M1B-minus> {incrfont -1}
+ bind . <$M1B-KP_Subtract> {incrfont -1}
wm protocol . WM_DELETE_WINDOW doquit
bind . <Button-1> "click %W"
bind $fstring <Key-Return> dofind
@@ -749,6 +886,7 @@ proc makewindow {} {
bind $cflist <1> {sel_flist %W %x %y; break}
bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
+ bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
set maincursor [. cget -cursor]
set textcursor [$ctext cget -cursor]
@@ -766,6 +904,19 @@ proc makewindow {} {
$rowctxmenu add command -label "Create new branch" -command mkbranch
$rowctxmenu add command -label "Cherry-pick this commit" \
-command cherrypick
+ $rowctxmenu add command -label "Reset HEAD branch to here" \
+ -command resethead
+
+ set fakerowmenu .fakerowmenu
+ menu $fakerowmenu -tearoff 0
+ $fakerowmenu add command -label "Diff this -> selected" \
+ -command {diffvssel 0}
+ $fakerowmenu add command -label "Diff selected -> this" \
+ -command {diffvssel 1}
+ $fakerowmenu add command -label "Make patch" -command mkpatch
+# $fakerowmenu add command -label "Commit" -command {mkcommit 0}
+# $fakerowmenu add command -label "Commit all" -command {mkcommit 1}
+# $fakerowmenu add command -label "Revert local changes" -command revertlocal
set headctxmenu .headctxmenu
menu $headctxmenu -tearoff 0
@@ -773,6 +924,32 @@ proc makewindow {} {
-command cobranch
$headctxmenu add command -label "Remove this branch" \
-command rmbranch
+
+ global flist_menu
+ set flist_menu .flistctxmenu
+ menu $flist_menu -tearoff 0
+ $flist_menu add command -label "Highlight this too" \
+ -command {flist_hl 0}
+ $flist_menu add command -label "Highlight this only" \
+ -command {flist_hl 1}
+}
+
+# Windows sends all mouse wheel events to the current focused window, not
+# the one where the mouse hovers, so bind those events here and redirect
+# to the correct window
+proc windows_mousewheel_redirector {W X Y D} {
+ global canv canv2 canv3
+ set w [winfo containing -displayof $W $X $Y]
+ if {$w ne ""} {
+ set u [expr {$D < 0 ? 5 : -5}]
+ if {$w == $canv || $w == $canv2 || $w == $canv3} {
+ allcanvs yview scroll $u units
+ } else {
+ catch {
+ $w yview scroll $u units
+ }
+ }
+ }
}
# mouse-2 makes all windows scan vertically, but only the one
@@ -812,20 +989,20 @@ proc bindkey {ev script} {
# set the focus back to the toplevel for any click outside
# the entry widgets
proc click {w} {
- global entries
- foreach e $entries {
+ global ctext entries
+ foreach e [concat $entries $ctext] {
if {$w == $e} return
}
focus .
}
proc savestuff {w} {
- global canv canv2 canv3 ctext cflist mainfont textfont uifont
+ global canv canv2 canv3 ctext cflist mainfont textfont uifont tabstop
global stuffsaved findmergefiles maxgraphpct
- global maxwidth showneartags
+ global maxwidth showneartags showlocalchanges
global viewname viewfiles viewargs viewperm nextviewnum
global cmitmode wrapcomment
- global colors bgcolor fgcolor diffcolors
+ global colors bgcolor fgcolor diffcolors selectbgcolor
if {$stuffsaved} return
if {![winfo viewable .]} return
@@ -834,16 +1011,19 @@ proc savestuff {w} {
puts $f [list set mainfont $mainfont]
puts $f [list set textfont $textfont]
puts $f [list set uifont $uifont]
+ puts $f [list set tabstop $tabstop]
puts $f [list set findmergefiles $findmergefiles]
puts $f [list set maxgraphpct $maxgraphpct]
puts $f [list set maxwidth $maxwidth]
puts $f [list set cmitmode $cmitmode]
puts $f [list set wrapcomment $wrapcomment]
puts $f [list set showneartags $showneartags]
+ puts $f [list set showlocalchanges $showlocalchanges]
puts $f [list set bgcolor $bgcolor]
puts $f [list set fgcolor $fgcolor]
puts $f [list set colors $colors]
puts $f [list set diffcolors $diffcolors]
+ puts $f [list set selectbgcolor $selectbgcolor]
puts $f "set geometry(main) [wm geometry .]"
puts $f "set geometry(topwidth) [winfo width .tf]"
@@ -965,12 +1145,17 @@ proc keys {} {
raise $w
return
}
+ if {[tk windowingsystem] eq {aqua}} {
+ set M1T Cmd
+ } else {
+ set M1T Ctrl
+ }
toplevel $w
wm title $w "Gitk key bindings"
- message $w.m -text {
+ message $w.m -text "
Gitk key bindings:
-<Ctrl-Q> Quit
+<$M1T-Q> Quit
<Home> Move to first commit
<End> Move to last commit
<Up>, p, i Move up one commit
@@ -979,12 +1164,12 @@ Gitk key bindings:
<Right>, x, l Go forward in history list
<PageUp> Move up one page in commit list
<PageDown> Move down one page in commit list
-<Ctrl-Home> Scroll to top of commit list
-<Ctrl-End> Scroll to bottom of commit list
-<Ctrl-Up> Scroll commit list up one line
-<Ctrl-Down> Scroll commit list down one line
-<Ctrl-PageUp> Scroll commit list up one page
-<Ctrl-PageDown> Scroll commit list down one page
+<$M1T-Home> Scroll to top of commit list
+<$M1T-End> Scroll to bottom of commit list
+<$M1T-Up> Scroll commit list up one line
+<$M1T-Down> Scroll commit list down one line
+<$M1T-PageUp> Scroll commit list up one page
+<$M1T-PageDown> Scroll commit list down one page
<Shift-Up> Move to previous highlighted line
<Shift-Down> Move to next highlighted line
<Delete>, b Scroll diff view up one page
@@ -992,20 +1177,20 @@ Gitk key bindings:
<Space> Scroll diff view down one page
u Scroll diff view up 18 lines
d Scroll diff view down 18 lines
-<Ctrl-F> Find
-<Ctrl-G> Move to next find hit
+<$M1T-F> Find
+<$M1T-G> Move to next find hit
<Return> Move to next find hit
/ Move to next find hit, or redo find
? Move to previous find hit
f Scroll diff view to next file
-<Ctrl-S> Search for next hit in diff view
-<Ctrl-R> Search for previous hit in diff view
-<Ctrl-KP+> Increase font size
-<Ctrl-plus> Increase font size
-<Ctrl-KP-> Decrease font size
-<Ctrl-minus> Decrease font size
+<$M1T-S> Search for next hit in diff view
+<$M1T-R> Search for previous hit in diff view
+<$M1T-KP+> Increase font size
+<$M1T-plus> Increase font size
+<$M1T-KP-> Decrease font size
+<$M1T-minus> Decrease font size
<F5> Update
-} \
+" \
-justify left -bg white -border 2 -relief groove
pack $w.m -side top -fill both -padx 2 -pady 2
$w.m configure -font $uifont
@@ -1093,6 +1278,9 @@ proc treeview {w l openlevs} {
set treeheight($prefix) $ht
incr ht [lindex $htstack end]
set htstack [lreplace $htstack end end]
+ set prefixend [lindex $prefendstack end]
+ set prefendstack [lreplace $prefendstack end end]
+ set prefix [string range $prefix 0 $prefixend]
}
$w conf -state disabled
}
@@ -1345,6 +1533,33 @@ proc sel_flist {w x y} {
}
}
+proc pop_flist_menu {w X Y x y} {
+ global ctext cflist cmitmode flist_menu flist_menu_file
+ global treediffs diffids
+
+ set l [lindex [split [$w index "@$x,$y"] "."] 0]
+ if {$l <= 1} return
+ if {$cmitmode eq "tree"} {
+ set e [linetoelt $l]
+ if {[string index $e end] eq "/"} return
+ } else {
+ set e [lindex $treediffs($diffids) [expr {$l-2}]]
+ }
+ set flist_menu_file $e
+ tk_popup $flist_menu $X $Y
+}
+
+proc flist_hl {only} {
+ global flist_menu_file highlight_files
+
+ set x [shellquote $flist_menu_file]
+ if {$only || $highlight_files eq {}} {
+ set highlight_files $x
+ } else {
+ append highlight_files " " $x
+ }
+}
+
# Functions for adding and removing shell-type quoting
proc shellquote {str} {
@@ -1562,9 +1777,9 @@ proc newviewok {top n} {
set viewargs($n) $newargs
addviewmenu $n
if {!$newishighlight} {
- after idle showview $n
+ run showview $n
} else {
- after idle addvhighlight $n
+ run addvhighlight $n
}
} else {
# editing an existing view
@@ -1580,7 +1795,7 @@ proc newviewok {top n} {
set viewfiles($n) $files
set viewargs($n) $newargs
if {$curview == $n} {
- after idle updatecommits
+ run updatecommits
}
}
}
@@ -1631,16 +1846,16 @@ proc unflatten {var l} {
proc showview {n} {
global curview viewdata viewfiles
- global displayorder parentlist childlist rowidlist rowoffsets
+ global displayorder parentlist rowidlist rowoffsets
global colormap rowtextx commitrow nextcolor canvxmax
- global numcommits rowrangelist commitlisted idrowranges
+ global numcommits rowrangelist commitlisted idrowranges rowchk
global selectedline currentid canv canvy0
- global matchinglines treediffs
+ global treediffs
global pending_select phase
- global commitidx rowlaidout rowoptim linesegends
- global commfd nextupdate
- global selectedview
- global vparentlist vchildlist vdisporder vcmitlisted
+ global commitidx rowlaidout rowoptim
+ global commfd
+ global selectedview selectfirst
+ global vparentlist vdisporder vcmitlisted
global hlview selectedhlview
if {$n == $curview} return
@@ -1657,27 +1872,27 @@ proc showview {n} {
} else {
set yscreen [expr {($ybot - $ytop) / 2}]
}
+ } elseif {[info exists pending_select]} {
+ set selid $pending_select
+ unset pending_select
}
unselectline
normalline
- stopfindproc
if {$curview >= 0} {
set vparentlist($curview) $parentlist
- set vchildlist($curview) $childlist
set vdisporder($curview) $displayorder
set vcmitlisted($curview) $commitlisted
if {$phase ne {}} {
set viewdata($curview) \
[list $phase $rowidlist $rowoffsets $rowrangelist \
[flatten idrowranges] [flatten idinlist] \
- $rowlaidout $rowoptim $numcommits $linesegends]
+ $rowlaidout $rowoptim $numcommits]
} elseif {![info exists viewdata($curview)]
|| [lindex $viewdata($curview) 0] ne {}} {
set viewdata($curview) \
[list {} $rowidlist $rowoffsets $rowrangelist]
}
}
- catch {unset matchinglines}
catch {unset treediffs}
clear_display
if {[info exists hlview] && $hlview == $n} {
@@ -1691,7 +1906,9 @@ proc showview {n} {
.bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
if {![info exists viewdata($n)]} {
- set pending_select $selid
+ if {$selid ne {}} {
+ set pending_select $selid
+ }
getcommits
return
}
@@ -1700,7 +1917,6 @@ proc showview {n} {
set phase [lindex $v 0]
set displayorder $vdisporder($n)
set parentlist $vparentlist($n)
- set childlist $vchildlist($n)
set commitlisted $vcmitlisted($n)
set rowidlist [lindex $v 1]
set rowoffsets [lindex $v 2]
@@ -1714,7 +1930,7 @@ proc showview {n} {
set rowlaidout [lindex $v 6]
set rowoptim [lindex $v 7]
set numcommits [lindex $v 8]
- set linesegends [lindex $v 9]
+ catch {unset rowchk}
}
catch {unset colormap}
@@ -1725,7 +1941,8 @@ proc showview {n} {
set row 0
setcanvscroll
set yf 0
- set row 0
+ set row {}
+ set selectfirst 0
if {$selid ne {} && [info exists commitrow($n,$selid)]} {
set row $commitrow($n,$selid)
# try to get the selected row in the same position on the screen
@@ -1738,16 +1955,23 @@ proc showview {n} {
}
allcanvs yview moveto $yf
drawvisible
- selectline $row 0
+ if {$row ne {}} {
+ selectline $row 0
+ } elseif {$selid ne {}} {
+ set pending_select $selid
+ } else {
+ set row [first_real_row]
+ if {$row < $numcommits} {
+ selectline $row 0
+ } else {
+ set selectfirst 1
+ }
+ }
if {$phase ne {}} {
if {$phase eq "getcommits"} {
show_status "Reading commits..."
}
- if {[info exists commfd($n)]} {
- layoutmore {}
- } else {
- finishcommits
- }
+ run chewcommits $n
} elseif {$numcommits == 0} {
show_status "No commits selected"
}
@@ -1825,7 +2049,6 @@ proc addvhighlight {n} {
if {$n != $curview && ![info exists viewdata($n)]} {
set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
set vparentlist($n) {}
- set vchildlist($n) {}
set vdisporder($n) {}
set vcmitlisted($n) {}
start_rev_list $n
@@ -1935,7 +2158,7 @@ proc do_file_hl {serial} {
set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
set filehighlight [open $cmd r+]
fconfigure $filehighlight -blocking 0
- fileevent $filehighlight readable readfhighlight
+ filerun $filehighlight readfhighlight
set fhl_list {}
drawvisible
flushhighlights
@@ -1963,7 +2186,11 @@ proc readfhighlight {} {
global filehighlight fhighlights commitrow curview mainfont iddrawn
global fhl_list
- while {[gets $filehighlight line] >= 0} {
+ if {![info exists filehighlight]} {
+ return 0
+ }
+ set nr 0
+ while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
set line [string trim $line]
set i [lsearch -exact $fhl_list $line]
if {$i < 0} continue
@@ -1987,8 +2214,10 @@ proc readfhighlight {} {
puts "oops, git diff-tree died"
catch {close $filehighlight}
unset filehighlight
+ return 0
}
next_hlcont
+ return 1
}
proc find_change {name ix op} {
@@ -2002,6 +2231,7 @@ proc find_change {name ix op} {
set boldnamerows {}
catch {unset nhighlights}
unbolden
+ unmarkmatches
if {$findtype ne "Regexp"} {
set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
$findstring]
@@ -2010,9 +2240,22 @@ proc find_change {name ix op} {
drawvisible
}
+proc doesmatch {f} {
+ global findtype findstring findpattern
+
+ if {$findtype eq "Regexp"} {
+ return [regexp $findstring $f]
+ } elseif {$findtype eq "IgnCase"} {
+ return [string match -nocase $findpattern $f]
+ } else {
+ return [string match $findpattern $f]
+ }
+}
+
proc askfindhighlight {row id} {
global nhighlights commitinfo iddrawn mainfont
- global findstring findtype findloc findpattern
+ global findloc
+ global markingmatches
if {![info exists commitinfo($id)]} {
getcommit $id
@@ -2021,41 +2264,59 @@ proc askfindhighlight {row id} {
set isbold 0
set fldtypes {Headline Author Date Committer CDate Comments}
foreach f $info ty $fldtypes {
- if {$findloc ne "All fields" && $findloc ne $ty} {
- continue
- }
- if {$findtype eq "Regexp"} {
- set doesmatch [regexp $findstring $f]
- } elseif {$findtype eq "IgnCase"} {
- set doesmatch [string match -nocase $findpattern $f]
- } else {
- set doesmatch [string match $findpattern $f]
- }
- if {$doesmatch} {
+ if {($findloc eq "All fields" || $findloc eq $ty) &&
+ [doesmatch $f]} {
if {$ty eq "Author"} {
set isbold 2
- } else {
- set isbold 1
+ break
}
+ set isbold 1
}
}
- if {[info exists iddrawn($id)]} {
- if {$isbold && ![ishighlighted $row]} {
- bolden $row [concat $mainfont bold]
+ if {$isbold && [info exists iddrawn($id)]} {
+ set f [concat $mainfont bold]
+ if {![ishighlighted $row]} {
+ bolden $row $f
+ if {$isbold > 1} {
+ bolden_name $row $f
+ }
}
- if {$isbold >= 2} {
- bolden_name $row [concat $mainfont bold]
+ if {$markingmatches} {
+ markrowmatches $row $id
}
}
set nhighlights($row) $isbold
}
+proc markrowmatches {row id} {
+ global canv canv2 linehtag linentag commitinfo findloc
+
+ set headline [lindex $commitinfo($id) 0]
+ set author [lindex $commitinfo($id) 1]
+ $canv delete match$row
+ $canv2 delete match$row
+ if {$findloc eq "All fields" || $findloc eq "Headline"} {
+ set m [findmatches $headline]
+ if {$m ne {}} {
+ markmatches $canv $row $headline $linehtag($row) $m \
+ [$canv itemcget $linehtag($row) -font] $row
+ }
+ }
+ if {$findloc eq "All fields" || $findloc eq "Author"} {
+ set m [findmatches $author]
+ if {$m ne {}} {
+ markmatches $canv2 $row $author $linentag($row) $m \
+ [$canv2 itemcget $linentag($row) -font] $row
+ }
+ }
+}
+
proc vrel_change {name ix op} {
global highlight_related
rhighlight_none
if {$highlight_related ne "None"} {
- after idle drawvisible
+ run drawvisible
}
}
@@ -2070,7 +2331,7 @@ proc rhighlight_sel {a} {
set anc_todo [list $a]
if {$highlight_related ne "None"} {
rhighlight_none
- after idle drawvisible
+ run drawvisible
}
}
@@ -2288,17 +2549,15 @@ proc ntimes {n o} {
}
proc usedinrange {id l1 l2} {
- global children commitrow childlist curview
+ global children commitrow curview
if {[info exists commitrow($curview,$id)]} {
set r $commitrow($curview,$id)
if {$l1 <= $r && $r <= $l2} {
return [expr {$r - $l1 + 1}]
}
- set kids [lindex $childlist $r]
- } else {
- set kids $children($curview,$id)
}
+ set kids $children($curview,$id)
foreach c $kids {
set r $commitrow($curview,$c)
if {$l1 <= $r && $r <= $l2} {
@@ -2341,7 +2600,7 @@ proc sanity {row {full 0}} {
}
proc makeuparrow {oid x y z} {
- global rowidlist rowoffsets uparrowlen idrowranges
+ global rowidlist rowoffsets uparrowlen idrowranges displayorder
for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
incr y -1
@@ -2364,7 +2623,7 @@ proc makeuparrow {oid x y z} {
}
set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
- lappend idrowranges($oid) $y
+ lappend idrowranges($oid) [lindex $displayorder $y]
}
proc initlayout {} {
@@ -2373,15 +2632,14 @@ proc initlayout {} {
global idinlist rowchk rowrangelist idrowranges
global numcommits canvxmax canv
global nextcolor
- global parentlist childlist children
+ global parentlist
global colormap rowtextx
- global linesegends
+ global selectfirst
set numcommits 0
set displayorder {}
set commitlisted {}
set parentlist {}
- set childlist {}
set rowrangelist {}
set nextcolor 0
set rowidlist {{}}
@@ -2394,7 +2652,7 @@ proc initlayout {} {
catch {unset colormap}
catch {unset rowtextx}
catch {unset idrowranges}
- set linesegends {}
+ set selectfirst 1
}
proc setcanvscroll {} {
@@ -2425,15 +2683,18 @@ proc visiblerows {} {
return [list $r0 $r1]
}
-proc layoutmore {tmax} {
+proc layoutmore {tmax allread} {
global rowlaidout rowoptim commitidx numcommits optim_delay
- global uparrowlen curview
+ global uparrowlen curview rowidlist idinlist
+ set showlast 0
+ set showdelay $optim_delay
+ set optdelay [expr {$uparrowlen + 1}]
while {1} {
- if {$rowoptim - $optim_delay > $numcommits} {
- showstuff [expr {$rowoptim - $optim_delay}]
- } elseif {$rowlaidout - $uparrowlen - 1 > $rowoptim} {
- set nr [expr {$rowlaidout - $uparrowlen - 1 - $rowoptim}]
+ if {$rowoptim - $showdelay > $numcommits} {
+ showstuff [expr {$rowoptim - $showdelay}] $showlast
+ } elseif {$rowlaidout - $optdelay > $rowoptim} {
+ set nr [expr {$rowlaidout - $optdelay - $rowoptim}]
if {$nr > 100} {
set nr 100
}
@@ -2447,10 +2708,24 @@ proc layoutmore {tmax} {
set nr 150
}
set row $rowlaidout
- set rowlaidout [layoutrows $row [expr {$row + $nr}] 0]
+ set rowlaidout [layoutrows $row [expr {$row + $nr}] $allread]
if {$rowlaidout == $row} {
return 0
}
+ } elseif {$allread} {
+ set optdelay 0
+ set nrows $commitidx($curview)
+ if {[lindex $rowidlist $nrows] ne {} ||
+ [array names idinlist] ne {}} {
+ layouttail
+ set rowlaidout $commitidx($curview)
+ } elseif {$rowoptim == $nrows} {
+ set showdelay 0
+ set showlast 1
+ if {$numcommits == $nrows} {
+ return 0
+ }
+ }
} else {
return 0
}
@@ -2460,57 +2735,161 @@ proc layoutmore {tmax} {
}
}
-proc showstuff {canshow} {
- global numcommits commitrow pending_select selectedline
- global linesegends idrowranges idrangedrawn curview
+proc showstuff {canshow last} {
+ global numcommits commitrow pending_select selectedline curview
+ global lookingforhead mainheadid displayorder selectfirst
+ global lastscrollset
if {$numcommits == 0} {
global phase
set phase "incrdraw"
allcanvs delete all
}
- set row $numcommits
+ set r0 $numcommits
+ set prev $numcommits
set numcommits $canshow
- setcanvscroll
+ set t [clock clicks -milliseconds]
+ if {$prev < 100 || $last || $t - $lastscrollset > 500} {
+ set lastscrollset $t
+ setcanvscroll
+ }
set rows [visiblerows]
- set r0 [lindex $rows 0]
set r1 [lindex $rows 1]
- set selrow -1
- for {set r $row} {$r < $canshow} {incr r} {
- foreach id [lindex $linesegends [expr {$r+1}]] {
- set i -1
- foreach {s e} [rowranges $id] {
- incr i
- if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
- && ![info exists idrangedrawn($id,$i)]} {
- drawlineseg $id $i
- set idrangedrawn($id,$i) 1
- }
- }
- }
- }
- if {$canshow > $r1} {
- set canshow $r1
+ if {$r1 >= $canshow} {
+ set r1 [expr {$canshow - 1}]
}
- while {$row < $canshow} {
- drawcmitrow $row
- incr row
+ if {$r0 <= $r1} {
+ drawcommits $r0 $r1
}
if {[info exists pending_select] &&
[info exists commitrow($curview,$pending_select)] &&
$commitrow($curview,$pending_select) < $numcommits} {
selectline $commitrow($curview,$pending_select) 1
}
- if {![info exists selectedline] && ![info exists pending_select]} {
- selectline 0 1
+ if {$selectfirst} {
+ if {[info exists selectedline] || [info exists pending_select]} {
+ set selectfirst 0
+ } else {
+ set l [first_real_row]
+ selectline $l 1
+ set selectfirst 0
+ }
+ }
+ if {$lookingforhead && [info exists commitrow($curview,$mainheadid)]
+ && ($last || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
+ set lookingforhead 0
+ dodiffindex
+ }
+}
+
+proc doshowlocalchanges {} {
+ global lookingforhead curview mainheadid phase commitrow
+
+ if {[info exists commitrow($curview,$mainheadid)] &&
+ ($phase eq {} || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
+ dodiffindex
+ } elseif {$phase ne {}} {
+ set lookingforhead 1
+ }
+}
+
+proc dohidelocalchanges {} {
+ global lookingforhead localfrow localirow lserial
+
+ set lookingforhead 0
+ if {$localfrow >= 0} {
+ removerow $localfrow
+ set localfrow -1
+ if {$localirow > 0} {
+ incr localirow -1
+ }
+ }
+ if {$localirow >= 0} {
+ removerow $localirow
+ set localirow -1
+ }
+ incr lserial
+}
+
+# spawn off a process to do git diff-index --cached HEAD
+proc dodiffindex {} {
+ global localirow localfrow lserial
+
+ incr lserial
+ set localfrow -1
+ set localirow -1
+ set fd [open "|git diff-index --cached HEAD" r]
+ fconfigure $fd -blocking 0
+ filerun $fd [list readdiffindex $fd $lserial]
+}
+
+proc readdiffindex {fd serial} {
+ global localirow commitrow mainheadid nullid2 curview
+ global commitinfo commitdata lserial
+
+ set isdiff 1
+ if {[gets $fd line] < 0} {
+ if {![eof $fd]} {
+ return 1
+ }
+ set isdiff 0
+ }
+ # we only need to see one line and we don't really care what it says...
+ close $fd
+
+ # now see if there are any local changes not checked in to the index
+ if {$serial == $lserial} {
+ set fd [open "|git diff-files" r]
+ fconfigure $fd -blocking 0
+ filerun $fd [list readdifffiles $fd $serial]
+ }
+
+ if {$isdiff && $serial == $lserial && $localirow == -1} {
+ # add the line for the changes in the index to the graph
+ set localirow $commitrow($curview,$mainheadid)
+ set hl "Local changes checked in to index but not committed"
+ set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
+ set commitdata($nullid2) "\n $hl\n"
+ insertrow $localirow $nullid2
+ }
+ return 0
+}
+
+proc readdifffiles {fd serial} {
+ global localirow localfrow commitrow mainheadid nullid curview
+ global commitinfo commitdata lserial
+
+ set isdiff 1
+ if {[gets $fd line] < 0} {
+ if {![eof $fd]} {
+ return 1
+ }
+ set isdiff 0
+ }
+ # we only need to see one line and we don't really care what it says...
+ close $fd
+
+ if {$isdiff && $serial == $lserial && $localfrow == -1} {
+ # add the line for the local diff to the graph
+ if {$localirow >= 0} {
+ set localfrow $localirow
+ incr localirow
+ } else {
+ set localfrow $commitrow($curview,$mainheadid)
+ }
+ set hl "Local uncommitted changes, not checked in to index"
+ set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
+ set commitdata($nullid) "\n $hl\n"
+ insertrow $localfrow $nullid
}
+ return 0
}
proc layoutrows {row endrow last} {
global rowidlist rowoffsets displayorder
global uparrowlen downarrowlen maxwidth mingaplen
- global childlist parentlist
- global idrowranges linesegends
+ global children parentlist
+ global idrowranges
global commitidx curview
global idinlist rowchk rowrangelist
@@ -2518,18 +2897,12 @@ proc layoutrows {row endrow last} {
set offs [lindex $rowoffsets $row]
while {$row < $endrow} {
set id [lindex $displayorder $row]
- set oldolds {}
- set newolds {}
+ set nev [expr {[llength $idlist] - $maxwidth + 1}]
foreach p [lindex $parentlist $row] {
- if {![info exists idinlist($p)]} {
- lappend newolds $p
- } elseif {!$idinlist($p)} {
- lappend oldolds $p
+ if {![info exists idinlist($p)] || !$idinlist($p)} {
+ incr nev
}
}
- set lse {}
- set nev [expr {[llength $idlist] + [llength $newolds]
- + [llength $oldolds] - $maxwidth + 1}]
if {$nev > 0} {
if {!$last &&
$row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
@@ -2544,25 +2917,33 @@ proc layoutrows {row endrow last} {
set offs [incrange $offs $x 1]
set idinlist($i) 0
set rm1 [expr {$row - 1}]
- lappend lse $i
- lappend idrowranges($i) $rm1
+ lappend idrowranges($i) [lindex $displayorder $rm1]
if {[incr nev -1] <= 0} break
continue
}
- set rowchk($id) [expr {$row + $r}]
+ set rowchk($i) [expr {$row + $r}]
}
}
lset rowidlist $row $idlist
lset rowoffsets $row $offs
}
- lappend linesegends $lse
+ set oldolds {}
+ set newolds {}
+ foreach p [lindex $parentlist $row] {
+ if {![info exists idinlist($p)]} {
+ lappend newolds $p
+ } elseif {!$idinlist($p)} {
+ lappend oldolds $p
+ }
+ set idinlist($p) 1
+ }
set col [lsearch -exact $idlist $id]
if {$col < 0} {
set col [llength $idlist]
lappend idlist $id
lset rowidlist $row $idlist
set z {}
- if {[lindex $childlist $row] ne {}} {
+ if {$children($curview,$id) ne {}} {
set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
unset idinlist($id)
}
@@ -2577,7 +2958,7 @@ proc layoutrows {row endrow last} {
set ranges {}
if {[info exists idrowranges($id)]} {
set ranges $idrowranges($id)
- lappend ranges $row
+ lappend ranges $id
unset idrowranges($id)
}
lappend rowrangelist $ranges
@@ -2601,12 +2982,10 @@ proc layoutrows {row endrow last} {
lset offs $col {}
}
foreach i $newolds {
- set idinlist($i) 1
- set idrowranges($i) $row
+ set idrowranges($i) $id
}
incr col $l
foreach oid $oldolds {
- set idinlist($oid) 1
set idlist [linsert $idlist $col $oid]
set offs [linsert $offs $col $o]
makeuparrow $oid $col $row $o
@@ -2621,7 +3000,7 @@ proc layoutrows {row endrow last} {
proc addextraid {id row} {
global displayorder commitrow commitinfo
global commitidx commitlisted
- global parentlist childlist children curview
+ global parentlist children curview
incr commitidx($curview)
lappend displayorder $id
@@ -2635,7 +3014,6 @@ proc addextraid {id row} {
if {![info exists children($curview,$id)]} {
set children($curview,$id) {}
}
- lappend childlist $children($curview,$id)
}
proc layouttail {} {
@@ -2648,8 +3026,8 @@ proc layouttail {} {
set col [expr {[llength $idlist] - 1}]
set id [lindex $idlist $col]
addextraid $id $row
- unset idinlist($id)
- lappend idrowranges($id) $row
+ catch {unset idinlist($id)}
+ lappend idrowranges($id) $id
lappend rowrangelist $idrowranges($id)
unset idrowranges($id)
incr row
@@ -2660,11 +3038,12 @@ proc layouttail {} {
}
foreach id [array names idinlist] {
+ unset idinlist($id)
addextraid $id $row
lset rowidlist $row [list $id]
lset rowoffsets $row 0
makeuparrow $id 0 $row 0
- lappend idrowranges($id) $row
+ lappend idrowranges($id) $id
lappend rowrangelist $idrowranges($id)
unset idrowranges($id)
incr row
@@ -2683,7 +3062,7 @@ proc insert_pad {row col npad} {
}
proc optimize_rows {row col endrow} {
- global rowidlist rowoffsets idrowranges displayorder
+ global rowidlist rowoffsets displayorder
for {} {$row < $endrow} {incr row} {
set idlist [lindex $rowidlist $row]
@@ -2707,7 +3086,13 @@ proc optimize_rows {row col endrow} {
set isarrow 1
}
}
+ # Looking at lines from this row to the previous row,
+ # make them go straight up if they end in an arrow on
+ # the previous row; otherwise make them go straight up
+ # or at 45 degrees.
if {$z < -1 || ($z < 0 && $isarrow)} {
+ # Line currently goes left too much;
+ # insert pads in the previous row, then optimize it
set npad [expr {-1 - $z + $isarrow}]
set offs [incrange $offs $col $npad]
insert_pad $y0 $x0 $npad
@@ -2718,6 +3103,8 @@ proc optimize_rows {row col endrow} {
set x0 [expr {$col + $z}]
set z0 [lindex $rowoffsets $y0 $x0]
} elseif {$z > 1 || ($z > 0 && $isarrow)} {
+ # Line currently goes right too much;
+ # insert pads in this line and adjust the next's rowoffsets
set npad [expr {$z - 1 + $isarrow}]
set y1 [expr {$row + 1}]
set offs2 [lindex $rowoffsets $y1]
@@ -2748,6 +3135,7 @@ proc optimize_rows {row col endrow} {
set z0 [expr {$xc - $x0}]
}
}
+ # avoid lines jigging left then immediately right
if {$z0 ne {} && $z < 0 && $z0 > 0} {
insert_pad $y0 $x0 1
set offs [incrange $offs $col 1]
@@ -2756,6 +3144,7 @@ proc optimize_rows {row col endrow} {
}
if {!$haspad} {
set o {}
+ # Find the first column that doesn't have a line going right
for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
set o [lindex $offs $col]
if {$o eq {}} {
@@ -2774,6 +3163,8 @@ proc optimize_rows {row col endrow} {
}
if {$o eq {} || $o <= 0} break
}
+ # Insert a pad at that column as long as it has a line and
+ # isn't the last column, and adjust the next row' offsets
if {$o ne {} && [incr col] < [llength $idlist]} {
set y1 [expr {$row + 1}]
set offs2 [lindex $rowoffsets $y1]
@@ -2827,99 +3218,216 @@ proc rowranges {id} {
} elseif {[info exists idrowranges($id)]} {
set ranges $idrowranges($id)
}
- return $ranges
+ set linenos {}
+ foreach rid $ranges {
+ lappend linenos $commitrow($curview,$rid)
+ }
+ if {$linenos ne {}} {
+ lset linenos 0 [expr {[lindex $linenos 0] + 1}]
+ }
+ return $linenos
}
-proc drawlineseg {id i} {
- global rowoffsets rowidlist
- global displayorder
- global canv colormap linespc
- global numcommits commitrow curview
+# work around tk8.4 refusal to draw arrows on diagonal segments
+proc adjarrowhigh {coords} {
+ global linespc
- set ranges [rowranges $id]
- set downarrow 1
- if {[info exists commitrow($curview,$id)]
- && $commitrow($curview,$id) < $numcommits} {
- set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
- } else {
- set downarrow 1
- }
- set startrow [lindex $ranges [expr {2 * $i}]]
- set row [lindex $ranges [expr {2 * $i + 1}]]
- if {$startrow == $row} return
- assigncolor $id
- set coords {}
- set col [lsearch -exact [lindex $rowidlist $row] $id]
- if {$col < 0} {
- puts "oops: drawline: id $id not on row $row"
- return
+ set x0 [lindex $coords 0]
+ set x1 [lindex $coords 2]
+ if {$x0 != $x1} {
+ set y0 [lindex $coords 1]
+ set y1 [lindex $coords 3]
+ if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
+ # we have a nearby vertical segment, just trim off the diag bit
+ set coords [lrange $coords 2 end]
+ } else {
+ set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
+ set xi [expr {$x0 - $slope * $linespc / 2}]
+ set yi [expr {$y0 - $linespc / 2}]
+ set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
+ }
}
- set lasto {}
- set ns 0
+ return $coords
+}
+
+proc drawlineseg {id row endrow arrowlow} {
+ global rowidlist displayorder iddrawn linesegs
+ global canv colormap linespc curview maxlinelen
+
+ set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
+ set le [expr {$row + 1}]
+ set arrowhigh 1
while {1} {
- set o [lindex $rowoffsets $row $col]
- if {$o eq {}} break
- if {$o ne $lasto} {
- # changing direction
- set x [xc $row $col]
- set y [yc $row]
- lappend coords $x $y
- set lasto $o
+ set c [lsearch -exact [lindex $rowidlist $le] $id]
+ if {$c < 0} {
+ incr le -1
+ break
+ }
+ lappend cols $c
+ set x [lindex $displayorder $le]
+ if {$x eq $id} {
+ set arrowhigh 0
+ break
+ }
+ if {[info exists iddrawn($x)] || $le == $endrow} {
+ set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
+ if {$c >= 0} {
+ lappend cols $c
+ set arrowhigh 0
+ }
+ break
}
- incr col $o
- incr row -1
+ incr le
}
- set x [xc $row $col]
- set y [yc $row]
- lappend coords $x $y
- if {$i == 0} {
- # draw the link to the first child as part of this line
- incr row -1
- set child [lindex $displayorder $row]
- set ccol [lsearch -exact [lindex $rowidlist $row] $child]
- if {$ccol >= 0} {
- set x [xc $row $ccol]
- set y [yc $row]
- if {$ccol < $col - 1} {
- lappend coords [xc $row [expr {$col - 1}]] [yc $row]
- } elseif {$ccol > $col + 1} {
- lappend coords [xc $row [expr {$col + 1}]] [yc $row]
- }
- lappend coords $x $y
- }
- }
- if {[llength $coords] < 4} return
- if {$downarrow} {
- # This line has an arrow at the lower end: check if the arrow is
- # on a diagonal segment, and if so, work around the Tk 8.4
- # refusal to draw arrows on diagonal lines.
- set x0 [lindex $coords 0]
- set x1 [lindex $coords 2]
- if {$x0 != $x1} {
- set y0 [lindex $coords 1]
- set y1 [lindex $coords 3]
- if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
- # we have a nearby vertical segment, just trim off the diag bit
- set coords [lrange $coords 2 end]
+ if {$le <= $row} {
+ return $row
+ }
+
+ set lines {}
+ set i 0
+ set joinhigh 0
+ if {[info exists linesegs($id)]} {
+ set lines $linesegs($id)
+ foreach li $lines {
+ set r0 [lindex $li 0]
+ if {$r0 > $row} {
+ if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
+ set joinhigh 1
+ }
+ break
+ }
+ incr i
+ }
+ }
+ set joinlow 0
+ if {$i > 0} {
+ set li [lindex $lines [expr {$i-1}]]
+ set r1 [lindex $li 1]
+ if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
+ set joinlow 1
+ }
+ }
+
+ set x [lindex $cols [expr {$le - $row}]]
+ set xp [lindex $cols [expr {$le - 1 - $row}]]
+ set dir [expr {$xp - $x}]
+ if {$joinhigh} {
+ set ith [lindex $lines $i 2]
+ set coords [$canv coords $ith]
+ set ah [$canv itemcget $ith -arrow]
+ set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
+ set x2 [lindex $cols [expr {$le + 1 - $row}]]
+ if {$x2 ne {} && $x - $x2 == $dir} {
+ set coords [lrange $coords 0 end-2]
+ }
+ } else {
+ set coords [list [xc $le $x] [yc $le]]
+ }
+ if {$joinlow} {
+ set itl [lindex $lines [expr {$i-1}] 2]
+ set al [$canv itemcget $itl -arrow]
+ set arrowlow [expr {$al eq "last" || $al eq "both"}]
+ } elseif {$arrowlow &&
+ [lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0} {
+ set arrowlow 0
+ }
+ set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
+ for {set y $le} {[incr y -1] > $row} {} {
+ set x $xp
+ set xp [lindex $cols [expr {$y - 1 - $row}]]
+ set ndir [expr {$xp - $x}]
+ if {$dir != $ndir || $xp < 0} {
+ lappend coords [xc $y $x] [yc $y]
+ }
+ set dir $ndir
+ }
+ if {!$joinlow} {
+ if {$xp < 0} {
+ # join parent line to first child
+ set ch [lindex $displayorder $row]
+ set xc [lsearch -exact [lindex $rowidlist $row] $ch]
+ if {$xc < 0} {
+ puts "oops: drawlineseg: child $ch not on row $row"
+ } else {
+ if {$xc < $x - 1} {
+ lappend coords [xc $row [expr {$x-1}]] [yc $row]
+ } elseif {$xc > $x + 1} {
+ lappend coords [xc $row [expr {$x+1}]] [yc $row]
+ }
+ set x $xc
+ }
+ lappend coords [xc $row $x] [yc $row]
+ } else {
+ set xn [xc $row $xp]
+ set yn [yc $row]
+ # work around tk8.4 refusal to draw arrows on diagonal segments
+ if {$arrowlow && $xn != [lindex $coords end-1]} {
+ if {[llength $coords] < 4 ||
+ [lindex $coords end-3] != [lindex $coords end-1] ||
+ [lindex $coords end] - $yn > 2 * $linespc} {
+ set xn [xc $row [expr {$xp - 0.5 * $dir}]]
+ set yo [yc [expr {$row + 0.5}]]
+ lappend coords $xn $yo $xn $yn
+ }
} else {
- set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
- set xi [expr {$x0 - $slope * $linespc / 2}]
- set yi [expr {$y0 - $linespc / 2}]
- set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
+ lappend coords $xn $yn
}
}
+ if {!$joinhigh} {
+ if {$arrowhigh} {
+ set coords [adjarrowhigh $coords]
+ }
+ assigncolor $id
+ set t [$canv create line $coords -width [linewidth $id] \
+ -fill $colormap($id) -tags lines.$id -arrow $arrow]
+ $canv lower $t
+ bindline $t $id
+ set lines [linsert $lines $i [list $row $le $t]]
+ } else {
+ $canv coords $ith $coords
+ if {$arrow ne $ah} {
+ $canv itemconf $ith -arrow $arrow
+ }
+ lset lines $i 0 $row
+ }
+ } else {
+ set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
+ set ndir [expr {$xo - $xp}]
+ set clow [$canv coords $itl]
+ if {$dir == $ndir} {
+ set clow [lrange $clow 2 end]
+ }
+ set coords [concat $coords $clow]
+ if {!$joinhigh} {
+ lset lines [expr {$i-1}] 1 $le
+ if {$arrowhigh} {
+ set coords [adjarrowhigh $coords]
+ }
+ } else {
+ # coalesce two pieces
+ $canv delete $ith
+ set b [lindex $lines [expr {$i-1}] 0]
+ set e [lindex $lines $i 1]
+ set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
+ }
+ $canv coords $itl $coords
+ if {$arrow ne $al} {
+ $canv itemconf $itl -arrow $arrow
+ }
}
- set arrow [expr {2 * ($i > 0) + $downarrow}]
- set arrow [lindex {none first last both} $arrow]
- set t [$canv create line $coords -width [linewidth $id] \
- -fill $colormap($id) -tags lines.$id -arrow $arrow]
- $canv lower $t
- bindline $t $id
+
+ set linesegs($id) $lines
+ return $le
}
-proc drawparentlinks {id row col olds} {
- global rowidlist canv colormap
+proc drawparentlinks {id row} {
+ global rowidlist canv colormap curview parentlist
+ global idpos
+ set rowids [lindex $rowidlist $row]
+ set col [lsearch -exact $rowids $id]
+ if {$col < 0} return
+ set olds [lindex $parentlist $row]
set row2 [expr {$row + 1}]
set x [xc $row $col]
set y [yc $row]
@@ -2937,9 +3445,7 @@ proc drawparentlinks {id row col olds} {
if {$x2 > $rmx} {
set rmx $x2
}
- set ranges [rowranges $p]
- if {$ranges ne {} && $row2 == [lindex $ranges 0]
- && $row2 < [lindex $ranges 1]} {
+ if {[lsearch -exact $rowids $p] < 0} {
# drawlineseg will do this one for us
continue
}
@@ -2957,52 +3463,70 @@ proc drawparentlinks {id row col olds} {
$canv lower $t
bindline $t $p
}
- return $rmx
+ if {$rmx > [lindex $idpos($id) 1]} {
+ lset idpos($id) 1 $rmx
+ redrawtags $id
+ }
}
proc drawlines {id} {
- global colormap canv
- global idrangedrawn
- global children iddrawn commitrow rowidlist curview
+ global canv
- $canv delete lines.$id
- set nr [expr {[llength [rowranges $id]] / 2}]
- for {set i 0} {$i < $nr} {incr i} {
- if {[info exists idrangedrawn($id,$i)]} {
- drawlineseg $id $i
- }
- }
- foreach child $children($curview,$id) {
- if {[info exists iddrawn($child)]} {
- set row $commitrow($curview,$child)
- set col [lsearch -exact [lindex $rowidlist $row] $child]
- if {$col >= 0} {
- drawparentlinks $child $row $col [list $id]
- }
- }
- }
+ $canv itemconf lines.$id -width [linewidth $id]
}
-proc drawcmittext {id row col rmx} {
- global linespc canv canv2 canv3 canvy0 fgcolor
- global commitlisted commitinfo rowidlist
+proc drawcmittext {id row col} {
+ global linespc canv canv2 canv3 canvy0 fgcolor curview
+ global commitlisted commitinfo rowidlist parentlist
global rowtextx idpos idtags idheads idotherrefs
global linehtag linentag linedtag
- global mainfont canvxmax boldrows boldnamerows fgcolor
-
- set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
+ global mainfont canvxmax boldrows boldnamerows fgcolor nullid nullid2
+
+ # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
+ set listed [lindex $commitlisted $row]
+ if {$id eq $nullid} {
+ set ofill red
+ } elseif {$id eq $nullid2} {
+ set ofill green
+ } else {
+ set ofill [expr {$listed != 0? "blue": "white"}]
+ }
set x [xc $row $col]
set y [yc $row]
set orad [expr {$linespc / 3}]
- set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
- [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
- -fill $ofill -outline $fgcolor -width 1 -tags circle]
+ if {$listed <= 1} {
+ set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
+ [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
+ -fill $ofill -outline $fgcolor -width 1 -tags circle]
+ } elseif {$listed == 2} {
+ # triangle pointing left for left-side commits
+ set t [$canv create polygon \
+ [expr {$x - $orad}] $y \
+ [expr {$x + $orad - 1}] [expr {$y - $orad}] \
+ [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
+ -fill $ofill -outline $fgcolor -width 1 -tags circle]
+ } else {
+ # triangle pointing right for right-side commits
+ set t [$canv create polygon \
+ [expr {$x + $orad - 1}] $y \
+ [expr {$x - $orad}] [expr {$y - $orad}] \
+ [expr {$x - $orad}] [expr {$y + $orad - 1}] \
+ -fill $ofill -outline $fgcolor -width 1 -tags circle]
+ }
$canv raise $t
$canv bind $t <1> {selcanvline {} %x %y}
- set xt [xc $row [llength [lindex $rowidlist $row]]]
- if {$xt < $rmx} {
- set xt $rmx
+ set rmx [llength [lindex $rowidlist $row]]
+ set olds [lindex $parentlist $row]
+ if {$olds ne {}} {
+ set nextids [lindex $rowidlist [expr {$row + 1}]]
+ foreach p $olds {
+ set i [lsearch -exact $nextids $p]
+ if {$i > $rmx} {
+ set rmx $i
+ }
+ }
}
+ set xt [xc $row $rmx]
set rowtextx($row) $xt
set idpos($id) [list $x $xt $y]
if {[info exists idtags($id)] || [info exists idheads($id)]
@@ -3040,29 +3564,13 @@ proc drawcmittext {id row col rmx} {
proc drawcmitrow {row} {
global displayorder rowidlist
- global idrangedrawn iddrawn
+ global iddrawn markingmatches
global commitinfo parentlist numcommits
global filehighlight fhighlights findstring nhighlights
global hlview vhighlights
global highlight_related rhighlights
if {$row >= $numcommits} return
- foreach id [lindex $rowidlist $row] {
- if {$id eq {}} continue
- set i -1
- foreach {s e} [rowranges $id] {
- incr i
- if {$row < $s} continue
- if {$e eq {}} break
- if {$row <= $e} {
- if {$e < $numcommits && ![info exists idrangedrawn($id,$i)]} {
- drawlineseg $id $i
- set idrangedrawn($id,$i) 1
- }
- break
- }
- }
- }
set id [lindex $displayorder $row]
if {[info exists hlview] && ![info exists vhighlights($row)]} {
@@ -3077,45 +3585,97 @@ proc drawcmitrow {row} {
if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
askrelhighlight $row $id
}
- if {[info exists iddrawn($id)]} return
- set col [lsearch -exact [lindex $rowidlist $row] $id]
- if {$col < 0} {
- puts "oops, row $row id $id not in list"
- return
+ if {![info exists iddrawn($id)]} {
+ set col [lsearch -exact [lindex $rowidlist $row] $id]
+ if {$col < 0} {
+ puts "oops, row $row id $id not in list"
+ return
+ }
+ if {![info exists commitinfo($id)]} {
+ getcommit $id
+ }
+ assigncolor $id
+ drawcmittext $id $row $col
+ set iddrawn($id) 1
}
- if {![info exists commitinfo($id)]} {
- getcommit $id
+ if {$markingmatches} {
+ markrowmatches $row $id
}
- assigncolor $id
- set olds [lindex $parentlist $row]
- if {$olds ne {}} {
- set rmx [drawparentlinks $id $row $col $olds]
- } else {
- set rmx 0
+}
+
+proc drawcommits {row {endrow {}}} {
+ global numcommits iddrawn displayorder curview
+ global parentlist rowidlist
+
+ if {$row < 0} {
+ set row 0
+ }
+ if {$endrow eq {}} {
+ set endrow $row
+ }
+ if {$endrow >= $numcommits} {
+ set endrow [expr {$numcommits - 1}]
+ }
+
+ # make the lines join to already-drawn rows either side
+ set r [expr {$row - 1}]
+ if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
+ set r $row
+ }
+ set er [expr {$endrow + 1}]
+ if {$er >= $numcommits ||
+ ![info exists iddrawn([lindex $displayorder $er])]} {
+ set er $endrow
+ }
+ for {} {$r <= $er} {incr r} {
+ set id [lindex $displayorder $r]
+ set wasdrawn [info exists iddrawn($id)]
+ drawcmitrow $r
+ if {$r == $er} break
+ set nextid [lindex $displayorder [expr {$r + 1}]]
+ if {$wasdrawn && [info exists iddrawn($nextid)]} {
+ catch {unset prevlines}
+ continue
+ }
+ drawparentlinks $id $r
+
+ if {[info exists lineends($r)]} {
+ foreach lid $lineends($r) {
+ unset prevlines($lid)
+ }
+ }
+ set rowids [lindex $rowidlist $r]
+ foreach lid $rowids {
+ if {$lid eq {}} continue
+ if {$lid eq $id} {
+ # see if this is the first child of any of its parents
+ foreach p [lindex $parentlist $r] {
+ if {[lsearch -exact $rowids $p] < 0} {
+ # make this line extend up to the child
+ set le [drawlineseg $p $r $er 0]
+ lappend lineends($le) $p
+ set prevlines($p) 1
+ }
+ }
+ } elseif {![info exists prevlines($lid)]} {
+ set le [drawlineseg $lid $r $er 1]
+ lappend lineends($le) $lid
+ set prevlines($lid) 1
+ }
+ }
}
- drawcmittext $id $row $col $rmx
- set iddrawn($id) 1
}
proc drawfrac {f0 f1} {
- global numcommits canv
- global linespc
+ global canv linespc
set ymax [lindex [$canv cget -scrollregion] 3]
if {$ymax eq {} || $ymax == 0} return
set y0 [expr {int($f0 * $ymax)}]
set row [expr {int(($y0 - 3) / $linespc) - 1}]
- if {$row < 0} {
- set row 0
- }
set y1 [expr {int($f1 * $ymax)}]
set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
- if {$endrow >= $numcommits} {
- set endrow [expr {$numcommits - 1}]
- }
- for {} {$row <= $endrow} {incr row} {
- drawcmitrow $row
- }
+ drawcommits $row $endrow
}
proc drawvisible {} {
@@ -3124,12 +3684,12 @@ proc drawvisible {} {
}
proc clear_display {} {
- global iddrawn idrangedrawn
+ global iddrawn linesegs
global vhighlights fhighlights nhighlights rhighlights
allcanvs delete all
catch {unset iddrawn}
- catch {unset idrangedrawn}
+ catch {unset linesegs}
catch {unset vhighlights}
catch {unset fhighlights}
catch {unset nhighlights}
@@ -3356,27 +3916,14 @@ proc show_status {msg} {
-tags text -fill $fgcolor
}
-proc finishcommits {} {
- global commitidx phase curview
- global pending_select
-
- if {$commitidx($curview) > 0} {
- drawrest
- } else {
- show_status "No commits selected"
- }
- set phase {}
- catch {unset pending_select}
-}
-
# Insert a new commit as the child of the commit on row $row.
# The new commit will be displayed on row $row and the commits
# on that row and below will move down one row.
proc insertrow {row newcmit} {
- global displayorder parentlist childlist commitlisted
+ global displayorder parentlist commitlisted children
global commitrow curview rowidlist rowoffsets numcommits
- global rowrangelist idrowranges rowlaidout rowoptim numcommits
- global linesegends selectedline
+ global rowrangelist rowlaidout rowoptim numcommits
+ global selectedline rowchk commitidx
if {$row >= $numcommits} {
puts "oops, inserting new row $row but only have $numcommits rows"
@@ -3385,16 +3932,17 @@ proc insertrow {row newcmit} {
set p [lindex $displayorder $row]
set displayorder [linsert $displayorder $row $newcmit]
set parentlist [linsert $parentlist $row $p]
- set kids [lindex $childlist $row]
+ set kids $children($curview,$p)
lappend kids $newcmit
- lset childlist $row $kids
- set childlist [linsert $childlist $row {}]
+ set children($curview,$p) $kids
+ set children($curview,$newcmit) {}
set commitlisted [linsert $commitlisted $row 1]
set l [llength $displayorder]
for {set r $row} {$r < $l} {incr r} {
set id [lindex $displayorder $r]
set commitrow($curview,$id) $r
}
+ incr commitidx($curview)
set idlist [lindex $rowidlist $row]
set offs [lindex $rowoffsets $row]
@@ -3419,47 +3967,18 @@ proc insertrow {row newcmit} {
set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
set rowrangelist [linsert $rowrangelist $row {}]
- set l [llength $rowrangelist]
- for {set r 0} {$r < $l} {incr r} {
- set ranges [lindex $rowrangelist $r]
- if {$ranges ne {} && [lindex $ranges end] >= $row} {
- set newranges {}
- foreach x $ranges {
- if {$x >= $row} {
- lappend newranges [expr {$x + 1}]
- } else {
- lappend newranges $x
- }
- }
- lset rowrangelist $r $newranges
- }
- }
if {[llength $kids] > 1} {
set rp1 [expr {$row + 1}]
set ranges [lindex $rowrangelist $rp1]
if {$ranges eq {}} {
- set ranges [list $row $rp1]
- } elseif {[lindex $ranges end-1] == $rp1} {
- lset ranges end-1 $row
+ set ranges [list $newcmit $p]
+ } elseif {[lindex $ranges end-1] eq $p} {
+ lset ranges end-1 $newcmit
}
lset rowrangelist $rp1 $ranges
}
- foreach id [array names idrowranges] {
- set ranges $idrowranges($id)
- if {$ranges ne {} && [lindex $ranges end] >= $row} {
- set newranges {}
- foreach x $ranges {
- if {$x >= $row} {
- lappend newranges [expr {$x + 1}]
- } else {
- lappend newranges $x
- }
- }
- set idrowranges($id) $newranges
- }
- }
- set linesegends [linsert $linesegends $row {}]
+ catch {unset rowchk}
incr rowlaidout
incr rowoptim
@@ -3471,6 +3990,65 @@ proc insertrow {row newcmit} {
redisplay
}
+# Remove a commit that was inserted with insertrow on row $row.
+proc removerow {row} {
+ global displayorder parentlist commitlisted children
+ global commitrow curview rowidlist rowoffsets numcommits
+ global rowrangelist idrowranges rowlaidout rowoptim numcommits
+ global linesegends selectedline rowchk commitidx
+
+ if {$row >= $numcommits} {
+ puts "oops, removing row $row but only have $numcommits rows"
+ return
+ }
+ set rp1 [expr {$row + 1}]
+ set id [lindex $displayorder $row]
+ set p [lindex $parentlist $row]
+ set displayorder [lreplace $displayorder $row $row]
+ set parentlist [lreplace $parentlist $row $row]
+ set commitlisted [lreplace $commitlisted $row $row]
+ set kids $children($curview,$p)
+ set i [lsearch -exact $kids $id]
+ if {$i >= 0} {
+ set kids [lreplace $kids $i $i]
+ set children($curview,$p) $kids
+ }
+ set l [llength $displayorder]
+ for {set r $row} {$r < $l} {incr r} {
+ set id [lindex $displayorder $r]
+ set commitrow($curview,$id) $r
+ }
+ incr commitidx($curview) -1
+
+ set rowidlist [lreplace $rowidlist $row $row]
+ set rowoffsets [lreplace $rowoffsets $rp1 $rp1]
+ if {$kids ne {}} {
+ set offs [lindex $rowoffsets $row]
+ set offs [lreplace $offs end end]
+ lset rowoffsets $row $offs
+ }
+
+ set rowrangelist [lreplace $rowrangelist $row $row]
+ if {[llength $kids] > 0} {
+ set ranges [lindex $rowrangelist $row]
+ if {[lindex $ranges end-1] eq $id} {
+ set ranges [lreplace $ranges end-1 end]
+ lset rowrangelist $row $ranges
+ }
+ }
+
+ catch {unset rowchk}
+
+ incr rowlaidout -1
+ incr rowoptim -1
+ incr numcommits -1
+
+ if {[info exists selectedline] && $selectedline > $row} {
+ incr selectedline -1
+ }
+ redisplay
+}
+
# Don't change the text pane cursor if it is currently the hand cursor,
# showing that we are over a sha1 ID link.
proc settextcursor {c} {
@@ -3502,125 +4080,172 @@ proc notbusy {what} {
}
}
-proc drawrest {} {
- global startmsecs
- global rowlaidout commitidx curview
- global pending_select
-
- set row $rowlaidout
- layoutrows $rowlaidout $commitidx($curview) 1
- layouttail
- optimize_rows $row 0 $commitidx($curview)
- showstuff $commitidx($curview)
- if {[info exists pending_select]} {
- selectline 0 1
- }
-
- set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
- #global numcommits
- #puts "overall $drawmsecs ms for $numcommits commits"
-}
-
proc findmatches {f} {
- global findtype foundstring foundstrlen
+ global findtype findstring
if {$findtype == "Regexp"} {
- set matches [regexp -indices -all -inline $foundstring $f]
+ set matches [regexp -indices -all -inline $findstring $f]
} else {
+ set fs $findstring
if {$findtype == "IgnCase"} {
- set str [string tolower $f]
- } else {
- set str $f
+ set f [string tolower $f]
+ set fs [string tolower $fs]
}
set matches {}
set i 0
- while {[set j [string first $foundstring $str $i]] >= 0} {
- lappend matches [list $j [expr {$j+$foundstrlen-1}]]
- set i [expr {$j + $foundstrlen}]
+ set l [string length $fs]
+ while {[set j [string first $fs $f $i]] >= 0} {
+ lappend matches [list $j [expr {$j+$l-1}]]
+ set i [expr {$j + $l}]
}
}
return $matches
}
-proc dofind {} {
- global findtype findloc findstring markedmatches commitinfo
- global numcommits displayorder linehtag linentag linedtag
- global mainfont canv canv2 canv3 selectedline
- global matchinglines foundstring foundstrlen matchstring
- global commitdata
+proc dofind {{rev 0}} {
+ global findstring findstartline findcurline selectedline numcommits
- stopfindproc
unmarkmatches
cancel_next_highlight
focus .
- set matchinglines {}
- if {$findtype == "IgnCase"} {
- set foundstring [string tolower $findstring]
+ if {$findstring eq {} || $numcommits == 0} return
+ if {![info exists selectedline]} {
+ set findstartline [lindex [visiblerows] $rev]
} else {
- set foundstring $findstring
+ set findstartline $selectedline
}
- set foundstrlen [string length $findstring]
- if {$foundstrlen == 0} return
- regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
- set matchstring "*$matchstring*"
- if {![info exists selectedline]} {
- set oldsel -1
+ set findcurline $findstartline
+ nowbusy finding
+ if {!$rev} {
+ run findmore
} else {
- set oldsel $selectedline
+ if {$findcurline == 0} {
+ set findcurline $numcommits
+ }
+ incr findcurline -1
+ run findmorerev
}
- set didsel 0
- set fldtypes {Headline Author Date Committer CDate Comments}
- set l -1
- foreach id $displayorder {
- set d $commitdata($id)
- incr l
- if {$findtype == "Regexp"} {
- set doesmatch [regexp $foundstring $d]
- } elseif {$findtype == "IgnCase"} {
- set doesmatch [string match -nocase $matchstring $d]
+}
+
+proc findnext {restart} {
+ global findcurline
+ if {![info exists findcurline]} {
+ if {$restart} {
+ dofind
} else {
- set doesmatch [string match $matchstring $d]
+ bell
}
- if {!$doesmatch} continue
+ } else {
+ run findmore
+ nowbusy finding
+ }
+}
+
+proc findprev {} {
+ global findcurline
+ if {![info exists findcurline]} {
+ dofind 1
+ } else {
+ run findmorerev
+ nowbusy finding
+ }
+}
+
+proc findmore {} {
+ global commitdata commitinfo numcommits findstring findpattern findloc
+ global findstartline findcurline displayorder
+
+ set fldtypes {Headline Author Date Committer CDate Comments}
+ set l [expr {$findcurline + 1}]
+ if {$l >= $numcommits} {
+ set l 0
+ }
+ if {$l <= $findstartline} {
+ set lim [expr {$findstartline + 1}]
+ } else {
+ set lim $numcommits
+ }
+ if {$lim - $l > 500} {
+ set lim [expr {$l + 500}]
+ }
+ set last 0
+ for {} {$l < $lim} {incr l} {
+ set id [lindex $displayorder $l]
+ # shouldn't happen unless git log doesn't give all the commits...
+ if {![info exists commitdata($id)]} continue
+ if {![doesmatch $commitdata($id)]} continue
if {![info exists commitinfo($id)]} {
getcommit $id
}
set info $commitinfo($id)
- set doesmatch 0
foreach f $info ty $fldtypes {
- if {$findloc != "All fields" && $findloc != $ty} {
- continue
+ if {($findloc eq "All fields" || $findloc eq $ty) &&
+ [doesmatch $f]} {
+ findselectline $l
+ notbusy finding
+ return 0
}
- set matches [findmatches $f]
- if {$matches == {}} continue
- set doesmatch 1
- if {$ty == "Headline"} {
- drawcmitrow $l
- markmatches $canv $l $f $linehtag($l) $matches $mainfont
- } elseif {$ty == "Author"} {
- drawcmitrow $l
- markmatches $canv2 $l $f $linentag($l) $matches $mainfont
- } elseif {$ty == "Date"} {
- drawcmitrow $l
- markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
- }
- }
- if {$doesmatch} {
- lappend matchinglines $l
- if {!$didsel && $l > $oldsel} {
+ }
+ }
+ if {$l == $findstartline + 1} {
+ bell
+ unset findcurline
+ notbusy finding
+ return 0
+ }
+ set findcurline [expr {$l - 1}]
+ return 1
+}
+
+proc findmorerev {} {
+ global commitdata commitinfo numcommits findstring findpattern findloc
+ global findstartline findcurline displayorder
+
+ set fldtypes {Headline Author Date Committer CDate Comments}
+ set l $findcurline
+ if {$l == 0} {
+ set l $numcommits
+ }
+ incr l -1
+ if {$l >= $findstartline} {
+ set lim [expr {$findstartline - 1}]
+ } else {
+ set lim -1
+ }
+ if {$l - $lim > 500} {
+ set lim [expr {$l - 500}]
+ }
+ set last 0
+ for {} {$l > $lim} {incr l -1} {
+ set id [lindex $displayorder $l]
+ if {![doesmatch $commitdata($id)]} continue
+ if {![info exists commitinfo($id)]} {
+ getcommit $id
+ }
+ set info $commitinfo($id)
+ foreach f $info ty $fldtypes {
+ if {($findloc eq "All fields" || $findloc eq $ty) &&
+ [doesmatch $f]} {
findselectline $l
- set didsel 1
+ notbusy finding
+ return 0
}
}
}
- if {$matchinglines == {}} {
+ if {$l == -1} {
bell
- } elseif {!$didsel} {
- findselectline [lindex $matchinglines 0]
+ unset findcurline
+ notbusy finding
+ return 0
}
+ set findcurline [expr {$l + 1}]
+ return 1
}
proc findselectline {l} {
- global findloc commentend ctext
+ global findloc commentend ctext findcurline markingmatches
+
+ set markingmatches 1
+ set findcurline $l
selectline $l 1
if {$findloc == "All fields" || $findloc == "Comments"} {
# highlight the matches in the comments
@@ -3632,75 +4257,13 @@ proc findselectline {l} {
$ctext tag add found "1.0 + $start c" "1.0 + $end c"
}
}
+ drawvisible
}
-proc findnext {restart} {
- global matchinglines selectedline
- if {![info exists matchinglines]} {
- if {$restart} {
- dofind
- }
- return
- }
- if {![info exists selectedline]} return
- foreach l $matchinglines {
- if {$l > $selectedline} {
- findselectline $l
- return
- }
- }
- bell
-}
-
-proc findprev {} {
- global matchinglines selectedline
- if {![info exists matchinglines]} {
- dofind
- return
- }
- if {![info exists selectedline]} return
- set prev {}
- foreach l $matchinglines {
- if {$l >= $selectedline} break
- set prev $l
- }
- if {$prev != {}} {
- findselectline $prev
- } else {
- bell
- }
-}
-
-proc stopfindproc {{done 0}} {
- global findprocpid findprocfile findids
- global ctext findoldcursor phase maincursor textcursor
- global findinprogress
-
- catch {unset findids}
- if {[info exists findprocpid]} {
- if {!$done} {
- catch {exec kill $findprocpid}
- }
- catch {close $findprocfile}
- unset findprocpid
- }
- catch {unset findinprogress}
- notbusy find
-}
-
-# mark a commit as matching by putting a yellow background
-# behind the headline
-proc markheadline {l id} {
- global canv mainfont linehtag
-
- drawcmitrow $l
- set bbox [$canv bbox $linehtag($l)]
- set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
- $canv lower $t
-}
+# mark the bits of a headline or author that match a find string
+proc markmatches {canv l str tag matches font row} {
+ global selectedline
-# mark the bits of a headline, author or date that match a find string
-proc markmatches {canv l str tag matches font} {
set bbox [$canv bbox $tag]
set x0 [lindex $bbox 0]
set y0 [lindex $bbox 1]
@@ -3713,16 +4276,21 @@ proc markmatches {canv l str tag matches font} {
set xlen [font measure $font [string range $str 0 [expr {$end}]]]
set t [$canv create rect [expr {$x0+$xoff}] $y0 \
[expr {$x0+$xlen+2}] $y1 \
- -outline {} -tags matches -fill yellow]
+ -outline {} -tags [list match$l matches] -fill yellow]
$canv lower $t
+ if {[info exists selectedline] && $row == $selectedline} {
+ $canv raise $t secsel
+ }
}
}
proc unmarkmatches {} {
- global matchinglines findids
+ global findids markingmatches findcurline
+
allcanvs delete matches
- catch {unset matchinglines}
catch {unset findids}
+ set markingmatches 0
+ catch {unset findcurline}
}
proc selcanvline {w x y} {
@@ -3798,75 +4366,106 @@ proc viewnextline {dir} {
# add a list of tag or branch names at position pos
# returns the number of names inserted
-proc appendrefs {pos tags var} {
- global ctext commitrow linknum curview $var
+proc appendrefs {pos ids var} {
+ global ctext commitrow linknum curview $var maxrefs
if {[catch {$ctext index $pos}]} {
return 0
}
- set tags [lsort $tags]
- set sep {}
- foreach tag $tags {
- set id [set $var\($tag\)]
- set lk link$linknum
- incr linknum
- $ctext insert $pos $sep
- $ctext insert $pos $tag $lk
- $ctext tag conf $lk -foreground blue
- if {[info exists commitrow($curview,$id)]} {
- $ctext tag bind $lk <1> \
- [list selectline $commitrow($curview,$id) 1]
- $ctext tag conf $lk -underline 1
- $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
- $ctext tag bind $lk <Leave> { %W configure -cursor $curtextcursor }
+ $ctext conf -state normal
+ $ctext delete $pos "$pos lineend"
+ set tags {}
+ foreach id $ids {
+ foreach tag [set $var\($id\)] {
+ lappend tags [list $tag $id]
}
- set sep ", "
}
+ if {[llength $tags] > $maxrefs} {
+ $ctext insert $pos "many ([llength $tags])"
+ } else {
+ set tags [lsort -index 0 -decreasing $tags]
+ set sep {}
+ foreach ti $tags {
+ set id [lindex $ti 1]
+ set lk link$linknum
+ incr linknum
+ $ctext tag delete $lk
+ $ctext insert $pos $sep
+ $ctext insert $pos [lindex $ti 0] $lk
+ if {[info exists commitrow($curview,$id)]} {
+ $ctext tag conf $lk -foreground blue
+ $ctext tag bind $lk <1> \
+ [list selectline $commitrow($curview,$id) 1]
+ $ctext tag conf $lk -underline 1
+ $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
+ $ctext tag bind $lk <Leave> \
+ { %W configure -cursor $curtextcursor }
+ }
+ set sep ", "
+ }
+ }
+ $ctext conf -state disabled
return [llength $tags]
}
-proc taglist {ids} {
- global idtags
+# called when we have finished computing the nearby tags
+proc dispneartags {delay} {
+ global selectedline currentid showneartags tagphase
- set tags {}
- foreach id $ids {
- foreach tag $idtags($id) {
- lappend tags $tag
- }
+ if {![info exists selectedline] || !$showneartags} return
+ after cancel dispnexttag
+ if {$delay} {
+ after 200 dispnexttag
+ set tagphase -1
+ } else {
+ after idle dispnexttag
+ set tagphase 0
}
- return $tags
}
-# called when we have finished computing the nearby tags
-proc dispneartags {} {
- global selectedline currentid ctext anc_tags desc_tags showneartags
- global desc_heads
+proc dispnexttag {} {
+ global selectedline currentid showneartags tagphase ctext
if {![info exists selectedline] || !$showneartags} return
- set id $currentid
- $ctext conf -state normal
- if {[info exists desc_heads($id)]} {
- if {[appendrefs branch $desc_heads($id) headids] > 1} {
- $ctext insert "branch -2c" "es"
+ switch -- $tagphase {
+ 0 {
+ set dtags [desctags $currentid]
+ if {$dtags ne {}} {
+ appendrefs precedes $dtags idtags
+ }
+ }
+ 1 {
+ set atags [anctags $currentid]
+ if {$atags ne {}} {
+ appendrefs follows $atags idtags
+ }
+ }
+ 2 {
+ set dheads [descheads $currentid]
+ if {$dheads ne {}} {
+ if {[appendrefs branch $dheads idheads] > 1
+ && [$ctext get "branch -3c"] eq "h"} {
+ # turn "Branch" into "Branches"
+ $ctext conf -state normal
+ $ctext insert "branch -2c" "es"
+ $ctext conf -state disabled
+ }
+ }
}
}
- if {[info exists anc_tags($id)]} {
- appendrefs follows [taglist $anc_tags($id)] tagids
- }
- if {[info exists desc_tags($id)]} {
- appendrefs precedes [taglist $desc_tags($id)] tagids
+ if {[incr tagphase] <= 2} {
+ after idle dispnexttag
}
- $ctext conf -state disabled
}
proc selectline {l isnew} {
global canv canv2 canv3 ctext commitinfo selectedline
global displayorder linehtag linentag linedtag
- global canvy0 linespc parentlist childlist
+ global canvy0 linespc parentlist children curview
global currentid sha1entry
global commentend idtags linknum
global mergemax numcommits pending_select
- global cmitmode desc_tags anc_tags showneartags allcommits desc_heads
+ global cmitmode showneartags allcommits
catch {unset pending_select}
$canv delete hover
@@ -3973,7 +4572,7 @@ proc selectline {l isnew} {
}
}
- foreach c [lindex $childlist $l] {
+ foreach c $children($curview,$id) {
append headers "Child: [commit_descriptor $c]"
}
@@ -3986,30 +4585,22 @@ proc selectline {l isnew} {
$ctext insert end "Branch: "
$ctext mark set branch "end -1c"
$ctext mark gravity branch left
- if {[info exists desc_heads($id)]} {
- if {[appendrefs branch $desc_heads($id) headids] > 1} {
- # turn "Branch" into "Branches"
- $ctext insert "branch -2c" "es"
- }
- }
$ctext insert end "\nFollows: "
$ctext mark set follows "end -1c"
$ctext mark gravity follows left
- if {[info exists anc_tags($id)]} {
- appendrefs follows [taglist $anc_tags($id)] tagids
- }
$ctext insert end "\nPrecedes: "
$ctext mark set precedes "end -1c"
$ctext mark gravity precedes left
- if {[info exists desc_tags($id)]} {
- appendrefs precedes [taglist $desc_tags($id)] tagids
- }
$ctext insert end "\n"
+ dispneartags 1
}
$ctext insert end "\n"
- appendwithlinks [lindex $info 5] {comment}
+ set comment [lindex $info 5]
+ if {[string first "\r" $comment] >= 0} {
+ set comment [string map {"\r" "\n "} $comment]
+ }
+ appendwithlinks $comment {comment}
- $ctext tag delete Comments
$ctext tag remove found 1.0 end
$ctext conf -state disabled
set commentend [$ctext index "end - 1c"]
@@ -4038,6 +4629,7 @@ proc sellastline {} {
proc selnextline {dir} {
global selectedline
+ focus .
if {![info exists selectedline]} return
set l [expr {$selectedline + $dir}]
unmarkmatches
@@ -4118,6 +4710,7 @@ proc godo {elt} {
proc goback {} {
global history historyindex
+ focus .
if {$historyindex > 1} {
incr historyindex -1
@@ -4131,6 +4724,7 @@ proc goback {} {
proc goforw {} {
global history historyindex
+ focus .
if {$historyindex < [llength $history]} {
set cmd [lindex $history $historyindex]
@@ -4145,19 +4739,27 @@ proc goforw {} {
proc gettree {id} {
global treefilelist treeidlist diffids diffmergeid treepending
+ global nullid nullid2
set diffids $id
catch {unset diffmergeid}
if {![info exists treefilelist($id)]} {
if {![info exists treepending]} {
- if {[catch {set gtf [open [concat | git ls-tree -r $id] r]}]} {
+ if {$id eq $nullid} {
+ set cmd [list | git ls-files]
+ } elseif {$id eq $nullid2} {
+ set cmd [list | git ls-files --stage -t]
+ } else {
+ set cmd [list | git ls-tree -r $id]
+ }
+ if {[catch {set gtf [open $cmd r]}]} {
return
}
set treepending $id
set treefilelist($id) {}
set treeidlist($id) {}
fconfigure $gtf -blocking 0
- fileevent $gtf readable [list gettreeline $gtf $id]
+ filerun $gtf [list gettreeline $gtf $id]
}
} else {
setfilelist $id
@@ -4165,16 +4767,28 @@ proc gettree {id} {
}
proc gettreeline {gtf id} {
- global treefilelist treeidlist treepending cmitmode diffids
+ global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
- while {[gets $gtf line] >= 0} {
- if {[lindex $line 1] ne "blob"} continue
- set sha1 [lindex $line 2]
- set fname [lindex $line 3]
+ set nl 0
+ while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
+ if {$diffids eq $nullid} {
+ set fname $line
+ } else {
+ if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
+ set i [string first "\t" $line]
+ if {$i < 0} continue
+ set sha1 [lindex $line 2]
+ set fname [string range $line [expr {$i+1}] end]
+ if {[string index $fname 0] eq "\""} {
+ set fname [lindex $fname 0]
+ }
+ lappend treeidlist($id) $sha1
+ }
lappend treefilelist($id) $fname
- lappend treeidlist($id) $sha1
}
- if {![eof $gtf]} return
+ if {![eof $gtf]} {
+ return [expr {$nl >= 1000? 2: 1}]
+ }
close $gtf
unset treepending
if {$cmitmode ne "tree"} {
@@ -4186,10 +4800,11 @@ proc gettreeline {gtf id} {
} else {
setfilelist $id
}
+ return 0
}
proc showfile {f} {
- global treefilelist treeidlist diffids
+ global treefilelist treeidlist diffids nullid nullid2
global ctext commentend
set i [lsearch -exact $treefilelist($diffids) $f]
@@ -4197,13 +4812,20 @@ proc showfile {f} {
puts "oops, $f not in list for id $diffids"
return
}
- set blob [lindex $treeidlist($diffids) $i]
- if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
- puts "oops, error reading blob $blob: $err"
- return
+ if {$diffids eq $nullid} {
+ if {[catch {set bf [open $f r]} err]} {
+ puts "oops, can't read $f: $err"
+ return
+ }
+ } else {
+ set blob [lindex $treeidlist($diffids) $i]
+ if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
+ puts "oops, error reading blob $blob: $err"
+ return
+ }
}
fconfigure $bf -blocking 0
- fileevent $bf readable [list getblobline $bf $diffids]
+ filerun $bf [list getblobline $bf $diffids]
$ctext config -state normal
clear_ctext $commentend
$ctext insert end "\n"
@@ -4217,18 +4839,21 @@ proc getblobline {bf id} {
if {$id ne $diffids || $cmitmode ne "tree"} {
catch {close $bf}
- return
+ return 0
}
$ctext config -state normal
- while {[gets $bf line] >= 0} {
+ set nl 0
+ while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
$ctext insert end "$line\n"
}
if {[eof $bf]} {
# delete last newline
$ctext delete "end - 2c" "end - 1c"
close $bf
+ return 0
}
$ctext config -state disabled
+ return [expr {$nl >= 1000? 2: 1}]
}
proc mergediff {id l} {
@@ -4248,91 +4873,88 @@ proc mergediff {id l} {
fconfigure $mdf -blocking 0
set mdifffd($id) $mdf
set np [llength [lindex $parentlist $l]]
- fileevent $mdf readable [list getmergediffline $mdf $id $np]
- set nextupdate [expr {[clock clicks -milliseconds] + 100}]
+ filerun $mdf [list getmergediffline $mdf $id $np]
}
proc getmergediffline {mdf id np} {
- global diffmergeid ctext cflist nextupdate mergemax
+ global diffmergeid ctext cflist mergemax
global difffilestart mdifffd
- set n [gets $mdf line]
- if {$n < 0} {
- if {[eof $mdf]} {
+ $ctext conf -state normal
+ set nr 0
+ while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
+ if {![info exists diffmergeid] || $id != $diffmergeid
+ || $mdf != $mdifffd($id)} {
close $mdf
+ return 0
}
- return
- }
- if {![info exists diffmergeid] || $id != $diffmergeid
- || $mdf != $mdifffd($id)} {
- return
- }
- $ctext conf -state normal
- if {[regexp {^diff --cc (.*)} $line match fname]} {
- # start of a new file
- $ctext insert end "\n"
- set here [$ctext index "end - 1c"]
- lappend difffilestart $here
- add_flist [list $fname]
- set l [expr {(78 - [string length $fname]) / 2}]
- set pad [string range "----------------------------------------" 1 $l]
- $ctext insert end "$pad $fname $pad\n" filesep
- } elseif {[regexp {^@@} $line]} {
- $ctext insert end "$line\n" hunksep
- } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
- # do nothing
- } else {
- # parse the prefix - one ' ', '-' or '+' for each parent
- set spaces {}
- set minuses {}
- set pluses {}
- set isbad 0
- for {set j 0} {$j < $np} {incr j} {
- set c [string range $line $j $j]
- if {$c == " "} {
- lappend spaces $j
- } elseif {$c == "-"} {
- lappend minuses $j
- } elseif {$c == "+"} {
- lappend pluses $j
- } else {
- set isbad 1
- break
+ if {[regexp {^diff --cc (.*)} $line match fname]} {
+ # start of a new file
+ $ctext insert end "\n"
+ set here [$ctext index "end - 1c"]
+ lappend difffilestart $here
+ add_flist [list $fname]
+ set l [expr {(78 - [string length $fname]) / 2}]
+ set pad [string range "----------------------------------------" 1 $l]
+ $ctext insert end "$pad $fname $pad\n" filesep
+ } elseif {[regexp {^@@} $line]} {
+ $ctext insert end "$line\n" hunksep
+ } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
+ # do nothing
+ } else {
+ # parse the prefix - one ' ', '-' or '+' for each parent
+ set spaces {}
+ set minuses {}
+ set pluses {}
+ set isbad 0
+ for {set j 0} {$j < $np} {incr j} {
+ set c [string range $line $j $j]
+ if {$c == " "} {
+ lappend spaces $j
+ } elseif {$c == "-"} {
+ lappend minuses $j
+ } elseif {$c == "+"} {
+ lappend pluses $j
+ } else {
+ set isbad 1
+ break
+ }
}
- }
- set tags {}
- set num {}
- if {!$isbad && $minuses ne {} && $pluses eq {}} {
- # line doesn't appear in result, parents in $minuses have the line
- set num [lindex $minuses 0]
- } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
- # line appears in result, parents in $pluses don't have the line
- lappend tags mresult
- set num [lindex $spaces 0]
- }
- if {$num ne {}} {
- if {$num >= $mergemax} {
- set num "max"
+ set tags {}
+ set num {}
+ if {!$isbad && $minuses ne {} && $pluses eq {}} {
+ # line doesn't appear in result, parents in $minuses have the line
+ set num [lindex $minuses 0]
+ } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
+ # line appears in result, parents in $pluses don't have the line
+ lappend tags mresult
+ set num [lindex $spaces 0]
}
- lappend tags m$num
+ if {$num ne {}} {
+ if {$num >= $mergemax} {
+ set num "max"
+ }
+ lappend tags m$num
+ }
+ $ctext insert end "$line\n" $tags
}
- $ctext insert end "$line\n" $tags
}
$ctext conf -state disabled
- if {[clock clicks -milliseconds] >= $nextupdate} {
- incr nextupdate 100
- fileevent $mdf readable {}
- update
- fileevent $mdf readable [list getmergediffline $mdf $id $np]
+ if {[eof $mdf]} {
+ close $mdf
+ return 0
}
+ return [expr {$nr >= 1000? 2: 1}]
}
proc startdiff {ids} {
- global treediffs diffids treepending diffmergeid
+ global treediffs diffids treepending diffmergeid nullid nullid2
set diffids $ids
catch {unset diffmergeid}
- if {![info exists treediffs($ids)]} {
+ if {![info exists treediffs($ids)] ||
+ [lsearch -exact $ids $nullid] >= 0 ||
+ [lsearch -exact $ids $nullid2] >= 0} {
if {![info exists treepending]} {
gettreediffs $ids
}
@@ -4347,59 +4969,102 @@ proc addtocflist {ids} {
getblobdiffs $ids
}
+proc diffcmd {ids flags} {
+ global nullid nullid2
+
+ set i [lsearch -exact $ids $nullid]
+ set j [lsearch -exact $ids $nullid2]
+ if {$i >= 0} {
+ if {[llength $ids] > 1 && $j < 0} {
+ # comparing working directory with some specific revision
+ set cmd [concat | git diff-index $flags]
+ if {$i == 0} {
+ lappend cmd -R [lindex $ids 1]
+ } else {
+ lappend cmd [lindex $ids 0]
+ }
+ } else {
+ # comparing working directory with index
+ set cmd [concat | git diff-files $flags]
+ if {$j == 1} {
+ lappend cmd -R
+ }
+ }
+ } elseif {$j >= 0} {
+ set cmd [concat | git diff-index --cached $flags]
+ if {[llength $ids] > 1} {
+ # comparing index with specific revision
+ if {$i == 0} {
+ lappend cmd -R [lindex $ids 1]
+ } else {
+ lappend cmd [lindex $ids 0]
+ }
+ } else {
+ # comparing index with HEAD
+ lappend cmd HEAD
+ }
+ } else {
+ set cmd [concat | git diff-tree -r $flags $ids]
+ }
+ return $cmd
+}
+
proc gettreediffs {ids} {
global treediff treepending
+
set treepending $ids
set treediff {}
- if {[catch \
- {set gdtf [open [concat | git diff-tree --no-commit-id -r $ids] r]} \
- ]} return
+ if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
fconfigure $gdtf -blocking 0
- fileevent $gdtf readable [list gettreediffline $gdtf $ids]
+ filerun $gdtf [list gettreediffline $gdtf $ids]
}
proc gettreediffline {gdtf ids} {
global treediff treediffs treepending diffids diffmergeid
global cmitmode
- set n [gets $gdtf line]
- if {$n < 0} {
- if {![eof $gdtf]} return
- close $gdtf
- set treediffs($ids) $treediff
- unset treepending
- if {$cmitmode eq "tree"} {
- gettree $diffids
- } elseif {$ids != $diffids} {
- if {![info exists diffmergeid]} {
- gettreediffs $diffids
+ set nr 0
+ while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
+ set i [string first "\t" $line]
+ if {$i >= 0} {
+ set file [string range $line [expr {$i+1}] end]
+ if {[string index $file 0] eq "\""} {
+ set file [lindex $file 0]
}
- } else {
- addtocflist $ids
+ lappend treediff $file
}
- return
}
- set file [lindex $line 5]
- lappend treediff $file
+ if {![eof $gdtf]} {
+ return [expr {$nr >= 1000? 2: 1}]
+ }
+ close $gdtf
+ set treediffs($ids) $treediff
+ unset treepending
+ if {$cmitmode eq "tree"} {
+ gettree $diffids
+ } elseif {$ids != $diffids} {
+ if {![info exists diffmergeid]} {
+ gettreediffs $diffids
+ }
+ } else {
+ addtocflist $ids
+ }
+ return 0
}
proc getblobdiffs {ids} {
- global diffopts blobdifffd diffids env curdifftag curtagstart
- global nextupdate diffinhdr treediffs
+ global diffopts blobdifffd diffids env
+ global diffinhdr treediffs
set env(GIT_DIFF_OPTS) $diffopts
- set cmd [concat | git diff-tree --no-commit-id -r -p -C $ids]
- if {[catch {set bdf [open $cmd r]} err]} {
+ if {[catch {set bdf [open [diffcmd $ids {-p -C --no-commit-id}] r]} err]} {
puts "error getting diffs: $err"
return
}
set diffinhdr 0
fconfigure $bdf -blocking 0
set blobdifffd($ids) $bdf
- set curdifftag Comments
- set curtagstart 0.0
- fileevent $bdf readable [list getblobdiffline $bdf $diffids]
- set nextupdate [expr {[clock clicks -milliseconds] + 100}]
+ filerun $bdf [list getblobdiffline $bdf $diffids]
}
proc setinlist {var i val} {
@@ -4415,84 +5080,111 @@ proc setinlist {var i val} {
}
}
+proc makediffhdr {fname ids} {
+ global ctext curdiffstart treediffs
+
+ set i [lsearch -exact $treediffs($ids) $fname]
+ if {$i >= 0} {
+ setinlist difffilestart $i $curdiffstart
+ }
+ set l [expr {(78 - [string length $fname]) / 2}]
+ set pad [string range "----------------------------------------" 1 $l]
+ $ctext insert $curdiffstart "$pad $fname $pad" filesep
+}
+
proc getblobdiffline {bdf ids} {
- global diffids blobdifffd ctext curdifftag curtagstart
+ global diffids blobdifffd ctext curdiffstart
global diffnexthead diffnextnote difffilestart
- global nextupdate diffinhdr treediffs
+ global diffinhdr treediffs
- set n [gets $bdf line]
- if {$n < 0} {
- if {[eof $bdf]} {
+ set nr 0
+ $ctext conf -state normal
+ while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
+ if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
close $bdf
- if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
- $ctext tag add $curdifftag $curtagstart end
- }
+ return 0
}
- return
- }
- if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
- return
- }
- $ctext conf -state normal
- if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
- # start of a new file
- $ctext insert end "\n"
- $ctext tag add $curdifftag $curtagstart end
- set here [$ctext index "end - 1c"]
- set curtagstart $here
- set header $newname
- set i [lsearch -exact $treediffs($ids) $fname]
- if {$i >= 0} {
- setinlist difffilestart $i $here
- }
- if {$newname ne $fname} {
- set i [lsearch -exact $treediffs($ids) $newname]
- if {$i >= 0} {
- setinlist difffilestart $i $here
- }
- }
- set curdifftag "f:$fname"
- $ctext tag delete $curdifftag
- set l [expr {(78 - [string length $header]) / 2}]
- set pad [string range "----------------------------------------" 1 $l]
- $ctext insert end "$pad $header $pad\n" filesep
- set diffinhdr 1
- } elseif {$diffinhdr && [string compare -length 3 $line "---"] == 0} {
- # do nothing
- } elseif {$diffinhdr && [string compare -length 3 $line "+++"] == 0} {
- set diffinhdr 0
- } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
- $line match f1l f1c f2l f2c rest]} {
- $ctext insert end "$line\n" hunksep
- set diffinhdr 0
- } else {
- set x [string range $line 0 0]
- if {$x == "-" || $x == "+"} {
- set tag [expr {$x == "+"}]
- $ctext insert end "$line\n" d$tag
- } elseif {$x == " "} {
- $ctext insert end "$line\n"
- } elseif {$diffinhdr || $x == "\\"} {
- # e.g. "\ No newline at end of file"
+ if {![string compare -length 11 "diff --git " $line]} {
+ # trim off "diff --git "
+ set line [string range $line 11 end]
+ set diffinhdr 1
+ # start of a new file
+ $ctext insert end "\n"
+ set curdiffstart [$ctext index "end - 1c"]
+ $ctext insert end "\n" filesep
+ # If the name hasn't changed the length will be odd,
+ # the middle char will be a space, and the two bits either
+ # side will be a/name and b/name, or "a/name" and "b/name".
+ # If the name has changed we'll get "rename from" and
+ # "rename to" lines following this, and we'll use them
+ # to get the filenames.
+ # This complexity is necessary because spaces in the filename(s)
+ # don't get escaped.
+ set l [string length $line]
+ set i [expr {$l / 2}]
+ if {!(($l & 1) && [string index $line $i] eq " " &&
+ [string range $line 2 [expr {$i - 1}]] eq \
+ [string range $line [expr {$i + 3}] end])} {
+ continue
+ }
+ # unescape if quoted and chop off the a/ from the front
+ if {[string index $line 0] eq "\""} {
+ set fname [string range [lindex $line 0] 2 end]
+ } else {
+ set fname [string range $line 2 [expr {$i - 1}]]
+ }
+ makediffhdr $fname $ids
+
+ } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
+ $line match f1l f1c f2l f2c rest]} {
+ $ctext insert end "$line\n" hunksep
+ set diffinhdr 0
+
+ } elseif {$diffinhdr} {
+ if {![string compare -length 12 "rename from " $line]} {
+ set fname [string range $line 12 end]
+ if {[string index $fname 0] eq "\""} {
+ set fname [lindex $fname 0]
+ }
+ set i [lsearch -exact $treediffs($ids) $fname]
+ if {$i >= 0} {
+ setinlist difffilestart $i $curdiffstart
+ }
+ } elseif {![string compare -length 10 $line "rename to "]} {
+ set fname [string range $line 10 end]
+ if {[string index $fname 0] eq "\""} {
+ set fname [lindex $fname 0]
+ }
+ makediffhdr $fname $ids
+ } elseif {[string compare -length 3 $line "---"] == 0} {
+ # do nothing
+ continue
+ } elseif {[string compare -length 3 $line "+++"] == 0} {
+ set diffinhdr 0
+ continue
+ }
$ctext insert end "$line\n" filesep
+
} else {
- # Something else we don't recognize
- if {$curdifftag != "Comments"} {
- $ctext insert end "\n"
- $ctext tag add $curdifftag $curtagstart end
- set curtagstart [$ctext index "end - 1c"]
- set curdifftag Comments
+ set x [string range $line 0 0]
+ if {$x == "-" || $x == "+"} {
+ set tag [expr {$x == "+"}]
+ $ctext insert end "$line\n" d$tag
+ } elseif {$x == " "} {
+ $ctext insert end "$line\n"
+ } else {
+ # "\ No newline at end of file",
+ # or something else we don't recognize
+ $ctext insert end "$line\n" hunksep
}
- $ctext insert end "$line\n" filesep
}
}
$ctext conf -state disabled
- if {[clock clicks -milliseconds] >= $nextupdate} {
- incr nextupdate 100
- fileevent $bdf readable {}
- update
- fileevent $bdf readable "getblobdiffline $bdf {$ids}"
+ if {[eof $bdf]} {
+ close $bdf
+ return 0
}
+ return [expr {$nr >= 1000? 2: 1}]
}
proc changediffdisp {} {
@@ -4690,13 +5382,15 @@ proc redisplay {} {
}
proc incrfont {inc} {
- global mainfont textfont ctext canv phase
+ global mainfont textfont ctext canv phase cflist
+ global charspc tabstop
global stopped entries
unmarkmatches
set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
setcoords
- $ctext conf -font $textfont
+ $ctext conf -font $textfont -tabs "[expr {$tabstop * $charspc}]"
+ $cflist conf -font $textfont
$ctext tag conf filesep -font [concat $textfont bold]
foreach e $entries {
$e conf -font $mainfont
@@ -4954,18 +5648,25 @@ proc mstime {} {
proc rowmenu {x y id} {
global rowctxmenu commitrow selectedline rowmenuid curview
+ global nullid nullid2 fakerowmenu mainhead
+ set rowmenuid $id
if {![info exists selectedline]
|| $commitrow($curview,$id) eq $selectedline} {
set state disabled
} else {
set state normal
}
- $rowctxmenu entryconfigure "Diff this*" -state $state
- $rowctxmenu entryconfigure "Diff selected*" -state $state
- $rowctxmenu entryconfigure "Make patch" -state $state
- set rowmenuid $id
- tk_popup $rowctxmenu $x $y
+ if {$id ne $nullid && $id ne $nullid2} {
+ set menu $rowctxmenu
+ $menu entryconfigure 7 -label "Reset $mainhead branch to here"
+ } else {
+ set menu $fakerowmenu
+ }
+ $menu entryconfigure "Diff this*" -state $state
+ $menu entryconfigure "Diff selected*" -state $state
+ $menu entryconfigure "Make patch" -state $state
+ tk_popup $menu $x $y
}
proc diffvssel {dirn} {
@@ -5005,7 +5706,6 @@ proc doseldiff {oldid newid} {
$ctext insert end [lindex $commitinfo($newid) 0]
$ctext insert end "\n"
$ctext conf -state disabled
- $ctext tag delete Comments
$ctext tag remove found 1.0 end
startdiff [list $oldid $newid]
}
@@ -5076,12 +5776,14 @@ proc mkpatchrev {} {
}
proc mkpatchgo {} {
- global patchtop
+ global patchtop nullid nullid2
set oldid [$patchtop.fromsha1 get]
set newid [$patchtop.tosha1 get]
set fname [$patchtop.fname get]
- if {[catch {exec git diff-tree -p $oldid $newid >$fname &} err]} {
+ set cmd [diffcmd [list $oldid $newid] -p]
+ lappend cmd >$fname &
+ if {[catch {eval exec $cmd} err]} {
error_popup "Error creating patch: $err"
}
catch {destroy $patchtop}
@@ -5158,10 +5860,11 @@ proc domktag {} {
proc redrawtags {id} {
global canv linehtag commitrow idpos selectedline curview
- global mainfont canvxmax
+ global mainfont canvxmax iddrawn
if {![info exists commitrow($curview,$id)]} return
- drawcmitrow $commitrow($curview,$id)
+ if {![info exists iddrawn($id)]} return
+ drawcommits $commitrow($curview,$id)
$canv delete tag.$id
set xt [eval drawtags $id $idpos($id)]
$canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
@@ -5288,26 +5991,28 @@ proc mkbrgo {top} {
notbusy newbranch
error_popup $err
} else {
+ set headids($name) $id
+ lappend idheads($id) $name
addedhead $id $name
- # XXX should update list of heads displayed for selected commit
notbusy newbranch
redrawtags $id
+ dispneartags 0
}
}
proc cherrypick {} {
global rowmenuid curview commitrow
- global mainhead desc_heads anc_tags desc_tags allparents allchildren
+ global mainhead
- if {[info exists desc_heads($rowmenuid)]
- && [lsearch -exact $desc_heads($rowmenuid) $mainhead] >= 0} {
+ set oldhead [exec git rev-parse HEAD]
+ set dheads [descheads $rowmenuid]
+ if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
included in branch $mainhead -- really re-apply it?"]
if {!$ok} return
}
nowbusy cherrypick
update
- set oldhead [exec git rev-parse HEAD]
# Unfortunately git-cherry-pick writes stuff to stderr even when
# no error occurs, and exec takes that as an indication of error...
if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
@@ -5321,16 +6026,11 @@ proc cherrypick {} {
error_popup "No changes committed"
return
}
- set allparents($newhead) $oldhead
- lappend allchildren($oldhead) $newhead
- set desc_heads($newhead) $mainhead
- if {[info exists anc_tags($oldhead)]} {
- set anc_tags($newhead) $anc_tags($oldhead)
- }
- set desc_tags($newhead) {}
+ addnewchild $newhead $oldhead
if {[info exists commitrow($curview,$oldhead)]} {
insertrow $commitrow($curview,$oldhead) $newhead
if {$mainhead ne {}} {
+ movehead $newhead $mainhead
movedhead $newhead $mainhead
}
redrawtags $oldhead
@@ -5339,48 +6039,146 @@ proc cherrypick {} {
notbusy cherrypick
}
+proc resethead {} {
+ global mainheadid mainhead rowmenuid confirm_ok resettype
+ global showlocalchanges
+
+ set confirm_ok 0
+ set w ".confirmreset"
+ toplevel $w
+ wm transient $w .
+ wm title $w "Confirm reset"
+ message $w.m -text \
+ "Reset branch $mainhead to [string range $rowmenuid 0 7]?" \
+ -justify center -aspect 1000
+ pack $w.m -side top -fill x -padx 20 -pady 20
+ frame $w.f -relief sunken -border 2
+ message $w.f.rt -text "Reset type:" -aspect 1000
+ grid $w.f.rt -sticky w
+ set resettype mixed
+ radiobutton $w.f.soft -value soft -variable resettype -justify left \
+ -text "Soft: Leave working tree and index untouched"
+ grid $w.f.soft -sticky w
+ radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
+ -text "Mixed: Leave working tree untouched, reset index"
+ grid $w.f.mixed -sticky w
+ radiobutton $w.f.hard -value hard -variable resettype -justify left \
+ -text "Hard: Reset working tree and index\n(discard ALL local changes)"
+ grid $w.f.hard -sticky w
+ pack $w.f -side top -fill x
+ button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
+ pack $w.ok -side left -fill x -padx 20 -pady 20
+ button $w.cancel -text Cancel -command "destroy $w"
+ pack $w.cancel -side right -fill x -padx 20 -pady 20
+ bind $w <Visibility> "grab $w; focus $w"
+ tkwait window $w
+ if {!$confirm_ok} return
+ if {[catch {set fd [open \
+ [list | sh -c "git reset --$resettype $rowmenuid 2>&1"] r]} err]} {
+ error_popup $err
+ } else {
+ dohidelocalchanges
+ set w ".resetprogress"
+ filerun $fd [list readresetstat $fd $w]
+ toplevel $w
+ wm transient $w
+ wm title $w "Reset progress"
+ message $w.m -text "Reset in progress, please wait..." \
+ -justify center -aspect 1000
+ pack $w.m -side top -fill x -padx 20 -pady 5
+ canvas $w.c -width 150 -height 20 -bg white
+ $w.c create rect 0 0 0 20 -fill green -tags rect
+ pack $w.c -side top -fill x -padx 20 -pady 5 -expand 1
+ nowbusy reset
+ }
+}
+
+proc readresetstat {fd w} {
+ global mainhead mainheadid showlocalchanges
+
+ if {[gets $fd line] >= 0} {
+ if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
+ set x [expr {($m * 150) / $n}]
+ $w.c coords rect 0 0 $x 20
+ }
+ return 1
+ }
+ destroy $w
+ notbusy reset
+ if {[catch {close $fd} err]} {
+ error_popup $err
+ }
+ set oldhead $mainheadid
+ set newhead [exec git rev-parse HEAD]
+ if {$newhead ne $oldhead} {
+ movehead $newhead $mainhead
+ movedhead $newhead $mainhead
+ set mainheadid $newhead
+ redrawtags $oldhead
+ redrawtags $newhead
+ }
+ if {$showlocalchanges} {
+ doshowlocalchanges
+ }
+ return 0
+}
+
# context menu for a head
proc headmenu {x y id head} {
- global headmenuid headmenuhead headctxmenu
+ global headmenuid headmenuhead headctxmenu mainhead
set headmenuid $id
set headmenuhead $head
+ set state normal
+ if {$head eq $mainhead} {
+ set state disabled
+ }
+ $headctxmenu entryconfigure 0 -state $state
+ $headctxmenu entryconfigure 1 -state $state
tk_popup $headctxmenu $x $y
}
proc cobranch {} {
global headmenuid headmenuhead mainhead headids
+ global showlocalchanges mainheadid
# check the tree is clean first??
set oldmainhead $mainhead
nowbusy checkout
update
+ dohidelocalchanges
if {[catch {
- exec git checkout $headmenuhead
+ exec git checkout -q $headmenuhead
} err]} {
notbusy checkout
error_popup $err
} else {
notbusy checkout
set mainhead $headmenuhead
+ set mainheadid $headmenuid
if {[info exists headids($oldmainhead)]} {
redrawtags $headids($oldmainhead)
}
redrawtags $headmenuid
}
+ if {$showlocalchanges} {
+ dodiffindex
+ }
}
proc rmbranch {} {
- global desc_heads headmenuid headmenuhead mainhead
+ global headmenuid headmenuhead mainhead
global headids idheads
set head $headmenuhead
set id $headmenuid
+ # this check shouldn't be needed any more...
if {$head eq $mainhead} {
error_popup "Cannot delete the currently checked-out branch"
return
}
- if {$desc_heads($id) eq $head} {
+ set dheads [descheads $id]
+ if {$dheads eq $headids($head)} {
# the stuff on this branch isn't on any other branch
if {![confirm_popup "The commits on branch $head aren't on any other\
branch.\nReally delete branch $head?"]} return
@@ -5392,385 +6190,883 @@ proc rmbranch {} {
error_popup $err
return
}
+ removehead $id $head
removedhead $id $head
redrawtags $id
notbusy rmbranch
+ dispneartags 0
}
# Stuff for finding nearby tags
proc getallcommits {} {
- global allcstart allcommits allcfd allids
+ global allcommits allids nbmp nextarc seeds
+
+ if {![info exists allcommits]} {
+ set allids {}
+ set nbmp 0
+ set nextarc 0
+ set allcommits 0
+ set seeds {}
+ }
- set allids {}
- set fd [open [concat | git rev-list --all --topo-order --parents] r]
- set allcfd $fd
+ set cmd [concat | git rev-list --all --parents]
+ foreach id $seeds {
+ lappend cmd "^$id"
+ }
+ set fd [open $cmd r]
fconfigure $fd -blocking 0
- set allcommits "reading"
+ incr allcommits
nowbusy allcommits
- restartgetall $fd
-}
+ filerun $fd [list getallclines $fd]
+}
+
+# Since most commits have 1 parent and 1 child, we group strings of
+# such commits into "arcs" joining branch/merge points (BMPs), which
+# are commits that either don't have 1 parent or don't have 1 child.
+#
+# arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
+# arcout(id) - outgoing arcs for BMP
+# arcids(a) - list of IDs on arc including end but not start
+# arcstart(a) - BMP ID at start of arc
+# arcend(a) - BMP ID at end of arc
+# growing(a) - arc a is still growing
+# arctags(a) - IDs out of arcids (excluding end) that have tags
+# archeads(a) - IDs out of arcids (excluding end) that have heads
+# The start of an arc is at the descendent end, so "incoming" means
+# coming from descendents, and "outgoing" means going towards ancestors.
+
+proc getallclines {fd} {
+ global allids allparents allchildren idtags idheads nextarc nbmp
+ global arcnos arcids arctags arcout arcend arcstart archeads growing
+ global seeds allcommits
-proc discardallcommits {} {
- global allparents allchildren allcommits allcfd
- global desc_tags anc_tags alldtags tagisdesc allids desc_heads
+ set nid 0
+ while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
+ set id [lindex $line 0]
+ if {[info exists allparents($id)]} {
+ # seen it already
+ continue
+ }
+ lappend allids $id
+ set olds [lrange $line 1 end]
+ set allparents($id) $olds
+ if {![info exists allchildren($id)]} {
+ set allchildren($id) {}
+ set arcnos($id) {}
+ lappend seeds $id
+ } else {
+ set a $arcnos($id)
+ if {[llength $olds] == 1 && [llength $a] == 1} {
+ lappend arcids($a) $id
+ if {[info exists idtags($id)]} {
+ lappend arctags($a) $id
+ }
+ if {[info exists idheads($id)]} {
+ lappend archeads($a) $id
+ }
+ if {[info exists allparents($olds)]} {
+ # seen parent already
+ if {![info exists arcout($olds)]} {
+ splitarc $olds
+ }
+ lappend arcids($a) $olds
+ set arcend($a) $olds
+ unset growing($a)
+ }
+ lappend allchildren($olds) $id
+ lappend arcnos($olds) $a
+ continue
+ }
+ }
+ incr nbmp
+ foreach a $arcnos($id) {
+ lappend arcids($a) $id
+ set arcend($a) $id
+ unset growing($a)
+ }
- if {![info exists allcommits]} return
- if {$allcommits eq "reading"} {
- catch {close $allcfd}
+ set ao {}
+ foreach p $olds {
+ lappend allchildren($p) $id
+ set a [incr nextarc]
+ set arcstart($a) $id
+ set archeads($a) {}
+ set arctags($a) {}
+ set archeads($a) {}
+ set arcids($a) {}
+ lappend ao $a
+ set growing($a) 1
+ if {[info exists allparents($p)]} {
+ # seen it already, may need to make a new branch
+ if {![info exists arcout($p)]} {
+ splitarc $p
+ }
+ lappend arcids($a) $p
+ set arcend($a) $p
+ unset growing($a)
+ }
+ lappend arcnos($p) $a
+ }
+ set arcout($id) $ao
}
- foreach v {allcommits allchildren allparents allids desc_tags anc_tags
- alldtags tagisdesc desc_heads} {
- catch {unset $v}
+ if {$nid > 0} {
+ global cached_dheads cached_dtags cached_atags
+ catch {unset cached_dheads}
+ catch {unset cached_dtags}
+ catch {unset cached_atags}
+ }
+ if {![eof $fd]} {
+ return [expr {$nid >= 1000? 2: 1}]
+ }
+ close $fd
+ if {[incr allcommits -1] == 0} {
+ notbusy allcommits
}
+ dispneartags 0
+ return 0
}
-proc restartgetall {fd} {
- global allcstart
+proc recalcarc {a} {
+ global arctags archeads arcids idtags idheads
- fileevent $fd readable [list getallclines $fd]
- set allcstart [clock clicks -milliseconds]
+ set at {}
+ set ah {}
+ foreach id [lrange $arcids($a) 0 end-1] {
+ if {[info exists idtags($id)]} {
+ lappend at $id
+ }
+ if {[info exists idheads($id)]} {
+ lappend ah $id
+ }
+ }
+ set arctags($a) $at
+ set archeads($a) $ah
}
-proc combine_dtags {l1 l2} {
- global tagisdesc notfirstd
+proc splitarc {p} {
+ global arcnos arcids nextarc nbmp arctags archeads idtags idheads
+ global arcstart arcend arcout allparents growing
- set res [lsort -unique [concat $l1 $l2]]
- for {set i 0} {$i < [llength $res]} {incr i} {
- set x [lindex $res $i]
- for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
- set y [lindex $res $j]
- if {[info exists tagisdesc($x,$y)]} {
- if {$tagisdesc($x,$y) > 0} {
- # x is a descendent of y, exclude x
- set res [lreplace $res $i $i]
- incr i -1
- break
- } else {
- # y is a descendent of x, exclude y
- set res [lreplace $res $j $j]
- }
- } else {
- # no relation, keep going
- incr j
+ set a $arcnos($p)
+ if {[llength $a] != 1} {
+ puts "oops splitarc called but [llength $a] arcs already"
+ return
+ }
+ set a [lindex $a 0]
+ set i [lsearch -exact $arcids($a) $p]
+ if {$i < 0} {
+ puts "oops splitarc $p not in arc $a"
+ return
+ }
+ set na [incr nextarc]
+ if {[info exists arcend($a)]} {
+ set arcend($na) $arcend($a)
+ } else {
+ set l [lindex $allparents([lindex $arcids($a) end]) 0]
+ set j [lsearch -exact $arcnos($l) $a]
+ set arcnos($l) [lreplace $arcnos($l) $j $j $na]
+ }
+ set tail [lrange $arcids($a) [expr {$i+1}] end]
+ set arcids($a) [lrange $arcids($a) 0 $i]
+ set arcend($a) $p
+ set arcstart($na) $p
+ set arcout($p) $na
+ set arcids($na) $tail
+ if {[info exists growing($a)]} {
+ set growing($na) 1
+ unset growing($a)
+ }
+ incr nbmp
+
+ foreach id $tail {
+ if {[llength $arcnos($id)] == 1} {
+ set arcnos($id) $na
+ } else {
+ set j [lsearch -exact $arcnos($id) $a]
+ set arcnos($id) [lreplace $arcnos($id) $j $j $na]
+ }
+ }
+
+ # reconstruct tags and heads lists
+ if {$arctags($a) ne {} || $archeads($a) ne {}} {
+ recalcarc $a
+ recalcarc $na
+ } else {
+ set arctags($na) {}
+ set archeads($na) {}
+ }
+}
+
+# Update things for a new commit added that is a child of one
+# existing commit. Used when cherry-picking.
+proc addnewchild {id p} {
+ global allids allparents allchildren idtags nextarc nbmp
+ global arcnos arcids arctags arcout arcend arcstart archeads growing
+ global seeds
+
+ lappend allids $id
+ set allparents($id) [list $p]
+ set allchildren($id) {}
+ set arcnos($id) {}
+ lappend seeds $id
+ incr nbmp
+ lappend allchildren($p) $id
+ set a [incr nextarc]
+ set arcstart($a) $id
+ set archeads($a) {}
+ set arctags($a) {}
+ set arcids($a) [list $p]
+ set arcend($a) $p
+ if {![info exists arcout($p)]} {
+ splitarc $p
+ }
+ lappend arcnos($p) $a
+ set arcout($id) [list $a]
+}
+
+# Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
+# or 0 if neither is true.
+proc anc_or_desc {a b} {
+ global arcout arcstart arcend arcnos cached_isanc
+
+ if {$arcnos($a) eq $arcnos($b)} {
+ # Both are on the same arc(s); either both are the same BMP,
+ # or if one is not a BMP, the other is also not a BMP or is
+ # the BMP at end of the arc (and it only has 1 incoming arc).
+ # Or both can be BMPs with no incoming arcs.
+ if {$a eq $b || $arcnos($a) eq {}} {
+ return 0
+ }
+ # assert {[llength $arcnos($a)] == 1}
+ set arc [lindex $arcnos($a) 0]
+ set i [lsearch -exact $arcids($arc) $a]
+ set j [lsearch -exact $arcids($arc) $b]
+ if {$i < 0 || $i > $j} {
+ return 1
+ } else {
+ return -1
+ }
+ }
+
+ if {![info exists arcout($a)]} {
+ set arc [lindex $arcnos($a) 0]
+ if {[info exists arcend($arc)]} {
+ set aend $arcend($arc)
+ } else {
+ set aend {}
+ }
+ set a $arcstart($arc)
+ } else {
+ set aend $a
+ }
+ if {![info exists arcout($b)]} {
+ set arc [lindex $arcnos($b) 0]
+ if {[info exists arcend($arc)]} {
+ set bend $arcend($arc)
+ } else {
+ set bend {}
+ }
+ set b $arcstart($arc)
+ } else {
+ set bend $b
+ }
+ if {$a eq $bend} {
+ return 1
+ }
+ if {$b eq $aend} {
+ return -1
+ }
+ if {[info exists cached_isanc($a,$bend)]} {
+ if {$cached_isanc($a,$bend)} {
+ return 1
+ }
+ }
+ if {[info exists cached_isanc($b,$aend)]} {
+ if {$cached_isanc($b,$aend)} {
+ return -1
+ }
+ if {[info exists cached_isanc($a,$bend)]} {
+ return 0
+ }
+ }
+
+ set todo [list $a $b]
+ set anc($a) a
+ set anc($b) b
+ for {set i 0} {$i < [llength $todo]} {incr i} {
+ set x [lindex $todo $i]
+ if {$anc($x) eq {}} {
+ continue
+ }
+ foreach arc $arcnos($x) {
+ set xd $arcstart($arc)
+ if {$xd eq $bend} {
+ set cached_isanc($a,$bend) 1
+ set cached_isanc($b,$aend) 0
+ return 1
+ } elseif {$xd eq $aend} {
+ set cached_isanc($b,$aend) 1
+ set cached_isanc($a,$bend) 0
+ return -1
+ }
+ if {![info exists anc($xd)]} {
+ set anc($xd) $anc($x)
+ lappend todo $xd
+ } elseif {$anc($xd) ne $anc($x)} {
+ set anc($xd) {}
}
}
}
- return $res
+ set cached_isanc($a,$bend) 0
+ set cached_isanc($b,$aend) 0
+ return 0
}
-proc combine_atags {l1 l2} {
- global tagisdesc
-
- set res [lsort -unique [concat $l1 $l2]]
- for {set i 0} {$i < [llength $res]} {incr i} {
- set x [lindex $res $i]
- for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
- set y [lindex $res $j]
- if {[info exists tagisdesc($x,$y)]} {
- if {$tagisdesc($x,$y) < 0} {
- # x is an ancestor of y, exclude x
- set res [lreplace $res $i $i]
- incr i -1
- break
- } else {
- # y is an ancestor of x, exclude y
- set res [lreplace $res $j $j]
+# This identifies whether $desc has an ancestor that is
+# a growing tip of the graph and which is not an ancestor of $anc
+# and returns 0 if so and 1 if not.
+# If we subsequently discover a tag on such a growing tip, and that
+# turns out to be a descendent of $anc (which it could, since we
+# don't necessarily see children before parents), then $desc
+# isn't a good choice to display as a descendent tag of
+# $anc (since it is the descendent of another tag which is
+# a descendent of $anc). Similarly, $anc isn't a good choice to
+# display as a ancestor tag of $desc.
+#
+proc is_certain {desc anc} {
+ global arcnos arcout arcstart arcend growing problems
+
+ set certain {}
+ if {[llength $arcnos($anc)] == 1} {
+ # tags on the same arc are certain
+ if {$arcnos($desc) eq $arcnos($anc)} {
+ return 1
+ }
+ if {![info exists arcout($anc)]} {
+ # if $anc is partway along an arc, use the start of the arc instead
+ set a [lindex $arcnos($anc) 0]
+ set anc $arcstart($a)
+ }
+ }
+ if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
+ set x $desc
+ } else {
+ set a [lindex $arcnos($desc) 0]
+ set x $arcend($a)
+ }
+ if {$x == $anc} {
+ return 1
+ }
+ set anclist [list $x]
+ set dl($x) 1
+ set nnh 1
+ set ngrowanc 0
+ for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
+ set x [lindex $anclist $i]
+ if {$dl($x)} {
+ incr nnh -1
+ }
+ set done($x) 1
+ foreach a $arcout($x) {
+ if {[info exists growing($a)]} {
+ if {![info exists growanc($x)] && $dl($x)} {
+ set growanc($x) 1
+ incr ngrowanc
}
} else {
- # no relation, keep going
- incr j
+ set y $arcend($a)
+ if {[info exists dl($y)]} {
+ if {$dl($y)} {
+ if {!$dl($x)} {
+ set dl($y) 0
+ if {![info exists done($y)]} {
+ incr nnh -1
+ }
+ if {[info exists growanc($x)]} {
+ incr ngrowanc -1
+ }
+ set xl [list $y]
+ for {set k 0} {$k < [llength $xl]} {incr k} {
+ set z [lindex $xl $k]
+ foreach c $arcout($z) {
+ if {[info exists arcend($c)]} {
+ set v $arcend($c)
+ if {[info exists dl($v)] && $dl($v)} {
+ set dl($v) 0
+ if {![info exists done($v)]} {
+ incr nnh -1
+ }
+ if {[info exists growanc($v)]} {
+ incr ngrowanc -1
+ }
+ lappend xl $v
+ }
+ }
+ }
+ }
+ }
+ }
+ } elseif {$y eq $anc || !$dl($x)} {
+ set dl($y) 0
+ lappend anclist $y
+ } else {
+ set dl($y) 1
+ lappend anclist $y
+ incr nnh
+ }
}
}
}
- return $res
+ foreach x [array names growanc] {
+ if {$dl($x)} {
+ return 0
+ }
+ return 0
+ }
+ return 1
}
-proc forward_pass {id children} {
- global idtags desc_tags idheads desc_heads alldtags tagisdesc
+proc validate_arctags {a} {
+ global arctags idtags
- set dtags {}
- set dheads {}
- foreach child $children {
- if {[info exists idtags($child)]} {
- set ctags [list $child]
- } else {
- set ctags $desc_tags($child)
+ set i -1
+ set na $arctags($a)
+ foreach id $arctags($a) {
+ incr i
+ if {![info exists idtags($id)]} {
+ set na [lreplace $na $i $i]
+ incr i -1
+ }
+ }
+ set arctags($a) $na
+}
+
+proc validate_archeads {a} {
+ global archeads idheads
+
+ set i -1
+ set na $archeads($a)
+ foreach id $archeads($a) {
+ incr i
+ if {![info exists idheads($id)]} {
+ set na [lreplace $na $i $i]
+ incr i -1
+ }
+ }
+ set archeads($a) $na
+}
+
+# Return the list of IDs that have tags that are descendents of id,
+# ignoring IDs that are descendents of IDs already reported.
+proc desctags {id} {
+ global arcnos arcstart arcids arctags idtags allparents
+ global growing cached_dtags
+
+ if {![info exists allparents($id)]} {
+ return {}
+ }
+ set t1 [clock clicks -milliseconds]
+ set argid $id
+ if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
+ # part-way along an arc; check that arc first
+ set a [lindex $arcnos($id) 0]
+ if {$arctags($a) ne {}} {
+ validate_arctags $a
+ set i [lsearch -exact $arcids($a) $id]
+ set tid {}
+ foreach t $arctags($a) {
+ set j [lsearch -exact $arcids($a) $t]
+ if {$j >= $i} break
+ set tid $t
+ }
+ if {$tid ne {}} {
+ return $tid
+ }
}
- if {$dtags eq {}} {
- set dtags $ctags
- } elseif {$ctags ne $dtags} {
- set dtags [combine_dtags $dtags $ctags]
+ set id $arcstart($a)
+ if {[info exists idtags($id)]} {
+ return $id
+ }
+ }
+ if {[info exists cached_dtags($id)]} {
+ return $cached_dtags($id)
+ }
+
+ set origid $id
+ set todo [list $id]
+ set queued($id) 1
+ set nc 1
+ for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
+ set id [lindex $todo $i]
+ set done($id) 1
+ set ta [info exists hastaggedancestor($id)]
+ if {!$ta} {
+ incr nc -1
+ }
+ # ignore tags on starting node
+ if {!$ta && $i > 0} {
+ if {[info exists idtags($id)]} {
+ set tagloc($id) $id
+ set ta 1
+ } elseif {[info exists cached_dtags($id)]} {
+ set tagloc($id) $cached_dtags($id)
+ set ta 1
+ }
}
- set cheads $desc_heads($child)
- if {$dheads eq {}} {
- set dheads $cheads
- } elseif {$cheads ne $dheads} {
- set dheads [lsort -unique [concat $dheads $cheads]]
+ foreach a $arcnos($id) {
+ set d $arcstart($a)
+ if {!$ta && $arctags($a) ne {}} {
+ validate_arctags $a
+ if {$arctags($a) ne {}} {
+ lappend tagloc($id) [lindex $arctags($a) end]
+ }
+ }
+ if {$ta || $arctags($a) ne {}} {
+ set tomark [list $d]
+ for {set j 0} {$j < [llength $tomark]} {incr j} {
+ set dd [lindex $tomark $j]
+ if {![info exists hastaggedancestor($dd)]} {
+ if {[info exists done($dd)]} {
+ foreach b $arcnos($dd) {
+ lappend tomark $arcstart($b)
+ }
+ if {[info exists tagloc($dd)]} {
+ unset tagloc($dd)
+ }
+ } elseif {[info exists queued($dd)]} {
+ incr nc -1
+ }
+ set hastaggedancestor($dd) 1
+ }
+ }
+ }
+ if {![info exists queued($d)]} {
+ lappend todo $d
+ set queued($d) 1
+ if {![info exists hastaggedancestor($d)]} {
+ incr nc
+ }
+ }
}
}
- set desc_tags($id) $dtags
- if {[info exists idtags($id)]} {
- set adt $dtags
- foreach tag $dtags {
- set adt [concat $adt $alldtags($tag)]
+ set tags {}
+ foreach id [array names tagloc] {
+ if {![info exists hastaggedancestor($id)]} {
+ foreach t $tagloc($id) {
+ if {[lsearch -exact $tags $t] < 0} {
+ lappend tags $t
+ }
+ }
+ }
+ }
+ set t2 [clock clicks -milliseconds]
+ set loopix $i
+
+ # remove tags that are descendents of other tags
+ for {set i 0} {$i < [llength $tags]} {incr i} {
+ set a [lindex $tags $i]
+ for {set j 0} {$j < $i} {incr j} {
+ set b [lindex $tags $j]
+ set r [anc_or_desc $a $b]
+ if {$r == 1} {
+ set tags [lreplace $tags $j $j]
+ incr j -1
+ incr i -1
+ } elseif {$r == -1} {
+ set tags [lreplace $tags $i $i]
+ incr i -1
+ break
+ }
+ }
+ }
+
+ if {[array names growing] ne {}} {
+ # graph isn't finished, need to check if any tag could get
+ # eclipsed by another tag coming later. Simply ignore any
+ # tags that could later get eclipsed.
+ set ctags {}
+ foreach t $tags {
+ if {[is_certain $t $origid]} {
+ lappend ctags $t
+ }
}
- set adt [lsort -unique $adt]
- set alldtags($id) $adt
- foreach tag $adt {
- set tagisdesc($id,$tag) -1
- set tagisdesc($tag,$id) 1
+ if {$tags eq $ctags} {
+ set cached_dtags($origid) $tags
+ } else {
+ set tags $ctags
}
+ } else {
+ set cached_dtags($origid) $tags
}
- if {[info exists idheads($id)]} {
- set dheads [concat $dheads $idheads($id)]
+ set t3 [clock clicks -milliseconds]
+ if {0 && $t3 - $t1 >= 100} {
+ puts "iterating descendents ($loopix/[llength $todo] nodes) took\
+ [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
}
- set desc_heads($id) $dheads
+ return $tags
}
-proc getallclines {fd} {
- global allparents allchildren allcommits allcstart
- global desc_tags anc_tags idtags tagisdesc allids
- global idheads travindex
-
- while {[gets $fd line] >= 0} {
- set id [lindex $line 0]
- lappend allids $id
- set olds [lrange $line 1 end]
- set allparents($id) $olds
- if {![info exists allchildren($id)]} {
- set allchildren($id) {}
+proc anctags {id} {
+ global arcnos arcids arcout arcend arctags idtags allparents
+ global growing cached_atags
+
+ if {![info exists allparents($id)]} {
+ return {}
+ }
+ set t1 [clock clicks -milliseconds]
+ set argid $id
+ if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
+ # part-way along an arc; check that arc first
+ set a [lindex $arcnos($id) 0]
+ if {$arctags($a) ne {}} {
+ validate_arctags $a
+ set i [lsearch -exact $arcids($a) $id]
+ foreach t $arctags($a) {
+ set j [lsearch -exact $arcids($a) $t]
+ if {$j > $i} {
+ return $t
+ }
+ }
}
- foreach p $olds {
- lappend allchildren($p) $id
+ if {![info exists arcend($a)]} {
+ return {}
+ }
+ set id $arcend($a)
+ if {[info exists idtags($id)]} {
+ return $id
+ }
+ }
+ if {[info exists cached_atags($id)]} {
+ return $cached_atags($id)
+ }
+
+ set origid $id
+ set todo [list $id]
+ set queued($id) 1
+ set taglist {}
+ set nc 1
+ for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
+ set id [lindex $todo $i]
+ set done($id) 1
+ set td [info exists hastaggeddescendent($id)]
+ if {!$td} {
+ incr nc -1
+ }
+ # ignore tags on starting node
+ if {!$td && $i > 0} {
+ if {[info exists idtags($id)]} {
+ set tagloc($id) $id
+ set td 1
+ } elseif {[info exists cached_atags($id)]} {
+ set tagloc($id) $cached_atags($id)
+ set td 1
+ }
}
- # compute nearest tagged descendents as we go
- # also compute descendent heads
- forward_pass $id $allchildren($id)
- if {[clock clicks -milliseconds] - $allcstart >= 50} {
- fileevent $fd readable {}
- after idle restartgetall $fd
- return
+ foreach a $arcout($id) {
+ if {!$td && $arctags($a) ne {}} {
+ validate_arctags $a
+ if {$arctags($a) ne {}} {
+ lappend tagloc($id) [lindex $arctags($a) 0]
+ }
+ }
+ if {![info exists arcend($a)]} continue
+ set d $arcend($a)
+ if {$td || $arctags($a) ne {}} {
+ set tomark [list $d]
+ for {set j 0} {$j < [llength $tomark]} {incr j} {
+ set dd [lindex $tomark $j]
+ if {![info exists hastaggeddescendent($dd)]} {
+ if {[info exists done($dd)]} {
+ foreach b $arcout($dd) {
+ if {[info exists arcend($b)]} {
+ lappend tomark $arcend($b)
+ }
+ }
+ if {[info exists tagloc($dd)]} {
+ unset tagloc($dd)
+ }
+ } elseif {[info exists queued($dd)]} {
+ incr nc -1
+ }
+ set hastaggeddescendent($dd) 1
+ }
+ }
+ }
+ if {![info exists queued($d)]} {
+ lappend todo $d
+ set queued($d) 1
+ if {![info exists hastaggeddescendent($d)]} {
+ incr nc
+ }
+ }
}
}
- if {[eof $fd]} {
- set travindex [llength $allids]
- set allcommits "traversing"
- after idle restartatags
- if {[catch {close $fd} err]} {
- error_popup "Error reading full commit graph: $err.\n\
- Results may be incomplete."
+ set t2 [clock clicks -milliseconds]
+ set loopix $i
+ set tags {}
+ foreach id [array names tagloc] {
+ if {![info exists hastaggeddescendent($id)]} {
+ foreach t $tagloc($id) {
+ if {[lsearch -exact $tags $t] < 0} {
+ lappend tags $t
+ }
+ }
}
}
-}
-
-# walk backward through the tree and compute nearest tagged ancestors
-proc restartatags {} {
- global allids allparents idtags anc_tags travindex
- set t0 [clock clicks -milliseconds]
- set i $travindex
- while {[incr i -1] >= 0} {
- set id [lindex $allids $i]
- set atags {}
- foreach p $allparents($id) {
- if {[info exists idtags($p)]} {
- set ptags [list $p]
- } else {
- set ptags $anc_tags($p)
+ # remove tags that are ancestors of other tags
+ for {set i 0} {$i < [llength $tags]} {incr i} {
+ set a [lindex $tags $i]
+ for {set j 0} {$j < $i} {incr j} {
+ set b [lindex $tags $j]
+ set r [anc_or_desc $a $b]
+ if {$r == -1} {
+ set tags [lreplace $tags $j $j]
+ incr j -1
+ incr i -1
+ } elseif {$r == 1} {
+ set tags [lreplace $tags $i $i]
+ incr i -1
+ break
}
- if {$atags eq {}} {
- set atags $ptags
- } elseif {$ptags ne $atags} {
- set atags [combine_atags $atags $ptags]
+ }
+ }
+
+ if {[array names growing] ne {}} {
+ # graph isn't finished, need to check if any tag could get
+ # eclipsed by another tag coming later. Simply ignore any
+ # tags that could later get eclipsed.
+ set ctags {}
+ foreach t $tags {
+ if {[is_certain $origid $t]} {
+ lappend ctags $t
}
}
- set anc_tags($id) $atags
- if {[clock clicks -milliseconds] - $t0 >= 50} {
- set travindex $i
- after idle restartatags
- return
+ if {$tags eq $ctags} {
+ set cached_atags($origid) $tags
+ } else {
+ set tags $ctags
}
+ } else {
+ set cached_atags($origid) $tags
+ }
+ set t3 [clock clicks -milliseconds]
+ if {0 && $t3 - $t1 >= 100} {
+ puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
+ [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
}
- set allcommits "done"
- set travindex 0
- notbusy allcommits
- dispneartags
+ return $tags
}
-# update the desc_tags and anc_tags arrays for a new tag just added
-proc addedtag {id} {
- global desc_tags anc_tags allparents allchildren allcommits
- global idtags tagisdesc alldtags
-
- if {![info exists desc_tags($id)]} return
- set adt $desc_tags($id)
- foreach t $desc_tags($id) {
- set adt [concat $adt $alldtags($t)]
- }
- set adt [lsort -unique $adt]
- set alldtags($id) $adt
- foreach t $adt {
- set tagisdesc($id,$t) -1
- set tagisdesc($t,$id) 1
- }
- if {[info exists anc_tags($id)]} {
- set todo $anc_tags($id)
- while {$todo ne {}} {
- set do [lindex $todo 0]
- set todo [lrange $todo 1 end]
- if {[info exists tagisdesc($id,$do)]} continue
- set tagisdesc($do,$id) -1
- set tagisdesc($id,$do) 1
- if {[info exists anc_tags($do)]} {
- set todo [concat $todo $anc_tags($do)]
- }
- }
- }
-
- set lastold $desc_tags($id)
- set lastnew [list $id]
- set nup 0
- set nch 0
- set todo $allparents($id)
- while {$todo ne {}} {
- set do [lindex $todo 0]
- set todo [lrange $todo 1 end]
- if {![info exists desc_tags($do)]} continue
- if {$desc_tags($do) ne $lastold} {
- set lastold $desc_tags($do)
- set lastnew [combine_dtags $lastold [list $id]]
- incr nch
- }
- if {$lastold eq $lastnew} continue
- set desc_tags($do) $lastnew
- incr nup
- if {![info exists idtags($do)]} {
- set todo [concat $todo $allparents($do)]
- }
- }
-
- if {![info exists anc_tags($id)]} return
- set lastold $anc_tags($id)
- set lastnew [list $id]
- set nup 0
- set nch 0
- set todo $allchildren($id)
- while {$todo ne {}} {
- set do [lindex $todo 0]
- set todo [lrange $todo 1 end]
- if {![info exists anc_tags($do)]} continue
- if {$anc_tags($do) ne $lastold} {
- set lastold $anc_tags($do)
- set lastnew [combine_atags $lastold [list $id]]
- incr nch
- }
- if {$lastold eq $lastnew} continue
- set anc_tags($do) $lastnew
- incr nup
- if {![info exists idtags($do)]} {
- set todo [concat $todo $allchildren($do)]
- }
- }
-}
-
-# update the desc_heads array for a new head just added
-proc addedhead {hid head} {
- global desc_heads allparents headids idheads
-
- set headids($head) $hid
- lappend idheads($hid) $head
-
- set todo [list $hid]
- while {$todo ne {}} {
- set do [lindex $todo 0]
- set todo [lrange $todo 1 end]
- if {![info exists desc_heads($do)] ||
- [lsearch -exact $desc_heads($do) $head] >= 0} continue
- set oldheads $desc_heads($do)
- lappend desc_heads($do) $head
- set heads $desc_heads($do)
- while {1} {
- set p $allparents($do)
- if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
- $desc_heads($p) ne $oldheads} break
- set do $p
- set desc_heads($do) $heads
+# Return the list of IDs that have heads that are descendents of id,
+# including id itself if it has a head.
+proc descheads {id} {
+ global arcnos arcstart arcids archeads idheads cached_dheads
+ global allparents
+
+ if {![info exists allparents($id)]} {
+ return {}
+ }
+ set aret {}
+ if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
+ # part-way along an arc; check it first
+ set a [lindex $arcnos($id) 0]
+ if {$archeads($a) ne {}} {
+ validate_archeads $a
+ set i [lsearch -exact $arcids($a) $id]
+ foreach t $archeads($a) {
+ set j [lsearch -exact $arcids($a) $t]
+ if {$j > $i} break
+ lappend aret $t
+ }
}
- set todo [concat $todo $p]
+ set id $arcstart($a)
}
+ set origid $id
+ set todo [list $id]
+ set seen($id) 1
+ set ret {}
+ for {set i 0} {$i < [llength $todo]} {incr i} {
+ set id [lindex $todo $i]
+ if {[info exists cached_dheads($id)]} {
+ set ret [concat $ret $cached_dheads($id)]
+ } else {
+ if {[info exists idheads($id)]} {
+ lappend ret $id
+ }
+ foreach a $arcnos($id) {
+ if {$archeads($a) ne {}} {
+ validate_archeads $a
+ if {$archeads($a) ne {}} {
+ set ret [concat $ret $archeads($a)]
+ }
+ }
+ set d $arcstart($a)
+ if {![info exists seen($d)]} {
+ lappend todo $d
+ set seen($d) 1
+ }
+ }
+ }
+ }
+ set ret [lsort -unique $ret]
+ set cached_dheads($origid) $ret
+ return [concat $ret $aret]
}
-# update the desc_heads array for a head just removed
-proc removedhead {hid head} {
- global desc_heads allparents headids idheads
+proc addedtag {id} {
+ global arcnos arcout cached_dtags cached_atags
- unset headids($head)
- if {$idheads($hid) eq $head} {
- unset idheads($hid)
- } else {
- set i [lsearch -exact $idheads($hid) $head]
- if {$i >= 0} {
- set idheads($hid) [lreplace $idheads($hid) $i $i]
- }
+ if {![info exists arcnos($id)]} return
+ if {![info exists arcout($id)]} {
+ recalcarc [lindex $arcnos($id) 0]
}
+ catch {unset cached_dtags}
+ catch {unset cached_atags}
+}
- set todo [list $hid]
- while {$todo ne {}} {
- set do [lindex $todo 0]
- set todo [lrange $todo 1 end]
- if {![info exists desc_heads($do)]} continue
- set i [lsearch -exact $desc_heads($do) $head]
- if {$i < 0} continue
- set oldheads $desc_heads($do)
- set heads [lreplace $desc_heads($do) $i $i]
- while {1} {
- set desc_heads($do) $heads
- set p $allparents($do)
- if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
- $desc_heads($p) ne $oldheads} break
- set do $p
- }
- set todo [concat $todo $p]
+proc addedhead {hid head} {
+ global arcnos arcout cached_dheads
+
+ if {![info exists arcnos($hid)]} return
+ if {![info exists arcout($hid)]} {
+ recalcarc [lindex $arcnos($hid) 0]
}
+ catch {unset cached_dheads}
}
-# update things for a head moved to a child of its previous location
-proc movedhead {id name} {
- global headids idheads
+proc removedhead {hid head} {
+ global cached_dheads
- set oldid $headids($name)
- set headids($name) $id
- if {$idheads($oldid) eq $name} {
- unset idheads($oldid)
- } else {
- set i [lsearch -exact $idheads($oldid) $name]
- if {$i >= 0} {
- set idheads($oldid) [lreplace $idheads($oldid) $i $i]
- }
- }
- lappend idheads($id) $name
+ catch {unset cached_dheads}
}
-proc changedrefs {} {
- global desc_heads desc_tags anc_tags allcommits allids
- global allchildren allparents idtags travindex
+proc movedhead {hid head} {
+ global arcnos arcout cached_dheads
- if {![info exists allcommits]} return
- catch {unset desc_heads}
- catch {unset desc_tags}
- catch {unset anc_tags}
- catch {unset alldtags}
- catch {unset tagisdesc}
- foreach id $allids {
- forward_pass $id $allchildren($id)
+ if {![info exists arcnos($hid)]} return
+ if {![info exists arcout($hid)]} {
+ recalcarc [lindex $arcnos($hid) 0]
}
- if {$allcommits ne "reading"} {
- set travindex [llength $allids]
- if {$allcommits ne "traversing"} {
- set allcommits "traversing"
- after idle restartatags
+ catch {unset cached_dheads}
+}
+
+proc changedrefs {} {
+ global cached_dheads cached_dtags cached_atags
+ global arctags archeads arcnos arcout idheads idtags
+
+ foreach id [concat [array names idheads] [array names idtags]] {
+ if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
+ set a [lindex $arcnos($id) 0]
+ if {![info exists donearc($a)]} {
+ recalcarc $a
+ set donearc($a) 1
+ }
}
}
+ catch {unset cached_dtags}
+ catch {unset cached_atags}
+ catch {unset cached_dheads}
}
proc rereadrefs {} {
@@ -5817,7 +7113,7 @@ proc listrefs {id} {
}
proc showtag {tag isnew} {
- global ctext tagcontents tagids linknum
+ global ctext tagcontents tagids linknum tagobjid
if {$isnew} {
addtohistory [list showtag $tag 0]
@@ -5825,6 +7121,11 @@ proc showtag {tag isnew} {
$ctext conf -state normal
clear_ctext
set linknum 0
+ if {![info exists tagcontents($tag)]} {
+ catch {
+ set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
+ }
+ }
if {[info exists tagcontents($tag)]} {
set text $tagcontents($tag)
} else {
@@ -5844,9 +7145,9 @@ proc doquit {} {
proc doprefs {} {
global maxwidth maxgraphpct diffopts
- global oldprefs prefstop showneartags
- global bgcolor fgcolor ctext diffcolors
- global uifont
+ global oldprefs prefstop showneartags showlocalchanges
+ global bgcolor fgcolor ctext diffcolors selectbgcolor
+ global uifont tabstop
set top .gitkprefs
set prefstop $top
@@ -5854,7 +7155,7 @@ proc doprefs {} {
raise $top
return
}
- foreach v {maxwidth maxgraphpct diffopts showneartags} {
+ foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} {
set oldprefs($v) [set $v]
}
toplevel $top
@@ -5871,6 +7172,11 @@ proc doprefs {} {
-font optionfont
spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
grid x $top.maxpctl $top.maxpct -sticky w
+ frame $top.showlocal
+ label $top.showlocal.l -text "Show local changes" -font optionfont
+ checkbutton $top.showlocal.b -variable showlocalchanges
+ pack $top.showlocal.b $top.showlocal.l -side left
+ grid x $top.showlocal -sticky w
label $top.ddisp -text "Diff display options"
$top.ddisp configure -font $uifont
@@ -5884,6 +7190,9 @@ proc doprefs {} {
checkbutton $top.ntag.b -variable showneartags
pack $top.ntag.b $top.ntag.l -side left
grid x $top.ntag -sticky w
+ label $top.tabstopl -text "tabstop" -font optionfont
+ spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
+ grid x $top.tabstopl $top.tabstop -sticky w
label $top.cdisp -text "Colors: press to choose"
$top.cdisp configure -font $uifont
@@ -5912,6 +7221,10 @@ proc doprefs {} {
"diff hunk header" \
[list $ctext tag conf hunksep -foreground]]
grid x $top.hunksepbut $top.hunksep -sticky w
+ label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
+ button $top.selbgbut -text "Select bg" -font optionfont \
+ -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg]
+ grid x $top.selbgbut $top.selbgsep -sticky w
frame $top.buts
button $top.buts.ok -text "OK" -command prefsok -default active
@@ -5936,6 +7249,16 @@ proc choosecolor {v vi w x cmd} {
eval $cmd $c
}
+proc setselbg {c} {
+ global bglist cflist
+ foreach w $bglist {
+ $w configure -selectbackground $c
+ }
+ $cflist tag configure highlight \
+ -background [$cflist cget -selectbackground]
+ allcanvs itemconf secsel -fill $c
+}
+
proc setbg {c} {
global bglist
@@ -5956,9 +7279,9 @@ proc setfg {c} {
proc prefscan {} {
global maxwidth maxgraphpct diffopts
- global oldprefs prefstop showneartags
+ global oldprefs prefstop showneartags showlocalchanges
- foreach v {maxwidth maxgraphpct diffopts showneartags} {
+ foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} {
set $v $oldprefs($v)
}
catch {destroy $prefstop}
@@ -5967,10 +7290,19 @@ proc prefscan {} {
proc prefsok {} {
global maxwidth maxgraphpct
- global oldprefs prefstop showneartags
+ global oldprefs prefstop showneartags showlocalchanges
+ global charspc ctext tabstop
catch {destroy $prefstop}
unset prefstop
+ $ctext configure -tabs "[expr {$tabstop * $charspc}]"
+ if {$showlocalchanges != $oldprefs(showlocalchanges)} {
+ if {$showlocalchanges} {
+ doshowlocalchanges
+ } else {
+ dohidelocalchanges
+ }
+ }
if {$maxwidth != $oldprefs(maxwidth)
|| $maxgraphpct != $oldprefs(maxgraphpct)} {
redisplay
@@ -5980,7 +7312,10 @@ proc prefsok {} {
}
proc formatdate {d} {
- return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+ if {$d ne {}} {
+ set d [clock format $d -format "%Y-%m-%d %H:%M:%S"]
+ }
+ return $d
}
# This list of encoding names and aliases is distilled from
@@ -6276,6 +7611,7 @@ if {$tclencoding == {}} {
set mainfont {Helvetica 9}
set textfont {Courier 9}
set uifont {Helvetica 9 bold}
+set tabstop 8
set findmergefiles 0
set maxgraphpct 50
set maxwidth 16
@@ -6287,45 +7623,65 @@ set mingaplen 30
set cmitmode "patch"
set wrapcomment "none"
set showneartags 1
+set maxrefs 20
+set maxlinelen 200
+set showlocalchanges 1
set colors {green red blue magenta darkgrey brown orange}
set bgcolor white
set fgcolor black
set diffcolors {red "#00a000" blue}
+set selectbgcolor gray85
catch {source ~/.gitk}
font create optionfont -family sans-serif -size -12
+# check that we can find a .git directory somewhere...
+if {[catch {set gitdir [gitdir]}]} {
+ show_error {} . "Cannot find a git repository here."
+ exit 1
+}
+if {![file isdirectory $gitdir]} {
+ show_error {} . "Cannot find the git directory \"$gitdir\"."
+ exit 1
+}
+
set revtreeargs {}
+set cmdline_files {}
+set i 0
foreach arg $argv {
- switch -regexp -- $arg {
- "^$" { }
- "^-d" { set datemode 1 }
+ switch -- $arg {
+ "" { }
+ "-d" { set datemode 1 }
+ "--" {
+ set cmdline_files [lrange $argv [expr {$i + 1}] end]
+ break
+ }
default {
lappend revtreeargs $arg
}
}
+ incr i
}
-# check that we can find a .git directory somewhere...
-set gitdir [gitdir]
-if {![file isdirectory $gitdir]} {
- show_error {} . "Cannot find the git directory \"$gitdir\"."
- exit 1
-}
-
-set cmdline_files {}
-set i [lsearch -exact $revtreeargs "--"]
-if {$i >= 0} {
- set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
- set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
-} elseif {$revtreeargs ne {}} {
+if {$i >= [llength $argv] && $revtreeargs ne {}} {
+ # no -- on command line, but some arguments (other than -d)
if {[catch {
set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
set cmdline_files [split $f "\n"]
set n [llength $cmdline_files]
set revtreeargs [lrange $revtreeargs 0 end-$n]
+ # Unfortunately git rev-parse doesn't produce an error when
+ # something is both a revision and a filename. To be consistent
+ # with git log and git rev-list, check revtreeargs for filenames.
+ foreach arg $revtreeargs {
+ if {[file exists $arg]} {
+ show_error {} . "Ambiguous argument '$arg': both revision\
+ and filename"
+ exit 1
+ }
+ }
} err]} {
# unfortunately we get both stdout and stderr in $err,
# so look for "fatal:".
@@ -6338,6 +7694,11 @@ if {$i >= 0} {
}
}
+set nullid "0000000000000000000000000000000000000000"
+set nullid2 "0000000000000000000000000000000000000001"
+
+
+set runq {}
set history {}
set historyindex 0
set fh_serial 0
@@ -6347,6 +7708,7 @@ set searchdirn -forwards
set boldrows {}
set boldnamerows {}
set diffelide {0 0}
+set markingmatches 0
set optim_delay 16
@@ -6362,8 +7724,14 @@ set cmdlineok 0
set stopped 0
set stuffsaved 0
set patchnum 0
+set lookingforhead 0
+set localirow -1
+set localfrow -1
+set lserial 0
setcoords
makewindow
+# wait for the window to become visible
+tkwait visibility .
wm title . "[file tail $argv0]: [file tail [pwd]]"
readrefs
diff --git a/gitweb/README b/gitweb/README
index e02e90f042..7186cede2f 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -79,4 +79,3 @@ Originally written by:
Any comment/question/concern to:
Git mailing list <git@vger.kernel.org>
-
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 9f0822fab3..096313bec0 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -181,10 +181,15 @@ table.diff_tree {
font-family: monospace;
}
+table.combined.diff_tree th {
+ text-align: center;
+}
+
table.combined.diff_tree td {
padding-right: 24px;
}
+table.combined.diff_tree th.link,
table.combined.diff_tree td.link {
padding: 0px 2px;
}
@@ -276,6 +281,12 @@ table.diff_tree span.file_status.copied {
color: #70a070;
}
+/* noage: "No commits" */
+table.project_list td.noage {
+ color: #808080;
+ font-style: italic;
+}
+
/* age2: 60*60*24*2 <= age */
table.project_list td.age2, table.blame td.age2 {
font-style: italic;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 5c7011a37b..f282a677aa 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -71,6 +71,9 @@ our $logo_label = "git homepage";
# source of projects list
our $projects_list = "++GITWEB_LIST++";
+# the width (in characters) of the projects list "Description" column
+our $projects_list_description_width = 25;
+
# default order of projects list
# valid values are none, project, descr, owner, and age
our $default_projects_order = "project";
@@ -94,6 +97,66 @@ our $default_text_plain_charset = undef;
# (relative to the current git repository)
our $mimetypes_file = undef;
+# assume this charset if line contains non-UTF-8 characters;
+# it should be valid encoding (see Encoding::Supported(3pm) for list),
+# for which encoding all byte sequences are valid, for example
+# 'iso-8859-1' aka 'latin1' (it is decoded without checking, so it
+# could be even 'utf-8' for the old behavior)
+our $fallback_encoding = 'latin1';
+
+# rename detection options for git-diff and git-diff-tree
+# - default is '-M', with the cost proportional to
+# (number of removed files) * (number of new files).
+# - more costly is '-C' (which implies '-M'), with the cost proportional to
+# (number of changed files + number of removed files) * (number of new files)
+# - even more costly is '-C', '--find-copies-harder' with cost
+# (number of files in the original tree) * (number of new files)
+# - one might want to include '-B' option, e.g. '-B', '-M'
+our @diff_opts = ('-M'); # taken from git_commit
+
+# information about snapshot formats that gitweb is capable of serving
+our %known_snapshot_formats = (
+ # name => {
+ # 'display' => display name,
+ # 'type' => mime type,
+ # 'suffix' => filename suffix,
+ # 'format' => --format for git-archive,
+ # 'compressor' => [compressor command and arguments]
+ # (array reference, optional)}
+ #
+ 'tgz' => {
+ 'display' => 'tar.gz',
+ 'type' => 'application/x-gzip',
+ 'suffix' => '.tar.gz',
+ 'format' => 'tar',
+ 'compressor' => ['gzip']},
+
+ 'tbz2' => {
+ 'display' => 'tar.bz2',
+ 'type' => 'application/x-bzip2',
+ 'suffix' => '.tar.bz2',
+ 'format' => 'tar',
+ 'compressor' => ['bzip2']},
+
+ 'zip' => {
+ 'display' => 'zip',
+ 'type' => 'application/x-zip',
+ 'suffix' => '.zip',
+ 'format' => 'zip'},
+);
+
+# Aliases so we understand old gitweb.snapshot values in repository
+# configuration.
+our %known_snapshot_format_aliases = (
+ 'gzip' => 'tgz',
+ 'bzip2' => 'tbz2',
+
+ # backward compatibility: legacy gitweb config support
+ 'x-gzip' => undef, 'gz' => undef,
+ 'x-bzip2' => undef, 'bz2' => undef,
+ 'x-zip' => undef, '' => undef,
+);
+
# You define site-wide feature defaults here; override them with
# $GITWEB_CONFIG as necessary.
our %feature = (
@@ -124,20 +187,22 @@ our %feature = (
'override' => 0,
'default' => [0]},
- # Enable the 'snapshot' link, providing a compressed tarball of any
+ # Enable the 'snapshot' link, providing a compressed archive of any
# tree. This can potentially generate high traffic if you have large
# project.
+ # Value is a list of formats defined in %known_snapshot_formats that
+ # you wish to offer.
# To disable system wide have in $GITWEB_CONFIG
- # $feature{'snapshot'}{'default'} = [undef];
+ # $feature{'snapshot'}{'default'} = [];
# To have project specific config enable override in $GITWEB_CONFIG
# $feature{'snapshot'}{'override'} = 1;
- # and in project config gitweb.snapshot = none|gzip|bzip2;
+ # and in project config, a comma-separated list of formats or "none"
+ # to disable. Example: gitweb.snapshot = tbz2,zip;
'snapshot' => {
'sub' => \&feature_snapshot,
'override' => 0,
- # => [content-encoding, suffix, program]
- 'default' => ['x-gzip', 'gz', 'gzip']},
+ 'default' => ['tgz']},
# Enable text search, which will list the commits which match author,
# committer or commit text to a given string. Enabled by default.
@@ -236,26 +301,15 @@ sub feature_blame {
}
sub feature_snapshot {
- my ($ctype, $suffix, $command) = @_;
+ my (@fmts) = @_;
my ($val) = git_get_project_config('snapshot');
- if ($val eq 'gzip') {
- return ('x-gzip', 'gz', 'gzip');
- } elsif ($val eq 'bzip2') {
- return ('x-bzip2', 'bz2', 'bzip2');
- } elsif ($val eq 'none') {
- return ();
+ if ($val) {
+ @fmts = ($val eq 'none' ? () : split /\s*[,\s]\s*/, $val);
}
- return ($ctype, $suffix, $command);
-}
-
-sub gitweb_have_snapshot {
- my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
- my $have_snapshot = (defined $ctype && defined $suffix);
-
- return $have_snapshot;
+ return @fmts;
}
sub feature_grep {
@@ -298,15 +352,17 @@ sub check_export_ok {
(!$export_ok || -e "$dir/$export_ok"));
}
-# rename detection options for git-diff and git-diff-tree
-# - default is '-M', with the cost proportional to
-# (number of removed files) * (number of new files).
-# - more costly is '-C' (or '-C', '-M'), with the cost proportional to
-# (number of changed files + number of removed files) * (number of new files)
-# - even more costly is '-C', '--find-copies-harder' with cost
-# (number of files in the original tree) * (number of new files)
-# - one might want to include '-B' option, e.g. '-B', '-M'
-our @diff_opts = ('-M'); # taken from git_commit
+# process alternate names for backward compatibility
+# filter out unsupported (unknown) snapshot formats
+sub filter_snapshot_fmts {
+ my @fmts = @_;
+
+ @fmts = map {
+ exists $known_snapshot_format_aliases{$_} ?
+ $known_snapshot_format_aliases{$_} : $_} @fmts;
+ @fmts = grep(exists $known_snapshot_formats{$_}, @fmts);
+
+}
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
@@ -374,6 +430,22 @@ if (defined $hash_base) {
}
}
+my %allowed_options = (
+ "--no-merges" => [ qw(rss atom log shortlog history) ],
+);
+
+our @extra_options = $cgi->param('opt');
+if (defined @extra_options) {
+ foreach my $opt (@extra_options) {
+ if (not exists $allowed_options{$opt}) {
+ die_error(undef, "Invalid option parameter");
+ }
+ if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
+ die_error(undef, "Invalid option parameter for this action");
+ }
+ }
+}
+
our $hash_parent_base = $cgi->param('hpb');
if (defined $hash_parent_base) {
if (!validate_refname($hash_parent_base)) {
@@ -533,6 +605,8 @@ sub href(%) {
order => "o",
searchtext => "s",
searchtype => "st",
+ snapshot_format => "sf",
+ extra_options => "opt",
);
my %mapping = @mapping;
@@ -555,7 +629,13 @@ sub href(%) {
for (my $i = 0; $i < @mapping; $i += 2) {
my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
if (defined $params{$name}) {
- push @result, $symbol . "=" . esc_param($params{$name});
+ if (ref($params{$name}) eq "ARRAY") {
+ foreach my $par (@{$params{$name}}) {
+ push @result, $symbol . "=" . esc_param($par);
+ }
+ } else {
+ push @result, $symbol . "=" . esc_param($params{$name});
+ }
}
}
$href .= "?" . join(';', @result) if scalar @result;
@@ -600,6 +680,20 @@ sub validate_refname {
return $input;
}
+# decode sequences of octets in utf8 into Perl's internal form,
+# which is utf-8 with utf8 flag set if needed. gitweb writes out
+# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
+sub to_utf8 {
+ my $str = shift;
+ my $res;
+ eval { $res = decode_utf8($str, Encode::FB_CROAK); };
+ if (defined $res) {
+ return $res;
+ } else {
+ return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
+ }
+}
+
# quote unsafe chars, but keep the slash, even when it's not
# correct, but quoted slashes look too horrible in bookmarks
sub esc_param {
@@ -624,7 +718,7 @@ sub esc_html ($;%) {
my $str = shift;
my %opts = @_;
- $str = decode_utf8($str);
+ $str = to_utf8($str);
$str = $cgi->escapeHTML($str);
if ($opts{'-nbsp'}) {
$str =~ s/ /&nbsp;/g;
@@ -638,7 +732,7 @@ sub esc_path {
my $str = shift;
my %opts = @_;
- $str = decode_utf8($str);
+ $str = to_utf8($str);
$str = $cgi->escapeHTML($str);
if ($opts{'-nbsp'}) {
$str =~ s/ /&nbsp;/g;
@@ -801,11 +895,25 @@ sub age_string {
return $age_str;
}
+use constant {
+ S_IFINVALID => 0030000,
+ S_IFGITLINK => 0160000,
+};
+
+# submodule/subproject, a commit object reference
+sub S_ISGITLINK($) {
+ my $mode = shift;
+
+ return (($mode & S_IFMT) == S_IFGITLINK)
+}
+
# convert file mode in octal to symbolic file mode string
sub mode_str {
my $mode = oct shift;
- if (S_ISDIR($mode & S_IFMT)) {
+ if (S_ISGITLINK($mode)) {
+ return 'm---------';
+ } elsif (S_ISDIR($mode & S_IFMT)) {
return 'drwxr-xr-x';
} elsif (S_ISLNK($mode)) {
return 'lrwxrwxrwx';
@@ -831,7 +939,9 @@ sub file_type {
$mode = oct $mode;
}
- if (S_ISDIR($mode & S_IFMT)) {
+ if (S_ISGITLINK($mode)) {
+ return "submodule";
+ } elsif (S_ISDIR($mode & S_IFMT)) {
return "directory";
} elsif (S_ISLNK($mode)) {
return "symlink";
@@ -852,7 +962,9 @@ sub file_type_long {
$mode = oct $mode;
}
- if (S_ISDIR($mode & S_IFMT)) {
+ if (S_ISGITLINK($mode)) {
+ return "submodule";
+ } elsif (S_ISDIR($mode & S_IFMT)) {
return "directory";
} elsif (S_ISLNK($mode)) {
return "symlink";
@@ -923,7 +1035,7 @@ sub format_subject_html {
if (length($short) < length($long)) {
return $cgi->a({-href => $href, -class => "list subject",
- -title => decode_utf8($long)},
+ -title => to_utf8($long)},
esc_html($short) . $extra);
} else {
return $cgi->a({-href => $href, -class => "list subject"},
@@ -931,7 +1043,200 @@ sub format_subject_html {
}
}
-# format patch (diff) line (rather not to be used for diff headers)
+# format git diff header line, i.e. "diff --(git|combined|cc) ..."
+sub format_git_diff_header_line {
+ my $line = shift;
+ my $diffinfo = shift;
+ my ($from, $to) = @_;
+
+ if ($diffinfo->{'nparents'}) {
+ # combined diff
+ $line =~ s!^(diff (.*?) )"?.*$!$1!;
+ if ($to->{'href'}) {
+ $line .= $cgi->a({-href => $to->{'href'}, -class => "path"},
+ esc_path($to->{'file'}));
+ } else { # file was deleted (no href)
+ $line .= esc_path($to->{'file'});
+ }
+ } else {
+ # "ordinary" diff
+ $line =~ s!^(diff (.*?) )"?a/.*$!$1!;
+ if ($from->{'href'}) {
+ $line .= $cgi->a({-href => $from->{'href'}, -class => "path"},
+ 'a/' . esc_path($from->{'file'}));
+ } else { # file was added (no href)
+ $line .= 'a/' . esc_path($from->{'file'});
+ }
+ $line .= ' ';
+ if ($to->{'href'}) {
+ $line .= $cgi->a({-href => $to->{'href'}, -class => "path"},
+ 'b/' . esc_path($to->{'file'}));
+ } else { # file was deleted
+ $line .= 'b/' . esc_path($to->{'file'});
+ }
+ }
+
+ return "<div class=\"diff header\">$line</div>\n";
+}
+
+# format extended diff header line, before patch itself
+sub format_extended_diff_header_line {
+ my $line = shift;
+ my $diffinfo = shift;
+ my ($from, $to) = @_;
+
+ # match <path>
+ if ($line =~ s!^((copy|rename) from ).*$!$1! && $from->{'href'}) {
+ $line .= $cgi->a({-href=>$from->{'href'}, -class=>"path"},
+ esc_path($from->{'file'}));
+ }
+ if ($line =~ s!^((copy|rename) to ).*$!$1! && $to->{'href'}) {
+ $line .= $cgi->a({-href=>$to->{'href'}, -class=>"path"},
+ esc_path($to->{'file'}));
+ }
+ # match single <mode>
+ if ($line =~ m/\s(\d{6})$/) {
+ $line .= '<span class="info"> (' .
+ file_type_long($1) .
+ ')</span>';
+ }
+ # match <hash>
+ if ($line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) {
+ # can match only for combined diff
+ $line = 'index ';
+ for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+ if ($from->{'href'}[$i]) {
+ $line .= $cgi->a({-href=>$from->{'href'}[$i],
+ -class=>"hash"},
+ substr($diffinfo->{'from_id'}[$i],0,7));
+ } else {
+ $line .= '0' x 7;
+ }
+ # separator
+ $line .= ',' if ($i < $diffinfo->{'nparents'} - 1);
+ }
+ $line .= '..';
+ if ($to->{'href'}) {
+ $line .= $cgi->a({-href=>$to->{'href'}, -class=>"hash"},
+ substr($diffinfo->{'to_id'},0,7));
+ } else {
+ $line .= '0' x 7;
+ }
+
+ } elsif ($line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) {
+ # can match only for ordinary diff
+ my ($from_link, $to_link);
+ if ($from->{'href'}) {
+ $from_link = $cgi->a({-href=>$from->{'href'}, -class=>"hash"},
+ substr($diffinfo->{'from_id'},0,7));
+ } else {
+ $from_link = '0' x 7;
+ }
+ if ($to->{'href'}) {
+ $to_link = $cgi->a({-href=>$to->{'href'}, -class=>"hash"},
+ substr($diffinfo->{'to_id'},0,7));
+ } else {
+ $to_link = '0' x 7;
+ }
+ my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
+ $line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
+ }
+
+ return $line . "<br/>\n";
+}
+
+# format from-file/to-file diff header
+sub format_diff_from_to_header {
+ my ($from_line, $to_line, $diffinfo, $from, $to, @parents) = @_;
+ my $line;
+ my $result = '';
+
+ $line = $from_line;
+ #assert($line =~ m/^---/) if DEBUG;
+ # no extra formatting for "^--- /dev/null"
+ if (! $diffinfo->{'nparents'}) {
+ # ordinary (single parent) diff
+ if ($line =~ m!^--- "?a/!) {
+ if ($from->{'href'}) {
+ $line = '--- a/' .
+ $cgi->a({-href=>$from->{'href'}, -class=>"path"},
+ esc_path($from->{'file'}));
+ } else {
+ $line = '--- a/' .
+ esc_path($from->{'file'});
+ }
+ }
+ $result .= qq!<div class="diff from_file">$line</div>\n!;
+
+ } else {
+ # combined diff (merge commit)
+ for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+ if ($from->{'href'}[$i]) {
+ $line = '--- ' .
+ $cgi->a({-href=>href(action=>"blobdiff",
+ hash_parent=>$diffinfo->{'from_id'}[$i],
+ hash_parent_base=>$parents[$i],
+ file_parent=>$from->{'file'}[$i],
+ hash=>$diffinfo->{'to_id'},
+ hash_base=>$hash,
+ file_name=>$to->{'file'}),
+ -class=>"path",
+ -title=>"diff" . ($i+1)},
+ $i+1) .
+ '/' .
+ $cgi->a({-href=>$from->{'href'}[$i], -class=>"path"},
+ esc_path($from->{'file'}[$i]));
+ } else {
+ $line = '--- /dev/null';
+ }
+ $result .= qq!<div class="diff from_file">$line</div>\n!;
+ }
+ }
+
+ $line = $to_line;
+ #assert($line =~ m/^\+\+\+/) if DEBUG;
+ # no extra formatting for "^+++ /dev/null"
+ if ($line =~ m!^\+\+\+ "?b/!) {
+ if ($to->{'href'}) {
+ $line = '+++ b/' .
+ $cgi->a({-href=>$to->{'href'}, -class=>"path"},
+ esc_path($to->{'file'}));
+ } else {
+ $line = '+++ b/' .
+ esc_path($to->{'file'});
+ }
+ }
+ $result .= qq!<div class="diff to_file">$line</div>\n!;
+
+ return $result;
+}
+
+# create note for patch simplified by combined diff
+sub format_diff_cc_simplified {
+ my ($diffinfo, @parents) = @_;
+ my $result = '';
+
+ $result .= "<div class=\"diff header\">" .
+ "diff --cc ";
+ if (!is_deleted($diffinfo)) {
+ $result .= $cgi->a({-href => href(action=>"blob",
+ hash_base=>$hash,
+ hash=>$diffinfo->{'to_id'},
+ file_name=>$diffinfo->{'to_file'}),
+ -class => "path"},
+ esc_path($diffinfo->{'to_file'}));
+ } else {
+ $result .= esc_path($diffinfo->{'to_file'});
+ }
+ $result .= "</div>\n" . # class="diff header"
+ "<div class=\"diff nodifferences\">" .
+ "Simple merge" .
+ "</div>\n"; # class="diff nodifferences"
+
+ return $result;
+}
+
+# format patch (diff) line (not to be used for diff headers)
sub format_diff_line {
my $line = shift;
my ($from, $to) = @_;
@@ -1020,6 +1325,43 @@ sub format_diff_line {
return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
}
+# Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
+# linked. Pass the hash of the tree/commit to snapshot.
+sub format_snapshot_links {
+ my ($hash) = @_;
+ my @snapshot_fmts = gitweb_check_feature('snapshot');
+ @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+ my $num_fmts = @snapshot_fmts;
+ if ($num_fmts > 1) {
+ # A parenthesized list of links bearing format names.
+ # e.g. "snapshot (_tar.gz_ _zip_)"
+ return "snapshot (" . join(' ', map
+ $cgi->a({
+ -href => href(
+ action=>"snapshot",
+ hash=>$hash,
+ snapshot_format=>$_
+ )
+ }, $known_snapshot_formats{$_}{'display'})
+ , @snapshot_fmts) . ")";
+ } elsif ($num_fmts == 1) {
+ # A single "snapshot" link whose tooltip bears the format name.
+ # i.e. "_snapshot_"
+ my ($fmt) = @snapshot_fmts;
+ return
+ $cgi->a({
+ -href => href(
+ action=>"snapshot",
+ hash=>$hash,
+ snapshot_format=>$fmt
+ ),
+ -title => "in format: $known_snapshot_formats{$fmt}{'display'}"
+ }, "snapshot");
+ } else { # $num_fmts == 0
+ return undef;
+ }
+}
+
## ----------------------------------------------------------------------
## git utility subroutines, invoking git commands
@@ -1173,6 +1515,7 @@ sub git_get_projects_list {
File::Find::find({
follow_fast => 1, # follow symbolic links
+ follow_skip => 2, # ignore duplicates
dangling_symlinks => 0, # ignore dangling symlinks, silently
wanted => sub {
# skip project-list toplevel, if we get it.
@@ -1237,7 +1580,7 @@ sub git_get_projects_list {
if (check_export_ok("$projectroot/$path")) {
my $pr = {
path => $path,
- owner => decode_utf8($owner),
+ owner => to_utf8($owner),
};
push @list, $pr;
(my $forks_path = $path) =~ s/\.git$//;
@@ -1249,12 +1592,12 @@ sub git_get_projects_list {
return @list;
}
-sub git_get_project_owner {
- my $project = shift;
- my $owner;
+our $gitweb_project_owner = undef;
+sub git_get_project_list_from_file {
- return undef unless $project;
+ return if (defined $gitweb_project_owner);
+ $gitweb_project_owner = {};
# read from file (url-encoded):
# 'git%2Fgit.git Linus+Torvalds'
# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
@@ -1266,13 +1609,25 @@ sub git_get_project_owner {
my ($pr, $ow) = split ' ', $line;
$pr = unescape($pr);
$ow = unescape($ow);
- if ($pr eq $project) {
- $owner = decode_utf8($ow);
- last;
- }
+ $gitweb_project_owner->{$pr} = to_utf8($ow);
}
close $fd;
}
+}
+
+sub git_get_project_owner {
+ my $project = shift;
+ my $owner;
+
+ return undef unless $project;
+
+ if (!defined $gitweb_project_owner) {
+ git_get_project_list_from_file();
+ }
+
+ if (exists $gitweb_project_owner->{$project}) {
+ $owner = $gitweb_project_owner->{$project};
+ }
if (!defined $owner) {
$owner = get_file_owner("$projectroot/$project");
}
@@ -1298,6 +1653,7 @@ sub git_get_last_activity {
my $age = time - $timestamp;
return ($age, age_string($age));
}
+ return (undef, undef);
}
sub git_get_references {
@@ -1541,6 +1897,7 @@ sub parse_commits {
($arg ? ($arg) : ()),
("--max-count=" . $maxcount),
("--skip=" . $skip),
+ @extra_options,
$commit_id,
"--",
($filename ? ($filename) : ())
@@ -1657,6 +2014,48 @@ sub parse_ls_tree_line ($;%) {
return wantarray ? %res : \%res;
}
+# generates _two_ hashes, references to which are passed as 2 and 3 argument
+sub parse_from_to_diffinfo {
+ my ($diffinfo, $from, $to, @parents) = @_;
+
+ if ($diffinfo->{'nparents'}) {
+ # combined diff
+ $from->{'file'} = [];
+ $from->{'href'} = [];
+ fill_from_file_info($diffinfo, @parents)
+ unless exists $diffinfo->{'from_file'};
+ for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+ $from->{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'};
+ if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file
+ $from->{'href'}[$i] = href(action=>"blob",
+ hash_base=>$parents[$i],
+ hash=>$diffinfo->{'from_id'}[$i],
+ file_name=>$from->{'file'}[$i]);
+ } else {
+ $from->{'href'}[$i] = undef;
+ }
+ }
+ } else {
+ $from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
+ if ($diffinfo->{'status'} ne "A") { # not new (added) file
+ $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent,
+ hash=>$diffinfo->{'from_id'},
+ file_name=>$from->{'file'});
+ } else {
+ delete $from->{'href'};
+ }
+ }
+
+ $to->{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'};
+ if (!is_deleted($diffinfo)) { # file exists in result
+ $to->{'href'} = href(action=>"blob", hash_base=>$hash,
+ hash=>$diffinfo->{'to_id'},
+ file_name=>$to->{'file'});
+ } else {
+ delete $to->{'href'};
+ }
+}
+
## ......................................................................
## parse to array of hashes functions
@@ -1757,7 +2156,7 @@ sub get_file_owner {
}
my $owner = $gcos;
$owner =~ s/[,;].*$//;
- return decode_utf8($owner);
+ return to_utf8($owner);
}
## ......................................................................
@@ -1840,7 +2239,7 @@ sub git_header_html {
my $title = "$site_name";
if (defined $project) {
- $title .= " - " . decode_utf8($project);
+ $title .= " - " . to_utf8($project);
if (defined $action) {
$title .= "/$action";
if (defined $file_name) {
@@ -1892,9 +2291,17 @@ EOF
printf('<link rel="alternate" title="%s log RSS feed" '.
'href="%s" type="application/rss+xml" />'."\n",
esc_param($project), href(action=>"rss"));
+ printf('<link rel="alternate" title="%s log RSS feed (no merges)" '.
+ 'href="%s" type="application/rss+xml" />'."\n",
+ esc_param($project), href(action=>"rss",
+ extra_options=>"--no-merges"));
printf('<link rel="alternate" title="%s log Atom feed" '.
'href="%s" type="application/atom+xml" />'."\n",
esc_param($project), href(action=>"atom"));
+ printf('<link rel="alternate" title="%s log Atom feed (no merges)" '.
+ 'href="%s" type="application/atom+xml" />'."\n",
+ esc_param($project), href(action=>"atom",
+ extra_options=>"--no-merges"));
} else {
printf('<link rel="alternate" title="%s projects list" '.
'href="%s" type="text/plain; charset=utf-8"/>'."\n",
@@ -1943,12 +2350,18 @@ EOF
} else {
$search_hash = "HEAD";
}
+ my $action = $my_uri;
+ my ($use_pathinfo) = gitweb_check_feature('pathinfo');
+ if ($use_pathinfo) {
+ $action .= "/$project";
+ } else {
+ $cgi->param("p", $project);
+ }
$cgi->param("a", "search");
$cgi->param("h", $search_hash);
- $cgi->param("p", $project);
- print $cgi->startform(-method => "get", -action => $my_uri) .
+ print $cgi->startform(-method => "get", -action => $action) .
"<div class=\"search\">\n" .
- $cgi->hidden(-name => "p") . "\n" .
+ (!$use_pathinfo && $cgi->hidden(-name => "p") . "\n") .
$cgi->hidden(-name => "a") . "\n" .
$cgi->hidden(-name => "h") . "\n" .
$cgi->popup_menu(-name => 'st', -default => 'commit',
@@ -2114,7 +2527,7 @@ sub git_print_page_path {
print "<div class=\"page_path\">";
print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
- -title => 'tree root'}, decode_utf8("[$project]"));
+ -title => 'tree root'}, to_utf8("[$project]"));
print " / ";
if (defined $name) {
my @dirname = split '/', $name;
@@ -2326,6 +2739,20 @@ sub git_print_tree_entry {
"history");
}
print "</td>\n";
+ } else {
+ # unknown object: we can only present history for it
+ # (this includes 'commit' object, i.e. submodule support)
+ print "<td class=\"list\">" .
+ esc_path($t->{'name'}) .
+ "</td>\n";
+ print "<td class=\"link\">";
+ if (defined $hash_base) {
+ print $cgi->a({-href => href(action=>"history",
+ hash_base=>$hash_base,
+ file_name=>"$basedir$t->{'name'}")},
+ "history");
+ }
+ print "</td>\n";
}
}
@@ -2364,6 +2791,11 @@ sub from_ids_eq {
}
}
+sub is_deleted {
+ my $diffinfo = shift;
+
+ return $diffinfo->{'to_id'} eq ('0' x 40);
+}
sub git_difftree_body {
my ($difftree, $hash, @parents) = @_;
@@ -2378,6 +2810,26 @@ sub git_difftree_body {
print "<table class=\"" .
(@parents > 1 ? "combined " : "") .
"diff_tree\">\n";
+
+ # header only for combined diff in 'commitdiff' view
+ my $has_header = @parents > 1 && $action eq 'commitdiff';
+ if ($has_header) {
+ # table header
+ print "<thead><tr>\n" .
+ "<th></th><th></th>\n"; # filename, patchN link
+ for (my $i = 0; $i < @parents; $i++) {
+ my $par = $parents[$i];
+ print "<th>" .
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$hash, hash_parent=>$par),
+ -title => 'commitdiff to parent number ' .
+ ($i+1) . ': ' . substr($par,0,7)},
+ $i+1) .
+ "&nbsp;</th>\n";
+ }
+ print "</tr></thead>\n<tbody>\n";
+ }
+
my $alternate = 1;
my $patchno = 0;
foreach my $line (@{$difftree}) {
@@ -2401,7 +2853,7 @@ sub git_difftree_body {
fill_from_file_info($diff, @parents)
unless exists $diff->{'from_file'};
- if ($diff->{'to_id'} ne ('0' x 40)) {
+ if (!is_deleted($diff)) {
# file exists in the result (child) commit
print "<td>" .
$cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
@@ -2650,6 +3102,7 @@ sub git_difftree_body {
} # we should not encounter Unmerged (U) or Unknown (X) status
print "</tr>\n";
}
+ print "</tbody>" if $has_header;
print "</table>\n";
}
@@ -2714,13 +3167,35 @@ sub git_patchset_body {
# advance raw git-diff output if needed
$patch_idx++ if defined $diffinfo;
- # read and prepare patch information
- if (ref($difftree->[$patch_idx]) eq "HASH") {
- # pre-parsed (or generated by hand)
- $diffinfo = $difftree->[$patch_idx];
- } else {
- $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
+ # compact combined diff output can have some patches skipped
+ # find which patch (using pathname of result) we are at now
+ my $to_name;
+ if ($diff_header[0] =~ m!^diff --cc "?(.*)"?$!) {
+ $to_name = $1;
}
+
+ do {
+ # read and prepare patch information
+ if (ref($difftree->[$patch_idx]) eq "HASH") {
+ # pre-parsed (or generated by hand)
+ $diffinfo = $difftree->[$patch_idx];
+ } else {
+ $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
+ }
+
+ # check if current raw line has no patch (it got simplified)
+ if (defined $to_name && $to_name ne $diffinfo->{'to_file'}) {
+ print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
+ format_diff_cc_simplified($diffinfo, @hash_parents) .
+ "</div>\n"; # class="patch"
+
+ $patch_idx++;
+ $patch_number++;
+ }
+ } until (!defined $to_name || $to_name eq $diffinfo->{'to_file'} ||
+ $patch_idx > $#$difftree);
+ # modifies %from, %to hashes
+ parse_from_to_diffinfo($diffinfo, \%from, \%to, @hash_parents);
if ($diffinfo->{'nparents'}) {
# combined diff
$from{'file'} = [];
@@ -2750,7 +3225,7 @@ sub git_patchset_body {
}
$to{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'};
- if ($diffinfo->{'to_id'} ne ('0' x 40)) { # file exists in result
+ if (!is_deleted($diffinfo)) { # file exists in result
$to{'href'} = href(action=>"blob", hash_base=>$hash,
hash=>$diffinfo->{'to_id'},
file_name=>$to{'file'});
@@ -2764,105 +3239,15 @@ sub git_patchset_body {
# print "git diff" header
$patch_line = shift @diff_header;
- if ($diffinfo->{'nparents'}) {
-
- # combined diff
- $patch_line =~ s!^(diff (.*?) )"?.*$!$1!;
- if ($to{'href'}) {
- $patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
- esc_path($to{'file'}));
- } else { # file was deleted
- $patch_line .= esc_path($to{'file'});
- }
-
- } else {
-
- $patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
- if ($from{'href'}) {
- $patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
- 'a/' . esc_path($from{'file'}));
- } else { # file was added
- $patch_line .= 'a/' . esc_path($from{'file'});
- }
- $patch_line .= ' ';
- if ($to{'href'}) {
- $patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
- 'b/' . esc_path($to{'file'}));
- } else { # file was deleted
- $patch_line .= 'b/' . esc_path($to{'file'});
- }
-
- }
- print "<div class=\"diff header\">$patch_line</div>\n";
+ print format_git_diff_header_line($patch_line, $diffinfo,
+ \%from, \%to);
# print extended diff header
print "<div class=\"diff extended_header\">\n" if (@diff_header > 0);
EXTENDED_HEADER:
foreach $patch_line (@diff_header) {
- # match <path>
- if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
- $patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
- esc_path($from{'file'}));
- }
- if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
- $patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"path"},
- esc_path($to{'file'}));
- }
- # match single <mode>
- if ($patch_line =~ m/\s(\d{6})$/) {
- $patch_line .= '<span class="info"> (' .
- file_type_long($1) .
- ')</span>';
- }
- # match <hash>
- if ($patch_line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) {
- # can match only for combined diff
- $patch_line = 'index ';
- for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
- if ($from{'href'}[$i]) {
- $patch_line .= $cgi->a({-href=>$from{'href'}[$i],
- -class=>"hash"},
- substr($diffinfo->{'from_id'}[$i],0,7));
- } else {
- $patch_line .= '0' x 7;
- }
- # separator
- $patch_line .= ',' if ($i < $diffinfo->{'nparents'} - 1);
- }
- $patch_line .= '..';
- if ($to{'href'}) {
- $patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"hash"},
- substr($diffinfo->{'to_id'},0,7));
- } else {
- $patch_line .= '0' x 7;
- }
-
- } elsif ($patch_line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) {
- # can match only for ordinary diff
- my ($from_link, $to_link);
- if ($from{'href'}) {
- $from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
- substr($diffinfo->{'from_id'},0,7));
- } else {
- $from_link = '0' x 7;
- }
- if ($to{'href'}) {
- $to_link = $cgi->a({-href=>$to{'href'}, -class=>"hash"},
- substr($diffinfo->{'to_id'},0,7));
- } else {
- $to_link = '0' x 7;
- }
- #affirm {
- # my ($from_hash, $to_hash) =
- # ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/);
- # my ($from_id, $to_id) =
- # ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
- # ($from_hash eq $from_id) && ($to_hash eq $to_id);
- #} if DEBUG;
- my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
- $patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
- }
- print $patch_line . "<br/>\n";
+ print format_extended_diff_header_line($patch_line, $diffinfo,
+ \%from, \%to);
}
print "</div>\n" if (@diff_header > 0); # class="diff extended_header"
@@ -2874,24 +3259,15 @@ sub git_patchset_body {
}
next PATCH if ($patch_line =~ m/^diff /);
#assert($patch_line =~ m/^---/) if DEBUG;
- if (!$diffinfo->{'nparents'} && # not from-file line for combined diff
- $from{'href'} && $patch_line =~ m!^--- "?a/!) {
- $patch_line = '--- a/' .
- $cgi->a({-href=>$from{'href'}, -class=>"path"},
- esc_path($from{'file'}));
- }
- print "<div class=\"diff from_file\">$patch_line</div>\n";
+ #assert($patch_line eq $last_patch_line) if DEBUG;
$patch_line = <$fd>;
chomp $patch_line;
+ #assert($patch_line =~ m/^\+\+\+/) if DEBUG;
- #assert($patch_line =~ m/^+++/) if DEBUG;
- if ($to{'href'} && $patch_line =~ m!^\+\+\+ "?b/!) {
- $patch_line = '+++ b/' .
- $cgi->a({-href=>$to{'href'}, -class=>"path"},
- esc_path($to{'file'}));
- }
- print "<div class=\"diff to_file\">$patch_line</div>\n";
+ print format_diff_from_to_header($last_patch_line, $patch_line,
+ $diffinfo, \%from, \%to,
+ @hash_parents);
# the patch itself
LINE:
@@ -2907,6 +3283,27 @@ sub git_patchset_body {
print "</div>\n"; # class="patch"
}
+ # for compact combined (--cc) format, with chunk and patch simpliciaction
+ # patchset might be empty, but there might be unprocessed raw lines
+ for ($patch_idx++ if $patch_number > 0;
+ $patch_idx < @$difftree;
+ $patch_idx++) {
+ # read and prepare patch information
+ if (ref($difftree->[$patch_idx]) eq "HASH") {
+ # pre-parsed (or generated by hand)
+ $diffinfo = $difftree->[$patch_idx];
+ } else {
+ $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
+ }
+
+ # generate anchor for "patch" links in difftree / whatchanged part
+ print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
+ format_diff_cc_simplified($diffinfo, @hash_parents) .
+ "</div>\n"; # class="patch"
+
+ $patch_number++;
+ }
+
if ($patch_number == 0) {
if (@hash_parents > 1) {
print "<div class=\"diff nodifferences\">Trivial merge</div>\n";
@@ -2934,11 +3331,11 @@ sub git_project_list_body {
($pr->{'age'}, $pr->{'age_string'}) = @aa;
if (!defined $pr->{'descr'}) {
my $descr = git_get_project_description($pr->{'path'}) || "";
- $pr->{'descr_long'} = decode_utf8($descr);
- $pr->{'descr'} = chop_str($descr, 25, 5);
+ $pr->{'descr_long'} = to_utf8($descr);
+ $pr->{'descr'} = chop_str($descr, $projects_list_description_width, 5);
}
if (!defined $pr->{'owner'}) {
- $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || "";
+ $pr->{'owner'} = git_get_project_owner("$pr->{'path'}") || "";
}
if ($check_forks) {
my $pname = $pr->{'path'};
@@ -3052,8 +3449,6 @@ sub git_shortlog_body {
# uses global variable $project
my ($commitlist, $from, $to, $refs, $extra) = @_;
- my $have_snapshot = gitweb_have_snapshot();
-
$from = 0 unless defined $from;
$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
@@ -3080,8 +3475,9 @@ sub git_shortlog_body {
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
- if ($have_snapshot) {
- print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
+ my $snapshot_links = format_snapshot_links($commit);
+ if (defined $snapshot_links) {
+ print " | " . $snapshot_links;
}
print "</td>\n" .
"</tr>\n";
@@ -3362,7 +3758,7 @@ sub git_project_index {
foreach my $pr (@projects) {
if (!exists $pr->{'owner'}) {
- $pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}");
+ $pr->{'owner'} = git_get_project_owner("$pr->{'path'}");
}
my ($path, $owner) = ($pr->{'path'}, $pr->{'owner'});
@@ -3863,8 +4259,6 @@ sub git_blob {
}
sub git_tree {
- my $have_snapshot = gitweb_have_snapshot();
-
if (!defined $hash_base) {
$hash_base = "HEAD";
}
@@ -3898,11 +4292,10 @@ sub git_tree {
hash_base=>"HEAD", file_name=>$file_name)},
"HEAD"),
}
- if ($have_snapshot) {
+ my $snapshot_links = format_snapshot_links($hash);
+ if (defined $snapshot_links) {
# FIXME: Should be available when we have no hash base as well.
- push @views_nav,
- $cgi->a({-href => href(action=>"snapshot", hash=>$hash)},
- "snapshot");
+ push @views_nav, $snapshot_links;
}
git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
@@ -3966,34 +4359,53 @@ sub git_tree {
}
sub git_snapshot {
- my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
- my $have_snapshot = (defined $ctype && defined $suffix);
- if (!$have_snapshot) {
+ my @supported_fmts = gitweb_check_feature('snapshot');
+ @supported_fmts = filter_snapshot_fmts(@supported_fmts);
+
+ my $format = $cgi->param('sf');
+ if (!@supported_fmts) {
die_error('403 Permission denied', "Permission denied");
}
+ # default to first supported snapshot format
+ $format ||= $supported_fmts[0];
+ if ($format !~ m/^[a-z0-9]+$/) {
+ die_error(undef, "Invalid snapshot format parameter");
+ } elsif (!exists($known_snapshot_formats{$format})) {
+ die_error(undef, "Unknown snapshot format");
+ } elsif (!grep($_ eq $format, @supported_fmts)) {
+ die_error(undef, "Unsupported snapshot format");
+ }
if (!defined $hash) {
$hash = git_get_head_hash($project);
}
- my $filename = decode_utf8(basename($project)) . "-$hash.tar.$suffix";
+ my $git_command = git_cmd_str();
+ my $name = $project;
+ $name =~ s,([^/])/*\.git$,$1,;
+ $name = basename($name);
+ my $filename = to_utf8($name);
+ $name =~ s/\047/\047\\\047\047/g;
+ my $cmd;
+ $filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}";
+ $cmd = "$git_command archive " .
+ "--format=$known_snapshot_formats{$format}{'format'} " .
+ "--prefix=\'$name\'/ $hash";
+ if (exists $known_snapshot_formats{$format}{'compressor'}) {
+ $cmd .= ' | ' . join ' ', @{$known_snapshot_formats{$format}{'compressor'}};
+ }
print $cgi->header(
- -type => "application/$ctype",
+ -type => $known_snapshot_formats{$format}{'type'},
-content_disposition => 'inline; filename="' . "$filename" . '"',
-status => '200 OK');
- my $git = git_cmd_str();
- my $name = $project;
- $name =~ s/\047/\047\\\047\047/g;
- open my $fd, "-|",
- "$git archive --format=tar --prefix=\'$name\'/ $hash | $command"
- or die_error(undef, "Execute git-tar-tree failed");
+ open my $fd, "-|", $cmd
+ or die_error(undef, "Execute git-archive failed");
binmode STDOUT, ':raw';
print <$fd>;
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
close $fd;
-
}
sub git_log {
@@ -4112,8 +4524,6 @@ sub git_commit {
my $refs = git_get_references();
my $ref = format_ref_marker($refs, $co{'id'});
- my $have_snapshot = gitweb_have_snapshot();
-
git_header_html(undef, $expires);
git_print_page_nav('commit', '',
$hash, $co{'tree'}, $hash,
@@ -4152,9 +4562,9 @@ sub git_commit {
"<td class=\"link\">" .
$cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)},
"tree");
- if ($have_snapshot) {
- print " | " .
- $cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, "snapshot");
+ my $snapshot_links = format_snapshot_links($hash);
+ if (defined $snapshot_links) {
+ print " | " . $snapshot_links;
}
print "</td>" .
"</tr>\n";
@@ -4413,7 +4823,11 @@ sub git_commitdiff {
die_error(undef, "Unknown commit object");
}
- # we need to prepare $formats_nav before any parameter munging
+ # choose format for commitdiff for merge
+ if (! defined $hash_parent && @{$co{'parents'}} > 1) {
+ $hash_parent = '--cc';
+ }
+ # we need to prepare $formats_nav before almost any parameter munging
my $formats_nav;
if ($format eq 'html') {
$formats_nav =
@@ -4421,14 +4835,22 @@ sub git_commitdiff {
hash=>$hash, hash_parent=>$hash_parent)},
"raw");
- if (defined $hash_parent) {
+ if (defined $hash_parent &&
+ $hash_parent ne '-c' && $hash_parent ne '--cc') {
# commitdiff with two commits given
my $hash_parent_short = $hash_parent;
if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
$hash_parent_short = substr($hash_parent, 0, 7);
}
$formats_nav .=
- ' (from: ' .
+ ' (from';
+ for (my $i = 0; $i < @{$co{'parents'}}; $i++) {
+ if ($co{'parents'}[$i] eq $hash_parent) {
+ $formats_nav .= ' parent ' . ($i+1);
+ last;
+ }
+ }
+ $formats_nav .= ': ' .
$cgi->a({-href => href(action=>"commitdiff",
hash=>$hash_parent)},
esc_html($hash_parent_short)) .
@@ -4446,6 +4868,17 @@ sub git_commitdiff {
')';
} else {
# merge commit
+ if ($hash_parent eq '--cc') {
+ $formats_nav .= ' | ' .
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$hash, hash_parent=>'-c')},
+ 'combined');
+ } else { # $hash_parent eq '-c'
+ $formats_nav .= ' | ' .
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$hash, hash_parent=>'--cc')},
+ 'compact');
+ }
$formats_nav .=
' (merge: ' .
join(' ', map {
@@ -4458,9 +4891,10 @@ sub git_commitdiff {
}
my $hash_parent_param = $hash_parent;
- if (!defined $hash_parent) {
+ if (!defined $hash_parent_param) {
+ # --cc for multiple parents, --root for parentless
$hash_parent_param =
- @{$co{'parents'}} > 1 ? '-c' : $co{'parent'} || '--root';
+ @{$co{'parents'}} > 1 ? '--cc' : $co{'parent'} || '--root';
}
# read commitdiff
@@ -4537,10 +4971,14 @@ TEXT
# write patch
if ($format eq 'html') {
- git_difftree_body(\@difftree, $hash, $hash_parent || @{$co{'parents'}});
+ my $use_parents = !defined $hash_parent ||
+ $hash_parent eq '-c' || $hash_parent eq '--cc';
+ git_difftree_body(\@difftree, $hash,
+ $use_parents ? @{$co{'parents'}} : $hash_parent);
print "<br/>\n";
- git_patchset_body($fd, \@difftree, $hash, $hash_parent || @{$co{'parents'}});
+ git_patchset_body($fd, \@difftree, $hash,
+ $use_parents ? @{$co{'parents'}} : $hash_parent);
close $fd;
print "</div>\n"; # class="page_body"
git_footer_html();
@@ -4928,7 +5366,7 @@ sub git_feed {
# log/feed of current (HEAD) branch, log of given branch, history of file/directory
my $head = $hash || 'HEAD';
- my @commitlist = parse_commits($head, 150);
+ my @commitlist = parse_commits($head, 150, 0, undef, $file_name);
my %latest_commit;
my %latest_date;
diff --git a/help.c b/help.c
index 6a9af4d175..1cd33ece6b 100644
--- a/help.c
+++ b/help.c
@@ -219,5 +219,3 @@ int cmd_help(int argc, const char **argv, const char *prefix)
return 0;
}
-
-
diff --git a/http-fetch.c b/http-fetch.c
index 09baedc18a..202fae0ba8 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -828,7 +828,7 @@ static void abort_object_request(struct object_request *obj_req)
}
unlink(obj_req->tmpfile);
if (obj_req->slot) {
- release_active_slot(obj_req->slot);
+ release_active_slot(obj_req->slot);
obj_req->slot = NULL;
}
release_object_request(obj_req);
diff --git a/http-push.c b/http-push.c
index e3f767582b..7c3720f602 100644
--- a/http-push.c
+++ b/http-push.c
@@ -9,6 +9,7 @@
#include "diff.h"
#include "revision.h"
#include "exec_cmd.h"
+#include "remote.h"
#include <expat.h>
@@ -517,7 +518,7 @@ static void start_put(struct transfer_request *request)
request->buffer.size = stream.total_out;
request->buffer.posn = 0;
- request->url = xmalloc(strlen(remote->url) +
+ request->url = xmalloc(strlen(remote->url) +
strlen(request->lock->token) + 51);
strcpy(request->url, remote->url);
posn = request->url + strlen(remote->url);
diff --git a/http.c b/http.c
index ae27e0c940..c6fb8ace9f 100644
--- a/http.c
+++ b/http.c
@@ -137,7 +137,7 @@ static int http_options(const char *var, const char *value)
return 0;
}
-#ifdef USE_CURL_MULTI
+#ifdef USE_CURL_MULTI
if (!strcmp("http.maxrequests", var)) {
if (max_requests == -1)
max_requests = git_config_int(var, value);
diff --git a/ident.c b/ident.c
index 3d49608e6f..9b2a852cb0 100644
--- a/ident.c
+++ b/ident.c
@@ -83,11 +83,18 @@ static void setup_ident(void)
}
if (!git_default_email[0]) {
- if (!pw)
- pw = getpwuid(getuid());
- if (!pw)
- die("You don't exist. Go away!");
- copy_email(pw);
+ const char *email = getenv("EMAIL");
+
+ if (email && email[0])
+ strlcpy(git_default_email, email,
+ sizeof(git_default_email));
+ else {
+ if (!pw)
+ pw = getpwuid(getuid());
+ if (!pw)
+ die("You don't exist. Go away!");
+ copy_email(pw);
+ }
}
/* And set the default date */
@@ -178,11 +185,11 @@ static const char *env_hint =
"\n"
"Run\n"
"\n"
-" git config user.email \"you@email.com\"\n"
-" git config user.name \"Your Name\"\n"
+" git config --global user.email \"you@email.com\"\n"
+" git config --global user.name \"Your Name\"\n"
"\n"
-"To set the identity in this repository.\n"
-"Add --global to set your account\'s default\n"
+"to set your account\'s default identity.\n"
+"Omit --global to set the identity only in this repository.\n"
"\n";
const char *fmt_ident(const char *name, const char *email,
@@ -197,8 +204,6 @@ const char *fmt_ident(const char *name, const char *email,
name = git_default_name;
if (!email)
email = git_default_email;
- if (!email)
- email = getenv("EMAIL");
if (!*name) {
struct passwd *pw;
diff --git a/imap-send.c b/imap-send.c
index 4283a4acda..a5a0696084 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1239,7 +1239,7 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
msg->data[ msg->len ] = 0;
*ofs += msg->len;
- return 1;
+ return 1;
}
static imap_server_conf_t server =
diff --git a/index-pack.c b/index-pack.c
index 58c4a9c41d..db58e05041 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -13,13 +13,11 @@ static const char index_pack_usage[] =
struct object_entry
{
- off_t offset;
+ struct pack_idx_entry idx;
unsigned long size;
unsigned int hdr_size;
- uint32_t crc32;
enum object_type type;
enum object_type real_type;
- unsigned char sha1[20];
};
union delta_base {
@@ -116,7 +114,7 @@ static const char *open_pack_file(const char *pack_name)
static char tmpfile[PATH_MAX];
snprintf(tmpfile, sizeof(tmpfile),
"%s/tmp_pack_XXXXXX", get_object_directory());
- output_fd = mkstemp(tmpfile);
+ output_fd = xmkstemp(tmpfile);
pack_name = xstrdup(tmpfile);
} else
output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
@@ -197,7 +195,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
unsigned shift;
void *data;
- obj->offset = consumed_bytes;
+ obj->idx.offset = consumed_bytes;
input_crc32 = crc32(0, Z_NULL, 0);
p = fill(1);
@@ -229,15 +227,15 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
while (c & 128) {
base_offset += 1;
if (!base_offset || MSB(base_offset, 7))
- bad_object(obj->offset, "offset value overflow for delta base object");
+ bad_object(obj->idx.offset, "offset value overflow for delta base object");
p = fill(1);
c = *p;
use(1);
base_offset = (base_offset << 7) + (c & 127);
}
- delta_base->offset = obj->offset - base_offset;
- if (delta_base->offset >= obj->offset)
- bad_object(obj->offset, "delta base offset is out of bound");
+ delta_base->offset = obj->idx.offset - base_offset;
+ if (delta_base->offset >= obj->idx.offset)
+ bad_object(obj->idx.offset, "delta base offset is out of bound");
break;
case OBJ_COMMIT:
case OBJ_TREE:
@@ -245,19 +243,19 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
case OBJ_TAG:
break;
default:
- bad_object(obj->offset, "unknown object type %d", obj->type);
+ bad_object(obj->idx.offset, "unknown object type %d", obj->type);
}
- obj->hdr_size = consumed_bytes - obj->offset;
+ obj->hdr_size = consumed_bytes - obj->idx.offset;
- data = unpack_entry_data(obj->offset, obj->size);
- obj->crc32 = input_crc32;
+ data = unpack_entry_data(obj->idx.offset, obj->size);
+ obj->idx.crc32 = input_crc32;
return data;
}
static void *get_data_from_pack(struct object_entry *obj)
{
- unsigned long from = obj[0].offset + obj[0].hdr_size;
- unsigned long len = obj[1].offset - from;
+ unsigned long from = obj[0].idx.offset + obj[0].hdr_size;
+ unsigned long len = obj[1].idx.offset - from;
unsigned long rdy = 0;
unsigned char *src, *data;
z_stream stream;
@@ -360,11 +358,11 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
&result_size);
free(delta_data);
if (!result)
- bad_object(delta_obj->offset, "failed to apply delta");
- sha1_object(result, result_size, type, delta_obj->sha1);
+ bad_object(delta_obj->idx.offset, "failed to apply delta");
+ sha1_object(result, result_size, type, delta_obj->idx.sha1);
nr_resolved_deltas++;
- hashcpy(delta_base.sha1, delta_obj->sha1);
+ hashcpy(delta_base.sha1, delta_obj->idx.sha1);
if (!find_delta_children(&delta_base, &first, &last)) {
for (j = first; j <= last; j++) {
struct object_entry *child = objects + deltas[j].obj_no;
@@ -374,7 +372,7 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
}
memset(&delta_base, 0, sizeof(delta_base));
- delta_base.offset = delta_obj->offset;
+ delta_base.offset = delta_obj->idx.offset;
if (!find_delta_children(&delta_base, &first, &last)) {
for (j = first; j <= last; j++) {
struct object_entry *child = objects + deltas[j].obj_no;
@@ -418,12 +416,12 @@ static void parse_pack_objects(unsigned char *sha1)
delta->obj_no = i;
delta++;
} else
- sha1_object(data, obj->size, obj->type, obj->sha1);
+ sha1_object(data, obj->size, obj->type, obj->idx.sha1);
free(data);
if (verbose)
display_progress(&progress, i+1);
}
- objects[i].offset = consumed_bytes;
+ objects[i].idx.offset = consumed_bytes;
if (verbose)
stop_progress(&progress);
@@ -465,10 +463,10 @@ static void parse_pack_objects(unsigned char *sha1)
if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
continue;
- hashcpy(base.sha1, obj->sha1);
+ hashcpy(base.sha1, obj->idx.sha1);
ref = !find_delta_children(&base, &ref_first, &ref_last);
memset(&base, 0, sizeof(base));
- base.offset = obj->offset;
+ base.offset = obj->idx.offset;
ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
if (!ref && !ofs)
continue;
@@ -535,11 +533,11 @@ static void append_obj_to_pack(const unsigned char *sha1, void *buf,
}
header[n++] = c;
write_or_die(output_fd, header, n);
- obj[0].crc32 = crc32(0, Z_NULL, 0);
- obj[0].crc32 = crc32(obj[0].crc32, header, n);
- obj[1].offset = obj[0].offset + n;
- obj[1].offset += write_compressed(output_fd, buf, size, &obj[0].crc32);
- hashcpy(obj->sha1, sha1);
+ obj[0].idx.crc32 = crc32(0, Z_NULL, 0);
+ obj[0].idx.crc32 = crc32(obj[0].idx.crc32, header, n);
+ obj[1].idx.offset = obj[0].idx.offset + n;
+ obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32);
+ hashcpy(obj->idx.sha1, sha1);
}
static int delta_pos_compare(const void *_a, const void *_b)
@@ -602,145 +600,6 @@ static void fix_unresolved_deltas(int nr_unresolved)
free(sorted_by_pos);
}
-static uint32_t index_default_version = 1;
-static uint32_t index_off32_limit = 0x7fffffff;
-
-static int sha1_compare(const void *_a, const void *_b)
-{
- struct object_entry *a = *(struct object_entry **)_a;
- struct object_entry *b = *(struct object_entry **)_b;
- return hashcmp(a->sha1, b->sha1);
-}
-
-/*
- * On entry *sha1 contains the pack content SHA1 hash, on exit it is
- * the SHA1 hash of sorted object names.
- */
-static const char *write_index_file(const char *index_name, unsigned char *sha1)
-{
- struct sha1file *f;
- struct object_entry **sorted_by_sha, **list, **last;
- uint32_t array[256];
- int i, fd;
- SHA_CTX ctx;
- uint32_t index_version;
-
- if (nr_objects) {
- sorted_by_sha =
- xcalloc(nr_objects, sizeof(struct object_entry *));
- list = sorted_by_sha;
- last = sorted_by_sha + nr_objects;
- for (i = 0; i < nr_objects; ++i)
- sorted_by_sha[i] = &objects[i];
- qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
- sha1_compare);
- }
- else
- sorted_by_sha = list = last = NULL;
-
- if (!index_name) {
- static char tmpfile[PATH_MAX];
- snprintf(tmpfile, sizeof(tmpfile),
- "%s/tmp_idx_XXXXXX", get_object_directory());
- fd = mkstemp(tmpfile);
- index_name = xstrdup(tmpfile);
- } else {
- unlink(index_name);
- fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
- }
- if (fd < 0)
- die("unable to create %s: %s", index_name, strerror(errno));
- f = sha1fd(fd, index_name);
-
- /* if last object's offset is >= 2^31 we should use index V2 */
- index_version = (objects[nr_objects-1].offset >> 31) ? 2 : index_default_version;
-
- /* index versions 2 and above need a header */
- if (index_version >= 2) {
- struct pack_idx_header hdr;
- hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
- hdr.idx_version = htonl(index_version);
- sha1write(f, &hdr, sizeof(hdr));
- }
-
- /*
- * Write the first-level table (the list is sorted,
- * but we use a 256-entry lookup to be able to avoid
- * having to do eight extra binary search iterations).
- */
- for (i = 0; i < 256; i++) {
- struct object_entry **next = list;
- while (next < last) {
- struct object_entry *obj = *next;
- if (obj->sha1[0] != i)
- break;
- next++;
- }
- array[i] = htonl(next - sorted_by_sha);
- list = next;
- }
- sha1write(f, array, 256 * 4);
-
- /* compute the SHA1 hash of sorted object names. */
- SHA1_Init(&ctx);
-
- /*
- * Write the actual SHA1 entries..
- */
- list = sorted_by_sha;
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *obj = *list++;
- if (index_version < 2) {
- uint32_t offset = htonl(obj->offset);
- sha1write(f, &offset, 4);
- }
- sha1write(f, obj->sha1, 20);
- SHA1_Update(&ctx, obj->sha1, 20);
- }
-
- if (index_version >= 2) {
- unsigned int nr_large_offset = 0;
-
- /* write the crc32 table */
- list = sorted_by_sha;
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *obj = *list++;
- uint32_t crc32_val = htonl(obj->crc32);
- sha1write(f, &crc32_val, 4);
- }
-
- /* write the 32-bit offset table */
- list = sorted_by_sha;
- for (i = 0; i < nr_objects; i++) {
- struct object_entry *obj = *list++;
- uint32_t offset = (obj->offset <= index_off32_limit) ?
- obj->offset : (0x80000000 | nr_large_offset++);
- offset = htonl(offset);
- sha1write(f, &offset, 4);
- }
-
- /* write the large offset table */
- list = sorted_by_sha;
- while (nr_large_offset) {
- struct object_entry *obj = *list++;
- uint64_t offset = obj->offset;
- if (offset > index_off32_limit) {
- uint32_t split[2];
- split[0] = htonl(offset >> 32);
- split[1] = htonl(offset & 0xffffffff);
- sha1write(f, split, 8);
- nr_large_offset--;
- }
- }
- }
-
- sha1write(f, sha1, 20);
- sha1close(f, NULL, 1);
- free(sorted_by_sha);
- SHA1_Final(sha1, &ctx);
- return index_name;
-}
-
static void final(const char *final_pack_name, const char *curr_pack_name,
const char *final_index_name, const char *curr_index_name,
const char *keep_name, const char *keep_msg,
@@ -775,7 +634,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
write_or_die(keep_fd, keep_msg, keep_msg_len);
write_or_die(keep_fd, "\n", 1);
}
- close(keep_fd);
+ if (close(keep_fd) != 0)
+ die("cannot write keep file");
report = "keep";
}
}
@@ -830,6 +690,7 @@ int main(int argc, char **argv)
const char *curr_index, *index_name = NULL;
const char *keep_name = NULL, *keep_msg = NULL;
char *index_name_buf = NULL, *keep_name_buf = NULL;
+ struct pack_idx_entry **idx_objects;
unsigned char sha1[20];
for (i = 1; i < argc; i++) {
@@ -865,12 +726,12 @@ int main(int argc, char **argv)
index_name = argv[++i];
} else if (!prefixcmp(arg, "--index-version=")) {
char *c;
- index_default_version = strtoul(arg + 16, &c, 10);
- if (index_default_version > 2)
+ pack_idx_default_version = strtoul(arg + 16, &c, 10);
+ if (pack_idx_default_version > 2)
die("bad %s", arg);
if (*c == ',')
- index_off32_limit = strtoul(c+1, &c, 0);
- if (*c || index_off32_limit & 0x80000000)
+ pack_idx_off32_limit = strtoul(c+1, &c, 0);
+ if (*c || pack_idx_off32_limit & 0x80000000)
die("bad %s", arg);
} else
usage(index_pack_usage);
@@ -940,7 +801,13 @@ int main(int argc, char **argv)
nr_deltas - nr_resolved_deltas);
}
free(deltas);
- curr_index = write_index_file(index_name, sha1);
+
+ idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
+ for (i = 0; i < nr_objects; i++)
+ idx_objects[i] = &objects[i].idx;
+ curr_index = write_idx_file(index_name, idx_objects, nr_objects, sha1);
+ free(idx_objects);
+
final(pack_name, curr_pack,
index_name, curr_index,
keep_name, keep_msg,
diff --git a/interpolate.c b/interpolate.c
index fb30694f47..00826778fc 100644
--- a/interpolate.c
+++ b/interpolate.c
@@ -44,33 +44,33 @@ void interp_clear_table(struct interp *table, int ninterps)
* { "%%", "%"},
* }
*
- * Returns 1 on a successful substitution pass that fits in result,
- * Returns 0 on a failed or overflowing substitution pass.
+ * Returns 0 on a successful substitution pass that fits in result,
+ * Returns a number of bytes needed to hold the full substituted
+ * string otherwise.
*/
-int interpolate(char *result, int reslen,
+unsigned long interpolate(char *result, unsigned long reslen,
const char *orig,
const struct interp *interps, int ninterps)
{
const char *src = orig;
char *dest = result;
- int newlen = 0;
+ unsigned long newlen = 0;
const char *name, *value;
- int namelen, valuelen;
+ unsigned long namelen, valuelen;
int i;
char c;
memset(result, 0, reslen);
- while ((c = *src) && newlen < reslen - 1) {
+ while ((c = *src)) {
if (c == '%') {
/* Try to match an interpolation string. */
for (i = 0; i < ninterps; i++) {
name = interps[i].name;
namelen = strlen(name);
- if (strncmp(src, name, namelen) == 0) {
+ if (strncmp(src, name, namelen) == 0)
break;
- }
}
/* Check for valid interpolation. */
@@ -78,29 +78,25 @@ int interpolate(char *result, int reslen,
value = interps[i].value;
valuelen = strlen(value);
- if (newlen + valuelen < reslen - 1) {
+ if (newlen + valuelen + 1 < reslen) {
/* Substitute. */
strncpy(dest, value, valuelen);
- newlen += valuelen;
dest += valuelen;
- src += namelen;
- } else {
- /* Something's not fitting. */
- return 0;
}
-
- } else {
- /* Skip bogus interpolation. */
- *dest++ = *src++;
- newlen++;
+ newlen += valuelen;
+ src += namelen;
+ continue;
}
-
- } else {
- /* Straight copy one non-interpolation character. */
- *dest++ = *src++;
- newlen++;
}
+ /* Straight copy one non-interpolation character. */
+ if (newlen + 1 < reslen)
+ *dest++ = *src;
+ src++;
+ newlen++;
}
- return newlen < reslen - 1;
+ if (newlen + 1 < reslen)
+ return 0;
+ else
+ return newlen + 2;
}
diff --git a/interpolate.h b/interpolate.h
index 16a26b9986..77407e67dc 100644
--- a/interpolate.h
+++ b/interpolate.h
@@ -19,8 +19,8 @@ struct interp {
extern void interp_set_entry(struct interp *table, int slot, const char *value);
extern void interp_clear_table(struct interp *table, int ninterps);
-extern int interpolate(char *result, int reslen,
- const char *orig,
- const struct interp *interps, int ninterps);
+extern unsigned long interpolate(char *result, unsigned long reslen,
+ const char *orig,
+ const struct interp *interps, int ninterps);
#endif /* INTERPOLATE_H */
diff --git a/list-objects.c b/list-objects.c
index 310f8d3908..e5c88c278f 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -87,7 +87,7 @@ static void process_tree(struct rev_info *revs,
process_tree(revs,
lookup_tree(entry.sha1),
p, &me, entry.path);
- else if (S_ISDIRLNK(entry.mode))
+ else if (S_ISGITLINK(entry.mode))
process_gitlink(revs, entry.sha1,
p, &me, entry.path);
else
diff --git a/local-fetch.c b/local-fetch.c
index 4b650efa8b..bf7ec6c2a3 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -114,7 +114,7 @@ static int fetch_pack(const unsigned char *sha1)
return -1;
target = find_sha1_pack(sha1, packs);
if (!target)
- return error("Couldn't find %s: not separate or in any pack",
+ return error("Couldn't find %s: not separate or in any pack",
sha1_to_hex(sha1));
if (get_verbosely) {
fprintf(stderr, "Getting pack %s\n",
@@ -122,11 +122,11 @@ static int fetch_pack(const unsigned char *sha1)
fprintf(stderr, " which contains %s\n",
sha1_to_hex(sha1));
}
- sprintf(filename, "%s/objects/pack/pack-%s.pack",
+ sprintf(filename, "%s/objects/pack/pack-%s.pack",
path, sha1_to_hex(target->sha1));
copy_file(filename, sha1_pack_name(target->sha1),
sha1_to_hex(target->sha1), 1);
- sprintf(filename, "%s/objects/pack/pack-%s.idx",
+ sprintf(filename, "%s/objects/pack/pack-%s.idx",
path, sha1_to_hex(target->sha1));
copy_file(filename, sha1_pack_index_name(target->sha1),
sha1_to_hex(target->sha1), 1);
@@ -141,7 +141,7 @@ static int fetch_file(const unsigned char *sha1)
char *hex = sha1_to_hex(sha1);
char *dest_filename = sha1_file_name(sha1);
- if (object_name_start < 0) {
+ if (object_name_start < 0) {
strcpy(filename, path); /* e.g. git.git */
strcat(filename, "/objects/");
object_name_start = strlen(filename);
diff --git a/lockfile.c b/lockfile.c
index 23db35aff2..9a1f64d8d7 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -25,22 +25,123 @@ static void remove_lock_file_on_signal(int signo)
raise(signo);
}
+/*
+ * p = absolute or relative path name
+ *
+ * Return a pointer into p showing the beginning of the last path name
+ * element. If p is empty or the root directory ("/"), just return p.
+ */
+static char *last_path_elm(char *p)
+{
+ /* r starts pointing to null at the end of the string */
+ char *r = strchr(p, '\0');
+
+ if (r == p)
+ return p; /* just return empty string */
+
+ r--; /* back up to last non-null character */
+
+ /* back up past trailing slashes, if any */
+ while (r > p && *r == '/')
+ r--;
+
+ /*
+ * then go backwards until I hit a slash, or the beginning of
+ * the string
+ */
+ while (r > p && *(r-1) != '/')
+ r--;
+ return r;
+}
+
+
+/* We allow "recursive" symbolic links. Only within reason, though */
+#define MAXDEPTH 5
+
+/*
+ * p = path that may be a symlink
+ * s = full size of p
+ *
+ * If p is a symlink, attempt to overwrite p with a path to the real
+ * file or directory (which may or may not exist), following a chain of
+ * symlinks if necessary. Otherwise, leave p unmodified.
+ *
+ * This is a best-effort routine. If an error occurs, p will either be
+ * left unmodified or will name a different symlink in a symlink chain
+ * that started with p's initial contents.
+ *
+ * Always returns p.
+ */
+
+static char *resolve_symlink(char *p, size_t s)
+{
+ int depth = MAXDEPTH;
+
+ while (depth--) {
+ char link[PATH_MAX];
+ int link_len = readlink(p, link, sizeof(link));
+ if (link_len < 0) {
+ /* not a symlink anymore */
+ return p;
+ }
+ else if (link_len < sizeof(link))
+ /* readlink() never null-terminates */
+ link[link_len] = '\0';
+ else {
+ warning("%s: symlink too long", p);
+ return p;
+ }
+
+ if (link[0] == '/') {
+ /* absolute path simply replaces p */
+ if (link_len < s)
+ strcpy(p, link);
+ else {
+ warning("%s: symlink too long", p);
+ return p;
+ }
+ } else {
+ /*
+ * link is a relative path, so I must replace the
+ * last element of p with it.
+ */
+ char *r = (char*)last_path_elm(p);
+ if (r - p + link_len < s)
+ strcpy(r, link);
+ else {
+ warning("%s: symlink too long", p);
+ return p;
+ }
+ }
+ }
+ return p;
+}
+
+
static int lock_file(struct lock_file *lk, const char *path)
{
int fd;
- sprintf(lk->filename, "%s.lock", path);
+
+ if (strlen(path) >= sizeof(lk->filename)) return -1;
+ strcpy(lk->filename, path);
+ /*
+ * subtract 5 from size to make sure there's room for adding
+ * ".lock" for the lock file name
+ */
+ resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+ strcat(lk->filename, ".lock");
fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= fd) {
+ if (!lock_file_list) {
+ signal(SIGINT, remove_lock_file_on_signal);
+ atexit(remove_lock_file);
+ }
lk->owner = getpid();
if (!lk->on_list) {
lk->next = lock_file_list;
lock_file_list = lk;
lk->on_list = 1;
}
- if (lock_file_list) {
- signal(SIGINT, remove_lock_file_on_signal);
- atexit(remove_lock_file);
- }
if (adjust_shared_perm(lk->filename))
return error("cannot fix permission bits on %s",
lk->filename);
@@ -97,4 +198,3 @@ void rollback_lock_file(struct lock_file *lk)
unlink(lk->filename);
lk->filename[0] = 0;
}
-
diff --git a/log-tree.c b/log-tree.c
index 4bef909144..a6423718e7 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -79,16 +79,25 @@ static int detect_any_signoff(char *letter, int size)
return seen_head && seen_name;
}
-static int append_signoff(char *buf, int buf_sz, int at, const char *signoff)
+static unsigned long append_signoff(char **buf_p, unsigned long *buf_sz_p,
+ unsigned long at, const char *signoff)
{
static const char signed_off_by[] = "Signed-off-by: ";
- int signoff_len = strlen(signoff);
+ size_t signoff_len = strlen(signoff);
int has_signoff = 0;
- char *cp = buf;
-
- /* Do we have enough space to add it? */
- if (buf_sz - at <= strlen(signed_off_by) + signoff_len + 3)
- return at;
+ char *cp;
+ char *buf;
+ unsigned long buf_sz;
+
+ buf = *buf_p;
+ buf_sz = *buf_sz_p;
+ if (buf_sz <= at + strlen(signed_off_by) + signoff_len + 3) {
+ buf_sz += strlen(signed_off_by) + signoff_len + 3;
+ buf = xrealloc(buf, buf_sz);
+ *buf_p = buf;
+ *buf_sz_p = buf_sz;
+ }
+ cp = buf;
/* First see if we already have the sign-off by the signer */
while ((cp = strstr(cp, signed_off_by))) {
@@ -133,7 +142,8 @@ static unsigned int digits_in_number(unsigned int number)
void show_log(struct rev_info *opt, const char *sep)
{
- static char this_header[16384];
+ char *msgbuf = NULL;
+ unsigned long msgbuf_len = 0;
struct log_info *log = opt->loginfo;
struct commit *commit = log->commit, *parent = log->parent;
int abbrev = opt->diffopt.abbrev;
@@ -190,7 +200,7 @@ void show_log(struct rev_info *opt, const char *sep)
digits_in_number(opt->total),
opt->nr, opt->total);
subject = buffer;
- } else if (opt->total == 0) {
+ } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
static char buffer[256];
snprintf(buffer, sizeof(buffer),
"Subject: [%s] ",
@@ -278,14 +288,18 @@ void show_log(struct rev_info *opt, const char *sep)
/*
* And then the pretty-printed message itself
*/
- len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header,
- sizeof(this_header), abbrev, subject,
+ len = pretty_print_commit(opt->commit_format, commit, ~0u,
+ &msgbuf, &msgbuf_len, abbrev, subject,
extra_headers, opt->date_mode);
if (opt->add_signoff)
- len = append_signoff(this_header, sizeof(this_header), len,
+ len = append_signoff(&msgbuf, &msgbuf_len, len,
opt->add_signoff);
- printf("%s%s%s", this_header, extra, sep);
+ if (opt->show_log_size)
+ printf("log size %i\n", len);
+
+ printf("%s%s%s", msgbuf, extra, sep);
+ free(msgbuf);
}
int log_tree_diff_flush(struct rev_info *opt)
@@ -397,5 +411,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
shown = 1;
}
opt->loginfo = NULL;
+ maybe_flush_or_die(stdout, "stdout");
return shown;
}
diff --git a/mailmap.c b/mailmap.c
index cb567a2832..8714167059 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -89,4 +89,3 @@ int map_email(struct path_list *map, const char *email, char *name, int maxlen)
}
return 0;
}
-
diff --git a/match-trees.c b/match-trees.c
index 23cafe47b4..d7e29c4d1d 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -301,4 +301,3 @@ void shift_tree(const unsigned char *hash1,
splice_tree(hash1, add_prefix, hash2, shifted);
}
-
diff --git a/merge-file.c b/merge-file.c
index 748d15c0e0..1e031eafe0 100644
--- a/merge-file.c
+++ b/merge-file.c
@@ -62,6 +62,7 @@ static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
xdemitcb_t ecb;
xpp.flags = XDF_NEED_MINIMAL;
+ memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_COMMON;
ecb.outf = common_outf;
diff --git a/merge-index.c b/merge-index.c
index 5599fd321b..fa719cb0b1 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -25,7 +25,7 @@ static void run_program(void)
static int merge_entry(int pos, const char *path)
{
int found;
-
+
if (pos >= active_nr)
die("git-merge-index: %s not in the cache", path);
arguments[0] = pgm;
diff --git a/merge-recursive.c b/merge-recursive.c
index 4a82b741ae..16f6a0f98b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -127,7 +127,7 @@ static void output(int v, const char *fmt, ...)
va_end(args);
}
-static void flush_output()
+static void flush_output(void)
{
struct output_buffer *b, *n;
for (b = output_list; b; b = n) {
@@ -216,13 +216,19 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
*/
static int index_only = 0;
+static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
+{
+ parse_tree(tree);
+ init_tree_desc(desc, tree->buffer, tree->size);
+}
+
static int git_merge_trees(int index_only,
struct tree *common,
struct tree *head,
struct tree *merge)
{
int rc;
- struct object_list *trees = NULL;
+ struct tree_desc t[3];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
@@ -234,11 +240,11 @@ static int git_merge_trees(int index_only,
opts.head_idx = 2;
opts.fn = threeway_merge;
- object_list_append(&common->object, &trees);
- object_list_append(&head->object, &trees);
- object_list_append(&merge->object, &trees);
+ init_tree_desc_from_tree(t+0, common);
+ init_tree_desc_from_tree(t+1, head);
+ init_tree_desc_from_tree(t+2, merge);
- rc = unpack_trees(trees, &opts);
+ rc = unpack_trees(3, t, &opts);
cache_tree_free(&active_cache_tree);
return rc;
}
@@ -671,6 +677,26 @@ struct ll_merge_driver {
/*
* Built-in low-levels
*/
+static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
+ const char *path_unused,
+ mmfile_t *orig,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
+ mmbuffer_t *result)
+{
+ /*
+ * The tentative merge result is "ours" for the final round,
+ * or common ancestor for an internal merge. Still return
+ * "conflicted merge" status.
+ */
+ mmfile_t *stolen = index_only ? orig : src1;
+
+ result->ptr = stolen->ptr;
+ result->size = stolen->size;
+ stolen->ptr = NULL;
+ return 1;
+}
+
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
const char *path_unused,
mmfile_t *orig,
@@ -681,10 +707,15 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
xpparam_t xpp;
if (buffer_is_binary(orig->ptr, orig->size) ||
- buffer_is_binary(src1->ptr, src1->size) ||
- buffer_is_binary(src2->ptr, src2->size))
- return error("Cannot merge binary files: %s vs. %s\n",
+ buffer_is_binary(src1->ptr, src1->size) ||
+ buffer_is_binary(src2->ptr, src2->size)) {
+ warning("Cannot merge binary files: %s vs. %s\n",
name1, name2);
+ return ll_binary_merge(drv_unused, path_unused,
+ orig, src1, name1,
+ src2, name2,
+ result);
+ }
memset(&xpp, 0, sizeof(xpp));
return xdl_merge(orig,
@@ -737,26 +768,6 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
return 0;
}
-static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
- const char *path_unused,
- mmfile_t *orig,
- mmfile_t *src1, const char *name1,
- mmfile_t *src2, const char *name2,
- mmbuffer_t *result)
-{
- /*
- * The tentative merge result is "ours" for the final round,
- * or common ancestor for an internal merge. Still return
- * "conflicted merge" status.
- */
- mmfile_t *stolen = index_only ? orig : src1;
-
- result->ptr = stolen->ptr;
- result->size = stolen->size;
- stolen->ptr = NULL;
- return 1;
-}
-
#define LL_BINARY_MERGE 0
#define LL_TEXT_MERGE 1
#define LL_UNION_MERGE 2
@@ -771,9 +782,7 @@ static void create_temp(mmfile_t *src, char *path)
int fd;
strcpy(path, ".merge_file_XXXXXX");
- fd = mkstemp(path);
- if (fd < 0)
- die("unable to create temp-file");
+ fd = xmkstemp(path);
if (write_in_full(fd, src->ptr, src->size) != src->size)
die("unable to write temp-file");
close(fd);
diff --git a/merge-tree.c b/merge-tree.c
index 3b8d9e6887..7d4f628444 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -106,8 +106,8 @@ static void show_diff(struct merge_list *entry)
xdemitcb_t ecb;
xpp.flags = XDF_NEED_MINIMAL;
+ memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 3;
- xecfg.flags = 0;
ecb.outf = show_outf;
ecb.priv = NULL;
diff --git a/mktag.c b/mktag.c
index 931011121e..38acd5a295 100644
--- a/mktag.c
+++ b/mktag.c
@@ -11,16 +11,9 @@
* The first three lines are guaranteed to be at least 63 bytes:
* "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
* shortest possible type-line, and "tag .\n" at 6 bytes is the
- * shortest single-character-tag line.
- *
- * We also artificially limit the size of the full object to 8kB.
- * Just because I'm a lazy bastard, and if you can't fit a signature
- * in that size, you're doing something wrong.
+ * shortest single-character-tag line.
*/
-/* Some random size */
-#define MAXSIZE (8192)
-
/*
* We refuse to tag something we can't verify. Just because.
*/
@@ -127,7 +120,7 @@ int main(int argc, char **argv)
setup_git_directory();
- if (read_pipe(0, &buffer, &size)) {
+ if (read_fd(0, &buffer, &size)) {
free(buffer);
die("could not read from stdin");
}
diff --git a/mozilla-sha1/sha1.c b/mozilla-sha1/sha1.c
index 847531d19f..3f06b83567 100644
--- a/mozilla-sha1/sha1.c
+++ b/mozilla-sha1/sha1.c
@@ -1,29 +1,29 @@
-/*
+/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
- *
+ *
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
- *
+ *
* The Original Code is SHA 180-1 Reference Implementation (Compact version)
- *
+ *
* The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
+ * Cryptography Research. Portions created by Paul Kocher are
* Copyright (C) 1995-9 by Cryptography Research, Inc. All
* Rights Reserved.
- *
+ *
* Contributor(s):
*
* Paul Kocher
- *
+ *
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
@@ -149,4 +149,3 @@ static void shaHashBlock(SHA_CTX *ctx) {
ctx->H[3] += D;
ctx->H[4] += E;
}
-
diff --git a/mozilla-sha1/sha1.h b/mozilla-sha1/sha1.h
index 5d82afa3bd..16f2d3d43c 100644
--- a/mozilla-sha1/sha1.h
+++ b/mozilla-sha1/sha1.h
@@ -1,29 +1,29 @@
-/*
+/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
- *
+ *
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
- *
+ *
* The Original Code is SHA 180-1 Header File
- *
+ *
* The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
+ * Cryptography Research. Portions created by Paul Kocher are
* Copyright (C) 1995-9 by Cryptography Research, Inc. All
* Rights Reserved.
- *
+ *
* Contributor(s):
*
* Paul Kocher
- *
+ *
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
diff --git a/object-refs.c b/object-refs.c
index 022e8d841c..5345671569 100644
--- a/object-refs.c
+++ b/object-refs.c
@@ -85,5 +85,3 @@ void mark_reachable(struct object *obj, unsigned int mask)
mark_reachable(refs->ref[i], mask);
}
}
-
-
diff --git a/object.h b/object.h
index 94f19eed86..397bbfa090 100644
--- a/object.h
+++ b/object.h
@@ -66,7 +66,7 @@ void set_object_refs(struct object *obj, struct object_refs *refs);
void mark_reachable(struct object *obj, unsigned int mask);
-struct object_list *object_list_insert(struct object *item,
+struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
void object_list_append(struct object *item,
diff --git a/pack-check.c b/pack-check.c
index d04536bbff..d7dd62bb83 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,6 +1,23 @@
#include "cache.h"
#include "pack.h"
+struct idx_entry
+{
+ const unsigned char *sha1;
+ off_t offset;
+};
+
+static int compare_entries(const void *e1, const void *e2)
+{
+ const struct idx_entry *entry1 = e1;
+ const struct idx_entry *entry2 = e2;
+ if (entry1->offset < entry2->offset)
+ return -1;
+ if (entry1->offset > entry2->offset)
+ return 1;
+ return 0;
+}
+
static int verify_packfile(struct packed_git *p,
struct pack_window **w_curs)
{
@@ -11,6 +28,7 @@ static int verify_packfile(struct packed_git *p,
off_t offset = 0, pack_sig = p->pack_size - 20;
uint32_t nr_objects, i;
int err;
+ struct idx_entry *entries;
/* Note that the pack header checks are actually performed by
* use_pack when it first opens the pack file. If anything
@@ -41,44 +59,48 @@ static int verify_packfile(struct packed_git *p,
* we do not do scan-streaming check on the pack file.
*/
nr_objects = p->num_objects;
+ entries = xmalloc(nr_objects * sizeof(*entries));
+ /* first sort entries by pack offset, since unpacking them is more efficient that way */
+ for (i = 0; i < nr_objects; i++) {
+ entries[i].sha1 = nth_packed_object_sha1(p, i);
+ if (!entries[i].sha1)
+ die("internal error pack-check nth-packed-object");
+ entries[i].offset = find_pack_entry_one(entries[i].sha1, p);
+ if (!entries[i].offset)
+ die("internal error pack-check find-pack-entry-one");
+ }
+ qsort(entries, nr_objects, sizeof(*entries), compare_entries);
+
for (i = 0, err = 0; i < nr_objects; i++) {
- const unsigned char *sha1;
void *data;
enum object_type type;
unsigned long size;
- off_t offset;
- sha1 = nth_packed_object_sha1(p, i);
- if (!sha1)
- die("internal error pack-check nth-packed-object");
- offset = find_pack_entry_one(sha1, p);
- if (!offset)
- die("internal error pack-check find-pack-entry-one");
- data = unpack_entry(p, offset, &type, &size);
+ data = unpack_entry(p, entries[i].offset, &type, &size);
if (!data) {
err = error("cannot unpack %s from %s",
- sha1_to_hex(sha1), p->pack_name);
+ sha1_to_hex(entries[i].sha1), p->pack_name);
continue;
}
- if (check_sha1_signature(sha1, data, size, typename(type))) {
+ if (check_sha1_signature(entries[i].sha1, data, size, typename(type))) {
err = error("packed %s from %s is corrupt",
- sha1_to_hex(sha1), p->pack_name);
+ sha1_to_hex(entries[i].sha1), p->pack_name);
free(data);
continue;
}
free(data);
}
+ free(entries);
return err;
}
-#define MAX_CHAIN 40
+#define MAX_CHAIN 50
static void show_pack_info(struct packed_git *p)
{
- uint32_t nr_objects, i, chain_histogram[MAX_CHAIN];
-
+ uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
nr_objects = p->num_objects;
memset(chain_histogram, 0, sizeof(chain_histogram));
@@ -109,32 +131,37 @@ static void show_pack_info(struct packed_git *p)
printf("%-6s %lu %"PRIuMAX" %u %s\n",
type, size, (uintmax_t)offset,
delta_chain_length, sha1_to_hex(base_sha1));
- if (delta_chain_length < MAX_CHAIN)
+ if (delta_chain_length <= MAX_CHAIN)
chain_histogram[delta_chain_length]++;
else
chain_histogram[0]++;
}
}
- for (i = 0; i < MAX_CHAIN; i++) {
+ for (i = 0; i <= MAX_CHAIN; i++) {
if (!chain_histogram[i])
continue;
- printf("chain length %s %d: %d object%s\n",
- i ? "=" : ">=",
- i ? i : MAX_CHAIN,
- chain_histogram[i],
- 1 < chain_histogram[i] ? "s" : "");
+ printf("chain length = %d: %d object%s\n", i,
+ chain_histogram[i], chain_histogram[i] > 1 ? "s" : "");
}
+ if (chain_histogram[0])
+ printf("chain length > %d: %d object%s\n", MAX_CHAIN,
+ chain_histogram[0], chain_histogram[0] > 1 ? "s" : "");
}
int verify_pack(struct packed_git *p, int verbose)
{
- off_t index_size = p->index_size;
- const unsigned char *index_base = p->index_data;
+ off_t index_size;
+ const unsigned char *index_base;
SHA_CTX ctx;
unsigned char sha1[20];
int ret;
+ if (open_pack_index(p))
+ return error("packfile %s index not opened", p->pack_name);
+ index_size = p->index_size;
+ index_base = p->index_data;
+
ret = 0;
/* Verify SHA1 sum of the index file */
SHA1_Init(&ctx);
diff --git a/pack-redundant.c b/pack-redundant.c
index 87077e150c..f5cd0ac59e 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -81,7 +81,7 @@ static struct llist * llist_copy(struct llist *list)
{
struct llist *ret;
struct llist_item *new, *old, *prev;
-
+
llist_init(&ret);
if ((ret->size = list->size) == 0)
@@ -100,7 +100,7 @@ static struct llist * llist_copy(struct llist *list)
}
new->next = NULL;
ret->back = new;
-
+
return ret;
}
@@ -550,6 +550,9 @@ static struct pack_list * add_pack(struct packed_git *p)
l.pack = p;
llist_init(&l.all_objects);
+ if (open_pack_index(p))
+ return NULL;
+
base = p->index_data;
base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
step = (p->index_version < 2) ? 24 : 20;
diff --git a/pack-write.c b/pack-write.c
index ae2e481e55..e59b197e5e 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,5 +1,147 @@
#include "cache.h"
#include "pack.h"
+#include "csum-file.h"
+
+uint32_t pack_idx_default_version = 1;
+uint32_t pack_idx_off32_limit = 0x7fffffff;
+
+static int sha1_compare(const void *_a, const void *_b)
+{
+ struct pack_idx_entry *a = *(struct pack_idx_entry **)_a;
+ struct pack_idx_entry *b = *(struct pack_idx_entry **)_b;
+ return hashcmp(a->sha1, b->sha1);
+}
+
+/*
+ * 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.
+ */
+const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1)
+{
+ struct sha1file *f;
+ struct pack_idx_entry **sorted_by_sha, **list, **last;
+ off_t last_obj_offset = 0;
+ uint32_t array[256];
+ int i, fd;
+ SHA_CTX ctx;
+ uint32_t index_version;
+
+ if (nr_objects) {
+ sorted_by_sha = objects;
+ list = sorted_by_sha;
+ last = sorted_by_sha + nr_objects;
+ for (i = 0; i < nr_objects; ++i) {
+ if (objects[i]->offset > last_obj_offset)
+ last_obj_offset = objects[i]->offset;
+ }
+ qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
+ sha1_compare);
+ }
+ else
+ sorted_by_sha = list = last = NULL;
+
+ if (!index_name) {
+ static char tmpfile[PATH_MAX];
+ snprintf(tmpfile, sizeof(tmpfile),
+ "%s/tmp_idx_XXXXXX", get_object_directory());
+ fd = xmkstemp(tmpfile);
+ index_name = xstrdup(tmpfile);
+ } else {
+ unlink(index_name);
+ fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+ }
+ if (fd < 0)
+ die("unable to create %s: %s", index_name, strerror(errno));
+ f = sha1fd(fd, index_name);
+
+ /* if last object's offset is >= 2^31 we should use index V2 */
+ index_version = (last_obj_offset >> 31) ? 2 : pack_idx_default_version;
+
+ /* index versions 2 and above need a header */
+ if (index_version >= 2) {
+ struct pack_idx_header hdr;
+ hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
+ hdr.idx_version = htonl(index_version);
+ sha1write(f, &hdr, sizeof(hdr));
+ }
+
+ /*
+ * Write the first-level table (the list is sorted,
+ * but we use a 256-entry lookup to be able to avoid
+ * having to do eight extra binary search iterations).
+ */
+ for (i = 0; i < 256; i++) {
+ struct pack_idx_entry **next = list;
+ while (next < last) {
+ struct pack_idx_entry *obj = *next;
+ if (obj->sha1[0] != i)
+ break;
+ next++;
+ }
+ array[i] = htonl(next - sorted_by_sha);
+ list = next;
+ }
+ sha1write(f, array, 256 * 4);
+
+ /* compute the SHA1 hash of sorted object names. */
+ SHA1_Init(&ctx);
+
+ /*
+ * Write the actual SHA1 entries..
+ */
+ 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);
+ sha1write(f, &offset, 4);
+ }
+ sha1write(f, obj->sha1, 20);
+ SHA1_Update(&ctx, obj->sha1, 20);
+ }
+
+ if (index_version >= 2) {
+ unsigned int nr_large_offset = 0;
+
+ /* write the crc32 table */
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct pack_idx_entry *obj = *list++;
+ uint32_t crc32_val = htonl(obj->crc32);
+ sha1write(f, &crc32_val, 4);
+ }
+
+ /* write the 32-bit offset table */
+ list = sorted_by_sha;
+ for (i = 0; i < nr_objects; i++) {
+ struct pack_idx_entry *obj = *list++;
+ uint32_t offset = (obj->offset <= pack_idx_off32_limit) ?
+ obj->offset : (0x80000000 | nr_large_offset++);
+ offset = htonl(offset);
+ sha1write(f, &offset, 4);
+ }
+
+ /* write the large offset table */
+ list = sorted_by_sha;
+ while (nr_large_offset) {
+ struct pack_idx_entry *obj = *list++;
+ uint64_t offset = obj->offset;
+ if (offset > pack_idx_off32_limit) {
+ uint32_t split[2];
+ split[0] = htonl(offset >> 32);
+ split[1] = htonl(offset & 0xffffffff);
+ sha1write(f, split, 8);
+ nr_large_offset--;
+ }
+ }
+ }
+
+ sha1write(f, sha1, 20);
+ sha1close(f, NULL, 1);
+ SHA1_Final(sha1, &ctx);
+ return index_name;
+}
void fixup_pack_header_footer(int pack_fd,
unsigned char *pack_file_sha1,
diff --git a/pack.h b/pack.h
index d667fb8d5a..f357c9f428 100644
--- a/pack.h
+++ b/pack.h
@@ -34,6 +34,10 @@ struct pack_header {
*/
#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */
+/* These may be overridden by command-line parameters */
+extern uint32_t pack_idx_default_version;
+extern uint32_t pack_idx_off32_limit;
+
/*
* Packed object index header
*/
@@ -42,6 +46,16 @@ struct pack_idx_header {
uint32_t idx_version;
};
+/*
+ * Common part of object structure used for write_idx_file
+ */
+struct pack_idx_entry {
+ unsigned char sha1[20];
+ uint32_t crc32;
+ off_t offset;
+};
+
+extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
extern int verify_pack(struct packed_git *, int);
extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t);
diff --git a/pager.c b/pager.c
index 5f280ab527..8bac9d9903 100644
--- a/pager.c
+++ b/pager.c
@@ -31,6 +31,11 @@ void setup_pager(void)
if (!isatty(1))
return;
+ if (!pager) {
+ if (!pager_program)
+ git_config(git_default_config);
+ pager = pager_program;
+ }
if (!pager)
pager = getenv("PAGER");
if (!pager)
diff --git a/patch-id.c b/patch-id.c
index 086d2d9c68..9349bc5580 100644
--- a/patch-id.c
+++ b/patch-id.c
@@ -81,4 +81,4 @@ int main(int argc, char **argv)
generate_id_list();
return 0;
-}
+}
diff --git a/path-list.c b/path-list.c
index caaa5cc57b..3d83b7ba9e 100644
--- a/path-list.c
+++ b/path-list.c
@@ -76,16 +76,18 @@ struct path_list_item *path_list_lookup(const char *path, struct path_list *list
return list->items + i;
}
-void path_list_clear(struct path_list *list, int free_items)
+void path_list_clear(struct path_list *list, int free_util)
{
if (list->items) {
int i;
- if (free_items)
- for (i = 0; i < list->nr; i++) {
- if (list->strdup_paths)
- free(list->items[i].path);
+ if (list->strdup_paths) {
+ for (i = 0; i < list->nr; i++)
+ free(list->items[i].path);
+ }
+ if (free_util) {
+ for (i = 0; i < list->nr; i++)
free(list->items[i].util);
- }
+ }
free(list->items);
}
list->items = NULL;
@@ -100,4 +102,3 @@ void print_path_list(const char *text, const struct path_list *p)
for (i = 0; i < p->nr; i++)
printf("%s:%p\n", p->items[i].path, p->items[i].util);
}
-
diff --git a/path-list.h b/path-list.h
index ce5ffabcce..5931e2cc0c 100644
--- a/path-list.h
+++ b/path-list.h
@@ -15,7 +15,7 @@ struct path_list
void print_path_list(const char *text, const struct path_list *p);
int path_list_has_path(const struct path_list *list, const char *path);
-void path_list_clear(struct path_list *list, int free_items);
+void path_list_clear(struct path_list *list, int free_util);
struct path_list_item *path_list_insert(const char *path, struct path_list *list);
struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
diff --git a/path.c b/path.c
index 6395cf2309..42609524a5 100644
--- a/path.c
+++ b/path.c
@@ -71,21 +71,17 @@ char *git_path(const char *fmt, ...)
/* git_mkstemp() - create tmp file honoring TMPDIR variable */
int git_mkstemp(char *path, size_t len, const char *template)
{
- char *env, *pch = path;
-
- if ((env = getenv("TMPDIR")) == NULL) {
- strcpy(pch, "/tmp/");
- len -= 5;
- pch += 5;
- } else {
- size_t n = snprintf(pch, len, "%s/", env);
-
- len -= n;
- pch += n;
+ const char *tmp;
+ size_t n;
+
+ tmp = getenv("TMPDIR");
+ if (!tmp)
+ tmp = "/tmp";
+ n = snprintf(path, len, "%s/%s", tmp, template);
+ if (len <= n) {
+ errno = ENAMETOOLONG;
+ return -1;
}
-
- strlcpy(pch, template, len);
-
return mkstemp(path);
}
@@ -252,7 +248,7 @@ char *enter_repo(char *path, int strict)
if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
validate_headref("HEAD") == 0) {
- setenv("GIT_DIR", ".", 1);
+ setenv(GIT_DIR_ENVIRONMENT, ".", 1);
check_repository_format();
return path;
}
@@ -292,3 +288,68 @@ int adjust_shared_perm(const char *path)
return -2;
return 0;
}
+
+/* We allow "recursive" symbolic links. Only within reason, though. */
+#define MAXDEPTH 5
+
+const char *make_absolute_path(const char *path)
+{
+ static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
+ char cwd[1024] = "";
+ int buf_index = 1, len;
+
+ int depth = MAXDEPTH;
+ char *last_elem = NULL;
+ struct stat st;
+
+ if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
+ die ("Too long path: %.*s", 60, path);
+
+ while (depth--) {
+ if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
+ char *last_slash = strrchr(buf, '/');
+ if (last_slash) {
+ *last_slash = '\0';
+ last_elem = xstrdup(last_slash + 1);
+ } else
+ last_elem = xstrdup(buf);
+ }
+
+ if (*buf) {
+ if (!*cwd && !getcwd(cwd, sizeof(cwd)))
+ die ("Could not get current working directory");
+
+ if (chdir(buf))
+ die ("Could not switch to '%s'", buf);
+ }
+ if (!getcwd(buf, PATH_MAX))
+ die ("Could not get current working directory");
+
+ if (last_elem) {
+ int len = strlen(buf);
+ if (len + strlen(last_elem) + 2 > PATH_MAX)
+ die ("Too long path name: '%s/%s'",
+ buf, last_elem);
+ buf[len] = '/';
+ strcpy(buf + len + 1, last_elem);
+ free(last_elem);
+ last_elem = NULL;
+ }
+
+ if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
+ len = readlink(buf, next_buf, PATH_MAX);
+ if (len < 0)
+ die ("Invalid symlink: %s", buf);
+ next_buf[len] = '\0';
+ buf = next_buf;
+ buf_index = 1 - buf_index;
+ next_buf = bufs[buf_index];
+ } else
+ break;
+ }
+
+ if (*cwd && chdir(cwd))
+ die ("Could not change back to '%s'", cwd);
+
+ return buf;
+}
diff --git a/peek-remote.c b/peek-remote.c
index 96bfac498b..ceb787170e 100644
--- a/peek-remote.c
+++ b/peek-remote.c
@@ -64,7 +64,7 @@ int main(int argc, char **argv)
if (!dest || i != argc - 1)
usage(peek_remote_usage);
- pid = git_connect(fd, dest, uploadpack);
+ pid = git_connect(fd, dest, uploadpack, 0);
if (pid < 0)
return 1;
ret = peek_remote(fd, flags);
diff --git a/perl/Makefile b/perl/Makefile
index 0d695fd2f3..5e079ad011 100644
--- a/perl/Makefile
+++ b/perl/Makefile
@@ -40,4 +40,3 @@ endif
# (even though GIT-CFLAGS aren't used yet. If ever)
../GIT-CFLAGS:
$(MAKE) -C .. GIT-CFLAGS
-
diff --git a/pkt-line.c b/pkt-line.c
index b60526869a..355546a1ad 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -5,7 +5,7 @@
* Write a packetized stream, where each line is preceded by
* its length (including the header) as a 4-byte hex number.
* A length of 'zero' means end of stream (and a length of 1-3
- * would be an error).
+ * would be an error).
*
* This is all pretty stupid, but we use this packetized line
* format to make a streaming format possible without ever
diff --git a/ppc/sha1.c b/ppc/sha1.c
index 0820398b00..738e36c1e8 100644
--- a/ppc/sha1.c
+++ b/ppc/sha1.c
@@ -50,7 +50,7 @@ int SHA1_Update(SHA_CTX *c, const void *ptr, unsigned long n)
p += nb;
}
return 0;
-}
+}
int SHA1_Final(unsigned char *hash, SHA_CTX *c)
{
diff --git a/progress.c b/progress.c
index 05f7890314..4344f4eed5 100644
--- a/progress.c
+++ b/progress.c
@@ -62,11 +62,13 @@ int display_progress(struct progress *progress, unsigned n)
fprintf(stderr, "%s%4u%% (%u/%u) done\r",
progress->prefix, percent, n, progress->total);
progress_update = 0;
+ progress->need_lf = 1;
return 1;
}
} else if (progress_update) {
fprintf(stderr, "%s%u\r", progress->prefix, n);
progress_update = 0;
+ progress->need_lf = 1;
return 1;
}
return 0;
@@ -80,6 +82,7 @@ void start_progress(struct progress *progress, const char *title,
progress->total = total;
progress->last_percent = -1;
progress->delay = 0;
+ progress->need_lf = 0;
if (snprintf(buf, sizeof(buf), title, total))
fprintf(stderr, "%s\n", buf);
set_progress_signal();
@@ -95,12 +98,13 @@ void start_progress_delay(struct progress *progress, const char *title,
progress->delayed_percent_treshold = percent_treshold;
progress->delayed_title = title;
progress->delay = delay;
+ progress->need_lf = 0;
set_progress_signal();
}
void stop_progress(struct progress *progress)
{
clear_progress_signal();
- if (progress->total)
+ if (progress->need_lf)
fputc('\n', stderr);
}
diff --git a/progress.h b/progress.h
index 5ae1a89e5a..a7c17ca7c4 100644
--- a/progress.h
+++ b/progress.h
@@ -8,6 +8,7 @@ struct progress {
unsigned delay;
unsigned delayed_percent_treshold;
const char *delayed_title;
+ int need_lf;
};
int display_progress(struct progress *progress, unsigned n);
diff --git a/quote.c b/quote.c
index fb9e4ca253..d88bf75159 100644
--- a/quote.c
+++ b/quote.c
@@ -20,7 +20,7 @@ static inline int need_bs_quote(char c)
return (c == '\'' || c == '!');
}
-size_t sq_quote_buf(char *dst, size_t n, const char *src)
+static size_t sq_quote_buf(char *dst, size_t n, const char *src)
{
char c;
char *bp = dst;
@@ -62,18 +62,6 @@ void sq_quote_print(FILE *stream, const char *src)
fputc('\'', stream);
}
-char *sq_quote(const char *src)
-{
- char *buf;
- size_t cnt;
-
- cnt = sq_quote_buf(NULL, 0, src) + 1;
- buf = xmalloc(cnt);
- sq_quote_buf(buf, cnt, src);
-
- return buf;
-}
-
char *sq_quote_argv(const char** argv, int count)
{
char *buf, *to;
@@ -200,7 +188,8 @@ static int quote_c_style_counted(const char *name, int namelen,
#define EMITQ() EMIT('\\')
const char *sp;
- int ch, count = 0, needquote = 0;
+ unsigned char ch;
+ int count = 0, needquote = 0;
if (!no_dq)
EMIT('"');
@@ -209,7 +198,7 @@ static int quote_c_style_counted(const char *name, int namelen,
if (!ch)
break;
if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
- (ch >= 0177)) {
+ (quote_path_fully && (ch >= 0177))) {
needquote = 1;
switch (ch) {
case '\a': EMITQ(); ch = 'a'; break;
diff --git a/quote.h b/quote.h
index bdc3610df5..8a59cc55d1 100644
--- a/quote.h
+++ b/quote.h
@@ -28,9 +28,7 @@
* excluding the final null regardless of the buffer size.
*/
-extern char *sq_quote(const char *src);
extern void sq_quote_print(FILE *stream, const char *src);
-extern size_t sq_quote_buf(char *dst, size_t n, const char *src);
extern char *sq_quote_argv(const char** argv, int count);
/*
diff --git a/reachable.c b/reachable.c
index c1aee603e7..6383401e2d 100644
--- a/reachable.c
+++ b/reachable.c
@@ -55,7 +55,7 @@ static void process_tree(struct tree *tree,
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
- else if (S_ISDIRLNK(entry.mode))
+ else if (S_ISGITLINK(entry.mode))
process_gitlink(entry.sha1, p, &me, entry.path);
else
process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
@@ -176,7 +176,7 @@ static void add_cache_refs(struct rev_info *revs)
* lookup_blob() on them, to avoid populating the hash table
* with invalid information
*/
- if (S_ISDIRLNK(ntohl(active_cache[i]->ce_mode)))
+ if (S_ISGITLINK(ntohl(active_cache[i]->ce_mode)))
continue;
lookup_blob(active_cache[i]->sha1);
diff --git a/read-cache.c b/read-cache.c
index d9f46da5cc..8b1c94e0e3 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -7,6 +7,7 @@
#include "cache.h"
#include "cache-tree.h"
#include "refs.h"
+#include "dir.h"
/* Index extensions.
*
@@ -92,7 +93,7 @@ static int ce_compare_gitlink(struct cache_entry *ce)
/*
* We don't actually require that the .git directory
- * under DIRLNK directory be a valid git directory. It
+ * under GITLINK directory be a valid git directory. It
* might even be missing (in case nobody populated that
* sub-project).
*
@@ -115,7 +116,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
return DATA_CHANGED;
break;
case S_IFDIR:
- if (S_ISDIRLNK(ntohl(ce->ce_mode)))
+ if (S_ISGITLINK(ntohl(ce->ce_mode)))
return 0;
default:
return TYPE_CHANGED;
@@ -142,7 +143,7 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
(has_symlinks || !S_ISREG(st->st_mode)))
changed |= TYPE_CHANGED;
break;
- case S_IFDIRLNK:
+ case S_IFGITLINK:
if (!S_ISDIR(st->st_mode))
changed |= TYPE_CHANGED;
else if (ce_compare_gitlink(ce))
@@ -166,7 +167,7 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
changed |= MTIME_CHANGED;
if (ce->ce_ctime.nsec != htonl(st->st_ctim.tv_nsec))
changed |= CTIME_CHANGED;
-#endif
+#endif
if (ce->ce_uid != htonl(st->st_uid) ||
ce->ce_gid != htonl(st->st_gid))
@@ -350,9 +351,37 @@ int remove_file_from_index(struct index_state *istate, const char *path)
return 0;
}
+static int compare_name(struct cache_entry *ce, const char *path, int namelen)
+{
+ return namelen != ce_namelen(ce) || memcmp(path, ce->name, namelen);
+}
+
+static int index_name_pos_also_unmerged(struct index_state *istate,
+ const char *path, int namelen)
+{
+ int pos = index_name_pos(istate, path, namelen);
+ struct cache_entry *ce;
+
+ if (pos >= 0)
+ return pos;
+
+ /* maybe unmerged? */
+ pos = -1 - pos;
+ if (pos >= istate->cache_nr ||
+ compare_name((ce = istate->cache[pos]), path, namelen))
+ return -1;
+
+ /* order of preference: stage 2, 1, 3 */
+ if (ce_stage(ce) == 1 && pos + 1 < istate->cache_nr &&
+ ce_stage((ce = istate->cache[pos + 1])) == 2 &&
+ !compare_name(ce, path, namelen))
+ pos++;
+ return pos;
+}
+
int add_file_to_index(struct index_state *istate, const char *path, int verbose)
{
- int size, namelen;
+ int size, namelen, pos;
struct stat st;
struct cache_entry *ce;
@@ -380,12 +409,21 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
* from it, otherwise assume unexecutable regular file.
*/
struct cache_entry *ent;
- int pos = index_name_pos(istate, path, namelen);
+ int pos = index_name_pos_also_unmerged(istate, path, namelen);
ent = (0 <= pos) ? istate->cache[pos] : NULL;
ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
}
+ pos = index_name_pos(istate, ce->name, namelen);
+ if (0 <= pos &&
+ !ce_stage(istate->cache[pos]) &&
+ !ie_modified(istate, istate->cache[pos], &st, 1)) {
+ /* Nothing changed, really */
+ free(ce);
+ return 0;
+ }
+
if (index_path(ce->sha1, path, &st, 1))
die("unable to index file %s", path);
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
@@ -597,7 +635,7 @@ static int has_dir_name(struct index_state *istate,
* is being added, or we already have path and path/file is being
* added. Either one would result in a nonsense tree that has path
* twice when git-write-tree tries to write it out. Prevent it.
- *
+ *
* If ok-to-replace is specified, we remove the conflicting entries
* from the cache so the caller should recompute the insert position.
* When this happens, we return non-zero.
@@ -628,7 +666,7 @@ static int check_file_directory_conflict(struct index_state *istate,
return retval + has_dir_name(istate, ce, pos, ok_to_replace);
}
-int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option)
+static int add_index_entry_with_check(struct index_state *istate, struct cache_entry *ce, int option)
{
int pos;
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
@@ -670,6 +708,22 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
pos = -pos-1;
}
+ return pos + 1;
+}
+
+int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option)
+{
+ int pos;
+
+ if (option & ADD_CACHE_JUST_APPEND)
+ pos = istate->cache_nr;
+ else {
+ int ret;
+ ret = add_index_entry_with_check(istate, ce, option);
+ if (ret <= 0)
+ return ret;
+ pos = ret - 1;
+ }
/* Make sure the array is big enough .. */
if (istate->cache_nr == istate->cache_alloc) {
@@ -680,7 +734,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
/* Add it in.. */
istate->cache_nr++;
- if (istate->cache_nr > pos)
+ if (istate->cache_nr > pos + 1)
memmove(istate->cache + pos + 1,
istate->cache + pos,
(istate->cache_nr - pos - 1) * sizeof(ce));
@@ -745,7 +799,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
return updated;
}
-int refresh_index(struct index_state *istate, unsigned int flags)
+int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen)
{
int i;
int has_errors = 0;
@@ -771,6 +825,9 @@ int refresh_index(struct index_state *istate, unsigned int flags)
continue;
}
+ if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
+ continue;
+
new = refresh_cache_ent(istate, ce, really, &cache_errno);
if (new == ce)
continue;
@@ -970,8 +1027,8 @@ static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
write_buffer_len = buffered;
len -= partial;
data = (char *) data + partial;
- }
- return 0;
+ }
+ return 0;
}
static int write_index_ext_header(SHA_CTX *context, int fd,
@@ -1037,7 +1094,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
* size to zero here, then the object name recorded
* in index is the 6-byte file but the cached stat information
* becomes zero --- which would then match what we would
- * obtain from the filesystem next time we stat("frotz").
+ * obtain from the filesystem next time we stat("frotz").
*
* However, the second update-index, before calling
* this function, notices that the cached size is 6
diff --git a/receive-pack.c b/receive-pack.c
index 26aa26bcb5..d3c422be58 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -209,7 +209,7 @@ static const char *update(struct command *cmd)
return NULL; /* good */
}
else {
- lock = lock_any_ref_for_update(name, old_sha1);
+ lock = lock_any_ref_for_update(name, old_sha1, 0);
if (!lock) {
error("failed to lock %s", name);
return "failed to lock";
diff --git a/reflog-walk.c b/reflog-walk.c
index c983858259..ee1456b45a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -136,7 +136,7 @@ void init_reflog_walk(struct reflog_walk_info** info)
*info = xcalloc(sizeof(struct reflog_walk_info), 1);
}
-void add_reflog_for_walk(struct reflog_walk_info *info,
+int add_reflog_for_walk(struct reflog_walk_info *info,
struct commit *commit, const char *name)
{
unsigned long timestamp = 0;
@@ -188,7 +188,7 @@ void add_reflog_for_walk(struct reflog_walk_info *info,
}
}
if (!reflogs || reflogs->nr == 0)
- die("No reflogs found for '%s'", branch);
+ return -1;
path_list_insert(branch, &info->complete_reflogs)->util
= reflogs;
}
@@ -200,13 +200,14 @@ void add_reflog_for_walk(struct reflog_walk_info *info,
if (commit_reflog->recno < 0) {
free(branch);
free(commit_reflog);
- return;
+ return -1;
}
} else
commit_reflog->recno = reflogs->nr - recno - 1;
commit_reflog->reflogs = reflogs;
add_commit_info(commit, commit_reflog, &info->reflogs);
+ return 0;
}
void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
diff --git a/reflog-walk.h b/reflog-walk.h
index a4f7015d3e..7ca1438f4d 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -2,7 +2,7 @@
#define REFLOG_WALK_H
extern void init_reflog_walk(struct reflog_walk_info** info);
-extern void add_reflog_for_walk(struct reflog_walk_info *info,
+extern int add_reflog_for_walk(struct reflog_walk_info *info,
struct commit *commit, const char *name);
extern void fake_reflog_parent(struct reflog_walk_info *info,
struct commit *commit);
diff --git a/refs.c b/refs.c
index 89876bff87..09a2c87fc2 100644
--- a/refs.c
+++ b/refs.c
@@ -150,7 +150,7 @@ static struct ref_list *sort_ref_list(struct ref_list *list)
* Future: need to be in "struct repository"
* when doing a full libification.
*/
-struct cached_refs {
+static struct cached_refs {
char did_loose;
char did_packed;
struct ref_list *loose;
@@ -603,15 +603,20 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
static inline int bad_ref_char(int ch)
{
- return (((unsigned) ch) <= ' ' ||
- ch == '~' || ch == '^' || ch == ':' ||
- /* 2.13 Pattern Matching Notation */
- ch == '?' || ch == '*' || ch == '[');
+ if (((unsigned) ch) <= ' ' ||
+ ch == '~' || ch == '^' || ch == ':')
+ return 1;
+ /* 2.13 Pattern Matching Notation */
+ if (ch == '?' || ch == '[') /* Unsupported */
+ return 1;
+ if (ch == '*') /* Supported at the end */
+ return 2;
+ return 0;
}
int check_ref_format(const char *ref)
{
- int ch, level;
+ int ch, level, bad_type;
const char *cp = ref;
level = 0;
@@ -622,13 +627,19 @@ int check_ref_format(const char *ref)
return -1; /* should not end with slashes */
/* we are at the beginning of the path component */
- if (ch == '.' || bad_ref_char(ch))
+ if (ch == '.')
return -1;
+ bad_type = bad_ref_char(ch);
+ if (bad_type) {
+ return (bad_type == 2 && !*cp) ? -3 : -1;
+ }
/* scan the rest of the path component */
while ((ch = *cp++) != 0) {
- if (bad_ref_char(ch))
- return -1;
+ bad_type = bad_ref_char(ch);
+ if (bad_type) {
+ return (bad_type == 2 && !*cp) ? -3 : -1;
+ }
if (ch == '/')
break;
if (ch == '.' && *cp == '.')
@@ -736,19 +747,20 @@ static int is_refname_available(const char *ref, const char *oldref,
return 1;
}
-static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
+static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p)
{
char *ref_file;
const char *orig_ref = ref;
struct ref_lock *lock;
struct stat st;
int last_errno = 0;
+ int type;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- ref = resolve_ref(ref, lock->old_sha1, mustexist, flag);
+ ref = resolve_ref(ref, lock->old_sha1, mustexist, &type);
if (!ref && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
@@ -761,8 +773,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
error("there are still refs under '%s'", orig_ref);
goto error_return;
}
- ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
+ ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type);
}
+ if (type_p)
+ *type_p = type;
if (!ref) {
last_errno = errno;
error("unable to resolve reference %s: %s",
@@ -780,10 +794,15 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
lock->lk = xcalloc(1, sizeof(struct lock_file));
+ if (flags & REF_NODEREF)
+ ref = orig_ref;
lock->ref_name = xstrdup(ref);
lock->orig_ref_name = xstrdup(orig_ref);
ref_file = git_path("%s", ref);
- lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
+ if (lstat(ref_file, &st) && errno == ENOENT)
+ lock->force_write = 1;
+ if ((flags & REF_NODEREF) && (type & REF_ISSYMREF))
+ lock->force_write = 1;
if (safe_create_leading_directories(ref_file)) {
last_errno = errno;
@@ -806,14 +825,14 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
if (check_ref_format(ref))
return NULL;
strcpy(refpath, mkpath("refs/%s", ref));
- return lock_ref_sha1_basic(refpath, old_sha1, NULL);
+ return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
}
-struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
+struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)
{
if (check_ref_format(ref) == -1)
return NULL;
- return lock_ref_sha1_basic(ref, old_sha1, NULL);
+ return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
}
static struct lock_file packlock;
@@ -850,6 +869,7 @@ static int repack_without_ref(const char *refname)
die("too long a refname '%s'", list->name);
write_or_die(fd, line, len);
}
+ close(fd);
return commit_lock_file(&packlock);
}
@@ -858,7 +878,7 @@ int delete_ref(const char *refname, const unsigned char *sha1)
struct ref_lock *lock;
int err, i, ret = 0, flag = 0;
- lock = lock_ref_sha1_basic(refname, sha1, &flag);
+ lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
if (!lock)
return 1;
if (!(flag & REF_ISPACKED)) {
@@ -909,7 +929,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
return 1;
- lock = lock_ref_sha1_basic(renamed_ref, NULL, NULL);
+ lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL);
if (!lock)
return error("unable to lock %s", renamed_ref);
lock->force_write = 1;
@@ -963,7 +983,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
}
logmoved = log;
- lock = lock_ref_sha1_basic(newref, NULL, NULL);
+ lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newref);
goto rollback;
@@ -979,7 +999,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
return 0;
rollback:
- lock = lock_ref_sha1_basic(oldref, NULL, NULL);
+ lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for rollback", oldref);
goto rollbacklog;
@@ -1017,6 +1037,32 @@ void unlock_ref(struct ref_lock *lock)
free(lock);
}
+/*
+ * copy the reflog message msg to buf, which has been allocated sufficiently
+ * large, while cleaning up the whitespaces. Especially, convert LF to space,
+ * because reflog file is one line per entry.
+ */
+static int copy_msg(char *buf, const char *msg)
+{
+ char *cp = buf;
+ char c;
+ int wasspace = 1;
+
+ *cp++ = '\t';
+ while ((c = *msg++)) {
+ if (wasspace && isspace(c))
+ continue;
+ wasspace = isspace(c);
+ if (wasspace)
+ c = ' ';
+ *cp++ = c;
+ }
+ while (buf < cp && isspace(cp[-1]))
+ cp--;
+ *cp++ = '\n';
+ return cp - buf;
+}
+
static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
const unsigned char *new_sha1, const char *msg)
{
@@ -1061,21 +1107,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
adjust_shared_perm(log_file);
- msglen = 0;
- if (msg) {
- /* clean up the message and make sure it is a single line */
- for ( ; *msg; msg++)
- if (!isspace(*msg))
- break;
- if (*msg) {
- const char *ep = strchr(msg, '\n');
- if (ep)
- msglen = ep - msg;
- else
- msglen = strlen(msg);
- }
- }
-
+ msglen = msg ? strlen(msg) : 0;
committer = git_committer_info(-1);
maxlen = strlen(committer) + msglen + 100;
logrec = xmalloc(maxlen);
@@ -1084,11 +1116,10 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
sha1_to_hex(new_sha1),
committer);
if (msglen)
- len += sprintf(logrec + len - 1, "\t%.*s\n", msglen, msg) - 1;
+ len += copy_msg(logrec + len - 1, msg) - 1;
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec);
- close(logfd);
- if (written != len)
+ if (close(logfd) != 0 || written != len)
return error("Unable to append to %s", log_file);
return 0;
}
@@ -1185,8 +1216,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
goto error_free_return;
}
written = write_in_full(fd, ref, len);
- close(fd);
- if (written != len) {
+ if (close(fd) != 0 || written != len) {
error("Unable to write to %s", lockpath);
goto error_unlink_return;
}
@@ -1283,7 +1313,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
if (hashcmp(logged_sha1, sha1)) {
fprintf(stderr,
"warning: Log %s has gap after %s.\n",
- logfile, show_rfc2822_date(date, tz));
+ logfile, show_date(date, tz, DATE_RFC2822));
}
}
else if (date == at_time) {
@@ -1296,7 +1326,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
if (hashcmp(logged_sha1, sha1)) {
fprintf(stderr,
"warning: Log %s unexpectedly ended on %s.\n",
- logfile, show_rfc2822_date(date, tz));
+ logfile, show_date(date, tz, DATE_RFC2822));
}
}
munmap(log_mapped, mapsz);
diff --git a/refs.h b/refs.h
index f61f6d934e..f234eb76ba 100644
--- a/refs.h
+++ b/refs.h
@@ -33,7 +33,8 @@ extern int get_ref_sha1(const char *ref, unsigned char *sha1);
extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
/** Locks any ref (for 'HEAD' type refs). */
-extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1);
+#define REF_NODEREF 0x01
+extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);
/** Release any lock taken but not written. **/
extern void unlock_ref(struct ref_lock *lock);
diff --git a/remote.c b/remote.c
new file mode 100644
index 0000000000..bb774d0bcc
--- /dev/null
+++ b/remote.c
@@ -0,0 +1,633 @@
+#include "cache.h"
+#include "remote.h"
+#include "refs.h"
+
+static struct remote **remotes;
+static int allocated_remotes;
+
+#define BUF_SIZE (2048)
+static char buffer[BUF_SIZE];
+
+static void add_push_refspec(struct remote *remote, const char *ref)
+{
+ int nr = remote->push_refspec_nr + 1;
+ remote->push_refspec =
+ xrealloc(remote->push_refspec, nr * sizeof(char *));
+ remote->push_refspec[nr-1] = ref;
+ remote->push_refspec_nr = nr;
+}
+
+static void add_fetch_refspec(struct remote *remote, const char *ref)
+{
+ int nr = remote->fetch_refspec_nr + 1;
+ remote->fetch_refspec =
+ xrealloc(remote->fetch_refspec, nr * sizeof(char *));
+ remote->fetch_refspec[nr-1] = ref;
+ remote->fetch_refspec_nr = nr;
+}
+
+static void add_uri(struct remote *remote, const char *uri)
+{
+ int nr = remote->uri_nr + 1;
+ remote->uri =
+ xrealloc(remote->uri, nr * sizeof(char *));
+ remote->uri[nr-1] = uri;
+ remote->uri_nr = nr;
+}
+
+static struct remote *make_remote(const char *name, int len)
+{
+ int i, empty = -1;
+
+ for (i = 0; i < allocated_remotes; i++) {
+ if (!remotes[i]) {
+ if (empty < 0)
+ empty = i;
+ } else {
+ if (len ? (!strncmp(name, remotes[i]->name, len) &&
+ !remotes[i]->name[len]) :
+ !strcmp(name, remotes[i]->name))
+ return remotes[i];
+ }
+ }
+
+ if (empty < 0) {
+ empty = allocated_remotes;
+ allocated_remotes += allocated_remotes ? allocated_remotes : 1;
+ remotes = xrealloc(remotes,
+ sizeof(*remotes) * allocated_remotes);
+ memset(remotes + empty, 0,
+ (allocated_remotes - empty) * sizeof(*remotes));
+ }
+ remotes[empty] = xcalloc(1, sizeof(struct remote));
+ if (len)
+ remotes[empty]->name = xstrndup(name, len);
+ else
+ remotes[empty]->name = xstrdup(name);
+ return remotes[empty];
+}
+
+static void read_remotes_file(struct remote *remote)
+{
+ FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
+
+ if (!f)
+ return;
+ while (fgets(buffer, BUF_SIZE, f)) {
+ int value_list;
+ char *s, *p;
+
+ if (!prefixcmp(buffer, "URL:")) {
+ value_list = 0;
+ s = buffer + 4;
+ } else if (!prefixcmp(buffer, "Push:")) {
+ value_list = 1;
+ s = buffer + 5;
+ } else if (!prefixcmp(buffer, "Pull:")) {
+ value_list = 2;
+ s = buffer + 5;
+ } else
+ continue;
+
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ continue;
+
+ p = s + strlen(s);
+ while (isspace(p[-1]))
+ *--p = 0;
+
+ switch (value_list) {
+ case 0:
+ add_uri(remote, xstrdup(s));
+ break;
+ case 1:
+ add_push_refspec(remote, xstrdup(s));
+ break;
+ case 2:
+ add_fetch_refspec(remote, xstrdup(s));
+ break;
+ }
+ }
+ fclose(f);
+}
+
+static void read_branches_file(struct remote *remote)
+{
+ const char *slash = strchr(remote->name, '/');
+ int n = slash ? slash - remote->name : 1000;
+ FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
+ char *s, *p;
+ int len;
+
+ if (!f)
+ return;
+ s = fgets(buffer, BUF_SIZE, f);
+ fclose(f);
+ if (!s)
+ return;
+ while (isspace(*s))
+ s++;
+ if (!*s)
+ return;
+ p = s + strlen(s);
+ while (isspace(p[-1]))
+ *--p = 0;
+ len = p - s;
+ if (slash)
+ len += strlen(slash);
+ p = xmalloc(len + 1);
+ strcpy(p, s);
+ if (slash)
+ strcat(p, slash);
+ add_uri(remote, p);
+}
+
+static char *default_remote_name = NULL;
+static const char *current_branch = NULL;
+static int current_branch_len = 0;
+
+static int handle_config(const char *key, const char *value)
+{
+ const char *name;
+ const char *subkey;
+ struct remote *remote;
+ if (!prefixcmp(key, "branch.") && current_branch &&
+ !strncmp(key + 7, current_branch, current_branch_len) &&
+ !strcmp(key + 7 + current_branch_len, ".remote")) {
+ free(default_remote_name);
+ default_remote_name = xstrdup(value);
+ }
+ if (prefixcmp(key, "remote."))
+ return 0;
+ name = key + 7;
+ subkey = strrchr(name, '.');
+ if (!subkey)
+ return error("Config with no key for remote %s", name);
+ if (*subkey == '/') {
+ warning("Config remote shorthand cannot begin with '/': %s", name);
+ return 0;
+ }
+ remote = make_remote(name, subkey - name);
+ if (!value) {
+ /* if we ever have a boolean variable, e.g. "remote.*.disabled"
+ * [remote "frotz"]
+ * disabled
+ * is a valid way to set it to true; we get NULL in value so
+ * we need to handle it here.
+ *
+ * if (!strcmp(subkey, ".disabled")) {
+ * val = git_config_bool(key, value);
+ * return 0;
+ * } else
+ *
+ */
+ return 0; /* ignore unknown booleans */
+ }
+ if (!strcmp(subkey, ".url")) {
+ add_uri(remote, xstrdup(value));
+ } else if (!strcmp(subkey, ".push")) {
+ add_push_refspec(remote, xstrdup(value));
+ } else if (!strcmp(subkey, ".fetch")) {
+ add_fetch_refspec(remote, xstrdup(value));
+ } else if (!strcmp(subkey, ".receivepack")) {
+ if (!remote->receivepack)
+ remote->receivepack = xstrdup(value);
+ else
+ error("more than one receivepack given, using the first");
+ }
+ return 0;
+}
+
+static void read_config(void)
+{
+ unsigned char sha1[20];
+ const char *head_ref;
+ int flag;
+ if (default_remote_name) // did this already
+ return;
+ default_remote_name = xstrdup("origin");
+ current_branch = NULL;
+ head_ref = resolve_ref("HEAD", sha1, 0, &flag);
+ if (head_ref && (flag & REF_ISSYMREF) &&
+ !prefixcmp(head_ref, "refs/heads/")) {
+ current_branch = head_ref + strlen("refs/heads/");
+ current_branch_len = strlen(current_branch);
+ }
+ git_config(handle_config);
+}
+
+static struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
+{
+ int i;
+ struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
+ for (i = 0; i < nr_refspec; i++) {
+ const char *sp, *ep, *gp;
+ sp = refspec[i];
+ if (*sp == '+') {
+ rs[i].force = 1;
+ sp++;
+ }
+ gp = strchr(sp, '*');
+ ep = strchr(sp, ':');
+ if (gp && ep && gp > ep)
+ gp = NULL;
+ if (ep) {
+ if (ep[1]) {
+ const char *glob = strchr(ep + 1, '*');
+ if (!glob)
+ gp = NULL;
+ if (gp)
+ rs[i].dst = xstrndup(ep + 1,
+ glob - ep - 1);
+ else
+ rs[i].dst = xstrdup(ep + 1);
+ }
+ } else {
+ ep = sp + strlen(sp);
+ }
+ if (gp) {
+ rs[i].pattern = 1;
+ ep = gp;
+ }
+ rs[i].src = xstrndup(sp, ep - sp);
+ }
+ return rs;
+}
+
+struct remote *remote_get(const char *name)
+{
+ struct remote *ret;
+
+ read_config();
+ if (!name)
+ name = default_remote_name;
+ ret = make_remote(name, 0);
+ if (name[0] != '/') {
+ if (!ret->uri)
+ read_remotes_file(ret);
+ if (!ret->uri)
+ read_branches_file(ret);
+ }
+ if (!ret->uri)
+ add_uri(ret, name);
+ if (!ret->uri)
+ return NULL;
+ ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
+ ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec);
+ return ret;
+}
+
+int for_each_remote(each_remote_fn fn, void *priv)
+{
+ int i, result = 0;
+ read_config();
+ for (i = 0; i < allocated_remotes && !result; i++) {
+ struct remote *r = remotes[i];
+ if (!r)
+ continue;
+ if (!r->fetch)
+ r->fetch = parse_ref_spec(r->fetch_refspec_nr,
+ r->fetch_refspec);
+ if (!r->push)
+ r->push = parse_ref_spec(r->push_refspec_nr,
+ r->push_refspec);
+ result = fn(r, priv);
+ }
+ return result;
+}
+
+int remote_has_uri(struct remote *remote, const char *uri)
+{
+ int i;
+ for (i = 0; i < remote->uri_nr; i++) {
+ if (!strcmp(remote->uri[i], uri))
+ return 1;
+ }
+ return 0;
+}
+
+int remote_find_tracking(struct remote *remote, struct refspec *refspec)
+{
+ int find_src = refspec->src == NULL;
+ char *needle, **result;
+ int i;
+
+ if (find_src) {
+ if (refspec->dst == NULL)
+ return error("find_tracking: need either src or dst");
+ needle = refspec->dst;
+ result = &refspec->src;
+ } else {
+ needle = refspec->src;
+ result = &refspec->dst;
+ }
+
+ for (i = 0; i < remote->fetch_refspec_nr; i++) {
+ struct refspec *fetch = &remote->fetch[i];
+ const char *key = find_src ? fetch->dst : fetch->src;
+ const char *value = find_src ? fetch->src : fetch->dst;
+ if (!fetch->dst)
+ continue;
+ if (fetch->pattern) {
+ if (!prefixcmp(needle, key)) {
+ *result = xmalloc(strlen(value) +
+ strlen(needle) -
+ strlen(key) + 1);
+ strcpy(*result, value);
+ strcpy(*result + strlen(value),
+ needle + strlen(key));
+ refspec->force = fetch->force;
+ return 0;
+ }
+ } else if (!strcmp(needle, key)) {
+ *result = xstrdup(value);
+ refspec->force = fetch->force;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+struct ref *alloc_ref(unsigned namelen)
+{
+ struct ref *ret = xmalloc(sizeof(struct ref) + namelen);
+ memset(ret, 0, sizeof(struct ref) + namelen);
+ return ret;
+}
+
+void free_refs(struct ref *ref)
+{
+ struct ref *next;
+ while (ref) {
+ next = ref->next;
+ if (ref->peer_ref)
+ free(ref->peer_ref);
+ free(ref);
+ ref = next;
+ }
+}
+
+static int count_refspec_match(const char *pattern,
+ struct ref *refs,
+ struct ref **matched_ref)
+{
+ int patlen = strlen(pattern);
+ struct ref *matched_weak = NULL;
+ struct ref *matched = NULL;
+ int weak_match = 0;
+ int match = 0;
+
+ for (weak_match = match = 0; refs; refs = refs->next) {
+ char *name = refs->name;
+ int namelen = strlen(name);
+
+ if (namelen < patlen ||
+ memcmp(name + namelen - patlen, pattern, patlen))
+ continue;
+ if (namelen != patlen && name[namelen - patlen - 1] != '/')
+ continue;
+
+ /* A match is "weak" if it is with refs outside
+ * heads or tags, and did not specify the pattern
+ * in full (e.g. "refs/remotes/origin/master") or at
+ * least from the toplevel (e.g. "remotes/origin/master");
+ * otherwise "git push $URL master" would result in
+ * ambiguity between remotes/origin/master and heads/master
+ * at the remote site.
+ */
+ if (namelen != patlen &&
+ patlen != namelen - 5 &&
+ prefixcmp(name, "refs/heads/") &&
+ prefixcmp(name, "refs/tags/")) {
+ /* We want to catch the case where only weak
+ * matches are found and there are multiple
+ * matches, and where more than one strong
+ * matches are found, as ambiguous. One
+ * strong match with zero or more weak matches
+ * are acceptable as a unique match.
+ */
+ matched_weak = refs;
+ weak_match++;
+ }
+ else {
+ matched = refs;
+ match++;
+ }
+ }
+ if (!matched) {
+ *matched_ref = matched_weak;
+ return weak_match;
+ }
+ else {
+ *matched_ref = matched;
+ return match;
+ }
+}
+
+static void tail_link_ref(struct ref *ref, struct ref ***tail)
+{
+ **tail = ref;
+ while (ref->next)
+ ref = ref->next;
+ *tail = &ref->next;
+}
+
+static struct ref *try_explicit_object_name(const char *name)
+{
+ unsigned char sha1[20];
+ struct ref *ref;
+ int len;
+
+ if (!*name) {
+ ref = alloc_ref(20);
+ strcpy(ref->name, "(delete)");
+ hashclr(ref->new_sha1);
+ return ref;
+ }
+ if (get_sha1(name, sha1))
+ return NULL;
+ len = strlen(name) + 1;
+ ref = alloc_ref(len);
+ memcpy(ref->name, name, len);
+ hashcpy(ref->new_sha1, sha1);
+ return ref;
+}
+
+static struct ref *make_linked_ref(const char *name, struct ref ***tail)
+{
+ struct ref *ret;
+ size_t len;
+
+ len = strlen(name) + 1;
+ ret = alloc_ref(len);
+ memcpy(ret->name, name, len);
+ tail_link_ref(ret, tail);
+ return ret;
+}
+
+static int match_explicit(struct ref *src, struct ref *dst,
+ struct ref ***dst_tail,
+ struct refspec *rs,
+ int errs)
+{
+ struct ref *matched_src, *matched_dst;
+
+ const char *dst_value = rs->dst;
+
+ if (rs->pattern)
+ return errs;
+
+ matched_src = matched_dst = NULL;
+ switch (count_refspec_match(rs->src, src, &matched_src)) {
+ case 1:
+ break;
+ case 0:
+ /* The source could be in the get_sha1() format
+ * not a reference name. :refs/other is a
+ * way to delete 'other' ref at the remote end.
+ */
+ matched_src = try_explicit_object_name(rs->src);
+ if (matched_src)
+ break;
+ error("src refspec %s does not match any.",
+ rs->src);
+ break;
+ default:
+ matched_src = NULL;
+ error("src refspec %s matches more than one.",
+ rs->src);
+ break;
+ }
+
+ if (!matched_src)
+ errs = 1;
+
+ if (dst_value == NULL)
+ dst_value = matched_src->name;
+
+ switch (count_refspec_match(dst_value, dst, &matched_dst)) {
+ case 1:
+ break;
+ case 0:
+ if (!memcmp(dst_value, "refs/", 5))
+ matched_dst = make_linked_ref(dst_value, dst_tail);
+ else
+ error("dst refspec %s does not match any "
+ "existing ref on the remote and does "
+ "not start with refs/.", dst_value);
+ break;
+ default:
+ matched_dst = NULL;
+ error("dst refspec %s matches more than one.",
+ dst_value);
+ break;
+ }
+ if (errs || matched_dst == NULL)
+ return 1;
+ if (matched_dst->peer_ref) {
+ errs = 1;
+ error("dst ref %s receives from more than one src.",
+ matched_dst->name);
+ }
+ else {
+ matched_dst->peer_ref = matched_src;
+ matched_dst->force = rs->force;
+ }
+ return errs;
+}
+
+static int match_explicit_refs(struct ref *src, struct ref *dst,
+ struct ref ***dst_tail, struct refspec *rs,
+ int rs_nr)
+{
+ int i, errs;
+ for (i = errs = 0; i < rs_nr; i++)
+ errs |= match_explicit(src, dst, dst_tail, &rs[i], errs);
+ return -errs;
+}
+
+static struct ref *find_ref_by_name(struct ref *list, const char *name)
+{
+ for ( ; list; list = list->next)
+ if (!strcmp(list->name, name))
+ return list;
+ return NULL;
+}
+
+static const struct refspec *check_pattern_match(const struct refspec *rs,
+ int rs_nr,
+ const struct ref *src)
+{
+ int i;
+ for (i = 0; i < rs_nr; i++) {
+ if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
+ return rs + i;
+ }
+ return NULL;
+}
+
+/*
+ * Note. This is used only by "push"; refspec matching rules for
+ * push and fetch are subtly different, so do not try to reuse it
+ * without thinking.
+ */
+int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
+ int nr_refspec, char **refspec, int all)
+{
+ struct refspec *rs =
+ parse_ref_spec(nr_refspec, (const char **) refspec);
+
+ if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
+ return -1;
+
+ /* pick the remainder */
+ for ( ; src; src = src->next) {
+ struct ref *dst_peer;
+ const struct refspec *pat = NULL;
+ char *dst_name;
+ if (src->peer_ref)
+ continue;
+ if (nr_refspec) {
+ pat = check_pattern_match(rs, nr_refspec, src);
+ if (!pat)
+ continue;
+ }
+ else if (prefixcmp(src->name, "refs/heads/"))
+ /*
+ * "matching refs"; traditionally we pushed everything
+ * including refs outside refs/heads/ hierarchy, but
+ * that does not make much sense these days.
+ */
+ continue;
+
+ if (pat) {
+ const char *dst_side = pat->dst ? pat->dst : pat->src;
+ dst_name = xmalloc(strlen(dst_side) +
+ strlen(src->name) -
+ strlen(pat->src) + 2);
+ strcpy(dst_name, dst_side);
+ strcat(dst_name, src->name + strlen(pat->src));
+ } else
+ dst_name = xstrdup(src->name);
+ dst_peer = find_ref_by_name(dst, dst_name);
+ if (dst_peer && dst_peer->peer_ref)
+ /* We're already sending something to this ref. */
+ goto free_name;
+ if (!dst_peer && !nr_refspec && !all)
+ /* Remote doesn't have it, and we have no
+ * explicit pattern, and we don't have
+ * --all. */
+ goto free_name;
+ if (!dst_peer) {
+ /* Create a new one and link it */
+ dst_peer = make_linked_ref(dst_name, dst_tail);
+ hashcpy(dst_peer->new_sha1, src->new_sha1);
+ }
+ dst_peer->peer_ref = src;
+ free_name:
+ free(dst_name);
+ }
+ return 0;
+}
diff --git a/remote.h b/remote.h
new file mode 100644
index 0000000000..17b8b5b5d5
--- /dev/null
+++ b/remote.h
@@ -0,0 +1,51 @@
+#ifndef REMOTE_H
+#define REMOTE_H
+
+struct remote {
+ const char *name;
+
+ const char **uri;
+ int uri_nr;
+
+ const char **push_refspec;
+ struct refspec *push;
+ int push_refspec_nr;
+
+ const char **fetch_refspec;
+ struct refspec *fetch;
+ int fetch_refspec_nr;
+
+ const char *receivepack;
+};
+
+struct remote *remote_get(const char *name);
+
+typedef int each_remote_fn(struct remote *remote, void *priv);
+int for_each_remote(each_remote_fn fn, void *priv);
+
+int remote_has_uri(struct remote *remote, const char *uri);
+
+struct refspec {
+ unsigned force : 1;
+ unsigned pattern : 1;
+
+ char *src;
+ char *dst;
+};
+
+struct ref *alloc_ref(unsigned namelen);
+
+/*
+ * Frees the entire list and peers of elements.
+ */
+void free_refs(struct ref *ref);
+
+int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
+ int nr_refspec, char **refspec, int all);
+
+/*
+ * For the given remote, reads the refspec's src and sets the other fields.
+ */
+int remote_find_tracking(struct remote *remote, struct refspec *refspec);
+
+#endif
diff --git a/revision.c b/revision.c
index e43c648978..7d32a89b0e 100644
--- a/revision.c
+++ b/revision.c
@@ -114,19 +114,20 @@ void mark_parents_uninteresting(struct commit *commit)
}
}
-void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
-{
- add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
-}
-
-void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
+static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
{
if (revs->no_walk && (obj->flags & UNINTERESTING))
die("object ranges do not make sense when not walking revisions");
+ if (revs->reflog_info && obj->type == OBJ_COMMIT &&
+ add_reflog_for_walk(revs->reflog_info,
+ (struct commit *)obj, name))
+ return;
add_object_array_with_mode(obj, name, &revs->pending, mode);
- if (revs->reflog_info && obj->type == OBJ_COMMIT)
- add_reflog_for_walk(revs->reflog_info,
- (struct commit *)obj, name);
+}
+
+void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+{
+ add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
}
static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
@@ -262,7 +263,7 @@ static void file_change(struct diff_options *options,
options->has_changes = 1;
}
-int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
+static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
{
if (!t1)
return REV_TREE_NEW;
@@ -276,7 +277,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
return tree_difference;
}
-int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
+static int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
{
int retval;
void *tree;
@@ -437,7 +438,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
return 0;
}
-static void cherry_pick_list(struct commit_list *list)
+static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
{
struct commit_list *p;
int left_count = 0, right_count = 0;
@@ -458,6 +459,11 @@ static void cherry_pick_list(struct commit_list *list)
left_first = left_count < right_count;
init_patch_ids(&ids);
+ if (revs->diffopt.nr_paths) {
+ ids.diffopts.nr_paths = revs->diffopt.nr_paths;
+ ids.diffopts.paths = revs->diffopt.paths;
+ ids.diffopts.pathlens = revs->diffopt.pathlens;
+ }
/* Compute patch-ids for one side */
for (p = list; p; p = p->next) {
@@ -546,7 +552,7 @@ static int limit_list(struct rev_info *revs)
p = &commit_list_insert(commit, p)->next;
}
if (revs->cherry_pick)
- cherry_pick_list(newlist);
+ cherry_pick_list(newlist, revs);
revs->commits = newlist;
return 0;
@@ -667,7 +673,6 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->min_age = -1;
revs->skip_count = -1;
revs->max_count = -1;
- revs->subject_prefix = "PATCH";
revs->prune_fn = NULL;
revs->prune_data = NULL;
@@ -881,6 +886,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
const char **unrecognized = argv + 1;
int left = 1;
int all_match = 0;
+ int regflags = 0;
/* First, search for "--" */
seen_dashdash = 0;
@@ -1128,6 +1134,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
if (!strncmp(arg, "--date=", 7)) {
if (!strcmp(arg + 7, "relative"))
revs->date_mode = DATE_RELATIVE;
+ else if (!strcmp(arg + 7, "iso8601") ||
+ !strcmp(arg + 7, "iso"))
+ revs->date_mode = DATE_ISO8601;
+ else if (!strcmp(arg + 7, "rfc2822") ||
+ !strcmp(arg + 7, "rfc"))
+ revs->date_mode = DATE_RFC2822;
+ else if (!strcmp(arg + 7, "short"))
+ revs->date_mode = DATE_SHORT;
else if (!strcmp(arg + 7, "local"))
revs->date_mode = DATE_LOCAL;
else if (!strcmp(arg + 7, "default"))
@@ -1136,6 +1150,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
die("unknown date format %s", arg);
continue;
}
+ if (!strcmp(arg, "--log-size")) {
+ revs->show_log_size = 1;
+ continue;
+ }
/*
* Grepping the commit log
@@ -1152,6 +1170,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
add_message_grep(revs, arg+7);
continue;
}
+ if (!strcmp(arg, "--extended-regexp") ||
+ !strcmp(arg, "-E")) {
+ regflags |= REG_EXTENDED;
+ continue;
+ }
+ if (!strcmp(arg, "--regexp-ignore-case") ||
+ !strcmp(arg, "-i")) {
+ regflags |= REG_ICASE;
+ continue;
+ }
if (!strcmp(arg, "--all-match")) {
all_match = 1;
continue;
@@ -1168,6 +1196,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
revs->reverse ^= 1;
continue;
}
+ if (!strcmp(arg, "--no-walk")) {
+ revs->no_walk = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--do-walk")) {
+ revs->no_walk = 0;
+ continue;
+ }
opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
if (opts > 0) {
@@ -1201,6 +1237,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
}
}
+ if (revs->grep_filter)
+ revs->grep_filter->regflags |= regflags;
+
if (show_merge)
prepare_show_merge(revs);
if (def && !revs->pending.nr) {
@@ -1218,7 +1257,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
if (revs->prune_data) {
diff_tree_setup_paths(revs->prune_data, &revs->pruning);
- revs->prune_fn = try_to_simplify_commit;
+ /* Can't prune commits with rename following: the paths change.. */
+ if (!revs->diffopt.follow_renames)
+ revs->prune_fn = try_to_simplify_commit;
if (!revs->full_diff)
diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
}
@@ -1295,6 +1336,26 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp
}
}
+static void remove_duplicate_parents(struct commit *commit)
+{
+ struct commit_list **pp, *p;
+
+ /* Examine existing parents while marking ones we have seen... */
+ pp = &commit->parents;
+ while ((p = *pp) != NULL) {
+ struct commit *parent = p->item;
+ if (parent->object.flags & TMP_MARK) {
+ *pp = p->next;
+ continue;
+ }
+ parent->object.flags |= TMP_MARK;
+ pp = &p->next;
+ }
+ /* ... and clear the temporary mark */
+ for (p = commit->parents; p; p = p->next)
+ p->item->object.flags &= ~TMP_MARK;
+}
+
static int rewrite_parents(struct rev_info *revs, struct commit *commit)
{
struct commit_list **pp = &commit->parents;
@@ -1311,6 +1372,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
}
pp = &parent->next;
}
+ remove_duplicate_parents(commit);
return 0;
}
diff --git a/revision.h b/revision.h
index 2845167746..98a0a8f3fa 100644
--- a/revision.h
+++ b/revision.h
@@ -81,6 +81,7 @@ struct rev_info {
const char *log_reencode;
const char *subject_prefix;
int no_inline;
+ int show_log_size;
/* Filter by commit log message */
struct grep_opt *grep_filter;
@@ -106,8 +107,6 @@ struct rev_info {
#define REV_TREE_DIFFERENT 2
/* revision.c */
-extern int rev_same_tree_as_empty(struct rev_info *, struct tree *t1);
-extern int rev_compare_tree(struct rev_info *, struct tree *t1, struct tree *t2);
extern void init_revisions(struct rev_info *revs, const char *prefix);
extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
@@ -131,6 +130,5 @@ extern void add_object(struct object *obj,
const char *name);
extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
-extern void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode);
#endif
diff --git a/rsh.h b/rsh.h
index 3b4194239d..ee2f499291 100644
--- a/rsh.h
+++ b/rsh.h
@@ -1,7 +1,7 @@
#ifndef RSH_H
#define RSH_H
-int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
+int setup_connection(int *fd_in, int *fd_out, const char *remote_prog,
char *url, int rmt_argc, char **rmt_argv);
#endif
diff --git a/run-command.c b/run-command.c
index eff523e191..7e779d33ee 100644
--- a/run-command.c
+++ b/run-command.c
@@ -73,6 +73,17 @@ int start_command(struct child_process *cmd)
close(cmd->out);
}
+ if (cmd->dir && chdir(cmd->dir))
+ die("exec %s: cd to %s failed (%s)", cmd->argv[0],
+ cmd->dir, strerror(errno));
+ if (cmd->env) {
+ for (; *cmd->env; cmd->env++) {
+ if (strchr(*cmd->env, '='))
+ putenv((char*)*cmd->env);
+ else
+ unsetenv(*cmd->env);
+ }
+ }
if (cmd->git_cmd) {
execv_git_cmd(cmd->argv);
} else {
@@ -133,13 +144,37 @@ int run_command(struct child_process *cmd)
return finish_command(cmd);
}
+static void prepare_run_command_v_opt(struct child_process *cmd,
+ const char **argv,
+ int opt)
+{
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->argv = argv;
+ cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
+ cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
+ cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+}
+
int run_command_v_opt(const char **argv, int opt)
{
struct child_process cmd;
- memset(&cmd, 0, sizeof(cmd));
- cmd.argv = argv;
- cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
- cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
- cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+ prepare_run_command_v_opt(&cmd, argv, opt);
+ return run_command(&cmd);
+}
+
+int run_command_v_opt_cd(const char **argv, int opt, const char *dir)
+{
+ struct child_process cmd;
+ prepare_run_command_v_opt(&cmd, argv, opt);
+ cmd.dir = dir;
+ return run_command(&cmd);
+}
+
+int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
+{
+ struct child_process cmd;
+ prepare_run_command_v_opt(&cmd, argv, opt);
+ cmd.dir = dir;
+ cmd.env = env;
return run_command(&cmd);
}
diff --git a/run-command.h b/run-command.h
index 3680ef9d45..7958eb1e0b 100644
--- a/run-command.h
+++ b/run-command.h
@@ -16,6 +16,8 @@ struct child_process {
pid_t pid;
int in;
int out;
+ const char *dir;
+ const char *const *env;
unsigned close_in:1;
unsigned close_out:1;
unsigned no_stdin:1;
@@ -32,5 +34,12 @@ int run_command(struct child_process *);
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
#define RUN_COMMAND_STDOUT_TO_STDERR 4
int run_command_v_opt(const char **argv, int opt);
+int run_command_v_opt_cd(const char **argv, int opt, const char *dir);
+
+/*
+ * env (the environment) is to be formatted like environ: "VAR=VALUE".
+ * To unset an environment variable use just "VAR".
+ */
+int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
#endif
diff --git a/send-pack.c b/send-pack.c
index d5b51628df..9fc8a812f4 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -4,6 +4,7 @@
#include "refs.h"
#include "pkt-line.h"
#include "run-command.h"
+#include "remote.h"
static const char send_pack_usage[] =
"git-send-pack [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -176,7 +177,7 @@ static int receive_status(int in)
return ret;
}
-static int send_pack(int in, int out, int nr_refspec, char **refspec)
+static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec)
{
struct ref *ref;
int new_refs;
@@ -213,18 +214,19 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
char old_hex[60], *new_hex;
- int delete_ref;
+ int will_delete_ref;
if (!ref->peer_ref)
continue;
- delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
- if (delete_ref && !allow_deleting_refs) {
+
+ will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
+ if (will_delete_ref && !allow_deleting_refs) {
error("remote does not support deleting refs");
ret = -2;
continue;
}
- if (!delete_ref &&
+ if (!will_delete_ref &&
!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
if (verbose)
fprintf(stderr, "'%s': up-to-date\n", ref->name);
@@ -251,7 +253,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
*/
if (!force_update &&
- !delete_ref &&
+ !will_delete_ref &&
!is_null_sha1(ref->old_sha1) &&
!ref->force) {
if (!has_sha1_file(ref->old_sha1) ||
@@ -275,7 +277,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
}
}
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
- if (!delete_ref)
+ if (!will_delete_ref)
new_refs++;
strcpy(old_hex, sha1_to_hex(ref->old_sha1));
new_hex = sha1_to_hex(ref->new_sha1);
@@ -290,7 +292,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
else
packet_write(out, "%s %s %s",
old_hex, new_hex, ref->name);
- if (delete_ref)
+ if (will_delete_ref)
fprintf(stderr, "deleting '%s'\n", ref->name);
else {
fprintf(stderr, "updating '%s'", ref->name);
@@ -300,6 +302,28 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
fprintf(stderr, "\n from %s\n to %s\n",
old_hex, new_hex);
}
+ if (remote) {
+ struct refspec rs;
+ rs.src = ref->name;
+ rs.dst = NULL;
+ if (!remote_find_tracking(remote, &rs)) {
+ struct ref_lock *lock;
+ fprintf(stderr, " Also local %s\n", rs.dst);
+ if (will_delete_ref) {
+ if (delete_ref(rs.dst, NULL)) {
+ error("Failed to delete");
+ }
+ } else {
+ lock = lock_any_ref_for_update(rs.dst, NULL, 0);
+ if (!lock)
+ error("Failed to lock");
+ else
+ write_ref_sha1(lock, ref->new_sha1,
+ "update by push");
+ }
+ free(rs.dst);
+ }
+ }
}
packet_flush(out);
@@ -330,6 +354,7 @@ static void verify_remote_names(int nr_heads, char **heads)
case -2: /* ok but a single level -- that is fine for
* a match pattern.
*/
+ case -3: /* ok but ends with a pattern-match character */
continue;
}
die("remote part of refspec is not a valid name in %s",
@@ -344,6 +369,8 @@ int main(int argc, char **argv)
char **heads = NULL;
int fd[2], ret;
pid_t pid;
+ char *remote_name = NULL;
+ struct remote *remote = NULL;
setup_git_directory();
git_config(git_default_config);
@@ -361,6 +388,10 @@ int main(int argc, char **argv)
receivepack = arg + 7;
continue;
}
+ if (!prefixcmp(arg, "--remote=")) {
+ remote_name = arg + 9;
+ continue;
+ }
if (!strcmp(arg, "--all")) {
send_all = 1;
continue;
@@ -393,10 +424,18 @@ int main(int argc, char **argv)
usage(send_pack_usage);
verify_remote_names(nr_heads, heads);
- pid = git_connect(fd, dest, receivepack);
+ if (remote_name) {
+ remote = remote_get(remote_name);
+ if (!remote_has_uri(remote, dest)) {
+ die("Destination %s is not a uri for %s",
+ dest, remote_name);
+ }
+ }
+
+ pid = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
if (pid < 0)
return 1;
- ret = send_pack(fd[0], fd[1], nr_heads, heads);
+ ret = send_pack(fd[0], fd[1], remote, nr_heads, heads);
close(fd[0]);
close(fd[1]);
ret |= finish_connect(pid);
diff --git a/server-info.c b/server-info.c
index f9be5a7f60..0d1312ca56 100644
--- a/server-info.c
+++ b/server-info.c
@@ -38,6 +38,7 @@ static int update_info_refs(int force)
return error("unable to update %s", path0);
for_each_ref(add_info_ref, NULL);
fclose(info_ref_fp);
+ adjust_shared_perm(path1);
rename(path1, path0);
free(path0);
free(path1);
@@ -227,6 +228,7 @@ static int update_info_packs(int force)
return error("cannot open %s", name);
write_pack_info_file(fp);
fclose(fp);
+ adjust_shared_perm(name);
rename(name, infofile);
return 0;
}
diff --git a/setup.c b/setup.c
index 2b8e8c0e5e..06004f1587 100644
--- a/setup.c
+++ b/setup.c
@@ -1,4 +1,8 @@
#include "cache.h"
+#include "dir.h"
+
+static int inside_git_dir = -1;
+static int inside_work_tree = -1;
const char *prefix_path(const char *prefix, int len, const char *path)
{
@@ -39,7 +43,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
if (len) {
int speclen = strlen(path);
char *n = xmalloc(speclen + len + 1);
-
+
memcpy(n, prefix, len);
memcpy(n + len, path, speclen+1);
path = n;
@@ -47,7 +51,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
return path;
}
-/*
+/*
* Unlike prefix_path, this should be used if the named file does
* not have to interact with index entry; i.e. name of a random file
* on the filesystem.
@@ -95,7 +99,7 @@ void verify_non_filename(const char *prefix, const char *arg)
const char *name;
struct stat st;
- if (is_inside_git_dir())
+ if (!is_inside_work_tree() || is_inside_git_dir())
return;
if (*arg == '-')
return; /* flag */
@@ -170,30 +174,45 @@ static int is_git_directory(const char *suspect)
return 1;
}
-static int inside_git_dir = -1;
-
int is_inside_git_dir(void)
{
- if (inside_git_dir < 0) {
- char buffer[1024];
-
- if (is_bare_repository())
- return (inside_git_dir = 1);
- if (getcwd(buffer, sizeof(buffer))) {
- const char *git_dir = get_git_dir(), *cwd = buffer;
- while (*git_dir && *git_dir == *cwd) {
- git_dir++;
- cwd++;
- }
- inside_git_dir = !*git_dir;
- } else
- inside_git_dir = 0;
- }
+ if (inside_git_dir < 0)
+ inside_git_dir = is_inside_dir(get_git_dir());
return inside_git_dir;
}
+int is_inside_work_tree(void)
+{
+ if (inside_work_tree < 0)
+ inside_work_tree = is_inside_dir(get_git_work_tree());
+ return inside_work_tree;
+}
+
+/*
+ * set_work_tree() is only ever called if you set GIT_DIR explicitely.
+ * The old behaviour (which we retain here) is to set the work tree root
+ * to the cwd, unless overridden by the config, the command line, or
+ * GIT_WORK_TREE.
+ */
+static const char *set_work_tree(const char *dir)
+{
+ char buffer[PATH_MAX + 1];
+
+ if (!getcwd(buffer, sizeof(buffer)))
+ die ("Could not get the current working directory");
+ git_work_tree_cfg = xstrdup(buffer);
+ inside_work_tree = 1;
+
+ return NULL;
+}
+
+/*
+ * We cannot decide in this function whether we are in the work tree or
+ * not, since the config can only be read _after_ this function was called.
+ */
const char *setup_git_directory_gently(int *nongit_ok)
{
+ const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
static char cwd[PATH_MAX+1];
const char *gitdirenv;
int len, offset;
@@ -207,8 +226,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
if (gitdirenv) {
if (PATH_MAX - 40 < strlen(gitdirenv))
die("'$%s' too big", GIT_DIR_ENVIRONMENT);
- if (is_git_directory(gitdirenv))
+ if (is_git_directory(gitdirenv)) {
+ if (!work_tree_env)
+ return set_work_tree(gitdirenv);
return NULL;
+ }
if (nongit_ok) {
*nongit_ok = 1;
return NULL;
@@ -216,23 +238,32 @@ const char *setup_git_directory_gently(int *nongit_ok)
die("Not a git repository: '%s'", gitdirenv);
}
- if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
+ if (!getcwd(cwd, sizeof(cwd)-1))
die("Unable to read current working directory");
+ /*
+ * Test in the following order (relative to the cwd):
+ * - .git/
+ * - ./ (bare)
+ * - ../.git/
+ * - ../ (bare)
+ * - ../../.git/
+ * etc.
+ */
offset = len = strlen(cwd);
for (;;) {
- if (is_git_directory(".git"))
+ if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
break;
+ if (is_git_directory(".")) {
+ inside_git_dir = 1;
+ if (!work_tree_env)
+ inside_work_tree = 0;
+ setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+ return NULL;
+ }
chdir("..");
do {
if (!offset) {
- if (is_git_directory(cwd)) {
- if (chdir(cwd))
- die("Cannot come back to cwd");
- setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
- inside_git_dir = 1;
- return NULL;
- }
if (nongit_ok) {
if (chdir(cwd))
die("Cannot come back to cwd");
@@ -244,6 +275,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
} while (cwd[--offset] != '/');
}
+ inside_git_dir = 0;
+ if (!work_tree_env)
+ inside_work_tree = 1;
+ git_work_tree_cfg = xstrndup(cwd, offset);
if (offset == len)
return NULL;
@@ -251,13 +286,13 @@ const char *setup_git_directory_gently(int *nongit_ok)
offset++;
cwd[len++] = '/';
cwd[len] = 0;
- inside_git_dir = !prefixcmp(cwd + offset, ".git/");
return cwd + offset;
}
int git_config_perm(const char *var, const char *value)
{
if (value) {
+ int i;
if (!strcmp(value, "umask"))
return PERM_UMASK;
if (!strcmp(value, "group"))
@@ -266,17 +301,30 @@ int git_config_perm(const char *var, const char *value)
!strcmp(value, "world") ||
!strcmp(value, "everybody"))
return PERM_EVERYBODY;
+ i = atoi(value);
+ if (i > 1)
+ return i;
}
return git_config_bool(var, value);
}
int check_repository_format_version(const char *var, const char *value)
{
- if (strcmp(var, "core.repositoryformatversion") == 0)
- repository_format_version = git_config_int(var, value);
+ if (strcmp(var, "core.repositoryformatversion") == 0)
+ repository_format_version = git_config_int(var, value);
else if (strcmp(var, "core.sharedrepository") == 0)
shared_repository = git_config_perm(var, value);
- return 0;
+ else if (strcmp(var, "core.bare") == 0) {
+ is_bare_repository_cfg = git_config_bool(var, value);
+ if (is_bare_repository_cfg == 1)
+ inside_work_tree = -1;
+ } else if (strcmp(var, "core.worktree") == 0) {
+ if (git_work_tree_cfg)
+ free(git_work_tree_cfg);
+ git_work_tree_cfg = xstrdup(value);
+ inside_work_tree = -1;
+ }
+ return 0;
}
int check_repository_format(void)
@@ -292,5 +340,16 @@ const char *setup_git_directory(void)
{
const char *retval = setup_git_directory_gently(NULL);
check_repository_format();
+
+ /* If the work tree is not the default one, recompute prefix */
+ if (inside_work_tree < 0) {
+ static char buffer[PATH_MAX + 1];
+ char *rel;
+ if (retval && chdir(retval))
+ die ("Could not jump back into original cwd");
+ rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
+ return rel && *rel ? strcat(rel, "/") : NULL;
+ }
+
return retval;
}
diff --git a/sha1_file.c b/sha1_file.c
index 1d328c8d61..b219d4d5f2 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -193,7 +193,7 @@ char *sha1_pack_name(const unsigned char *sha1)
*buf++ = hex[val >> 4];
*buf++ = hex[val & 0xf];
}
-
+
return base;
}
@@ -218,7 +218,7 @@ char *sha1_pack_index_name(const unsigned char *sha1)
*buf++ = hex[val >> 4];
*buf++ = hex[val & 0xf];
}
-
+
return base;
}
@@ -380,11 +380,12 @@ void prepare_alt_odb(void)
{
const char *alt;
+ if (alt_odb_tail)
+ return;
+
alt = getenv(ALTERNATE_DB_ENVIRONMENT);
if (!alt) alt = "";
- if (alt_odb_tail)
- return;
alt_odb_tail = &alt_odb_list;
link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);
@@ -416,7 +417,7 @@ static size_t peak_pack_mapped;
static size_t pack_mapped;
struct packed_git *packed_git;
-void pack_report()
+void pack_report(void)
{
fprintf(stderr,
"pack_report: getpagesize() = %10" SZ_FMT "\n"
@@ -496,7 +497,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
*/
if (idx_size != 4*256 + nr * 24 + 20 + 20) {
munmap(idx_map, idx_size);
- return error("wrong index file size in %s", path);
+ return error("wrong index v1 file size in %s", path);
}
} else if (version == 2) {
/*
@@ -518,7 +519,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
max_size += (nr - 1)*8;
if (idx_size < min_size || idx_size > max_size) {
munmap(idx_map, idx_size);
- return error("wrong index file size in %s", path);
+ return error("wrong index v2 file size in %s", path);
}
if (idx_size != min_size) {
/* make sure we can deal with large pack offsets */
@@ -537,6 +538,21 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
return 0;
}
+int open_pack_index(struct packed_git *p)
+{
+ char *idx_name;
+ int ret;
+
+ if (p->index_data)
+ return 0;
+
+ idx_name = xstrdup(p->pack_name);
+ strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx");
+ ret = check_packed_git_idx(idx_name, p);
+ free(idx_name);
+ return ret;
+}
+
static void scan_windows(struct packed_git *p,
struct packed_git **lru_p,
struct pack_window **lru_w,
@@ -612,6 +628,9 @@ static int open_packed_git_1(struct packed_git *p)
unsigned char *idx_sha1;
long fd_flag;
+ if (!p->index_data && open_pack_index(p))
+ return error("packfile %s index unavailable", p->pack_name);
+
p->pack_fd = open(p->pack_name, O_RDONLY);
if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
return -1;
@@ -764,8 +783,7 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
return NULL;
memcpy(p->pack_name, path, path_len);
strcpy(p->pack_name + path_len, ".pack");
- if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) ||
- check_packed_git_idx(path, p)) {
+ if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
free(p);
return NULL;
}
@@ -773,6 +791,10 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
/* ok, it looks sane as far as we can check without
* actually mapping the pack file.
*/
+ p->index_version = 0;
+ p->index_data = NULL;
+ p->index_size = 0;
+ p->num_objects = 0;
p->pack_size = st.st_size;
p->next = NULL;
p->windows = NULL;
@@ -950,7 +972,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz
return hashcmp(sha1, real_sha1) ? -1 : 0;
}
-void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
+static void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
{
struct stat st;
void *map;
@@ -985,7 +1007,7 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
return map;
}
-int legacy_loose_object(unsigned char *map)
+static int legacy_loose_object(unsigned char *map)
{
unsigned int word;
@@ -1047,6 +1069,14 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
return inflate(stream, 0);
}
+
+ /*
+ * There used to be a second loose object header format which
+ * was meant to mimic the in-pack format, allowing for direct
+ * copy of the object data. This format turned up not to be
+ * really worth it and we don't write it any longer. But we
+ * can still read it.
+ */
used = unpack_object_header_gently(map, mapsize, &type, &size);
if (!used || !valid_loose_object_type[type])
return -1;
@@ -1122,7 +1152,7 @@ static int parse_sha1_header(const char *hdr, unsigned long *sizep)
unsigned long size;
/*
- * The type can be at most ten bytes (including the
+ * The type can be at most ten bytes (including the
* terminating '\0' that we add), and is followed by
* a space.
*/
@@ -1577,10 +1607,15 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
return data;
}
-const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
+const unsigned char *nth_packed_object_sha1(struct packed_git *p,
uint32_t n)
{
const unsigned char *index = p->index_data;
+ if (!index) {
+ if (open_pack_index(p))
+ return NULL;
+ index = p->index_data;
+ }
if (n >= p->num_objects)
return NULL;
index += 4 * 256;
@@ -1617,6 +1652,12 @@ off_t find_pack_entry_one(const unsigned char *sha1,
const unsigned char *index = p->index_data;
unsigned hi, lo;
+ if (!index) {
+ if (open_pack_index(p))
+ return 0;
+ level1_ofs = p->index_data;
+ index = p->index_data;
+ }
if (p->index_version > 1) {
level1_ofs += 2;
index += 8;
@@ -1659,20 +1700,25 @@ static int matches_pack_name(struct packed_git *p, const char *ig)
static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
{
+ static struct packed_git *last_found = (void *)1;
struct packed_git *p;
off_t offset;
prepare_packed_git();
+ if (!packed_git)
+ return 0;
+ p = (last_found == (void *)1) ? packed_git : last_found;
- for (p = packed_git; p; p = p->next) {
+ do {
if (ignore_packed) {
const char **ig;
for (ig = ignore_packed; *ig; ig++)
if (!matches_pack_name(p, *ig))
break;
if (*ig)
- continue;
+ goto next;
}
+
offset = find_pack_entry_one(sha1, p);
if (offset) {
/*
@@ -1685,18 +1731,27 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
*/
if (p->pack_fd == -1 && open_packed_git(p)) {
error("packfile %s cannot be accessed", p->pack_name);
- continue;
+ goto next;
}
e->offset = offset;
e->p = p;
hashcpy(e->sha1, sha1);
+ last_found = p;
return 1;
}
- }
+
+ next:
+ if (p == last_found)
+ p = packed_git;
+ else
+ p = p->next;
+ if (p == last_found)
+ p = p->next;
+ } while (p);
return 0;
}
-struct packed_git *find_sha1_pack(const unsigned char *sha1,
+struct packed_git *find_sha1_pack(const unsigned char *sha1,
struct packed_git *packs)
{
struct packed_git *p;
@@ -1975,40 +2030,6 @@ static int write_buffer(int fd, const void *buf, size_t len)
return 0;
}
-static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len)
-{
- int hdr_len;
- unsigned char c;
-
- c = (type << 4) | (len & 15);
- len >>= 4;
- hdr_len = 1;
- while (len) {
- *hdr++ = c | 0x80;
- hdr_len++;
- c = (len & 0x7f);
- len >>= 7;
- }
- *hdr = c;
- return hdr_len;
-}
-
-static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
-{
- int obj_type, hdrlen;
-
- if (use_legacy_headers) {
- while (deflate(stream, 0) == Z_OK)
- /* nothing */;
- return;
- }
- obj_type = type_from_string(type);
- hdrlen = write_binary_header(stream->next_out, obj_type, len);
- stream->total_out = hdrlen;
- stream->next_out += hdrlen;
- stream->avail_out -= hdrlen;
-}
-
int hash_sha1_file(const void *buf, unsigned long len, const char *type,
unsigned char *sha1)
{
@@ -2075,7 +2096,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
/* First header.. */
stream.next_in = (unsigned char *)hdr;
stream.avail_in = hdrlen;
- setup_object_header(&stream, type, len);
+ while (deflate(&stream, 0) == Z_OK)
+ /* nothing */;
/* Then the data itself.. */
stream.next_in = buf;
@@ -2282,27 +2304,36 @@ int has_sha1_file(const unsigned char *sha1)
*
* returns 0 if anything went fine and -1 otherwise
*
+ * The buffer is always NUL-terminated, not including it in returned size.
+ *
* NOTE: both buf and size may change, but even when -1 is returned
* you still have to free() it yourself.
*/
-int read_pipe(int fd, char** return_buf, unsigned long* return_size)
+int read_fd(int fd, char **return_buf, unsigned long *return_size)
{
- char* buf = *return_buf;
+ char *buf = *return_buf;
unsigned long size = *return_size;
ssize_t iret;
unsigned long off = 0;
+ if (!buf || size <= 1) {
+ size = 1024;
+ buf = xrealloc(buf, size);
+ }
+
do {
- iret = xread(fd, buf + off, size - off);
+ iret = xread(fd, buf + off, (size - 1) - off);
if (iret > 0) {
off += iret;
- if (off == size) {
- size *= 2;
+ if (off == size - 1) {
+ size = alloc_nr(size);
buf = xrealloc(buf, size);
}
}
} while (iret > 0);
+ buf[off] = '\0';
+
*return_buf = buf;
*return_size = off;
@@ -2317,7 +2348,7 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
char *buf = xmalloc(size);
int ret;
- if (read_pipe(fd, &buf, &size)) {
+ if (read_fd(fd, &buf, &size)) {
free(buf);
return -1;
}
diff --git a/sha1_name.c b/sha1_name.c
index 55f25a2d3b..2d727d54dc 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -76,8 +76,11 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
prepare_packed_git();
for (p = packed_git; p && found < 2; p = p->next) {
- uint32_t num = p->num_objects;
- uint32_t first = 0, last = num;
+ uint32_t num, last;
+ uint32_t first = 0;
+ open_pack_index(p);
+ num = p->num_objects;
+ last = num;
while (first < last) {
uint32_t mid = (first + last) / 2;
const unsigned char *now;
@@ -133,6 +136,7 @@ static int find_unique_short_object(int len, char *canonical,
int has_unpacked, has_packed;
unsigned char unpacked_sha1[20], packed_sha1[20];
+ prepare_alt_odb();
has_unpacked = find_short_object_filename(len, canonical, unpacked_sha1);
has_packed = find_short_packed_object(len, res, packed_sha1);
if (!has_unpacked && !has_packed)
@@ -366,7 +370,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
fprintf(stderr,
"warning: Log for '%.*s' only goes "
"back to %s.\n", len, str,
- show_rfc2822_date(co_time, co_tz));
+ show_date(co_time, co_tz, DATE_RFC2822));
else
fprintf(stderr,
"warning: Log for '%.*s' only has "
@@ -654,7 +658,6 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
const char *cp;
*mode = S_IFINVALID;
- prepare_alt_odb();
ret = get_sha1_1(name, namelen, sha1);
if (!ret)
return ret;
@@ -679,8 +682,6 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
namelen = namelen - (cp - name);
if (!active_cache)
read_cache();
- if (active_nr < 0)
- return -1;
pos = cache_name_pos(cp, namelen);
if (pos < 0)
pos = -pos - 1;
diff --git a/shallow.c b/shallow.c
index d17868929c..dbd9f5ad0a 100644
--- a/shallow.c
+++ b/shallow.c
@@ -101,4 +101,3 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
return result;
}
-
diff --git a/ssh-upload.c b/ssh-upload.c
index 498d41e19b..20c35f03dd 100644
--- a/ssh-upload.c
+++ b/ssh-upload.c
@@ -29,24 +29,24 @@ static int serve_object(int fd_in, int fd_out) {
}
if (!size)
return -1;
-
+
if (verbose)
fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1));
remote = 0;
-
+
if (!has_sha1_file(sha1)) {
fprintf(stderr, "git-ssh-upload: could not find %s\n",
sha1_to_hex(sha1));
remote = -1;
}
-
+
if (write_in_full(fd_out, &remote, 1) != 1)
return 0;
-
+
if (remote < 0)
return 0;
-
+
return write_sha1_to_fd(fd_out, sha1);
}
diff --git a/strbuf.c b/strbuf.c
index 7f14b0fb59..e33d06b87c 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -39,4 +39,3 @@ void read_line(struct strbuf *sb, FILE *fp, int term) {
sb->eof = 1;
strbuf_end(sb);
}
-
diff --git a/t/Makefile b/t/Makefile
index 19e38508a7..72d7884232 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -6,6 +6,7 @@
#GIT_TEST_OPTS=--verbose --debug
SHELL_PATH ?= $(SHELL)
TAR ?= $(TAR)
+RM ?= rm -f
# Shell quote;
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
@@ -19,7 +20,7 @@ $(T):
@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
clean:
- rm -fr trash
+ $(RM) -r trash
# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
full-svn-test:
@@ -28,4 +29,3 @@ full-svn-test:
.PHONY: $(T) clean
.NOTPARALLEL:
-
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index f6fe78cd27..8d4a447213 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -48,3 +48,37 @@ svnrepo="file://$svnrepo"
poke() {
test-chmtime +1 "$1"
}
+
+SVN_HTTPD_MODULE_PATH=${SVN_HTTPD_MODULE_PATH-'/usr/lib/apache2/modules'}
+SVN_HTTPD_PATH=${SVN_HTTPD_PATH-'/usr/sbin/apache2'}
+
+start_httpd () {
+ if test -z "$SVN_HTTPD_PORT"
+ then
+ echo >&2 'SVN_HTTPD_PORT is not defined!'
+ return
+ fi
+
+ mkdir "$GIT_DIR"/logs
+
+ cat > "$GIT_DIR/httpd.conf" <<EOF
+ServerName "git-svn test"
+ServerRoot "$GIT_DIR"
+DocumentRoot "$GIT_DIR"
+PidFile "$GIT_DIR/httpd.pid"
+Listen 127.0.0.1:$SVN_HTTPD_PORT
+LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
+LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
+<Location /svn>
+ DAV svn
+ SVNPath $rawsvnrepo
+</Location>
+EOF
+ "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
+ svnrepo=http://127.0.0.1:$SVN_HTTPD_PORT/svn
+}
+
+stop_httpd () {
+ test -z "$SVN_HTTPD_PORT" && return
+ "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
+}
diff --git a/t/lib-read-tree-m-3way.sh b/t/lib-read-tree-m-3way.sh
index d195603dfa..168329adbc 100644
--- a/t/lib-read-tree-m-3way.sh
+++ b/t/lib-read-tree-m-3way.sh
@@ -10,14 +10,14 @@ do
echo This is Z/$p from the original tree. >Z/$p
test_expect_success \
"adding test file $p and Z/$p" \
- 'git-update-index --add $p &&
- git-update-index --add Z/$p'
+ 'git update-index --add $p &&
+ git update-index --add Z/$p'
done
done
echo This is SS from the original tree. >SS
test_expect_success \
'adding test file SS' \
- 'git-update-index --add SS'
+ 'git update-index --add SS'
cat >TT <<\EOF
This is a trivial merge sample text.
Branch A is expected to upcase this word, here.
@@ -32,10 +32,10 @@ This concludes the trivial merge sample file.
EOF
test_expect_success \
'adding test file TT' \
- 'git-update-index --add TT'
+ 'git update-index --add TT'
test_expect_success \
'prepare initial tree' \
- 'tree_O=$(git-write-tree)'
+ 'tree_O=$(git write-tree)'
################################################################
# Branch A and B makes the changes according to the above matrix.
@@ -47,14 +47,14 @@ to_remove=$(echo D? Z/D?)
rm -f $to_remove
test_expect_success \
'change in branch A (removal)' \
- 'git-update-index --remove $to_remove'
+ 'git update-index --remove $to_remove'
for p in M? Z/M?
do
echo This is modified $p in the branch A. >$p
test_expect_success \
'change in branch A (modification)' \
- "git-update-index $p"
+ "git update-index $p"
done
for p in AN AA Z/AN Z/AA
@@ -62,32 +62,32 @@ do
echo This is added $p in the branch A. >$p
test_expect_success \
'change in branch A (addition)' \
- "git-update-index --add $p"
+ "git update-index --add $p"
done
echo This is SS from the modified tree. >SS
echo This is LL from the modified tree. >LL
test_expect_success \
'change in branch A (addition)' \
- 'git-update-index --add LL &&
- git-update-index SS'
+ 'git update-index --add LL &&
+ git update-index SS'
mv TT TT-
sed -e '/Branch A/s/word/WORD/g' <TT- >TT
rm -f TT-
test_expect_success \
'change in branch A (edit)' \
- 'git-update-index TT'
+ 'git update-index TT'
mkdir DF
echo Branch A makes a file at DF/DF, creating a directory DF. >DF/DF
test_expect_success \
'change in branch A (change file to directory)' \
- 'git-update-index --add DF/DF'
+ 'git update-index --add DF/DF'
test_expect_success \
'recording branch A tree' \
- 'tree_A=$(git-write-tree)'
-
+ 'tree_A=$(git write-tree)'
+
################################################################
# Branch B
# Start from O
@@ -96,21 +96,21 @@ rm -rf [NDMASLT][NDMASLT] Z DF
mkdir Z
test_expect_success \
'reading original tree and checking out' \
- 'git-read-tree $tree_O &&
- git-checkout-index -a'
+ 'git read-tree $tree_O &&
+ git checkout-index -a'
to_remove=$(echo ?D Z/?D)
rm -f $to_remove
test_expect_success \
'change in branch B (removal)' \
- "git-update-index --remove $to_remove"
+ "git update-index --remove $to_remove"
for p in ?M Z/?M
do
echo This is modified $p in the branch B. >$p
test_expect_success \
'change in branch B (modification)' \
- "git-update-index $p"
+ "git update-index $p"
done
for p in NA AA Z/NA Z/AA
@@ -118,41 +118,41 @@ do
echo This is added $p in the branch B. >$p
test_expect_success \
'change in branch B (addition)' \
- "git-update-index --add $p"
+ "git update-index --add $p"
done
echo This is SS from the modified tree. >SS
echo This is LL from the modified tree. >LL
test_expect_success \
'change in branch B (addition and modification)' \
- 'git-update-index --add LL &&
- git-update-index SS'
+ 'git update-index --add LL &&
+ git update-index SS'
mv TT TT-
sed -e '/Branch B/s/word/WORD/g' <TT- >TT
rm -f TT-
test_expect_success \
'change in branch B (modification)' \
- 'git-update-index TT'
+ 'git update-index TT'
echo Branch B makes a file at DF. >DF
test_expect_success \
'change in branch B (addition of a file to conflict with directory)' \
- 'git-update-index --add DF'
+ 'git update-index --add DF'
test_expect_success \
'recording branch B tree' \
- 'tree_B=$(git-write-tree)'
+ 'tree_B=$(git write-tree)'
test_expect_success \
'keep contents of 3 trees for easy access' \
'rm -f .git/index &&
- git-read-tree $tree_O &&
+ git read-tree $tree_O &&
mkdir .orig-O &&
- git-checkout-index --prefix=.orig-O/ -f -q -a &&
+ git checkout-index --prefix=.orig-O/ -f -q -a &&
rm -f .git/index &&
- git-read-tree $tree_A &&
+ git read-tree $tree_A &&
mkdir .orig-A &&
- git-checkout-index --prefix=.orig-A/ -f -q -a &&
+ git checkout-index --prefix=.orig-A/ -f -q -a &&
rm -f .git/index &&
- git-read-tree $tree_B &&
+ git read-tree $tree_B &&
mkdir .orig-B &&
- git-checkout-index --prefix=.orig-B/ -f -q -a'
+ git checkout-index --prefix=.orig-B/ -f -q -a'
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 186de70243..4e49d59065 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -31,13 +31,13 @@ fi
. ./test-lib.sh
################################################################
-# git-init has been done in an empty repository.
+# git init has been done in an empty repository.
# make sure it is empty.
find .git/objects -type f -print >should-be-empty
test_expect_success \
- '.git/objects should be empty after git-init in an empty repo.' \
- 'cmp -s /dev/null should-be-empty'
+ '.git/objects should be empty after git init in an empty repo.' \
+ 'cmp -s /dev/null should-be-empty'
# also it should have 2 subdirectories; no fan-out anymore, pack, and info.
# 3 is counting "objects" itself
@@ -51,17 +51,17 @@ test_expect_success \
# updating a new file without --add should fail.
test_expect_failure \
- 'git-update-index without --add should fail adding.' \
- 'git-update-index should-be-empty'
+ 'git update-index without --add should fail adding.' \
+ 'git update-index should-be-empty'
# and with --add it should succeed, even if it is empty (it used to fail).
test_expect_success \
- 'git-update-index with --add should succeed.' \
- 'git-update-index --add should-be-empty'
+ 'git update-index with --add should succeed.' \
+ 'git update-index --add should-be-empty'
test_expect_success \
- 'writing tree out with git-write-tree' \
- 'tree=$(git-write-tree)'
+ 'writing tree out with git write-tree' \
+ 'tree=$(git write-tree)'
# we know the shape and contents of the tree and know the object ID for it.
test_expect_success \
@@ -71,17 +71,17 @@ test_expect_success \
# Removing paths.
rm -f should-be-empty full-of-directories
test_expect_failure \
- 'git-update-index without --remove should fail removing.' \
- 'git-update-index should-be-empty'
+ 'git update-index without --remove should fail removing.' \
+ 'git update-index should-be-empty'
test_expect_success \
- 'git-update-index with --remove should be able to remove.' \
- 'git-update-index --remove should-be-empty'
+ 'git update-index with --remove should be able to remove.' \
+ 'git update-index --remove should-be-empty'
# Empty tree can be written with recent write-tree.
test_expect_success \
- 'git-write-tree should be able to write an empty tree.' \
- 'tree=$(git-write-tree)'
+ 'git write-tree should be able to write an empty tree.' \
+ 'tree=$(git write-tree)'
test_expect_success \
'validate object ID of a known tree.' \
@@ -95,13 +95,13 @@ do
ln -s "hello $p" ${p}sym
done
test_expect_success \
- 'adding various types of objects with git-update-index --add.' \
- 'find path* ! -type d -print | xargs git-update-index --add'
+ 'adding various types of objects with git update-index --add.' \
+ 'find path* ! -type d -print | xargs git update-index --add'
# Show them and see that matches what we expect.
test_expect_success \
- 'showing stage with git-ls-files --stage' \
- 'git-ls-files --stage >current'
+ 'showing stage with git ls-files --stage' \
+ 'git ls-files --stage >current'
cat >expected <<\EOF
100644 f87290f8eb2cbbea7857214459a0739927eab154 0 path0
@@ -114,19 +114,19 @@ cat >expected <<\EOF
120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0 path3/subp3/file3sym
EOF
test_expect_success \
- 'validate git-ls-files output for a known tree.' \
+ 'validate git ls-files output for a known tree.' \
'diff current expected'
test_expect_success \
- 'writing tree out with git-write-tree.' \
- 'tree=$(git-write-tree)'
+ 'writing tree out with git write-tree.' \
+ 'tree=$(git write-tree)'
test_expect_success \
'validate object ID for a known tree.' \
'test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b'
test_expect_success \
- 'showing tree with git-ls-tree' \
- 'git-ls-tree $tree >current'
+ 'showing tree with git ls-tree' \
+ 'git ls-tree $tree >current'
cat >expected <<\EOF
100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
@@ -134,14 +134,14 @@ cat >expected <<\EOF
040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
EOF
test_expect_success \
- 'git-ls-tree output for a known tree.' \
+ 'git ls-tree output for a known tree.' \
'diff current expected'
# This changed in ls-tree pathspec change -- recursive does
# not show tree nodes anymore.
test_expect_success \
- 'showing tree with git-ls-tree -r' \
- 'git-ls-tree -r $tree >current'
+ 'showing tree with git ls-tree -r' \
+ 'git ls-tree -r $tree >current'
cat >expected <<\EOF
100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
@@ -153,13 +153,13 @@ cat >expected <<\EOF
120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
EOF
test_expect_success \
- 'git-ls-tree -r output for a known tree.' \
+ 'git ls-tree -r output for a known tree.' \
'diff current expected'
# But with -r -t we can have both.
test_expect_success \
- 'showing tree with git-ls-tree -r -t' \
- 'git-ls-tree -r -t $tree >current'
+ 'showing tree with git ls-tree -r -t' \
+ 'git ls-tree -r -t $tree >current'
cat >expected <<\EOF
100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
@@ -174,19 +174,19 @@ cat >expected <<\EOF
120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
EOF
test_expect_success \
- 'git-ls-tree -r output for a known tree.' \
+ 'git ls-tree -r output for a known tree.' \
'diff current expected'
test_expect_success \
- 'writing partial tree out with git-write-tree --prefix.' \
- 'ptree=$(git-write-tree --prefix=path3)'
+ 'writing partial tree out with git write-tree --prefix.' \
+ 'ptree=$(git write-tree --prefix=path3)'
test_expect_success \
'validate object ID for a known tree.' \
'test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3'
test_expect_success \
- 'writing partial tree out with git-write-tree --prefix.' \
- 'ptree=$(git-write-tree --prefix=path3/subp3)'
+ 'writing partial tree out with git write-tree --prefix.' \
+ 'ptree=$(git write-tree --prefix=path3/subp3)'
test_expect_success \
'validate object ID for a known tree.' \
'test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2'
@@ -202,24 +202,24 @@ EOF
rm .git/index
test_expect_success \
'put invalid objects into the index.' \
- 'git-update-index --index-info < badobjects'
+ 'git update-index --index-info < badobjects'
test_expect_failure \
'writing this tree without --missing-ok.' \
- 'git-write-tree'
+ 'git write-tree'
test_expect_success \
'writing this tree with --missing-ok.' \
- 'git-write-tree --missing-ok'
+ 'git write-tree --missing-ok'
################################################################
rm .git/index
test_expect_success \
- 'git-read-tree followed by write-tree should be idempotent.' \
- 'git-read-tree $tree &&
+ 'git read-tree followed by write-tree should be idempotent.' \
+ 'git read-tree $tree &&
test -f .git/index &&
- newtree=$(git-write-tree) &&
+ newtree=$(git write-tree) &&
test "$newtree" = "$tree"'
cat >expected <<\EOF
@@ -233,36 +233,36 @@ cat >expected <<\EOF
:120000 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0000000000000000000000000000000000000000 M path3/subp3/file3sym
EOF
test_expect_success \
- 'validate git-diff-files output for a know cache/work tree state.' \
- 'git-diff-files >current && diff >/dev/null -b current expected'
+ 'validate git diff-files output for a know cache/work tree state.' \
+ 'git diff-files >current && diff >/dev/null -b current expected'
test_expect_success \
- 'git-update-index --refresh should succeed.' \
- 'git-update-index --refresh'
+ 'git update-index --refresh should succeed.' \
+ 'git update-index --refresh'
test_expect_success \
- 'no diff after checkout and git-update-index --refresh.' \
- 'git-diff-files >current && cmp -s current /dev/null'
+ 'no diff after checkout and git update-index --refresh.' \
+ 'git diff-files >current && cmp -s current /dev/null'
################################################################
P=087704a96baf1c2d1c869a8b084481e121c88b5b
test_expect_success \
- 'git-commit-tree records the correct tree in a commit.' \
- 'commit0=$(echo NO | git-commit-tree $P) &&
+ 'git commit-tree records the correct tree in a commit.' \
+ 'commit0=$(echo NO | git commit-tree $P) &&
tree=$(git show --pretty=raw $commit0 |
sed -n -e "s/^tree //p" -e "/^author /q") &&
test "z$tree" = "z$P"'
test_expect_success \
- 'git-commit-tree records the correct parent in a commit.' \
- 'commit1=$(echo NO | git-commit-tree $P -p $commit0) &&
+ 'git commit-tree records the correct parent in a commit.' \
+ 'commit1=$(echo NO | git commit-tree $P -p $commit0) &&
parent=$(git show --pretty=raw $commit1 |
sed -n -e "s/^parent //p" -e "/^author /q") &&
test "z$commit0" = "z$parent"'
test_expect_success \
- 'git-commit-tree omits duplicated parent in a commit.' \
- 'commit2=$(echo NO | git-commit-tree $P -p $commit0 -p $commit0) &&
+ 'git commit-tree omits duplicated parent in a commit.' \
+ 'commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
parent=$(git show --pretty=raw $commit2 |
sed -n -e "s/^parent //p" -e "/^author /q" |
sort -u) &&
@@ -281,4 +281,20 @@ test_expect_success 'update-index D/F conflict' '
test $numpath0 = 1
'
+test_expect_success 'absolute path works as expected' '
+ mkdir first &&
+ ln -s ../.git first/.git &&
+ mkdir second &&
+ ln -s ../first second/other &&
+ mkdir third &&
+ dir="$(cd .git; pwd -P)" &&
+ dir2=third/../second/other/.git &&
+ test "$dir" = "$(test-absolute-path $dir2)" &&
+ file="$dir"/index &&
+ test "$file" = "$(test-absolute-path $dir2/index)" &&
+ ln -s ../first/file .git/syml &&
+ sym="$(cd first; pwd -P)"/file &&
+ test "$sym" = "$(test-absolute-path $dir2/syml)"
+'
+
test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index fe1dfd08a0..0807d9f01a 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -290,4 +290,85 @@ test_expect_success '.gitattributes says two and three are text' '
fi
'
+test_expect_success 'in-tree .gitattributes (1)' '
+
+ echo "one -crlf" >>.gitattributes &&
+ git add .gitattributes &&
+ git commit -m "Add .gitattributes" &&
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset -u HEAD &&
+
+ if remove_cr one >/dev/null
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ remove_cr three >/dev/null || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
+test_expect_success 'in-tree .gitattributes (2)' '
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset HEAD &&
+ git checkout-index -f -q -u -a &&
+
+ if remove_cr one >/dev/null
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ remove_cr three >/dev/null || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
+test_expect_success 'in-tree .gitattributes (3)' '
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset HEAD &&
+ git checkout-index -u .gitattributes &&
+ git checkout-index -u one dir/two three &&
+
+ if remove_cr one >/dev/null
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ remove_cr three >/dev/null || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
+test_expect_success 'in-tree .gitattributes (4)' '
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset HEAD &&
+ git checkout-index -u one dir/two three &&
+ git checkout-index -u .gitattributes &&
+
+ if remove_cr one >/dev/null
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ remove_cr three >/dev/null || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
test_done
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
new file mode 100755
index 0000000000..430a1d1d38
--- /dev/null
+++ b/t/t0022-crlf-rename.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='ignore CR in CRLF sequence while computing similiarity'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ cat ../t0022-crlf-rename.sh >sample &&
+ git add sample &&
+
+ test_tick &&
+ git commit -m Initial &&
+
+ sed -e "s/\$/ /" ../t0022-crlf-rename.sh >elpmas &&
+ git add elpmas &&
+ rm -f sample &&
+
+ test_tick &&
+ git commit -a -m Second
+
+'
+
+test_expect_success 'diff -M' '
+
+ git diff-tree -M -r --name-status HEAD^ HEAD |
+ sed -e "s/R[0-9]*/RNUM/" >actual &&
+ echo "RNUM sample elpmas" >expect &&
+ diff -u expect actual
+
+'
+
+test_done
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
new file mode 100755
index 0000000000..cad95f35ad
--- /dev/null
+++ b/t/t0030-stripspace.sh
@@ -0,0 +1,400 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carlos Rica
+#
+
+test_description='git stripspace'
+
+. ./test-lib.sh
+
+t40='A quick brown fox jumps over the lazy do'
+s40=' '
+sss="$s40$s40$s40$s40$s40$s40$s40$s40$s40$s40" # 400
+ttt="$t40$t40$t40$t40$t40$t40$t40$t40$t40$t40" # 400
+
+test_expect_success \
+ 'long lines without spaces should be unchanged' '
+ echo "$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual &&
+
+ echo "$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual &&
+
+ echo "$ttt$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual &&
+
+ echo "$ttt$ttt$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'lines with spaces at the beginning should be unchanged' '
+ echo "$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual &&
+
+ echo "$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual &&
+
+ echo "$sss$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'lines with intermediate spaces should be unchanged' '
+ echo "$ttt$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual &&
+
+ echo "$ttt$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'consecutive blank lines should be unified' '
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'only consecutive blank lines should be completely removed' '
+ > expect &&
+
+ printf "\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "\n\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'consecutive blank lines at the beginning should be removed' '
+ printf "$ttt\n" > expect &&
+ printf "\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n" > expect &&
+ printf "\n\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n" > expect &&
+
+ printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'consecutive blank lines at the end should be removed' '
+ printf "$ttt\n" > expect &&
+ printf "$ttt\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n" > expect &&
+
+ printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'text without newline at end should end with newline' '
+ test `printf "$ttt" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$ttt" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$ttt$ttt" | git stripspace | wc -l` -gt 0
+'
+
+# text plus spaces at the end:
+
+test_expect_success \
+ 'text plus spaces without newline at end should end with newline' '
+ test `printf "$ttt$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$ttt$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$sss$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$sss$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$sss$sss$sss" | git stripspace | wc -l` -gt 0
+'
+
+test_expect_failure \
+ 'text plus spaces without newline at end should not show spaces' '
+ printf "$ttt$sss" | git stripspace | grep -q " " ||
+ printf "$ttt$ttt$sss" | git stripspace | grep -q " " ||
+ printf "$ttt$ttt$ttt$sss" | git stripspace | grep -q " " ||
+ printf "$ttt$sss$sss" | git stripspace | grep -q " " ||
+ printf "$ttt$ttt$sss$sss" | git stripspace | grep -q " " ||
+ printf "$ttt$sss$sss$sss" | git stripspace | grep -q " "
+'
+
+test_expect_success \
+ 'text plus spaces without newline should show the correct lines' '
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_failure \
+ 'text plus spaces at end should not show spaces' '
+ echo "$ttt$sss" | git stripspace | grep -q " " ||
+ echo "$ttt$ttt$sss" | git stripspace | grep -q " " ||
+ echo "$ttt$ttt$ttt$sss" | git stripspace | grep -q " " ||
+ echo "$ttt$sss$sss" | git stripspace | grep -q " " ||
+ echo "$ttt$ttt$sss$sss" | git stripspace | grep -q " " ||
+ echo "$ttt$sss$sss$sss" | git stripspace | grep -q " "
+'
+
+test_expect_success \
+ 'text plus spaces at end should be cleaned and newline must remain' '
+ echo "$ttt" >expect &&
+ echo "$ttt$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$ttt" >expect &&
+ echo "$ttt$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$ttt" >expect &&
+ echo "$ttt$sss$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$ttt$ttt" >expect &&
+ echo "$ttt$ttt$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$ttt$ttt" >expect &&
+ echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$ttt$ttt$ttt" >expect &&
+ echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+ git diff expect actual
+'
+
+# spaces only:
+
+test_expect_success \
+ 'spaces with newline at end should be replaced with empty string' '
+ printf "" >expect &&
+
+ echo | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$sss$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ echo "$sss$sss$sss$sss" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_failure \
+ 'spaces without newline at end should not show spaces' '
+ printf "" | git stripspace | grep -q " " ||
+ printf "$sss" | git stripspace | grep -q " " ||
+ printf "$sss$sss" | git stripspace | grep -q " " ||
+ printf "$sss$sss$sss" | git stripspace | grep -q " " ||
+ printf "$sss$sss$sss$sss" | git stripspace | grep -q " "
+'
+
+test_expect_success \
+ 'spaces without newline at end should be replaced with empty string' '
+ printf "" >expect &&
+
+ printf "" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss$sss$sss" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$sss$sss$sss$sss" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'consecutive text lines should be unchanged' '
+ printf "$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
+ printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+ git diff expect actual &&
+
+ printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
+ git diff expect actual
+'
+
+test_expect_success 'strip comments, too' '
+ test ! -z "$(echo "# comment" | git stripspace)" &&
+ test -z "$(echo "# comment" | git stripspace -s)"
+'
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index de4e5eb61f..37add1b504 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -130,28 +130,28 @@ _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
check_result () {
- git-ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current &&
+ git ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current &&
git diff expected current
}
# This is done on an empty work directory, which is the normal
# merge person behaviour.
test_expect_success \
- '3-way merge with git-read-tree -m, empty cache' \
+ '3-way merge with git read-tree -m, empty cache' \
"rm -fr [NDMALTS][NDMALTSF] Z &&
rm .git/index &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
# This starts out with the first head, which is the normal
# patch submitter behaviour.
test_expect_success \
- '3-way merge with git-read-tree -m, match H' \
+ '3-way merge with git read-tree -m, match H' \
"rm -fr [NDMALTS][NDMALTSF] Z &&
rm .git/index &&
- git-read-tree $tree_A &&
- git-checkout-index -f -u -a &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git read-tree $tree_A &&
+ git checkout-index -f -u -a &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
: <<\END_OF_CASE_TABLE
@@ -160,7 +160,7 @@ We have so far tested only empty index and clean-and-matching-A index
case which are trivial. Make sure index requirements are also
checked.
-"git-read-tree -m O A B"
+"git read-tree -m O A B"
O A B result index requirements
-------------------------------------------------------------------
@@ -214,87 +214,87 @@ test_expect_failure \
'1 - must not have an entry not in A.' \
"rm -f .git/index XX &&
echo XX >XX &&
- git-update-index --add XX &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add XX &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'2 - must match B in !O && !A && B case.' \
"rm -f .git/index NA &&
cp .orig-B/NA NA &&
- git-update-index --add NA &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add NA &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'2 - matching B alone is OK in !O && !A && B case.' \
"rm -f .git/index NA &&
cp .orig-B/NA NA &&
- git-update-index --add NA &&
+ git update-index --add NA &&
echo extra >>NA &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'3 - must match A in !O && A && !B case.' \
"rm -f .git/index AN &&
cp .orig-A/AN AN &&
- git-update-index --add AN &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add AN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
'3 - matching A alone is OK in !O && A && !B case.' \
"rm -f .git/index AN &&
cp .orig-A/AN AN &&
- git-update-index --add AN &&
+ git update-index --add AN &&
echo extra >>AN &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'3 (fail) - must match A in !O && A && !B case.' \
"rm -f .git/index AN &&
cp .orig-A/AN AN &&
echo extra >>AN &&
- git-update-index --add AN &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add AN &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'4 - must match and be up-to-date in !O && A && B && A!=B case.' \
"rm -f .git/index AA &&
cp .orig-A/AA AA &&
- git-update-index --add AA &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add AA &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
'4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
"rm -f .git/index AA &&
cp .orig-A/AA AA &&
- git-update-index --add AA &&
+ git update-index --add AA &&
echo extra >>AA &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
"rm -f .git/index AA &&
cp .orig-A/AA AA &&
echo extra >>AA &&
- git-update-index --add AA &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add AA &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'5 - must match in !O && A && B && A==B case.' \
"rm -f .git/index LL &&
cp .orig-A/LL LL &&
- git-update-index --add LL &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add LL &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
'5 - must match in !O && A && B && A==B case.' \
"rm -f .git/index LL &&
cp .orig-A/LL LL &&
- git-update-index --add LL &&
+ git update-index --add LL &&
echo extra >>LL &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
@@ -302,117 +302,117 @@ test_expect_failure \
"rm -f .git/index LL &&
cp .orig-A/LL LL &&
echo extra >>LL &&
- git-update-index --add LL &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add LL &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'6 - must not exist in O && !A && !B case' \
"rm -f .git/index DD &&
echo DD >DD
- git-update-index --add DD &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add DD &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'7 - must not exist in O && !A && B && O!=B case' \
"rm -f .git/index DM &&
cp .orig-B/DM DM &&
- git-update-index --add DM &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add DM &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'8 - must not exist in O && !A && B && O==B case' \
"rm -f .git/index DN &&
cp .orig-B/DN DN &&
- git-update-index --add DN &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add DN &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'9 - must match and be up-to-date in O && A && !B && O!=A case' \
"rm -f .git/index MD &&
cp .orig-A/MD MD &&
- git-update-index --add MD &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add MD &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
'9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
"rm -f .git/index MD &&
cp .orig-A/MD MD &&
- git-update-index --add MD &&
+ git update-index --add MD &&
echo extra >>MD &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
"rm -f .git/index MD &&
cp .orig-A/MD MD &&
echo extra >>MD &&
- git-update-index --add MD &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add MD &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'10 - must match and be up-to-date in O && A && !B && O==A case' \
"rm -f .git/index ND &&
cp .orig-A/ND ND &&
- git-update-index --add ND &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add ND &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
'10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
"rm -f .git/index ND &&
cp .orig-A/ND ND &&
- git-update-index --add ND &&
+ git update-index --add ND &&
echo extra >>ND &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
"rm -f .git/index ND &&
cp .orig-A/ND ND &&
echo extra >>ND &&
- git-update-index --add ND &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add ND &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'11 - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
"rm -f .git/index MM &&
cp .orig-A/MM MM &&
- git-update-index --add MM &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add MM &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
'11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
"rm -f .git/index MM &&
cp .orig-A/MM MM &&
- git-update-index --add MM &&
+ git update-index --add MM &&
echo extra >>MM &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
"rm -f .git/index MM &&
cp .orig-A/MM MM &&
echo extra >>MM &&
- git-update-index --add MM &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add MM &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'12 - must match A in O && A && B && O!=A && A==B case' \
"rm -f .git/index SS &&
cp .orig-A/SS SS &&
- git-update-index --add SS &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add SS &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
'12 - must match A in O && A && B && O!=A && A==B case' \
"rm -f .git/index SS &&
cp .orig-A/SS SS &&
- git-update-index --add SS &&
+ git update-index --add SS &&
echo extra >>SS &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
@@ -420,74 +420,74 @@ test_expect_failure \
"rm -f .git/index SS &&
cp .orig-A/SS SS &&
echo extra >>SS &&
- git-update-index --add SS &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add SS &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'13 - must match A in O && A && B && O!=A && O==B case' \
"rm -f .git/index MN &&
cp .orig-A/MN MN &&
- git-update-index --add MN &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add MN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
'13 - must match A in O && A && B && O!=A && O==B case' \
"rm -f .git/index MN &&
cp .orig-A/MN MN &&
- git-update-index --add MN &&
+ git update-index --add MN &&
echo extra >>MN &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
'14 - must match and be up-to-date in O && A && B && O==A && O!=B case' \
"rm -f .git/index NM &&
cp .orig-A/NM NM &&
- git-update-index --add NM &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add NM &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
'14 - may match B in O && A && B && O==A && O!=B case' \
"rm -f .git/index NM &&
cp .orig-B/NM NM &&
- git-update-index --add NM &&
+ git update-index --add NM &&
echo extra >>NM &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
'14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
"rm -f .git/index NM &&
cp .orig-A/NM NM &&
- git-update-index --add NM &&
+ git update-index --add NM &&
echo extra >>NM &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_failure \
'14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
"rm -f .git/index NM &&
cp .orig-A/NM NM &&
echo extra >>NM &&
- git-update-index --add NM &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add NM &&
+ git read-tree -m $tree_O $tree_A $tree_B"
test_expect_success \
'15 - must match A in O && A && B && O==A && O==B case' \
"rm -f .git/index NN &&
cp .orig-A/NN NN &&
- git-update-index --add NN &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git update-index --add NN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_success \
'15 - must match A in O && A && B && O==A && O==B case' \
"rm -f .git/index NN &&
cp .orig-A/NN NN &&
- git-update-index --add NN &&
+ git update-index --add NN &&
echo extra >>NN &&
- git-read-tree -m $tree_O $tree_A $tree_B &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
check_result"
test_expect_failure \
@@ -495,20 +495,20 @@ test_expect_failure \
"rm -f .git/index NN &&
cp .orig-A/NN NN &&
echo extra >>NN &&
- git-update-index --add NN &&
- git-read-tree -m $tree_O $tree_A $tree_B"
+ git update-index --add NN &&
+ git read-tree -m $tree_O $tree_A $tree_B"
# #16
test_expect_success \
'16 - A matches in one and B matches in another.' \
'rm -f .git/index F16 &&
echo F16 >F16 &&
- git-update-index --add F16 &&
- tree0=`git-write-tree` &&
+ git update-index --add F16 &&
+ tree0=`git write-tree` &&
echo E16 >F16 &&
- git-update-index F16 &&
- tree1=`git-write-tree` &&
- git-read-tree -m $tree0 $tree1 $tree1 $tree0 &&
- git-ls-files --stage'
+ git update-index F16 &&
+ tree1=`git write-tree` &&
+ git read-tree -m $tree0 $tree1 $tree1 $tree0 &&
+ git ls-files --stage'
test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 030226bbfb..b01b0037a0 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -11,7 +11,7 @@ There is the head (called H) and another commit (called M), which is
simply ahead of H. The index and the work tree contains a state that
is derived from H, but may also have local changes. This test checks
all the combinations described in the two-tree merge "carry forward"
-rules, found in <Documentation/git-read-tree.txt>.
+rules, found in <Documentation/git read-tree.txt>.
In the test, these paths are used:
bozbar - in H, stays in M, modified from bozbar to gnusto
@@ -23,7 +23,7 @@ In the test, these paths are used:
. ./test-lib.sh
read_tree_twoway () {
- git-read-tree -m "$1" "$2" && git-ls-files --stage
+ git read-tree -m "$1" "$2" && git ls-files --stage
}
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
@@ -37,7 +37,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git-diff-files -- "$1"`
+ clean_if_empty=`git diff-files -- "$1"`
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
@@ -68,24 +68,24 @@ test_expect_success \
cat bozbar-old >bozbar &&
echo rezrov >rezrov &&
echo yomin >yomin &&
- git-update-index --add nitfol bozbar rezrov &&
- treeH=`git-write-tree` &&
+ git update-index --add nitfol bozbar rezrov &&
+ treeH=`git write-tree` &&
echo treeH $treeH &&
- git-ls-tree $treeH &&
+ git ls-tree $treeH &&
cat bozbar-new >bozbar &&
- git-update-index --add frotz bozbar --force-remove rezrov &&
- git-ls-files --stage >M.out &&
- treeM=`git-write-tree` &&
+ git update-index --add frotz bozbar --force-remove rezrov &&
+ git ls-files --stage >M.out &&
+ treeM=`git write-tree` &&
echo treeM $treeM &&
- git-ls-tree $treeM &&
- git-diff-tree $treeH $treeM'
+ git ls-tree $treeM &&
+ git diff-tree $treeH $treeM'
test_expect_success \
'1, 2, 3 - no carry forward' \
'rm -f .git/index &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >1-3.out &&
+ git ls-files --stage >1-3.out &&
git diff M.out 1-3.out &&
check_cache_at bozbar dirty &&
check_cache_at frotz dirty &&
@@ -96,11 +96,11 @@ echo '+100644 X 0 yomin' >expected
test_expect_success \
'4 - carry forward local addition.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
- git-update-index --add yomin &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ git update-index --add yomin &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >4.out || return 1
+ git ls-files --stage >4.out || return 1
git diff M.out 4.out >4diff.out
compare_change 4diff.out expected &&
check_cache_at yomin clean'
@@ -108,13 +108,13 @@ test_expect_success \
test_expect_success \
'5 - carry forward local addition.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo yomin >yomin &&
- git-update-index --add yomin &&
+ git update-index --add yomin &&
echo yomin yomin >yomin &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >5.out || return 1
+ git ls-files --stage >5.out || return 1
git diff M.out 5.out >5diff.out
compare_change 5diff.out expected &&
check_cache_at yomin dirty'
@@ -122,83 +122,83 @@ test_expect_success \
test_expect_success \
'6 - local addition already has the same.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
- git-update-index --add frotz &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ git update-index --add frotz &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >6.out &&
+ git ls-files --stage >6.out &&
git diff M.out 6.out &&
check_cache_at frotz clean'
test_expect_success \
'7 - local addition already has the same.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo frotz >frotz &&
- git-update-index --add frotz &&
+ git update-index --add frotz &&
echo frotz frotz >frotz &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >7.out &&
+ git ls-files --stage >7.out &&
git diff M.out 7.out &&
check_cache_at frotz dirty'
test_expect_success \
'8 - conflicting addition.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo frotz frotz >frotz &&
- git-update-index --add frotz &&
+ git update-index --add frotz &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
test_expect_success \
'9 - conflicting addition.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo frotz frotz >frotz &&
- git-update-index --add frotz &&
+ git update-index --add frotz &&
echo frotz >frotz &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
test_expect_success \
'10 - path removed.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo rezrov >rezrov &&
- git-update-index --add rezrov &&
+ git update-index --add rezrov &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >10.out &&
+ git ls-files --stage >10.out &&
git diff M.out 10.out'
test_expect_success \
'11 - dirty path removed.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo rezrov >rezrov &&
- git-update-index --add rezrov &&
+ git update-index --add rezrov &&
echo rezrov rezrov >rezrov &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
test_expect_success \
'12 - unmatching local changes being removed.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo rezrov rezrov >rezrov &&
- git-update-index --add rezrov &&
+ git update-index --add rezrov &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
test_expect_success \
'13 - unmatching local changes being removed.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo rezrov rezrov >rezrov &&
- git-update-index --add rezrov &&
+ git update-index --add rezrov &&
echo rezrov >rezrov &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
@@ -210,12 +210,12 @@ EOF
test_expect_success \
'14 - unchanged in two heads.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo nitfol nitfol >nitfol &&
- git-update-index --add nitfol &&
+ git update-index --add nitfol &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >14.out || return 1
+ git ls-files --stage >14.out || return 1
git diff M.out 14.out >14diff.out
compare_change 14diff.out expected &&
check_cache_at nitfol clean'
@@ -223,13 +223,13 @@ test_expect_success \
test_expect_success \
'15 - unchanged in two heads.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo nitfol nitfol >nitfol &&
- git-update-index --add nitfol &&
+ git update-index --add nitfol &&
echo nitfol nitfol nitfol >nitfol &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >15.out || return 1
+ git ls-files --stage >15.out || return 1
git diff M.out 15.out >15diff.out
compare_change 15diff.out expected &&
check_cache_at nitfol dirty'
@@ -237,66 +237,66 @@ test_expect_success \
test_expect_success \
'16 - conflicting local change.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo bozbar bozbar >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
test_expect_success \
'17 - conflicting local change.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
echo bozbar bozbar >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
echo bozbar bozbar bozbar >bozbar &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
test_expect_success \
'18 - local change already having a good result.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
cat bozbar-new >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >18.out &&
+ git ls-files --stage >18.out &&
git diff M.out 18.out &&
check_cache_at bozbar clean'
test_expect_success \
'19 - local change already having a good result, further modified.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
cat bozbar-new >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >19.out &&
+ git ls-files --stage >19.out &&
git diff M.out 19.out &&
check_cache_at bozbar dirty'
test_expect_success \
'20 - no local change, use new tree.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
cat bozbar-old >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
read_tree_twoway $treeH $treeM &&
- git-ls-files --stage >20.out &&
+ git ls-files --stage >20.out &&
git diff M.out 20.out &&
check_cache_at bozbar dirty'
test_expect_success \
'21 - no local change, dirty cache.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
cat bozbar-old >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
@@ -304,10 +304,10 @@ test_expect_success \
test_expect_success \
'22 - local change cache updated.' \
'rm -f .git/index &&
- git-read-tree $treeH &&
- git-checkout-index -u -f -q -a &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
if read_tree_twoway $treeH $treeM; then false; else :; fi'
# Also make sure we did not break DF vs DF/DF case.
@@ -315,28 +315,28 @@ test_expect_success \
'DF vs DF/DF case setup.' \
'rm -f .git/index &&
echo DF >DF &&
- git-update-index --add DF &&
- treeDF=`git-write-tree` &&
+ git update-index --add DF &&
+ treeDF=`git write-tree` &&
echo treeDF $treeDF &&
- git-ls-tree $treeDF &&
+ git ls-tree $treeDF &&
rm -f DF &&
mkdir DF &&
echo DF/DF >DF/DF &&
- git-update-index --add --remove DF DF/DF &&
- treeDFDF=`git-write-tree` &&
+ git update-index --add --remove DF DF/DF &&
+ treeDFDF=`git write-tree` &&
echo treeDFDF $treeDFDF &&
- git-ls-tree $treeDFDF &&
- git-ls-files --stage >DFDF.out'
+ git ls-tree $treeDFDF &&
+ git ls-files --stage >DFDF.out'
test_expect_success \
'DF vs DF/DF case test.' \
'rm -f .git/index &&
rm -fr DF &&
echo DF >DF &&
- git-update-index --add DF &&
+ git update-index --add DF &&
read_tree_twoway $treeDF $treeDFDF &&
- git-ls-files --stage >DFDFcheck.out &&
+ git ls-files --stage >DFDFcheck.out &&
git diff DFDF.out DFDFcheck.out &&
check_cache_at DF/DF dirty &&
:'
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 87fe993f59..42e5cf8181 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -20,7 +20,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git-diff-files -- "$1"`
+ clean_if_empty=`git diff-files -- "$1"`
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
@@ -39,26 +39,26 @@ test_expect_success \
echo nitfol >nitfol &&
echo bozbar >bozbar &&
echo rezrov >rezrov &&
- git-update-index --add nitfol bozbar rezrov &&
- treeH=`git-write-tree` &&
+ git update-index --add nitfol bozbar rezrov &&
+ treeH=`git write-tree` &&
echo treeH $treeH &&
- git-ls-tree $treeH &&
+ git ls-tree $treeH &&
echo gnusto >bozbar &&
- git-update-index --add frotz bozbar --force-remove rezrov &&
- git-ls-files --stage >M.out &&
- treeM=`git-write-tree` &&
+ git update-index --add frotz bozbar --force-remove rezrov &&
+ git ls-files --stage >M.out &&
+ treeM=`git write-tree` &&
echo treeM $treeM &&
- git-ls-tree $treeM &&
+ git ls-tree $treeM &&
sum bozbar frotz nitfol >M.sum &&
- git-diff-tree $treeH $treeM'
+ git diff-tree $treeH $treeM'
test_expect_success \
'1, 2, 3 - no carry forward' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >1-3.out &&
+ git read-tree --reset -u $treeH &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >1-3.out &&
cmp M.out 1-3.out &&
sum bozbar frotz nitfol >actual3.sum &&
cmp M.sum actual3.sum &&
@@ -69,12 +69,12 @@ test_expect_success \
test_expect_success \
'4 - carry forward local addition.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo "+100644 X 0 yomin" >expected &&
echo yomin >yomin &&
- git-update-index --add yomin &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >4.out || return 1
+ git update-index --add yomin &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >4.out || return 1
diff -U0 M.out 4.out >4diff.out
compare_change 4diff.out expected &&
check_cache_at yomin clean &&
@@ -87,13 +87,13 @@ test_expect_success \
test_expect_success \
'5 - carry forward local addition.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
- git-read-tree -m -u $treeH &&
+ git read-tree --reset -u $treeH &&
+ git read-tree -m -u $treeH &&
echo yomin >yomin &&
- git-update-index --add yomin &&
+ git update-index --add yomin &&
echo yomin yomin >yomin &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >5.out || return 1
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >5.out || return 1
diff -U0 M.out 5.out >5diff.out
compare_change 5diff.out expected &&
check_cache_at yomin dirty &&
@@ -107,11 +107,11 @@ test_expect_success \
test_expect_success \
'6 - local addition already has the same.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo frotz >frotz &&
- git-update-index --add frotz &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >6.out &&
+ git update-index --add frotz &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >6.out &&
diff -U0 M.out 6.out &&
check_cache_at frotz clean &&
sum bozbar frotz nitfol >actual3.sum &&
@@ -123,12 +123,12 @@ test_expect_success \
test_expect_success \
'7 - local addition already has the same.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo frotz >frotz &&
- git-update-index --add frotz &&
+ git update-index --add frotz &&
echo frotz frotz >frotz &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >7.out &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >7.out &&
diff -U0 M.out 7.out &&
check_cache_at frotz dirty &&
sum bozbar frotz nitfol >actual7.sum &&
@@ -141,28 +141,28 @@ test_expect_success \
test_expect_success \
'8 - conflicting addition.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo frotz frotz >frotz &&
- git-update-index --add frotz &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ git update-index --add frotz &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'9 - conflicting addition.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo frotz frotz >frotz &&
- git-update-index --add frotz &&
+ git update-index --add frotz &&
echo frotz >frotz &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'10 - path removed.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo rezrov >rezrov &&
- git-update-index --add rezrov &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >10.out &&
+ git update-index --add rezrov &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >10.out &&
cmp M.out 10.out &&
sum bozbar frotz nitfol >actual10.sum &&
cmp M.sum actual10.sum'
@@ -170,28 +170,28 @@ test_expect_success \
test_expect_success \
'11 - dirty path removed.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo rezrov >rezrov &&
- git-update-index --add rezrov &&
+ git update-index --add rezrov &&
echo rezrov rezrov >rezrov &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'12 - unmatching local changes being removed.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo rezrov rezrov >rezrov &&
- git-update-index --add rezrov &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ git update-index --add rezrov &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'13 - unmatching local changes being removed.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo rezrov rezrov >rezrov &&
- git-update-index --add rezrov &&
+ git update-index --add rezrov &&
echo rezrov >rezrov &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
cat >expected <<EOF
-100644 X 0 nitfol
@@ -201,11 +201,11 @@ EOF
test_expect_success \
'14 - unchanged in two heads.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo nitfol nitfol >nitfol &&
- git-update-index --add nitfol &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >14.out || return 1
+ git update-index --add nitfol &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >14.out || return 1
diff -U0 M.out 14.out >14diff.out
compare_change 14diff.out expected &&
sum bozbar frotz >actual14.sum &&
@@ -221,12 +221,12 @@ test_expect_success \
test_expect_success \
'15 - unchanged in two heads.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo nitfol nitfol >nitfol &&
- git-update-index --add nitfol &&
+ git update-index --add nitfol &&
echo nitfol nitfol nitfol >nitfol &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >15.out || return 1
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >15.out || return 1
diff -U0 M.out 15.out >15diff.out
compare_change 15diff.out expected &&
check_cache_at nitfol dirty &&
@@ -242,28 +242,28 @@ test_expect_success \
test_expect_success \
'16 - conflicting local change.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo bozbar bozbar >bozbar &&
- git-update-index --add bozbar &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ git update-index --add bozbar &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'17 - conflicting local change.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo bozbar bozbar >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
echo bozbar bozbar bozbar >bozbar &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
test_expect_success \
'18 - local change already having a good result.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo gnusto >bozbar &&
- git-update-index --add bozbar &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >18.out &&
+ git update-index --add bozbar &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >18.out &&
diff -U0 M.out 18.out &&
check_cache_at bozbar clean &&
sum bozbar frotz nitfol >actual18.sum &&
@@ -272,12 +272,12 @@ test_expect_success \
test_expect_success \
'19 - local change already having a good result, further modified.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo gnusto >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >19.out &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >19.out &&
diff -U0 M.out 19.out &&
check_cache_at bozbar dirty &&
sum frotz nitfol >actual19.sum &&
@@ -292,11 +292,11 @@ test_expect_success \
test_expect_success \
'20 - no local change, use new tree.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo bozbar >bozbar &&
- git-update-index --add bozbar &&
- git-read-tree -m -u $treeH $treeM &&
- git-ls-files --stage >20.out &&
+ git update-index --add bozbar &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >20.out &&
diff -U0 M.out 20.out &&
check_cache_at bozbar clean &&
sum bozbar frotz nitfol >actual20.sum &&
@@ -305,39 +305,39 @@ test_expect_success \
test_expect_success \
'21 - no local change, dirty cache.' \
'rm -f .git/index nitfol bozbar rezrov frotz &&
- git-read-tree --reset -u $treeH &&
+ git read-tree --reset -u $treeH &&
echo bozbar >bozbar &&
- git-update-index --add bozbar &&
+ git update-index --add bozbar &&
echo gnusto gnusto >bozbar &&
- if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
# Also make sure we did not break DF vs DF/DF case.
test_expect_success \
'DF vs DF/DF case setup.' \
'rm -f .git/index
echo DF >DF &&
- git-update-index --add DF &&
- treeDF=`git-write-tree` &&
+ git update-index --add DF &&
+ treeDF=`git write-tree` &&
echo treeDF $treeDF &&
- git-ls-tree $treeDF &&
+ git ls-tree $treeDF &&
rm -f DF &&
mkdir DF &&
echo DF/DF >DF/DF &&
- git-update-index --add --remove DF DF/DF &&
- treeDFDF=`git-write-tree` &&
+ git update-index --add --remove DF DF/DF &&
+ treeDFDF=`git write-tree` &&
echo treeDFDF $treeDFDF &&
- git-ls-tree $treeDFDF &&
- git-ls-files --stage >DFDF.out'
+ git ls-tree $treeDFDF &&
+ git ls-files --stage >DFDF.out'
test_expect_success \
'DF vs DF/DF case test.' \
'rm -f .git/index &&
rm -fr DF &&
echo DF >DF &&
- git-update-index --add DF &&
- git-read-tree -m -u $treeDF $treeDFDF &&
- git-ls-files --stage >DFDFcheck.out &&
+ git update-index --add DF &&
+ git read-tree -m -u $treeDF $treeDFDF &&
+ git ls-files --stage >DFDFcheck.out &&
diff -U0 DFDF.out DFDFcheck.out &&
check_cache_at DF/DF clean'
diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh
index 48ab117d75..8c6d67edda 100755
--- a/t/t1003-read-tree-prefix.sh
+++ b/t/t1003-read-tree-prefix.sh
@@ -3,15 +3,15 @@
# Copyright (c) 2006 Junio C Hamano
#
-test_description='git-read-tree --prefix test.
+test_description='git read-tree --prefix test.
'
. ./test-lib.sh
test_expect_success setup '
echo hello >one &&
- git-update-index --add one &&
- tree=`git-write-tree` &&
+ git update-index --add one &&
+ tree=`git write-tree` &&
echo tree is $tree
'
@@ -19,8 +19,8 @@ echo 'one
two/one' >expect
test_expect_success 'read-tree --prefix' '
- git-read-tree --prefix=two/ $tree &&
- git-ls-files >actual &&
+ git read-tree --prefix=two/ $tree &&
+ git ls-files >actual &&
cmp expect actual
'
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index c11420a8b6..d609a551ae 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -84,7 +84,7 @@ test_expect_success 'three-way not complaining on an untracked path in both' '
echo >file2 file two is untracked on the master side &&
echo >subdir/file2 file two is untracked on the master side &&
- git-read-tree -m -u branch-point master side
+ git read-tree -m -u branch-point master side
'
test_expect_success 'three-way not clobbering a working tree file' '
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 1e8f9e59df..b9cef3422c 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -22,19 +22,19 @@ LF='
test_expect_success 'update-index and ls-files' '
cd $HERE &&
- git-update-index --add one &&
- case "`git-ls-files`" in
+ git update-index --add one &&
+ case "`git ls-files`" in
one) echo ok one ;;
*) echo bad one; exit 1 ;;
esac &&
cd dir &&
- git-update-index --add two &&
- case "`git-ls-files`" in
+ git update-index --add two &&
+ case "`git ls-files`" in
two) echo ok two ;;
*) echo bad two; exit 1 ;;
esac &&
cd .. &&
- case "`git-ls-files`" in
+ case "`git ls-files`" in
dir/two"$LF"one) echo ok both ;;
*) echo bad; exit 1 ;;
esac
@@ -42,13 +42,13 @@ test_expect_success 'update-index and ls-files' '
test_expect_success 'cat-file' '
cd $HERE &&
- two=`git-ls-files -s dir/two` &&
+ two=`git ls-files -s dir/two` &&
two=`expr "$two" : "[0-7]* \\([0-9a-f]*\\)"` &&
echo "$two" &&
- git-cat-file -p "$two" >actual &&
+ git cat-file -p "$two" >actual &&
cmp dir/two actual &&
cd dir &&
- git-cat-file -p "$two" >actual &&
+ git cat-file -p "$two" >actual &&
cmp two actual
'
rm -f actual dir/actual
@@ -57,17 +57,17 @@ test_expect_success 'diff-files' '
cd $HERE &&
echo a >>one &&
echo d >>dir/two &&
- case "`git-diff-files --name-only`" in
+ case "`git diff-files --name-only`" in
dir/two"$LF"one) echo ok top ;;
*) echo bad top; exit 1 ;;
esac &&
# diff should not omit leading paths
cd dir &&
- case "`git-diff-files --name-only`" in
+ case "`git diff-files --name-only`" in
dir/two"$LF"one) echo ok subdir ;;
*) echo bad subdir; exit 1 ;;
esac &&
- case "`git-diff-files --name-only .`" in
+ case "`git diff-files --name-only .`" in
dir/two) echo ok subdir limited ;;
*) echo bad subdir limited; exit 1 ;;
esac
@@ -75,33 +75,33 @@ test_expect_success 'diff-files' '
test_expect_success 'write-tree' '
cd $HERE &&
- top=`git-write-tree` &&
+ top=`git write-tree` &&
echo $top &&
cd dir &&
- sub=`git-write-tree` &&
+ sub=`git write-tree` &&
echo $sub &&
test "z$top" = "z$sub"
'
test_expect_success 'checkout-index' '
cd $HERE &&
- git-checkout-index -f -u one &&
+ git checkout-index -f -u one &&
cmp one original.one &&
cd dir &&
- git-checkout-index -f -u two &&
+ git checkout-index -f -u two &&
cmp two ../original.two
'
test_expect_success 'read-tree' '
cd $HERE &&
rm -f one dir/two &&
- tree=`git-write-tree` &&
- git-read-tree --reset -u "$tree" &&
+ tree=`git write-tree` &&
+ git read-tree --reset -u "$tree" &&
cmp one original.one &&
cmp dir/two original.two &&
cd dir &&
rm -f two &&
- git-read-tree --reset -u "$tree" &&
+ git read-tree --reset -u "$tree" &&
cmp two ../original.two &&
cmp ../one ../original.one
'
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
index 19a0ed4d20..7f7fc36734 100755
--- a/t/t1100-commit-tree-options.sh
+++ b/t/t1100-commit-tree-options.sh
@@ -3,9 +3,9 @@
# Copyright (C) 2005 Rene Scharfe
#
-test_description='git-commit-tree options test
+test_description='git commit-tree options test
-This test checks that git-commit-tree can create a specific commit
+This test checks that git commit-tree can create a specific commit
object by defining all environment variables that it understands.
'
@@ -21,7 +21,7 @@ EOF
test_expect_success \
'test preparation: write empty tree' \
- 'git-write-tree >treeid'
+ 'git write-tree >treeid'
test_expect_success \
'construct commit' \
@@ -32,11 +32,11 @@ test_expect_success \
GIT_COMMITTER_NAME="Committer Name" \
GIT_COMMITTER_EMAIL="committer@email" \
GIT_COMMITTER_DATE="2005-05-26 23:30" \
- TZ=GMT git-commit-tree `cat treeid` >commitid 2>/dev/null'
+ TZ=GMT git commit-tree `cat treeid` >commitid 2>/dev/null'
test_expect_success \
'read commit' \
- 'git-cat-file commit `cat commitid` >commit'
+ 'git cat-file commit `cat commitid` >commit'
test_expect_success \
'compare commit' \
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index ca2c30f7af..991d3c5e9c 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -10,11 +10,11 @@ test_description='A simple turial in the form of a test case'
echo "Hello World" > hello
echo "Silly example" > example
-git-update-index --add hello example
+git update-index --add hello example
-test_expect_success 'blob' "test blob = \"$(git-cat-file -t 557db03)\""
+test_expect_success 'blob' "test blob = \"$(git cat-file -t 557db03)\""
-test_expect_success 'blob 557db03' "test \"Hello World\" = \"$(git-cat-file blob 557db03)\""
+test_expect_success 'blob 557db03' "test \"Hello World\" = \"$(git cat-file blob 557db03)\""
echo "It's a new day for git" >>hello
cat > diff.expect << EOF
@@ -26,25 +26,25 @@ index 557db03..263414f 100644
Hello World
+It's a new day for git
EOF
-git-diff-files -p > diff.output
-test_expect_success 'git-diff-files -p' 'cmp diff.expect diff.output'
+git diff-files -p > diff.output
+test_expect_success 'git diff-files -p' 'cmp diff.expect diff.output'
git diff > diff.output
test_expect_success 'git diff' 'cmp diff.expect diff.output'
-tree=$(git-write-tree 2>/dev/null)
+tree=$(git write-tree 2>/dev/null)
test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tree"
-output="$(echo "Initial commit" | git-commit-tree $(git-write-tree) 2>&1 > .git/refs/heads/master)"
+output="$(echo "Initial commit" | git commit-tree $(git write-tree) 2>&1 > .git/refs/heads/master)"
-git-diff-index -p HEAD > diff.output
-test_expect_success 'git-diff-index -p HEAD' 'cmp diff.expect diff.output'
+git diff-index -p HEAD > diff.output
+test_expect_success 'git diff-index -p HEAD' 'cmp diff.expect diff.output'
git diff HEAD > diff.output
test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
#rm hello
-#test_expect_success 'git-read-tree --reset HEAD' "git-read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git-update-index --refresh)\""
+#test_expect_success 'git read-tree --reset HEAD' "git read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git update-index --refresh)\""
cat > whatchanged.expect << EOF
commit VARIABLE
@@ -69,11 +69,11 @@ index 0000000..557db03
+Hello World
EOF
-git-whatchanged -p --root | \
+git whatchanged -p --root | \
sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
-e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
> whatchanged.output
-test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
+test_expect_success 'git whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
git tag my-first-tag
test_expect_success 'git tag my-first-tag' 'cmp .git/refs/heads/master .git/refs/tags/my-first-tag'
@@ -159,4 +159,3 @@ test_expect_success 'git prune-packed' 'git prune-packed'
test_expect_failure '-> only packed objects' 'find -type f .git/objects/[0-9a-f][0-9a-f]'
test_done
-
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index f1a78b19ac..1d2bf2c060 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -3,13 +3,13 @@
# Copyright (c) 2005 Johannes Schindelin
#
-test_description='Test git-config in different settings'
+test_description='Test git config in different settings'
. ./test-lib.sh
test -f .git/config && rm .git/config
-git-config core.penguin "little blue"
+git config core.penguin "little blue"
cat > expect << EOF
[core]
@@ -18,7 +18,7 @@ EOF
test_expect_success 'initial' 'cmp .git/config expect'
-git-config Core.Movie BadPhysics
+git config Core.Movie BadPhysics
cat > expect << EOF
[core]
@@ -28,7 +28,7 @@ EOF
test_expect_success 'mixed case' 'cmp .git/config expect'
-git-config Cores.WhatEver Second
+git config Cores.WhatEver Second
cat > expect << EOF
[core]
@@ -40,7 +40,7 @@ EOF
test_expect_success 'similar section' 'cmp .git/config expect'
-git-config CORE.UPPERCASE true
+git config CORE.UPPERCASE true
cat > expect << EOF
[core]
@@ -54,10 +54,10 @@ EOF
test_expect_success 'similar section' 'cmp .git/config expect'
test_expect_success 'replace with non-match' \
- 'git-config core.penguin kingpin !blue'
+ 'git config core.penguin kingpin !blue'
test_expect_success 'replace with non-match (actually matching)' \
- 'git-config core.penguin "very blue" !kingpin'
+ 'git config core.penguin "very blue" !kingpin'
cat > expect << EOF
[core]
@@ -86,7 +86,7 @@ EOF
cp .git/config .git/config2
test_expect_success 'multiple unset' \
- 'git-config --unset-all beta.haha'
+ 'git config --unset-all beta.haha'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -102,7 +102,7 @@ test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
mv .git/config2 .git/config
test_expect_success '--replace-all' \
- 'git-config --replace-all beta.haha gamma'
+ 'git config --replace-all beta.haha gamma'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -116,7 +116,7 @@ EOF
test_expect_success 'all replaced' 'cmp .git/config expect'
-git-config beta.haha alpha
+git config beta.haha alpha
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -130,7 +130,7 @@ EOF
test_expect_success 'really mean test' 'cmp .git/config expect'
-git-config nextsection.nonewline wow
+git config nextsection.nonewline wow
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -145,8 +145,8 @@ EOF
test_expect_success 'really really mean test' 'cmp .git/config expect'
-test_expect_success 'get value' 'test alpha = $(git-config beta.haha)'
-git-config --unset beta.haha
+test_expect_success 'get value' 'test alpha = $(git config beta.haha)'
+git config --unset beta.haha
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -160,7 +160,7 @@ EOF
test_expect_success 'unset' 'cmp .git/config expect'
-git-config nextsection.NoNewLine "wow2 for me" "for me$"
+git config nextsection.NoNewLine "wow2 for me" "for me$"
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -176,18 +176,18 @@ EOF
test_expect_success 'multivar' 'cmp .git/config expect'
test_expect_success 'non-match' \
- 'git-config --get nextsection.nonewline !for'
+ 'git config --get nextsection.nonewline !for'
test_expect_success 'non-match value' \
- 'test wow = $(git-config --get nextsection.nonewline !for)'
+ 'test wow = $(git config --get nextsection.nonewline !for)'
test_expect_failure 'ambiguous get' \
- 'git-config --get nextsection.nonewline'
+ 'git config --get nextsection.nonewline'
test_expect_success 'get multivar' \
- 'git-config --get-all nextsection.nonewline'
+ 'git config --get-all nextsection.nonewline'
-git-config nextsection.nonewline "wow3" "wow$"
+git config nextsection.nonewline "wow3" "wow$"
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -202,15 +202,15 @@ EOF
test_expect_success 'multivar replace' 'cmp .git/config expect'
-test_expect_failure 'ambiguous value' 'git-config nextsection.nonewline'
+test_expect_failure 'ambiguous value' 'git config nextsection.nonewline'
test_expect_failure 'ambiguous unset' \
- 'git-config --unset nextsection.nonewline'
+ 'git config --unset nextsection.nonewline'
test_expect_failure 'invalid unset' \
- 'git-config --unset somesection.nonewline'
+ 'git config --unset somesection.nonewline'
-git-config --unset nextsection.nonewline "wow3$"
+git config --unset nextsection.nonewline "wow3$"
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -224,12 +224,12 @@ EOF
test_expect_success 'multivar unset' 'cmp .git/config expect'
-test_expect_failure 'invalid key' 'git-config inval.2key blabla'
+test_expect_failure 'invalid key' 'git config inval.2key blabla'
-test_expect_success 'correct key' 'git-config 123456.a123 987'
+test_expect_success 'correct key' 'git config 123456.a123 987'
test_expect_success 'hierarchical section' \
- 'git-config Version.1.2.3eX.Alpha beta'
+ 'git config Version.1.2.3eX.Alpha beta'
cat > expect << EOF
[beta] ; silly comment # another comment
@@ -255,7 +255,7 @@ version.1.2.3eX.alpha=beta
EOF
test_expect_success 'working --list' \
- 'git-config --list > output && cmp output expect'
+ 'git config --list > output && cmp output expect'
cat > expect << EOF
beta.noindent sillyValue
@@ -263,9 +263,9 @@ nextsection.nonewline wow2 for me
EOF
test_expect_success '--get-regexp' \
- 'git-config --get-regexp in > output && cmp output expect'
+ 'git config --get-regexp in > output && cmp output expect'
-git-config --add nextsection.nonewline "wow4 for you"
+git config --add nextsection.nonewline "wow4 for you"
cat > expect << EOF
wow2 for me
@@ -273,7 +273,7 @@ wow4 for you
EOF
test_expect_success '--add' \
- 'git-config --get-all nextsection.nonewline > output && cmp output expect'
+ 'git config --get-all nextsection.nonewline > output && cmp output expect'
cat > .git/config << EOF
[novalue]
@@ -281,15 +281,15 @@ cat > .git/config << EOF
EOF
test_expect_success 'get variable with no value' \
- 'git-config --get novalue.variable ^$'
+ 'git config --get novalue.variable ^$'
echo novalue.variable > expect
test_expect_success 'get-regexp variable with no value' \
- 'git-config --get-regexp novalue > output &&
+ 'git config --get-regexp novalue > output &&
cmp output expect'
-git-config > output 2>&1
+git config > output 2>&1
test_expect_success 'no arguments, but no crash' \
"test $? = 129 && grep usage output"
@@ -299,7 +299,7 @@ cat > .git/config << EOF
c = d
EOF
-git-config a.x y
+git config a.x y
cat > expect << EOF
[a.b]
@@ -310,8 +310,8 @@ EOF
test_expect_success 'new section is partial match of another' 'cmp .git/config expect'
-git-config b.x y
-git-config a.b c
+git config b.x y
+git config a.b c
cat > expect << EOF
[a.b]
@@ -325,6 +325,9 @@ EOF
test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
+test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \
+ 'git config --file non-existing-config -l; test $? != 0'
+
cat > other-config << EOF
[ein]
bahn = strasse
@@ -334,11 +337,14 @@ cat > expect << EOF
ein.bahn=strasse
EOF
-GIT_CONFIG=other-config git-config -l > output
+GIT_CONFIG=other-config git config -l > output
test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
-GIT_CONFIG=other-config git-config anwohner.park ausweis
+test_expect_success 'alternative GIT_CONFIG (--file)' \
+ 'git config --file other-config -l > output && cmp output expect'
+
+GIT_CONFIG=other-config git config anwohner.park ausweis
cat > expect << EOF
[ein]
@@ -361,7 +367,7 @@ weird
EOF
test_expect_success "rename section" \
- "git-config --rename-section branch.eins branch.zwei"
+ "git config --rename-section branch.eins branch.zwei"
cat > expect << EOF
# Hallo
@@ -377,12 +383,12 @@ EOF
test_expect_success "rename succeeded" "git diff expect .git/config"
test_expect_failure "rename non-existing section" \
- 'git-config --rename-section branch."world domination" branch.drei'
+ 'git config --rename-section branch."world domination" branch.drei'
test_expect_success "rename succeeded" "git diff expect .git/config"
test_expect_success "rename another section" \
- 'git-config --rename-section branch."1 234 blabl/a" branch.drei'
+ 'git config --rename-section branch."1 234 blabl/a" branch.drei'
cat > expect << EOF
# Hallo
@@ -425,20 +431,20 @@ EOF
test_expect_success 'section ending' '
- git-config gitcvs.enabled true &&
- git-config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
- git-config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ git config gitcvs.enabled true &&
+ git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
cmp .git/config expect
'
test_expect_success numbers '
- git-config kilo.gram 1k &&
- git-config mega.ton 1m &&
- k=$(git-config --int --get kilo.gram) &&
+ git config kilo.gram 1k &&
+ git config mega.ton 1m &&
+ k=$(git config --int --get kilo.gram) &&
test z1024 = "z$k" &&
- m=$(git-config --int --get mega.ton) &&
+ m=$(git config --int --get mega.ton) &&
test z1048576 = "z$m"
'
@@ -455,33 +461,79 @@ EOF
test_expect_success bool '
- git-config bool.true1 01 &&
- git-config bool.true2 -1 &&
- git-config bool.true3 YeS &&
- git-config bool.true4 true &&
- git-config bool.false1 000 &&
- git-config bool.false2 "" &&
- git-config bool.false3 nO &&
- git-config bool.false4 FALSE &&
+ git config bool.true1 01 &&
+ git config bool.true2 -1 &&
+ git config bool.true3 YeS &&
+ git config bool.true4 true &&
+ git config bool.false1 000 &&
+ git config bool.false2 "" &&
+ git config bool.false3 nO &&
+ git config bool.false4 FALSE &&
rm -f result &&
for i in 1 2 3 4
do
- git-config --bool --get bool.true$i >>result
- git-config --bool --get bool.false$i >>result
+ git config --bool --get bool.true$i >>result
+ git config --bool --get bool.false$i >>result
done &&
cmp expect result'
-test_expect_failure 'invalid bool' '
+test_expect_failure 'invalid bool (--get)' '
+
+ git config bool.nobool foobar &&
+ git config --bool --get bool.nobool'
+
+test_expect_failure 'invalid bool (set)' '
+
+ git config --bool bool.nobool foobar'
+
+rm .git/config
+
+cat > expect <<\EOF
+[bool]
+ true1 = true
+ true2 = true
+ true3 = true
+ true4 = true
+ false1 = false
+ false2 = false
+ false3 = false
+ false4 = false
+EOF
+
+test_expect_success 'set --bool' '
+
+ git config --bool bool.true1 01 &&
+ git config --bool bool.true2 -1 &&
+ git config --bool bool.true3 YeS &&
+ git config --bool bool.true4 true &&
+ git config --bool bool.false1 000 &&
+ git config --bool bool.false2 "" &&
+ git config --bool bool.false3 nO &&
+ git config --bool bool.false4 FALSE &&
+ cmp expect .git/config'
+
+rm .git/config
+
+cat > expect <<\EOF
+[int]
+ val1 = 1
+ val2 = -1
+ val3 = 5242880
+EOF
- git-config bool.nobool foobar &&
- git-config --bool --get bool.nobool'
+test_expect_success 'set --int' '
+
+ git config --int int.val1 01 &&
+ git config --int int.val2 -1 &&
+ git config --int int.val3 5m &&
+ cmp expect .git/config'
rm .git/config
-git-config quote.leading " test"
-git-config quote.ending "test "
-git-config quote.semicolon "test;test"
-git-config quote.hash "test#test"
+git config quote.leading " test"
+git config quote.ending "test "
+git config quote.semicolon "test;test"
+git config quote.hash "test#test"
cat > expect << EOF
[quote]
@@ -519,5 +571,49 @@ git config --list > result
test_expect_success 'value continued on next line' 'cmp result expect'
-test_done
+cat > .git/config <<\EOF
+[section "sub=section"]
+ val1 = foo=bar
+ val2 = foo\nbar
+ val3 = \n\n
+ val4 =
+ val5
+EOF
+
+cat > expect <<\EOF
+section.sub=section.val1
+foo=barQsection.sub=section.val2
+foo
+barQsection.sub=section.val3
+
+
+Qsection.sub=section.val4
+Qsection.sub=section.val5Q
+EOF
+
+git config --null --list | tr '[\000]' 'Q' > result
+echo >>result
+test_expect_success '--null --list' 'cmp result expect'
+
+git config --null --get-regexp 'val[0-9]' | tr '[\000]' 'Q' > result
+echo >>result
+
+test_expect_success '--null --get-regexp' 'cmp result expect'
+
+test_expect_success 'symlinked configuration' '
+
+ ln -s notyet myconfig &&
+ GIT_CONFIG=myconfig git config test.frotz nitfol &&
+ test -h myconfig &&
+ test -f notyet &&
+ test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
+ GIT_CONFIG=myconfig git config test.xyzzy rezrov &&
+ test -h myconfig &&
+ test -f notyet &&
+ test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
+ test "z$(GIT_CONFIG=notyet git config test.xyzzy)" = zrezrov
+
+'
+
+test_done
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
new file mode 100755
index 0000000000..bb5f30220a
--- /dev/null
+++ b/t/t1301-shared-repo.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Schindelin
+#
+
+test_description='Test shared repository initialization'
+
+. ./test-lib.sh
+
+test_expect_success 'shared=all' '
+ mkdir sub &&
+ cd sub &&
+ git init --shared=all &&
+ test 2 = $(git config core.sharedrepository)
+'
+
+test_expect_success 'update-server-info honors core.sharedRepository' '
+ : > a1 &&
+ git add a1 &&
+ test_tick &&
+ git commit -m a1 &&
+ umask 0277 &&
+ git update-server-info &&
+ test 444 = $(stat -c %a .git/info/refs)
+'
+
+test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index d0aba2c2ae..c4c0dfaab1 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Shawn Pearce
#
-test_description='Test git-update-ref and basic ref logging'
+test_description='Test git update-ref and basic ref logging'
. ./test-lib.sh
Z=0000000000000000000000000000000000000000
@@ -19,34 +19,34 @@ n=$n_dir/fixes
test_expect_success \
"create $m" \
- "git-update-ref $m $A &&
+ "git update-ref $m $A &&
test $A"' = $(cat .git/'"$m"')'
test_expect_success \
"create $m" \
- "git-update-ref $m $B $A &&
+ "git update-ref $m $B $A &&
test $B"' = $(cat .git/'"$m"')'
rm -f .git/$m
test_expect_success \
"fail to create $n" \
"touch .git/$n_dir
- git-update-ref $n $A >out 2>err"'
+ git update-ref $n $A >out 2>err"'
test $? != 0'
rm -f .git/$n_dir out err
test_expect_success \
"create $m (by HEAD)" \
- "git-update-ref HEAD $A &&
+ "git update-ref HEAD $A &&
test $A"' = $(cat .git/'"$m"')'
test_expect_success \
"create $m (by HEAD)" \
- "git-update-ref HEAD $B $A &&
+ "git update-ref HEAD $B $A &&
test $B"' = $(cat .git/'"$m"')'
rm -f .git/$m
test_expect_failure \
'(not) create HEAD with old sha1' \
- "git-update-ref HEAD $A $B"
+ "git update-ref HEAD $A $B"
test_expect_failure \
"(not) prior created .git/$m" \
"test -f .git/$m"
@@ -54,10 +54,10 @@ rm -f .git/$m
test_expect_success \
"create HEAD" \
- "git-update-ref HEAD $A"
+ "git update-ref HEAD $A"
test_expect_failure \
'(not) change HEAD with wrong SHA1' \
- "git-update-ref HEAD $B $Z"
+ "git update-ref HEAD $B $Z"
test_expect_failure \
"(not) changed .git/$m" \
"test $B"' = $(cat .git/'"$m"')'
@@ -68,17 +68,17 @@ rm -f .git/$m
test_expect_success \
"create $m (logged by touch)" \
'GIT_COMMITTER_DATE="2005-05-26 23:30" \
- git-update-ref HEAD '"$A"' -m "Initial Creation" &&
+ git update-ref HEAD '"$A"' -m "Initial Creation" &&
test '"$A"' = $(cat .git/'"$m"')'
test_expect_success \
"update $m (logged by touch)" \
'GIT_COMMITTER_DATE="2005-05-26 23:31" \
- git-update-ref HEAD'" $B $A "'-m "Switch" &&
+ git update-ref HEAD'" $B $A "'-m "Switch" &&
test '"$B"' = $(cat .git/'"$m"')'
test_expect_success \
"set $m (logged by touch)" \
'GIT_COMMITTER_DATE="2005-05-26 23:41" \
- git-update-ref HEAD'" $A &&
+ git update-ref HEAD'" $A &&
test $A"' = $(cat .git/'"$m"')'
cat >expect <<EOF
@@ -93,23 +93,23 @@ rm -rf .git/$m .git/logs expect
test_expect_success \
'enable core.logAllRefUpdates' \
- 'git-config core.logAllRefUpdates true &&
- test true = $(git-config --bool --get core.logAllRefUpdates)'
+ 'git config core.logAllRefUpdates true &&
+ test true = $(git config --bool --get core.logAllRefUpdates)'
test_expect_success \
"create $m (logged by config)" \
'GIT_COMMITTER_DATE="2005-05-26 23:32" \
- git-update-ref HEAD'" $A "'-m "Initial Creation" &&
+ git update-ref HEAD'" $A "'-m "Initial Creation" &&
test '"$A"' = $(cat .git/'"$m"')'
test_expect_success \
"update $m (logged by config)" \
'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"' = $(cat .git/'"$m"')'
test_expect_success \
"set $m (logged by config)" \
'GIT_COMMITTER_DATE="2005-05-26 23:43" \
- git-update-ref HEAD '"$A &&
+ git update-ref HEAD '"$A &&
test $A"' = $(cat .git/'"$m"')'
cat >expect <<EOF
@@ -122,7 +122,7 @@ test_expect_success \
'diff expect .git/logs/$m'
rm -f .git/$m .git/logs/$m expect
-git-update-ref $m $D
+git update-ref $m $D
cat >.git/logs/$m <<EOF
$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
@@ -136,49 +136,49 @@ ld="Thu, 26 May 2005 18:43:00 -0500"
test_expect_success \
'Query "master@{May 25 2005}" (before history)' \
'rm -f o e
- git-rev-parse --verify "master@{May 25 2005}" >o 2>e &&
+ git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
test '"$C"' = $(cat o) &&
test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
test_expect_success \
"Query master@{2005-05-25} (before history)" \
'rm -f o e
- git-rev-parse --verify master@{2005-05-25} >o 2>e &&
+ git rev-parse --verify master@{2005-05-25} >o 2>e &&
test '"$C"' = $(cat o) &&
echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
test_expect_success \
'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
'rm -f o e
- git-rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
+ git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
test '"$C"' = $(cat o) &&
test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"'
test_expect_success \
'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
'rm -f o e
- git-rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
+ git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
test '"$A"' = $(cat o) &&
test "" = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
'rm -f o e
- git-rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
+ git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
test '"$B"' = $(cat o) &&
test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
'rm -f o e
- git-rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
+ git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
test '"$Z"' = $(cat o) &&
test "" = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
'rm -f o e
- git-rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
+ git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
test '"$E"' = $(cat o) &&
test "" = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-28}" (past end of history)' \
'rm -f o e
- git-rev-parse --verify "master@{2005-05-28}" >o 2>e &&
+ git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
test '"$D"' = $(cat o) &&
test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
@@ -188,26 +188,26 @@ rm -f .git/$m .git/logs/$m expect
test_expect_success \
'creating initial files' \
'echo TEST >F &&
- git-add F &&
+ git add F &&
GIT_AUTHOR_DATE="2005-05-26 23:30" \
GIT_COMMITTER_DATE="2005-05-26 23:30" git-commit -m add -a &&
- h_TEST=$(git-rev-parse --verify HEAD)
+ h_TEST=$(git rev-parse --verify HEAD)
echo The other day this did not work. >M &&
echo And then Bob told me how to fix it. >>M &&
echo OTHER >F &&
GIT_AUTHOR_DATE="2005-05-26 23:41" \
GIT_COMMITTER_DATE="2005-05-26 23:41" git-commit -F M -a &&
- h_OTHER=$(git-rev-parse --verify HEAD) &&
+ h_OTHER=$(git rev-parse --verify HEAD) &&
echo FIXED >F &&
GIT_AUTHOR_DATE="2005-05-26 23:44" \
GIT_COMMITTER_DATE="2005-05-26 23:44" git-commit --amend &&
- h_FIXED=$(git-rev-parse --verify HEAD) &&
+ h_FIXED=$(git rev-parse --verify HEAD) &&
echo TEST+FIXED >F &&
echo Merged initial commit and a later commit. >M &&
echo $h_TEST >.git/MERGE_HEAD &&
GIT_AUTHOR_DATE="2005-05-26 23:45" \
GIT_COMMITTER_DATE="2005-05-26 23:45" git-commit -F M &&
- h_MERGED=$(git-rev-parse --verify HEAD)
+ h_MERGED=$(git rev-parse --verify HEAD)
rm -f M'
cat >expect <<EOF
@@ -222,13 +222,13 @@ test_expect_success \
unset h_TEST h_OTHER h_FIXED h_MERGED
test_expect_success \
- 'git-cat-file blob master:F (expect OTHER)' \
- 'test OTHER = $(git-cat-file blob master:F)'
+ 'git cat-file blob master:F (expect OTHER)' \
+ 'test OTHER = $(git cat-file blob master:F)'
test_expect_success \
- 'git-cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' \
- 'test TEST = $(git-cat-file blob "master@{2005-05-26 23:30}:F")'
+ 'git cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' \
+ 'test TEST = $(git cat-file blob "master@{2005-05-26 23:30}:F")'
test_expect_success \
- 'git-cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
- 'test OTHER = $(git-cat-file blob "master@{2005-05-26 23:42}:F")'
+ 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
+ 'test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F")'
test_done
diff --git a/t/t1420-lost-found.sh b/t/t1420-lost-found.sh
new file mode 100755
index 0000000000..dc9e402c55
--- /dev/null
+++ b/t/t1420-lost-found.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test fsck --lost-found'
+. ./test-lib.sh
+
+test_expect_success setup '
+ git config core.logAllRefUpdates 0 &&
+ : > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m initial &&
+ echo 1 > file1 &&
+ echo 2 > file2 &&
+ git add file1 file2 &&
+ test_tick &&
+ git commit -m second &&
+ echo 3 > file3 &&
+ git add file3
+'
+
+test_expect_success 'lost and found something' '
+ git rev-parse HEAD > lost-commit &&
+ git rev-parse :file3 > lost-other &&
+ test_tick &&
+ git reset --hard HEAD^ &&
+ git fsck --lost-found &&
+ test 2 = $(ls .git/lost-found/*/* | wc -l) &&
+ test -f .git/lost-found/commit/$(cat lost-commit) &&
+ test -f .git/lost-found/other/$(cat lost-other)
+'
+
+test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
new file mode 100755
index 0000000000..e474b3f1d5
--- /dev/null
+++ b/t/t1500-rev-parse.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='test git rev-parse'
+. ./test-lib.sh
+
+test_rev_parse() {
+ name=$1
+ shift
+
+ test_expect_success "$name: is-bare-repository" \
+ "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-git-dir" \
+ "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-work-tree" \
+ "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: prefix" \
+ "test '$1' = \"\$(git rev-parse --show-prefix)\""
+ shift
+ [ $# -eq 0 ] && return
+}
+
+# label is-bare is-inside-git is-inside-work prefix
+
+test_rev_parse toplevel false false true ''
+
+cd .git || exit 1
+test_rev_parse .git/ true true false ''
+cd objects || exit 1
+test_rev_parse .git/objects/ true true false ''
+cd ../.. || exit 1
+
+mkdir -p sub/dir || exit 1
+cd sub/dir || exit 1
+test_rev_parse subdirectory false false true sub/dir/
+cd ../.. || exit 1
+
+git config core.bare true
+test_rev_parse 'core.bare = true' true false false
+
+git config --unset core.bare
+test_rev_parse 'core.bare undefined' false false true
+
+mkdir work || exit 1
+cd work || exit 1
+export GIT_DIR=../.git
+export GIT_CONFIG="$(pwd)"/../.git/config
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
+
+mv ../.git ../repo.git || exit 1
+export GIT_DIR=../repo.git
+export GIT_CONFIG="$(pwd)"/../repo.git/config
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false false ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
+
+test_done
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
new file mode 100755
index 0000000000..732216184f
--- /dev/null
+++ b/t/t1501-worktree.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+test_description='test separate work tree'
+. ./test-lib.sh
+
+test_rev_parse() {
+ name=$1
+ shift
+
+ test_expect_success "$name: is-bare-repository" \
+ "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-git-dir" \
+ "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-work-tree" \
+ "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: prefix" \
+ "test '$1' = \"\$(git rev-parse --show-prefix)\""
+ shift
+ [ $# -eq 0 ] && return
+}
+
+mkdir -p work/sub/dir || exit 1
+mv .git repo.git || exit 1
+
+say "core.worktree = relative path"
+export GIT_DIR=repo.git
+export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+unset GIT_WORK_TREE
+git config core.worktree ../work
+test_rev_parse 'outside' false false false
+cd work || exit 1
+export GIT_DIR=../repo.git
+export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+test_rev_parse 'inside' false false true ''
+cd sub/dir || exit 1
+export GIT_DIR=../../../repo.git
+export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "core.worktree = absolute path"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+git config core.worktree "$(pwd)/work"
+test_rev_parse 'outside' false false false
+cd work || exit 1
+test_rev_parse 'inside' false false true ''
+cd sub/dir || exit 1
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "GIT_WORK_TREE=relative path (override core.worktree)"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+git config core.worktree non-existent
+export GIT_WORK_TREE=work
+test_rev_parse 'outside' false false false
+cd work || exit 1
+export GIT_WORK_TREE=.
+test_rev_parse 'inside' false false true ''
+cd sub/dir || exit 1
+export GIT_WORK_TREE=../..
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+mv work repo.git/work
+
+say "GIT_WORK_TREE=absolute path, work tree below git dir"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+export GIT_WORK_TREE=$(pwd)/repo.git/work
+test_rev_parse 'outside' false false false
+cd repo.git || exit 1
+test_rev_parse 'in repo.git' false true false
+cd objects || exit 1
+test_rev_parse 'in repo.git/objects' false true false
+cd ../work || exit 1
+test_rev_parse 'in repo.git/work' false true true ''
+cd sub/dir || exit 1
+test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
+cd ../../../.. || exit 1
+
+test_expect_success 'repo finds its work tree' '
+ (cd repo.git &&
+ : > work/sub/dir/untracked &&
+ test sub/dir/untracked = "$(git ls-files --others)")
+'
+
+test_expect_success 'repo finds its work tree from work tree, too' '
+ (cd repo.git/work/sub/dir &&
+ : > tracked &&
+ git --git-dir=../../.. add tracked &&
+ cd ../../.. &&
+ test sub/dir/tracked = "$(git ls-files)")
+'
+
+test_done
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
index 03ea4dece4..ac84335b0a 100755
--- a/t/t2000-checkout-cache-clash.sh
+++ b/t/t2000-checkout-cache-clash.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-checkout-index test.
+test_description='git checkout-index test.
This test registers the following filesystem structure in the
cache:
@@ -16,7 +16,7 @@ And then tries to checkout in a work tree that has the following:
path0/file0 - a file in a directory
path1 - a file
-The git-checkout-index command should fail when attempting to checkout
+The git checkout-index command should fail when attempting to checkout
path0, finding it is occupied by a directory, and path1/file1, finding
path1 is occupied by a non-directory. With "-f" flag, it should remove
the conflicting paths and succeed.
@@ -28,8 +28,8 @@ mkdir path1
date >path1/file1
test_expect_success \
- 'git-update-index --add various paths.' \
- 'git-update-index --add path0 path1/file1'
+ 'git update-index --add various paths.' \
+ 'git update-index --add path0 path1/file1'
rm -fr path0 path1
mkdir path0
@@ -37,17 +37,15 @@ date >path0/file0
date >path1
test_expect_failure \
- 'git-checkout-index without -f should fail on conflicting work tree.' \
- 'git-checkout-index -a'
+ 'git checkout-index without -f should fail on conflicting work tree.' \
+ 'git checkout-index -a'
test_expect_success \
- 'git-checkout-index with -f should succeed.' \
- 'git-checkout-index -f -a'
+ 'git checkout-index with -f should succeed.' \
+ 'git checkout-index -f -a'
test_expect_success \
- 'git-checkout-index conflicting paths.' \
+ 'git checkout-index conflicting paths.' \
'test -f path0 && test -d path1 && test -f path1/file1'
test_done
-
-
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
index 0dcab8f1de..ef007532b1 100755
--- a/t/t2001-checkout-cache-clash.sh
+++ b/t/t2001-checkout-cache-clash.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-checkout-index test.
+test_description='git checkout-index test.
This test registers the following filesystem structure in the cache:
@@ -26,46 +26,46 @@ show_files() {
find path? -ls |
sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
# what's in the cache, just mode and name
- git-ls-files --stage |
+ git ls-files --stage |
sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
# what's in the tree, just mode and name.
- git-ls-tree -r "$1" |
+ git ls-tree -r "$1" |
sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /'
}
mkdir path0
date >path0/file0
test_expect_success \
- 'git-update-index --add path0/file0' \
- 'git-update-index --add path0/file0'
+ 'git update-index --add path0/file0' \
+ 'git update-index --add path0/file0'
test_expect_success \
- 'writing tree out with git-write-tree' \
- 'tree1=$(git-write-tree)'
+ 'writing tree out with git write-tree' \
+ 'tree1=$(git write-tree)'
test_debug 'show_files $tree1'
mkdir path1
date >path1/file1
test_expect_success \
- 'git-update-index --add path1/file1' \
- 'git-update-index --add path1/file1'
+ 'git update-index --add path1/file1' \
+ 'git update-index --add path1/file1'
test_expect_success \
- 'writing tree out with git-write-tree' \
- 'tree2=$(git-write-tree)'
+ 'writing tree out with git write-tree' \
+ 'tree2=$(git write-tree)'
test_debug 'show_files $tree2'
rm -fr path1
test_expect_success \
'read previously written tree and checkout.' \
- 'git-read-tree -m $tree1 && git-checkout-index -f -a'
+ 'git read-tree -m $tree1 && git checkout-index -f -a'
test_debug 'show_files $tree1'
ln -s path0 path1
test_expect_success \
- 'git-update-index --add a symlink.' \
- 'git-update-index --add path1'
+ 'git update-index --add a symlink.' \
+ 'git update-index --add path1'
test_expect_success \
- 'writing tree out with git-write-tree' \
- 'tree3=$(git-write-tree)'
+ 'writing tree out with git write-tree' \
+ 'tree3=$(git write-tree)'
test_debug 'show_files $tree3'
# Morten says "Got that?" here.
@@ -73,7 +73,7 @@ test_debug 'show_files $tree3'
test_expect_success \
'read previously written tree and checkout.' \
- 'git-read-tree $tree2 && git-checkout-index -f -a'
+ 'git read-tree $tree2 && git checkout-index -f -a'
test_debug 'show_files $tree2'
test_expect_success \
@@ -84,4 +84,3 @@ test_expect_success \
test ! -h path1/file1 && test -f path1/file1'
test_done
-
diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh
index 4352ddb1cb..f7a0055920 100755
--- a/t/t2002-checkout-cache-u.sh
+++ b/t/t2002-checkout-cache-u.sh
@@ -3,31 +3,31 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-checkout-index -u test.
+test_description='git checkout-index -u test.
-With -u flag, git-checkout-index internally runs the equivalent of
-git-update-index --refresh on the checked out entry.'
+With -u flag, git checkout-index internally runs the equivalent of
+git update-index --refresh on the checked out entry.'
. ./test-lib.sh
test_expect_success \
'preparation' '
echo frotz >path0 &&
-git-update-index --add path0 &&
-t=$(git-write-tree)'
+git update-index --add path0 &&
+t=$(git write-tree)'
test_expect_failure \
-'without -u, git-checkout-index smudges stat information.' '
+'without -u, git checkout-index smudges stat information.' '
rm -f path0 &&
-git-read-tree $t &&
-git-checkout-index -f -a &&
-git-diff-files | diff - /dev/null'
+git read-tree $t &&
+git checkout-index -f -a &&
+git diff-files | diff - /dev/null'
test_expect_success \
-'with -u, git-checkout-index picks up stat information from new files.' '
+'with -u, git checkout-index picks up stat information from new files.' '
rm -f path0 &&
-git-read-tree $t &&
-git-checkout-index -u -f -a &&
-git-diff-files | diff - /dev/null'
+git read-tree $t &&
+git checkout-index -u -f -a &&
+git diff-files | diff - /dev/null'
test_done
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
index f9bc90aee4..71894b3743 100755
--- a/t/t2003-checkout-cache-mkdir.sh
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-checkout-index --prefix test.
+test_description='git checkout-index --prefix test.
This test makes sure that --prefix option works as advertised, and
also verifies that such leading path may contain symlinks, unlike
@@ -17,14 +17,14 @@ test_expect_success \
'mkdir path1 &&
echo frotz >path0 &&
echo rezrov >path1/file1 &&
- git-update-index --add path0 path1/file1'
+ git update-index --add path0 path1/file1'
test_expect_success \
'have symlink in place where dir is expected.' \
'rm -fr path0 path1 &&
mkdir path2 &&
ln -s path2 path1 &&
- git-checkout-index -f -a &&
+ git checkout-index -f -a &&
test ! -h path1 && test -d path1 &&
test -f path1/file1 && test ! -f path2/file1'
@@ -32,7 +32,7 @@ test_expect_success \
'use --prefix=path2/' \
'rm -fr path0 path1 path2 &&
mkdir path2 &&
- git-checkout-index --prefix=path2/ -f -a &&
+ git checkout-index --prefix=path2/ -f -a &&
test -f path2/path0 &&
test -f path2/path1/file1 &&
test ! -f path0 &&
@@ -41,7 +41,7 @@ test_expect_success \
test_expect_success \
'use --prefix=tmp-' \
'rm -fr path0 path1 path2 tmp* &&
- git-checkout-index --prefix=tmp- -f -a &&
+ git checkout-index --prefix=tmp- -f -a &&
test -f tmp-path0 &&
test -f tmp-path1/file1 &&
test ! -f path0 &&
@@ -52,7 +52,7 @@ test_expect_success \
'rm -fr path0 path1 path2 tmp* &&
echo nitfol >tmp-path1 &&
mkdir tmp-path0 &&
- git-checkout-index --prefix=tmp- -f -a &&
+ git checkout-index --prefix=tmp- -f -a &&
test -f tmp-path0 &&
test -f tmp-path1/file1 &&
test ! -f path0 &&
@@ -64,7 +64,7 @@ test_expect_success \
'rm -fr path0 path1 path2 tmp* &&
mkdir tmp1 tmp1/orary &&
ln -s tmp1 tmp &&
- git-checkout-index --prefix=tmp/orary/ -f -a &&
+ git checkout-index --prefix=tmp/orary/ -f -a &&
test -d tmp1/orary &&
test -f tmp1/orary/path0 &&
test -f tmp1/orary/path1/file1 &&
@@ -76,7 +76,7 @@ test_expect_success \
'rm -fr path0 path1 path2 tmp* &&
mkdir tmp1 &&
ln -s tmp1 tmp &&
- git-checkout-index --prefix=tmp/orary- -f -a &&
+ git checkout-index --prefix=tmp/orary- -f -a &&
test -f tmp1/orary-path0 &&
test -f tmp1/orary-path1/file1 &&
test -h tmp'
@@ -87,7 +87,7 @@ test_expect_success \
'rm -fr path0 path1 path2 tmp* &&
mkdir tmp1 &&
ln -s tmp1 tmp-path1 &&
- git-checkout-index --prefix=tmp- -f -a &&
+ git checkout-index --prefix=tmp- -f -a &&
test -f tmp-path0 &&
test ! -h tmp-path1 &&
test -d tmp-path1 &&
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index c100959cad..39133b8c7a 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2006 Shawn Pearce
#
-test_description='git-checkout-index --temp test.
+test_description='git checkout-index --temp test.
-With --temp flag, git-checkout-index writes to temporary merge files
+With --temp flag, git checkout-index writes to temporary merge files
rather than the tracked path.'
. ./test-lib.sh
@@ -18,28 +18,28 @@ echo tree1path1 >path1 &&
echo tree1path3 >path3 &&
echo tree1path4 >path4 &&
echo tree1asubdir/path5 >asubdir/path5 &&
-git-update-index --add path0 path1 path3 path4 asubdir/path5 &&
-t1=$(git-write-tree) &&
+git update-index --add path0 path1 path3 path4 asubdir/path5 &&
+t1=$(git write-tree) &&
rm -f path* .merge_* out .git/index &&
echo tree2path0 >path0 &&
echo tree2path1 >path1 &&
echo tree2path2 >path2 &&
echo tree2path4 >path4 &&
-git-update-index --add path0 path1 path2 path4 &&
-t2=$(git-write-tree) &&
+git update-index --add path0 path1 path2 path4 &&
+t2=$(git write-tree) &&
rm -f path* .merge_* out .git/index &&
echo tree2path0 >path0 &&
echo tree3path1 >path1 &&
echo tree3path2 >path2 &&
echo tree3path3 >path3 &&
-git-update-index --add path0 path1 path2 path3 &&
-t3=$(git-write-tree)'
+git update-index --add path0 path1 path2 path3 &&
+t3=$(git write-tree)'
test_expect_success \
'checkout one stage 0 to temporary file' '
rm -f path* .merge_* out .git/index &&
-git-read-tree $t1 &&
-git-checkout-index --temp -- path1 >out &&
+git read-tree $t1 &&
+git checkout-index --temp -- path1 >out &&
test $(wc -l <out) = 1 &&
test $(cut "-d " -f2 out) = path1 &&
p=$(cut "-d " -f1 out) &&
@@ -49,8 +49,8 @@ test $(cat $p) = tree1path1'
test_expect_success \
'checkout all stage 0 to temporary files' '
rm -f path* .merge_* out .git/index &&
-git-read-tree $t1 &&
-git-checkout-index -a --temp >out &&
+git read-tree $t1 &&
+git checkout-index -a --temp >out &&
test $(wc -l <out) = 5 &&
for f in path0 path1 path3 path4 asubdir/path5
do
@@ -63,12 +63,12 @@ done'
test_expect_success \
'prepare 3-way merge' '
rm -f path* .merge_* out .git/index &&
-git-read-tree -m $t1 $t2 $t3'
+git read-tree -m $t1 $t2 $t3'
test_expect_success \
'checkout one stage 2 to temporary file' '
rm -f path* .merge_* out &&
-git-checkout-index --stage=2 --temp -- path1 >out &&
+git checkout-index --stage=2 --temp -- path1 >out &&
test $(wc -l <out) = 1 &&
test $(cut "-d " -f2 out) = path1 &&
p=$(cut "-d " -f1 out) &&
@@ -78,7 +78,7 @@ test $(cat $p) = tree2path1'
test_expect_success \
'checkout all stage 2 to temporary files' '
rm -f path* .merge_* out &&
-git-checkout-index --all --stage=2 --temp >out &&
+git checkout-index --all --stage=2 --temp >out &&
test $(wc -l <out) = 3 &&
for f in path1 path2 path4
do
@@ -91,13 +91,13 @@ done'
test_expect_success \
'checkout all stages/one file to nothing' '
rm -f path* .merge_* out &&
-git-checkout-index --stage=all --temp -- path0 >out &&
+git checkout-index --stage=all --temp -- path0 >out &&
test $(wc -l <out) = 0'
test_expect_success \
'checkout all stages/one file to temporary files' '
rm -f path* .merge_* out &&
-git-checkout-index --stage=all --temp -- path1 >out &&
+git checkout-index --stage=all --temp -- path1 >out &&
test $(wc -l <out) = 1 &&
test $(cut "-d " -f2 out) = path1 &&
cut "-d " -f1 out | (read s1 s2 s3 &&
@@ -111,7 +111,7 @@ test $(cat $s3) = tree3path1)'
test_expect_success \
'checkout some stages/one file to temporary files' '
rm -f path* .merge_* out &&
-git-checkout-index --stage=all --temp -- path2 >out &&
+git checkout-index --stage=all --temp -- path2 >out &&
test $(wc -l <out) = 1 &&
test $(cut "-d " -f2 out) = path2 &&
cut "-d " -f1 out | (read s1 s2 s3 &&
@@ -124,7 +124,7 @@ test $(cat $s3) = tree3path2)'
test_expect_success \
'checkout all stages/all files to temporary files' '
rm -f path* .merge_* out &&
-git-checkout-index -a --stage=all --temp >out &&
+git checkout-index -a --stage=all --temp >out &&
test $(wc -l <out) = 5'
test_expect_success \
@@ -184,7 +184,7 @@ test $(cat $s1) = tree1asubdir/path5)'
test_expect_success \
'checkout --temp within subdir' '
(cd asubdir &&
- git-checkout-index -a --stage=all >out &&
+ git checkout-index -a --stage=all >out &&
test $(wc -l <out) = 1 &&
test $(grep path5 out | cut "-d " -f2) = path5 &&
grep path5 out | cut "-d " -f1 | (read s1 s2 s3 &&
@@ -198,11 +198,11 @@ test_expect_success \
'checkout --temp symlink' '
rm -f path* .merge_* out .git/index &&
ln -s b a &&
-git-update-index --add a &&
-t4=$(git-write-tree) &&
+git update-index --add a &&
+t4=$(git write-tree) &&
rm -f .git/index &&
-git-read-tree $t4 &&
-git-checkout-index --temp -a >out &&
+git read-tree $t4 &&
+git checkout-index --temp -a >out &&
test $(wc -l <out) = 1 &&
test $(cut "-d " -f2 out) = a &&
p=$(cut "-d " -f1 out) &&
diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh
index e34a515333..a84c5a6af9 100755
--- a/t/t2005-checkout-index-symlinks.sh
+++ b/t/t2005-checkout-index-symlinks.sh
@@ -3,22 +3,22 @@
# Copyright (c) 2007 Johannes Sixt
#
-test_description='git-checkout-index on filesystem w/o symlinks test.
+test_description='git checkout-index on filesystem w/o symlinks test.
-This tests that git-checkout-index creates a symbolic link as a plain
+This tests that git checkout-index creates a symbolic link as a plain
file if core.symlinks is false.'
. ./test-lib.sh
test_expect_success \
'preparation' '
-git-config core.symlinks false &&
+git config core.symlinks false &&
l=$(echo -n file | git-hash-object -t blob -w --stdin) &&
-echo "120000 $l symlink" | git-update-index --index-info'
+echo "120000 $l symlink" | git update-index --index-info'
test_expect_success \
'the checked-out symlink must be a file' '
-git-checkout-index symlink &&
+git checkout-index symlink &&
test -f symlink'
test_expect_success \
diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh
new file mode 100755
index 0000000000..88f268b9d7
--- /dev/null
+++ b/t/t2050-git-dir-relative.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='check problems with relative GIT_DIR
+
+This test creates a working tree state with a file and subdir:
+
+ top (committed several times)
+ subdir (a subdirectory)
+
+It creates a commit-hook and tests it, then moves .git
+into the subdir while keeping the worktree location,
+and tries commits from the top and the subdir, checking
+that the commit-hook still gets called.'
+
+. ./test-lib.sh
+
+COMMIT_FILE="$(pwd)/output"
+export COMMIT_FILE
+
+test_expect_success 'Setting up post-commit hook' '
+mkdir -p .git/hooks &&
+echo >.git/hooks/post-commit "#!/bin/sh
+touch \"\${COMMIT_FILE}\"
+echo Post commit hook was called." &&
+chmod +x .git/hooks/post-commit'
+
+test_expect_success 'post-commit hook used ordinarily' '
+echo initial >top &&
+git-add top
+git-commit -m initial &&
+test -r "${COMMIT_FILE}"
+'
+
+rm -rf "${COMMIT_FILE}"
+mkdir subdir
+mv .git subdir
+
+test_expect_success 'post-commit-hook created and used from top dir' '
+echo changed >top &&
+git --git-dir subdir/.git add top &&
+git --git-dir subdir/.git commit -m topcommit &&
+test -r "${COMMIT_FILE}"
+'
+
+rm -rf "${COMMIT_FILE}"
+
+test_expect_success 'post-commit-hook from sub dir' '
+echo changed again >top
+cd subdir &&
+git --git-dir .git --work-tree .. add ../top &&
+git --git-dir .git --work-tree .. commit -m subcommit &&
+test -r "${COMMIT_FILE}"
+'
+
+test_done
diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh
index 5bc0a3bed3..04a1ed1a6b 100755
--- a/t/t2100-update-cache-badpath.sh
+++ b/t/t2100-update-cache-badpath.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-update-index nonsense-path test.
+test_description='git update-index nonsense-path test.
This test creates the following structure in the cache:
@@ -12,7 +12,7 @@ This test creates the following structure in the cache:
path2/file2 - a file in a directory
path3/file3 - a file in a directory
-and tries to git-update-index --add the following:
+and tries to git update-index --add the following:
path0/file0 - a file in a directory
path1/file1 - a file in a directory
@@ -31,8 +31,8 @@ date >path2/file2
date >path3/file3
test_expect_success \
- 'git-update-index --add to add various paths.' \
- 'git-update-index --add -- path0 path1 path2/file2 path3/file3'
+ 'git update-index --add to add various paths.' \
+ 'git update-index --add -- path0 path1 path2/file2 path3/file3'
rm -fr path?
@@ -45,7 +45,7 @@ date >path1/file1
for p in path0/file0 path1/file1 path2 path3
do
test_expect_failure \
- "git-update-index to add conflicting path $p should fail." \
- "git-update-index --add -- $p"
+ "git update-index to add conflicting path $p should fail." \
+ "git update-index --add -- $p"
done
test_done
diff --git a/t/t2101-update-index-reupdate.sh b/t/t2101-update-index-reupdate.sh
index a78ea7f0b0..59b560bfdf 100755
--- a/t/t2101-update-index-reupdate.sh
+++ b/t/t2101-update-index-reupdate.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Junio C Hamano
#
-test_description='git-update-index --again test.
+test_description='git update-index --again test.
'
. ./test-lib.sh
@@ -15,29 +15,29 @@ EOF
test_expect_success 'update-index --add' \
'echo hello world >file1 &&
echo goodbye people >file2 &&
- git-update-index --add file1 file2 &&
- git-ls-files -s >current &&
+ git update-index --add file1 file2 &&
+ git ls-files -s >current &&
cmp current expected'
test_expect_success 'update-index --again' \
'rm -f file1 &&
echo hello everybody >file2 &&
- if git-update-index --again
+ if git update-index --again
then
echo should have refused to remove file1
exit 1
else
echo happy - failed as expected
fi &&
- git-ls-files -s >current &&
+ git ls-files -s >current &&
cmp current expected'
cat > expected <<\EOF
100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
EOF
test_expect_success 'update-index --remove --again' \
- 'git-update-index --remove --again &&
- git-ls-files -s >current &&
+ 'git update-index --remove --again &&
+ git ls-files -s >current &&
cmp current expected'
test_expect_success 'first commit' 'git-commit -m initial'
@@ -50,11 +50,11 @@ test_expect_success 'update-index again' \
'mkdir -p dir1 &&
echo hello world >dir1/file3 &&
echo goodbye people >file2 &&
- git-update-index --add file2 dir1/file3 &&
+ git update-index --add file2 dir1/file3 &&
echo hello everybody >file2
echo happy >dir1/file3 &&
- git-update-index --again &&
- git-ls-files -s >current &&
+ git update-index --again &&
+ git ls-files -s >current &&
cmp current expected'
cat > expected <<\EOF
@@ -65,9 +65,9 @@ test_expect_success 'update-index --update from subdir' \
'echo not so happy >file2 &&
cd dir1 &&
cat ../file2 >file3 &&
- git-update-index --again &&
+ git update-index --again &&
cd .. &&
- git-ls-files -s >current &&
+ git ls-files -s >current &&
cmp current expected'
cat > expected <<\EOF
@@ -77,8 +77,8 @@ EOF
test_expect_success 'update-index --update with pathspec' \
'echo very happy >file2 &&
cat file2 >dir1/file3 &&
- git-update-index --again dir1/ &&
- git-ls-files -s >current &&
+ git update-index --again dir1/ &&
+ git ls-files -s >current &&
cmp current expected'
test_done
diff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh
index 969ef891d3..19d0894d26 100755
--- a/t/t2102-update-index-symlinks.sh
+++ b/t/t2102-update-index-symlinks.sh
@@ -3,29 +3,29 @@
# Copyright (c) 2007 Johannes Sixt
#
-test_description='git-update-index on filesystem w/o symlinks test.
+test_description='git update-index on filesystem w/o symlinks test.
-This tests that git-update-index keeps the symbolic link property
+This tests that git update-index keeps the symbolic link property
even if a plain file is in the working tree if core.symlinks is false.'
. ./test-lib.sh
test_expect_success \
'preparation' '
-git-config core.symlinks false &&
+git config core.symlinks false &&
l=$(echo -n file | git-hash-object -t blob -w --stdin) &&
-echo "120000 $l symlink" | git-update-index --index-info'
+echo "120000 $l symlink" | git update-index --index-info'
test_expect_success \
'modify the symbolic link' '
echo -n new-file > symlink &&
-git-update-index symlink'
+git update-index symlink'
test_expect_success \
'the index entry must still be a symbolic link' '
-case "`git-ls-files --stage --cached symlink`" in
+case "`git ls-files --stage --cached symlink`" in
120000" "*symlink) echo ok;;
-*) echo fail; git-ls-files --stage --cached symlink; (exit 1);;
+*) echo fail; git ls-files --stage --cached symlink; (exit 1);;
esac'
test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 58cd7f31be..61d08bb431 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='git-add -u with path limiting
+test_description='git add -u with path limiting
This test creates a working tree state with three files:
@@ -8,7 +8,7 @@ This test creates a working tree state with three files:
dir/sub (previously committed, modified)
dir/other (untracked)
-and issues a git-add -u with path limiting on "dir" to add
+and issues a git add -u with path limiting on "dir" to add
only the updates to dir/sub.'
. ./test-lib.sh
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index adcbe03d56..bc0a351392 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-ls-files test (--others should pick up symlinks).
+test_description='git ls-files test (--others should pick up symlinks).
-This test runs git-ls-files --others with the following on the
+This test runs git ls-files --others with the following on the
filesystem.
path0 - a file
@@ -23,7 +23,7 @@ date >path2/file2
date >path2-junk
date >path3/file3
date >path3-junk
-git-update-index --add path3-junk path3/file3
+git update-index --add path3-junk path3/file3
cat >expected1 <<EOF
expected1
@@ -37,20 +37,20 @@ EOF
sed -e 's|path2/file2|path2/|' <expected1 >expected2
test_expect_success \
- 'git-ls-files --others to show output.' \
- 'git-ls-files --others >output'
+ 'git ls-files --others to show output.' \
+ 'git ls-files --others >output'
test_expect_success \
- 'git-ls-files --others should pick up symlinks.' \
+ 'git ls-files --others should pick up symlinks.' \
'diff output expected1'
test_expect_success \
- 'git-ls-files --others --directory to show output.' \
- 'git-ls-files --others --directory >output'
+ 'git ls-files --others --directory to show output.' \
+ 'git ls-files --others --directory >output'
test_expect_success \
- 'git-ls-files --others --directory should not get confused.' \
+ 'git ls-files --others --directory should not get confused.' \
'diff output expected2'
test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index fcfcfbba7d..ae0639d8f3 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-ls-files --others --exclude
+test_description='git ls-files --others --exclude
-This test runs git-ls-files --others and tests --exclude patterns.
+This test runs git ls-files --others and tests --exclude patterns.
'
. ./test-lib.sh
@@ -59,8 +59,8 @@ echo '!*.2
!*.8' >one/two/.gitignore
test_expect_success \
- 'git-ls-files --others with various exclude options.' \
- 'git-ls-files --others \
+ 'git ls-files --others with various exclude options.' \
+ 'git ls-files --others \
--exclude=\*.6 \
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
@@ -71,8 +71,8 @@ test_expect_success \
printf '*.1\r\n/*.3\r\n!*.6\r\n' >.gitignore
test_expect_success \
- 'git-ls-files --others with \r\n line endings.' \
- 'git-ls-files --others \
+ 'git ls-files --others with \r\n line endings.' \
+ 'git ls-files --others \
--exclude=\*.6 \
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
@@ -84,9 +84,9 @@ cat > excludes-file << EOF
e*
EOF
-git-config core.excludesFile excludes-file
+git config core.excludesFile excludes-file
-git-runstatus | grep "^# " > output
+git runstatus | grep "^# " > output
cat > expect << EOF
# .gitignore
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
index cc8967d76b..8687a01d2b 100755
--- a/t/t3002-ls-files-dashpath.sh
+++ b/t/t3002-ls-files-dashpath.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-ls-files test (-- to terminate the path list).
+test_description='git ls-files test (-- to terminate the path list).
-This test runs git-ls-files --others with the following on the
+This test runs git ls-files --others with the following on the
filesystem.
path0 - a file
@@ -21,8 +21,8 @@ test_expect_success \
echo frotz >./--'
test_expect_success \
- 'git-ls-files without path restriction.' \
- 'git-ls-files --others >output &&
+ 'git ls-files without path restriction.' \
+ 'git ls-files --others >output &&
git diff output - <<EOF
--
-foo
@@ -32,32 +32,32 @@ EOF
'
test_expect_success \
- 'git-ls-files with path restriction.' \
- 'git-ls-files --others path0 >output &&
+ 'git ls-files with path restriction.' \
+ 'git ls-files --others path0 >output &&
git diff output - <<EOF
path0
EOF
'
test_expect_success \
- 'git-ls-files with path restriction with --.' \
- 'git-ls-files --others -- path0 >output &&
+ 'git ls-files with path restriction with --.' \
+ 'git ls-files --others -- path0 >output &&
git diff output - <<EOF
path0
EOF
'
test_expect_success \
- 'git-ls-files with path restriction with -- --.' \
- 'git-ls-files --others -- -- >output &&
+ 'git ls-files with path restriction with -- --.' \
+ 'git ls-files --others -- -- >output &&
git diff output - <<EOF
--
EOF
'
test_expect_success \
- 'git-ls-files with no path restriction.' \
- 'git-ls-files --others -- >output &&
+ 'git ls-files with no path restriction.' \
+ 'git ls-files --others -- >output &&
git diff output - <<EOF
--
-foo
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index 5fc1976711..ec14040637 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-ls-files -k and -m flags test.
+test_description='git ls-files -k and -m flags test.
This test prepares the following in the cache:
@@ -22,7 +22,7 @@ and the following on the filesystem:
path5 - a symlink
path6/file6 - a file in a directory
-git-ls-files -k should report that existing filesystem
+git ls-files -k should report that existing filesystem
objects except path4, path5 and path6/file6 to be killed.
Also for modification test, the cache and working tree have:
@@ -47,8 +47,8 @@ date >path8
: >path9
date >path10
test_expect_success \
- 'git-update-index --add to add various paths.' \
- "git-update-index --add -- path0 path1 path?/file? path7 path8 path9 path10"
+ 'git update-index --add to add various paths.' \
+ "git update-index --add -- path0 path1 path?/file? path7 path8 path9 path10"
rm -fr path? ;# leave path10 alone
date >path2
@@ -64,8 +64,8 @@ date >path7
touch path10
test_expect_success \
- 'git-ls-files -k to show killed files.' \
- 'git-ls-files -k >.output'
+ 'git ls-files -k to show killed files.' \
+ 'git ls-files -k >.output'
cat >.expected <<EOF
path0/file0
path1/file1
@@ -74,12 +74,12 @@ path3
EOF
test_expect_success \
- 'validate git-ls-files -k output.' \
+ 'validate git ls-files -k output.' \
'diff .output .expected'
test_expect_success \
- 'git-ls-files -m to show modified files.' \
- 'git-ls-files -m >.output'
+ 'git ls-files -m to show modified files.' \
+ 'git ls-files -m >.output'
cat >.expected <<EOF
path0
path1
@@ -90,7 +90,7 @@ path8
EOF
test_expect_success \
- 'validate git-ls-files -m output.' \
+ 'validate git ls-files -m output.' \
'diff .output .expected'
test_done
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
index d55559e553..c83f820ad2 100755
--- a/t/t3020-ls-files-error-unmatch.sh
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -3,25 +3,25 @@
# Copyright (c) 2006 Carl D. Worth
#
-test_description='git-ls-files test for --error-unmatch option
+test_description='git ls-files test for --error-unmatch option
-This test runs git-ls-files --error-unmatch to ensure it correctly
+This test runs git ls-files --error-unmatch to ensure it correctly
returns an error when a non-existent path is provided on the command
line.
'
. ./test-lib.sh
touch foo bar
-git-update-index --add foo bar
+git update-index --add foo bar
git-commit -m "add foo bar"
test_expect_failure \
- 'git-ls-files --error-unmatch should fail with unmatched path.' \
- 'git-ls-files --error-unmatch foo bar-does-not-match'
+ 'git ls-files --error-unmatch should fail with unmatched path.' \
+ 'git ls-files --error-unmatch foo bar-does-not-match'
test_expect_success \
- 'git-ls-files --error-unmatch should succeed eith matched paths.' \
- 'git-ls-files --error-unmatch foo bar'
+ 'git ls-files --error-unmatch should succeed eith matched paths.' \
+ 'git ls-files --error-unmatch foo bar'
test_done
1
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index 86ee2b0bd3..607f57ff94 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -525,4 +525,3 @@ test_expect_success 'reset and bind merge' '
'
test_done
-
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index e10749245b..46427e3f36 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -3,9 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-ls-tree test.
+test_description='git ls-tree test.
-This test runs git-ls-tree with the following in a tree.
+This test runs git ls-tree with the following in a tree.
path0 - a file
path1 - a symlink
@@ -27,8 +27,8 @@ test_expect_success \
ln -s ../path1 path2/bazbo &&
echo Mi >path2/baz/b &&
find path? \( -type f -o -type l \) -print |
- xargs git-update-index --add &&
- tree=`git-write-tree` &&
+ xargs git update-index --add &&
+ tree=`git write-tree` &&
echo $tree'
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
@@ -40,7 +40,7 @@ test_output () {
test_expect_success \
'ls-tree plain' \
- 'git-ls-tree $tree >current &&
+ 'git ls-tree $tree >current &&
cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
@@ -50,7 +50,7 @@ EOF
test_expect_success \
'ls-tree recursive' \
- 'git-ls-tree -r $tree >current &&
+ 'git ls-tree -r $tree >current &&
cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
@@ -62,7 +62,7 @@ EOF
test_expect_success \
'ls-tree recursive with -t' \
- 'git-ls-tree -r -t $tree >current &&
+ 'git ls-tree -r -t $tree >current &&
cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
@@ -76,7 +76,7 @@ EOF
test_expect_success \
'ls-tree recursive with -d' \
- 'git-ls-tree -r -d $tree >current &&
+ 'git ls-tree -r -d $tree >current &&
cat >expected <<\EOF &&
040000 tree X path2
040000 tree X path2/baz
@@ -85,7 +85,7 @@ EOF
test_expect_success \
'ls-tree filtered with path' \
- 'git-ls-tree $tree path >current &&
+ 'git ls-tree $tree path >current &&
cat >expected <<\EOF &&
EOF
test_output'
@@ -95,7 +95,7 @@ EOF
# they are shown in canonical order.
test_expect_success \
'ls-tree filtered with path1 path0' \
- 'git-ls-tree $tree path1 path0 >current &&
+ 'git ls-tree $tree path1 path0 >current &&
cat >expected <<\EOF &&
100644 blob X path0
120000 blob X path1
@@ -104,7 +104,7 @@ EOF
test_expect_success \
'ls-tree filtered with path0/' \
- 'git-ls-tree $tree path0/ >current &&
+ 'git ls-tree $tree path0/ >current &&
cat >expected <<\EOF &&
EOF
test_output'
@@ -113,7 +113,7 @@ EOF
# with pathspec semantics it shows only path2
test_expect_success \
'ls-tree filtered with path2' \
- 'git-ls-tree $tree path2 >current &&
+ 'git ls-tree $tree path2 >current &&
cat >expected <<\EOF &&
040000 tree X path2
EOF
@@ -122,7 +122,7 @@ EOF
# ... and path2/ shows the children.
test_expect_success \
'ls-tree filtered with path2/' \
- 'git-ls-tree $tree path2/ >current &&
+ 'git ls-tree $tree path2/ >current &&
cat >expected <<\EOF &&
040000 tree X path2/baz
120000 blob X path2/bazbo
@@ -134,7 +134,7 @@ EOF
# path2/baz
test_expect_success \
'ls-tree filtered with path2/baz' \
- 'git-ls-tree $tree path2/baz >current &&
+ 'git ls-tree $tree path2/baz >current &&
cat >expected <<\EOF &&
040000 tree X path2/baz
EOF
@@ -142,14 +142,14 @@ EOF
test_expect_success \
'ls-tree filtered with path2/bak' \
- 'git-ls-tree $tree path2/bak >current &&
+ 'git ls-tree $tree path2/bak >current &&
cat >expected <<\EOF &&
EOF
test_output'
test_expect_success \
'ls-tree -t filtered with path2/bak' \
- 'git-ls-tree -t $tree path2/bak >current &&
+ 'git ls-tree -t $tree path2/bak >current &&
cat >expected <<\EOF &&
040000 tree X path2
EOF
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 087929a4bf..39fe2676dc 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -4,9 +4,9 @@
# Copyright (c) 2005 Robert Fitzsimons
#
-test_description='git-ls-tree directory and filenames handling.
+test_description='git ls-tree directory and filenames handling.
-This test runs git-ls-tree with the following in a tree.
+This test runs git ls-tree with the following in a tree.
1.txt - a file
2.txt - a file
@@ -35,8 +35,8 @@ test_expect_success \
echo 111 >path3/1.txt &&
echo 222 >path3/2.txt &&
find *.txt path* \( -type f -o -type l \) -print |
- xargs git-update-index --add &&
- tree=`git-write-tree` &&
+ xargs git update-index --add &&
+ tree=`git write-tree` &&
echo $tree'
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
@@ -48,7 +48,7 @@ test_output () {
test_expect_success \
'ls-tree plain' \
- 'git-ls-tree $tree >current &&
+ 'git ls-tree $tree >current &&
cat >expected <<\EOF &&
100644 blob X 1.txt
100644 blob X 2.txt
@@ -62,7 +62,7 @@ EOF
# Recursive does not show tree nodes anymore...
test_expect_success \
'ls-tree recursive' \
- 'git-ls-tree -r $tree >current &&
+ 'git ls-tree -r $tree >current &&
cat >expected <<\EOF &&
100644 blob X 1.txt
100644 blob X 2.txt
@@ -76,7 +76,7 @@ EOF
test_expect_success \
'ls-tree filter 1.txt' \
- 'git-ls-tree $tree 1.txt >current &&
+ 'git ls-tree $tree 1.txt >current &&
cat >expected <<\EOF &&
100644 blob X 1.txt
EOF
@@ -84,7 +84,7 @@ EOF
test_expect_success \
'ls-tree filter path1/b/c/1.txt' \
- 'git-ls-tree $tree path1/b/c/1.txt >current &&
+ 'git ls-tree $tree path1/b/c/1.txt >current &&
cat >expected <<\EOF &&
100644 blob X path1/b/c/1.txt
EOF
@@ -92,7 +92,7 @@ EOF
test_expect_success \
'ls-tree filter all 1.txt files' \
- 'git-ls-tree $tree 1.txt path0/a/b/c/1.txt path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
+ 'git ls-tree $tree 1.txt path0/a/b/c/1.txt path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
cat >expected <<\EOF &&
100644 blob X 1.txt
100644 blob X path0/a/b/c/1.txt
@@ -107,7 +107,7 @@ EOF
# it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
test_expect_success \
'ls-tree filter directories' \
- 'git-ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
+ 'git ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
cat >expected <<\EOF &&
040000 tree X path0/a/b/c
040000 tree X path1/b/c
@@ -120,7 +120,7 @@ EOF
# having 1.txt and path3
test_expect_success \
'ls-tree filter odd names' \
- 'git-ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
+ 'git ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
cat >expected <<\EOF &&
100644 blob X 1.txt
100644 blob X path3/1.txt
@@ -130,7 +130,7 @@ EOF
test_expect_success \
'ls-tree filter missing files and extra slashes' \
- 'git-ls-tree $tree 1.txt/ abc.txt path3//23.txt path3/2.txt/// >current &&
+ 'git ls-tree $tree 1.txt/ abc.txt path3//23.txt path3/2.txt/// >current &&
cat >expected <<\EOF &&
EOF
test_output'
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 6f6d8844e8..ef1eeb7d8a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -13,22 +13,22 @@ handled. Specifically, that a bogus branch is not created.
test_expect_success \
'prepare a trivial repository' \
'echo Hello > A &&
- git-update-index --add A &&
+ git update-index --add A &&
git-commit -m "Initial commit." &&
- HEAD=$(git-rev-parse --verify HEAD)'
+ HEAD=$(git rev-parse --verify HEAD)'
test_expect_failure \
'git branch --help should not have created a bogus branch' \
- 'git-branch --help </dev/null >/dev/null 2>/dev/null || :
+ 'git branch --help </dev/null >/dev/null 2>/dev/null || :
test -f .git/refs/heads/--help'
test_expect_success \
'git branch abc should create a branch' \
- 'git-branch abc && test -f .git/refs/heads/abc'
+ 'git branch abc && test -f .git/refs/heads/abc'
test_expect_success \
'git branch a/b/c should create a branch' \
- 'git-branch a/b/c && test -f .git/refs/heads/a/b/c'
+ 'git branch a/b/c && test -f .git/refs/heads/a/b/c'
cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
@@ -36,140 +36,159 @@ EOF
test_expect_success \
'git branch -l d/e/f should create a branch and a log' \
'GIT_COMMITTER_DATE="2005-05-26 23:30" \
- git-branch -l d/e/f &&
+ git branch -l d/e/f &&
test -f .git/refs/heads/d/e/f &&
test -f .git/logs/refs/heads/d/e/f &&
diff expect .git/logs/refs/heads/d/e/f'
test_expect_success \
'git branch -d d/e/f should delete a branch and a log' \
- 'git-branch -d d/e/f &&
+ 'git branch -d d/e/f &&
test ! -f .git/refs/heads/d/e/f &&
test ! -f .git/logs/refs/heads/d/e/f'
test_expect_success \
'git branch j/k should work after branch j has been deleted' \
- 'git-branch j &&
- git-branch -d j &&
- git-branch j/k'
+ 'git branch j &&
+ git branch -d j &&
+ git branch j/k'
test_expect_success \
'git branch l should work after branch l/m has been deleted' \
- 'git-branch l/m &&
- git-branch -d l/m &&
- git-branch l'
+ 'git branch l/m &&
+ git branch -d l/m &&
+ git branch l'
test_expect_success \
'git branch -m m m/m should work' \
- 'git-branch -l m &&
- git-branch -m m m/m &&
+ 'git branch -l m &&
+ git branch -m m m/m &&
test -f .git/logs/refs/heads/m/m'
test_expect_success \
'git branch -m n/n n should work' \
- 'git-branch -l n/n &&
- git-branch -m n/n n
+ 'git branch -l n/n &&
+ git branch -m n/n n
test -f .git/logs/refs/heads/n'
test_expect_failure \
'git branch -m o/o o should fail when o/p exists' \
- 'git-branch o/o &&
- git-branch o/p &&
- git-branch -m o/o o'
+ 'git branch o/o &&
+ git branch o/p &&
+ git branch -m o/o o'
test_expect_failure \
'git branch -m q r/q should fail when r exists' \
- 'git-branch q &&
- git-branch r &&
- git-branch -m q r/q'
+ 'git branch q &&
+ git branch r &&
+ git branch -m q r/q'
mv .git/config .git/config-saved
test_expect_success 'git branch -m q q2 without config should succeed' '
- git-branch -m q q2 &&
- git-branch -m q2 q
+ git branch -m q q2 &&
+ git branch -m q2 q
'
mv .git/config-saved .git/config
-git-config branch.s/s.dummy Hello
+git config branch.s/s.dummy Hello
test_expect_success \
'git branch -m s/s s should work when s/t is deleted' \
- 'git-branch -l s/s &&
+ 'git branch -l s/s &&
test -f .git/logs/refs/heads/s/s &&
- git-branch -l s/t &&
+ git branch -l s/t &&
test -f .git/logs/refs/heads/s/t &&
- git-branch -d s/t &&
- git-branch -m s/s s &&
+ git branch -d s/t &&
+ git branch -m s/s s &&
test -f .git/logs/refs/heads/s'
test_expect_success 'config information was renamed, too' \
- "test $(git-config branch.s.dummy) = Hello &&
- ! git-config branch.s/s/dummy"
+ "test $(git config branch.s.dummy) = Hello &&
+ ! git config branch.s/s/dummy"
test_expect_failure \
- 'git-branch -m u v should fail when the reflog for u is a symlink' \
- 'git-branch -l u &&
+ 'git branch -m u v should fail when the reflog for u is a symlink' \
+ 'git branch -l u &&
mv .git/logs/refs/heads/u real-u &&
ln -s real-u .git/logs/refs/heads/u &&
- git-branch -m u v'
+ git branch -m u v'
test_expect_success 'test tracking setup via --track' \
- 'git-config remote.local.url . &&
- git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
- (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
- git-branch --track my1 local/master &&
- test $(git-config branch.my1.remote) = local &&
- test $(git-config branch.my1.merge) = refs/heads/master'
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+ git branch --track my1 local/master &&
+ test $(git config branch.my1.remote) = local &&
+ test $(git config branch.my1.merge) = refs/heads/master'
test_expect_success 'test tracking setup (non-wildcard, matching)' \
- 'git-config remote.local.url . &&
- git-config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
- (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
- git-branch --track my4 local/master &&
- test $(git-config branch.my4.remote) = local &&
- test $(git-config branch.my4.merge) = refs/heads/master'
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
+ (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+ git branch --track my4 local/master &&
+ test $(git config branch.my4.remote) = local &&
+ test $(git config branch.my4.merge) = refs/heads/master'
test_expect_success 'test tracking setup (non-wildcard, not matching)' \
- 'git-config remote.local.url . &&
- git-config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
- (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
- git-branch --track my5 local/master &&
- ! test "$(git-config branch.my5.remote)" = local &&
- ! test "$(git-config branch.my5.merge)" = refs/heads/master'
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
+ (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+ git branch --track my5 local/master &&
+ ! test "$(git config branch.my5.remote)" = local &&
+ ! test "$(git config branch.my5.merge)" = refs/heads/master'
test_expect_success 'test tracking setup via config' \
- 'git-config branch.autosetupmerge true &&
- git-config remote.local.url . &&
- git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
- (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
- git-branch my3 local/master &&
- test $(git-config branch.my3.remote) = local &&
- test $(git-config branch.my3.merge) = refs/heads/master'
+ 'git config branch.autosetupmerge true &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+ git branch my3 local/master &&
+ test $(git config branch.my3.remote) = local &&
+ test $(git config branch.my3.merge) = refs/heads/master'
+
+test_expect_success 'avoid ambiguous track' '
+ git config branch.autosetupmerge true &&
+ git config remote.ambi1.url = lalala &&
+ git config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&
+ git config remote.ambi2.url = lilili &&
+ git config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&
+ git branch all1 master &&
+ test -z "$(git config branch.all1.merge)"
+'
test_expect_success 'test overriding tracking setup via --no-track' \
- 'git-config branch.autosetupmerge true &&
- git-config remote.local.url . &&
- git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
- (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
- git-branch --no-track my2 local/master &&
- git-config branch.autosetupmerge false &&
- ! test "$(git-config branch.my2.remote)" = local &&
- ! test "$(git-config branch.my2.merge)" = refs/heads/master'
-
-test_expect_success 'test local tracking setup' \
+ 'git config branch.autosetupmerge true &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+ git branch --no-track my2 local/master &&
+ git config branch.autosetupmerge false &&
+ ! test "$(git config branch.my2.remote)" = local &&
+ ! test "$(git config branch.my2.merge)" = refs/heads/master'
+
+test_expect_success 'no tracking without .fetch entries' \
'git branch --track my6 s &&
- test $(git-config branch.my6.remote) = . &&
- test $(git-config branch.my6.merge) = refs/heads/s'
+ test -z "$(git config branch.my6.remote)" &&
+ test -z "$(git config branch.my6.merge)"'
test_expect_success 'test tracking setup via --track but deeper' \
- 'git-config remote.local.url . &&
- git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
- (git-show-ref -q refs/remotes/local/o/o || git-fetch local) &&
- git-branch --track my7 local/o/o &&
- test "$(git-config branch.my7.remote)" = local &&
- test "$(git-config branch.my7.merge)" = refs/heads/o/o'
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/o/o || git-fetch local) &&
+ git branch --track my7 local/o/o &&
+ test "$(git config branch.my7.remote)" = local &&
+ test "$(git config branch.my7.merge)" = refs/heads/o/o'
+
+test_expect_success 'test deleting branch deletes branch config' \
+ 'git branch -d my7 &&
+ test -z "$(git config branch.my7.remote)" &&
+ test -z "$(git config branch.my7.merge)"'
+
+test_expect_success 'test deleting branch without config' \
+ 'git branch my7 s &&
+ test "$(git branch -d my7 2>&1)" = "Deleted branch my7."'
# Keep this test last, as it changes the current branch
cat >expect <<EOF
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index f0c7e22b36..4ddc6342a9 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -16,92 +16,92 @@ echo '[core] logallrefupdates = true' >>.git/config
test_expect_success \
'prepare a trivial repository' \
'echo Hello > A &&
- git-update-index --add A &&
+ git update-index --add A &&
git-commit -m "Initial commit." &&
- HEAD=$(git-rev-parse --verify HEAD)'
+ HEAD=$(git rev-parse --verify HEAD)'
SHA1=
test_expect_success \
'see if git show-ref works as expected' \
- 'git-branch a &&
+ 'git branch a &&
SHA1=`cat .git/refs/heads/a` &&
echo "$SHA1 refs/heads/a" >expect &&
- git-show-ref a >result &&
+ git show-ref a >result &&
diff expect result'
test_expect_success \
'see if a branch still exists when packed' \
- 'git-branch b &&
- git-pack-refs --all &&
+ 'git branch b &&
+ git pack-refs --all &&
rm -f .git/refs/heads/b &&
echo "$SHA1 refs/heads/b" >expect &&
- git-show-ref b >result &&
+ git show-ref b >result &&
diff expect result'
test_expect_failure \
'git branch c/d should barf if branch c exists' \
- 'git-branch c &&
- git-pack-refs --all &&
+ 'git branch c &&
+ git pack-refs --all &&
rm .git/refs/heads/c &&
- git-branch c/d'
+ git branch c/d'
test_expect_success \
'see if a branch still exists after git pack-refs --prune' \
- 'git-branch e &&
- git-pack-refs --all --prune &&
+ 'git branch e &&
+ git pack-refs --all --prune &&
echo "$SHA1 refs/heads/e" >expect &&
- git-show-ref e >result &&
+ git show-ref e >result &&
diff expect result'
test_expect_failure \
'see if git pack-refs --prune remove ref files' \
- 'git-branch f &&
- git-pack-refs --all --prune &&
+ 'git branch f &&
+ git pack-refs --all --prune &&
ls .git/refs/heads/f'
test_expect_success \
'git branch g should work when git branch g/h has been deleted' \
- 'git-branch g/h &&
- git-pack-refs --all --prune &&
- git-branch -d g/h &&
- git-branch g &&
- git-pack-refs --all &&
- git-branch -d g'
+ 'git branch g/h &&
+ git pack-refs --all --prune &&
+ git branch -d g/h &&
+ git branch g &&
+ git pack-refs --all &&
+ git branch -d g'
test_expect_failure \
'git branch i/j/k should barf if branch i exists' \
- 'git-branch i &&
- git-pack-refs --all --prune &&
- git-branch i/j/k'
+ 'git branch i &&
+ git pack-refs --all --prune &&
+ git branch i/j/k'
test_expect_success \
'test git branch k after branch k/l/m and k/lm have been deleted' \
- 'git-branch k/l &&
- git-branch k/lm &&
- git-branch -d k/l &&
- git-branch k/l/m &&
- git-branch -d k/l/m &&
- git-branch -d k/lm &&
- git-branch k'
+ 'git branch k/l &&
+ git branch k/lm &&
+ git branch -d k/l &&
+ git branch k/l/m &&
+ git branch -d k/l/m &&
+ git branch -d k/lm &&
+ git branch k'
test_expect_success \
'test git branch n after some branch deletion and pruning' \
- 'git-branch n/o &&
- git-branch n/op &&
- git-branch -d n/o &&
- git-branch n/o/p &&
- git-branch -d n/op &&
- git-pack-refs --all --prune &&
- git-branch -d n/o/p &&
- git-branch n'
+ 'git branch n/o &&
+ git branch n/op &&
+ git branch -d n/o &&
+ git branch n/o/p &&
+ git branch -d n/op &&
+ git pack-refs --all --prune &&
+ git branch -d n/o/p &&
+ git branch n'
test_expect_success 'pack, prune and repack' '
git-tag foo &&
- git-pack-refs --all --prune &&
- git-show-ref >all-of-them &&
- git-pack-refs &&
- git-show-ref >again &&
+ git pack-refs --all --prune &&
+ git show-ref >all-of-them &&
+ git pack-refs &&
+ git show-ref >again &&
diff all-of-them again
'
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index b5a1400e18..dc8c369310 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -32,12 +32,12 @@ test -f "$p1" && cmp "$p0" "$p1" || {
echo 'just space
no-funny' >expected
-test_expect_success 'git-ls-files no-funny' \
- 'git-update-index --add "$p0" "$p2" &&
- git-ls-files >current &&
+test_expect_success 'git ls-files no-funny' \
+ 'git update-index --add "$p0" "$p2" &&
+ git ls-files >current &&
git diff expected current'
-t0=`git-write-tree`
+t0=`git write-tree`
echo "$t0" >t0
cat > expected <<\EOF
@@ -45,19 +45,19 @@ just space
no-funny
"tabs\t,\" (dq) and spaces"
EOF
-test_expect_success 'git-ls-files with-funny' \
- 'git-update-index --add "$p1" &&
- git-ls-files >current &&
+test_expect_success 'git ls-files with-funny' \
+ 'git update-index --add "$p1" &&
+ git ls-files >current &&
git diff expected current'
echo 'just space
no-funny
tabs ," (dq) and spaces' >expected
-test_expect_success 'git-ls-files -z with-funny' \
- 'git-ls-files -z | tr \\0 \\012 >current &&
+test_expect_success 'git ls-files -z with-funny' \
+ 'git ls-files -z | tr \\0 \\012 >current &&
git diff expected current'
-t1=`git-write-tree`
+t1=`git write-tree`
echo "$t1" >t1
cat > expected <<\EOF
@@ -65,45 +65,45 @@ just space
no-funny
"tabs\t,\" (dq) and spaces"
EOF
-test_expect_success 'git-ls-tree with funny' \
- 'git-ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current &&
+test_expect_success 'git ls-tree with funny' \
+ 'git ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current &&
git diff expected current'
cat > expected <<\EOF
A "tabs\t,\" (dq) and spaces"
EOF
-test_expect_success 'git-diff-index with-funny' \
- 'git-diff-index --name-status $t0 >current &&
+test_expect_success 'git diff-index with-funny' \
+ 'git diff-index --name-status $t0 >current &&
git diff expected current'
-test_expect_success 'git-diff-tree with-funny' \
- 'git-diff-tree --name-status $t0 $t1 >current &&
+test_expect_success 'git diff-tree with-funny' \
+ 'git diff-tree --name-status $t0 $t1 >current &&
git diff expected current'
echo 'A
tabs ," (dq) and spaces' >expected
-test_expect_success 'git-diff-index -z with-funny' \
- 'git-diff-index -z --name-status $t0 | tr \\0 \\012 >current &&
+test_expect_success 'git diff-index -z with-funny' \
+ 'git diff-index -z --name-status $t0 | tr \\0 \\012 >current &&
git diff expected current'
-test_expect_success 'git-diff-tree -z with-funny' \
- 'git-diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current &&
+test_expect_success 'git diff-tree -z with-funny' \
+ 'git diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current &&
git diff expected current'
cat > expected <<\EOF
CNUM no-funny "tabs\t,\" (dq) and spaces"
EOF
-test_expect_success 'git-diff-tree -C with-funny' \
- 'git-diff-tree -C --find-copies-harder --name-status \
+test_expect_success 'git diff-tree -C with-funny' \
+ 'git diff-tree -C --find-copies-harder --name-status \
$t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
git diff expected current'
cat > expected <<\EOF
RNUM no-funny "tabs\t,\" (dq) and spaces"
EOF
-test_expect_success 'git-diff-tree delete with-funny' \
- 'git-update-index --force-remove "$p0" &&
- git-diff-index -M --name-status \
+test_expect_success 'git diff-tree delete with-funny' \
+ 'git update-index --force-remove "$p0" &&
+ git diff-index -M --name-status \
$t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
git diff expected current'
@@ -113,8 +113,8 @@ similarity index NUM%
rename from no-funny
rename to "tabs\t,\" (dq) and spaces"
EOF
-test_expect_success 'git-diff-tree delete with-funny' \
- 'git-diff-index -M -p $t0 |
+test_expect_success 'git diff-tree delete with-funny' \
+ 'git diff-index -M -p $t0 |
sed -e "s/index [0-9]*%/index NUM%/" >current &&
git diff expected current'
@@ -127,8 +127,8 @@ similarity index NUM%
rename from no-funny
rename to "tabs\t,\" (dq) and spaces"
EOF
-test_expect_success 'git-diff-tree delete with-funny' \
- 'git-diff-index -M -p $t0 |
+test_expect_success 'git diff-tree delete with-funny' \
+ 'git diff-index -M -p $t0 |
sed -e "s/index [0-9]*%/index NUM%/" >current &&
git diff expected current'
@@ -136,9 +136,9 @@ cat >expected <<\EOF
"tabs\t,\" (dq) and spaces"
1 files changed, 0 insertions(+), 0 deletions(-)
EOF
-test_expect_success 'git-diff-tree rename with-funny applied' \
- 'git-diff-index -M -p $t0 |
- git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+test_expect_success 'git diff-tree rename with-funny applied' \
+ 'git diff-index -M -p $t0 |
+ git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
git diff expected current'
cat > expected <<\EOF
@@ -146,15 +146,15 @@ cat > expected <<\EOF
"tabs\t,\" (dq) and spaces"
2 files changed, 3 insertions(+), 3 deletions(-)
EOF
-test_expect_success 'git-diff-tree delete with-funny applied' \
- 'git-diff-index -p $t0 |
- git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+test_expect_success 'git diff-tree delete with-funny applied' \
+ 'git diff-index -p $t0 |
+ git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
git diff expected current'
-test_expect_success 'git-apply non-git diff' \
- 'git-diff-index -p $t0 |
+test_expect_success 'git apply non-git diff' \
+ 'git diff-index -p $t0 |
sed -ne "/^[-+@]/p" |
- git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+ git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
git diff expected current'
test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index b9d3131cc2..62205b2531 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -12,23 +12,60 @@ This test runs git rebase and checks that the author information is not lost.
export GIT_AUTHOR_EMAIL=bogus_email_address
test_expect_success \
- 'prepare repository with topic branch, then rebase against master' \
+ 'prepare repository with topic branches' \
'echo First > A &&
- git-update-index --add A &&
+ git update-index --add A &&
git-commit -m "Add A." &&
git checkout -b my-topic-branch &&
echo Second > B &&
- git-update-index --add B &&
+ git update-index --add B &&
git-commit -m "Add B." &&
git checkout -f master &&
echo Third >> A &&
- git-update-index A &&
+ git update-index A &&
git-commit -m "Modify A." &&
+ git checkout -b side my-topic-branch &&
+ echo Side >> C &&
+ git add C &&
+ git commit -m "Add C" &&
+ git checkout -b nonlinear my-topic-branch &&
+ echo Edit >> B &&
+ git add B &&
+ git commit -m "Modify B" &&
+ git merge side &&
+ git checkout -b upstream-merged-nonlinear &&
+ git merge master &&
git checkout -f my-topic-branch &&
+ git tag topic
+'
+
+test_expect_success 'rebase against master' '
git rebase master'
test_expect_failure \
'the rebase operation should not have destroyed author information' \
'git log | grep "Author:" | grep "<>"'
+test_expect_success 'rebase after merge master' '
+ git reset --hard topic &&
+ git merge master &&
+ git rebase master &&
+ ! git show | grep "^Merge:"
+'
+
+test_expect_success 'rebase of history with merges is linearized' '
+ git checkout nonlinear &&
+ test 4 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
+'
+
+test_expect_success \
+ 'rebase of history with merges after upstream merge is linearized' '
+ git checkout upstream-merged-nonlinear &&
+ test 5 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
+'
+
test_done
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
index 8b19d3ccea..4934a4e010 100755
--- a/t/t3401-rebase-partial.sh
+++ b/t/t3401-rebase-partial.sh
@@ -14,37 +14,37 @@ local branch.
test_expect_success \
'prepare repository with topic branch' \
'echo First > A &&
- git-update-index --add A &&
+ git update-index --add A &&
git-commit -m "Add A." &&
git-checkout -b my-topic-branch &&
echo Second > B &&
- git-update-index --add B &&
+ git update-index --add B &&
git-commit -m "Add B." &&
echo AnotherSecond > C &&
- git-update-index --add C &&
+ git update-index --add C &&
git-commit -m "Add C." &&
git-checkout -f master &&
echo Third >> A &&
- git-update-index A &&
+ git update-index A &&
git-commit -m "Modify A."
'
test_expect_success \
'pick top patch from topic branch into master' \
- 'git-cherry-pick my-topic-branch^0 &&
+ 'git cherry-pick my-topic-branch^0 &&
git-checkout -f my-topic-branch &&
- git-branch master-merge master &&
- git-branch my-topic-branch-merge my-topic-branch
+ git branch master-merge master &&
+ git branch my-topic-branch-merge my-topic-branch
'
test_debug \
- 'git-cherry master &&
- git-format-patch -k --stdout --full-index master >/dev/null &&
+ 'git cherry master &&
+ git format-patch -k --stdout --full-index master >/dev/null &&
gitk --all & sleep 1
'
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index 977c498f00..eab053c3e0 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -49,9 +49,8 @@ test_expect_success 'rebase --skip with --merge' '
'
test_expect_success 'merge and reference trees equal' \
- 'test -z "`git-diff-tree skip-merge skip-reference`"'
+ 'test -z "`git diff-tree skip-merge skip-reference`"'
test_debug 'gitk --all & sleep 1'
test_done
-
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
new file mode 100755
index 0000000000..40d6799ed6
--- /dev/null
+++ b/t/t3404-rebase-interactive.sh
@@ -0,0 +1,279 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git rebase interactive
+
+This test runs git rebase "interactively", by faking an edit, and verifies
+that the result still makes sense.
+'
+. ./test-lib.sh
+
+# set up two branches like this:
+#
+# A - B - C - D - E
+# \
+# F - G - H
+# \
+# I
+#
+# where B, D and G touch the same file.
+
+test_expect_success 'setup' '
+ : > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m A &&
+ git tag A &&
+ echo 1 > file1 &&
+ test_tick &&
+ git commit -m B file1 &&
+ : > file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -m C &&
+ echo 2 > file1 &&
+ test_tick &&
+ git commit -m D file1 &&
+ : > file3 &&
+ git add file3 &&
+ test_tick &&
+ git commit -m E &&
+ git checkout -b branch1 A &&
+ : > file4 &&
+ git add file4 &&
+ test_tick &&
+ git commit -m F &&
+ git tag F &&
+ echo 3 > file1 &&
+ test_tick &&
+ git commit -m G file1 &&
+ : > file5 &&
+ git add file5 &&
+ test_tick &&
+ git commit -m H &&
+ git checkout -b branch2 F &&
+ : > file6 &&
+ git add file6 &&
+ test_tick &&
+ git commit -m I &&
+ git tag I
+'
+
+cat > fake-editor.sh <<\EOF
+#!/bin/sh
+case "$1" in
+*/COMMIT_EDITMSG)
+ test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+ test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
+ exit
+ ;;
+esac
+test -z "$EXPECT_COUNT" ||
+ test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+ exit
+test -z "$FAKE_LINES" && exit
+grep -v '^#' < "$1" > "$1".tmp
+rm -f "$1"
+cat "$1".tmp
+action=pick
+for line in $FAKE_LINES; do
+ case $line in
+ squash)
+ action="$line";;
+ *)
+ echo sed -n "${line}s/^pick/$action/p"
+ sed -n "${line}p" < "$1".tmp
+ sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+ action=pick;;
+ esac
+done
+EOF
+
+chmod a+x fake-editor.sh
+VISUAL="$(pwd)/fake-editor.sh"
+export VISUAL
+
+test_expect_success 'no changes are a nop' '
+ git rebase -i F &&
+ test $(git rev-parse I) = $(git rev-parse HEAD)
+'
+
+test_expect_success 'test the [branch] option' '
+ git checkout -b dead-end &&
+ git rm file6 &&
+ git commit -m "stop here" &&
+ git rebase -i F branch2 &&
+ test $(git rev-parse I) = $(git rev-parse HEAD)
+'
+
+test_expect_success 'rebase on top of a non-conflicting commit' '
+ git checkout branch1 &&
+ git tag original-branch1 &&
+ git rebase -i branch2 &&
+ test file6 = $(git diff --name-only original-branch1) &&
+ test $(git rev-parse I) = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'reflog for the branch shows state before rebase' '
+ test $(git rev-parse branch1@{1}) = $(git rev-parse original-branch1)
+'
+
+test_expect_success 'exchange two commits' '
+ FAKE_LINES="2 1" git rebase -i HEAD~2 &&
+ test H = $(git cat-file commit HEAD^ | tail -n 1) &&
+ test G = $(git cat-file commit HEAD | tail -n 1)
+'
+
+cat > expect << EOF
+diff --git a/file1 b/file1
+index e69de29..00750ed 100644
+--- a/file1
++++ b/file1
+@@ -0,0 +1 @@
++3
+EOF
+
+cat > expect2 << EOF
+<<<<<<< HEAD:file1
+2
+=======
+3
+>>>>>>> b7ca976... G:file1
+EOF
+
+test_expect_success 'stop on conflicting pick' '
+ git tag new-branch1 &&
+ ! git rebase -i master &&
+ diff -u expect .git/.dotest-merge/patch &&
+ diff -u expect2 file1 &&
+ test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
+ test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l)
+'
+
+test_expect_success 'abort' '
+ git rebase --abort &&
+ test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
+ ! test -d .git/.dotest-merge
+'
+
+test_expect_success 'retain authorship' '
+ echo A > file7 &&
+ git add file7 &&
+ test_tick &&
+ GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+ git tag twerp &&
+ git rebase -i --onto master HEAD^ &&
+ git show HEAD | grep "^Author: Twerp Snog"
+'
+
+test_expect_success 'squash' '
+ git reset --hard twerp &&
+ echo B > file7 &&
+ test_tick &&
+ GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
+ echo "******************************" &&
+ FAKE_LINES="1 squash 2" git rebase -i --onto master HEAD~2 &&
+ test B = $(cat file7) &&
+ test $(git rev-parse HEAD^) = $(git rev-parse master)
+'
+
+test_expect_success 'retain authorship when squashing' '
+ git show HEAD | grep "^Author: Nitfol"
+'
+
+test_expect_success 'preserve merges with -p' '
+ git checkout -b to-be-preserved master^ &&
+ : > unrelated-file &&
+ git add unrelated-file &&
+ test_tick &&
+ git commit -m "unrelated" &&
+ git checkout -b to-be-rebased master &&
+ echo B > file1 &&
+ test_tick &&
+ git commit -m J file1 &&
+ test_tick &&
+ git merge to-be-preserved &&
+ echo C > file1 &&
+ test_tick &&
+ git commit -m K file1 &&
+ test_tick &&
+ git rebase -i -p --onto branch1 master &&
+ test $(git rev-parse HEAD^^2) = $(git rev-parse to-be-preserved) &&
+ test $(git rev-parse HEAD~3) = $(git rev-parse branch1) &&
+ test $(git show HEAD:file1) = C &&
+ test $(git show HEAD~2:file1) = B
+'
+
+test_expect_success '--continue tries to commit' '
+ test_tick &&
+ ! git rebase -i --onto new-branch1 HEAD^ &&
+ echo resolved > file1 &&
+ git add file1 &&
+ FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue &&
+ test $(git rev-parse HEAD^) = $(git rev-parse new-branch1) &&
+ git show HEAD | grep chouette
+'
+
+test_expect_success 'verbose flag is heeded, even after --continue' '
+ git reset --hard HEAD@{1} &&
+ test_tick &&
+ ! git rebase -v -i --onto new-branch1 HEAD^ &&
+ echo resolved > file1 &&
+ git add file1 &&
+ git rebase --continue > output &&
+ grep "^ file1 | 2 +-$" output
+'
+
+test_expect_success 'multi-squash only fires up editor once' '
+ base=$(git rev-parse HEAD~4) &&
+ FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
+ git rebase -i $base &&
+ test $base = $(git rev-parse HEAD^) &&
+ test 1 = $(git show | grep ONCE | wc -l)
+'
+
+test_expect_success 'squash works as expected' '
+ for n in one two three four
+ do
+ echo $n >> file$n &&
+ git add file$n &&
+ git commit -m $n
+ done &&
+ one=$(git rev-parse HEAD~3) &&
+ FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
+ test $one = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'interrupted squash works as expected' '
+ for n in one two three four
+ do
+ echo $n >> conflict &&
+ git add conflict &&
+ git commit -m $n
+ done &&
+ one=$(git rev-parse HEAD~3) &&
+ ! FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
+ (echo one; echo two; echo four) > conflict &&
+ git add conflict &&
+ ! git rebase --continue &&
+ echo resolved > conflict &&
+ git add conflict &&
+ git rebase --continue &&
+ test $one = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'ignore patch if in upstream' '
+ HEAD=$(git rev-parse HEAD) &&
+ git checkout -b has-cherry-picked HEAD^ &&
+ echo unrelated > file7 &&
+ git add file7 &&
+ test_tick &&
+ git commit -m "unrelated change" &&
+ git cherry-pick $HEAD &&
+ EXPECT_COUNT=1 git rebase -i $HEAD &&
+ test $HEAD = $(git rev-parse HEAD^)
+'
+
+test_done
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
new file mode 100755
index 0000000000..e4e2e649ed
--- /dev/null
+++ b/t/t3405-rebase-malformed.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='rebase should not insist on git message convention'
+
+. ./test-lib.sh
+
+cat >F <<\EOF
+This is an example of a commit log message
+that does not conform to git commit convention.
+
+It has two paragraphs, but its first paragraph is not friendly
+to oneline summary format.
+EOF
+
+test_expect_success setup '
+
+ >file1 &&
+ >file2 &&
+ git add file1 file2 &&
+ test_tick &&
+ git commit -m "Initial commit" &&
+
+ git checkout -b side &&
+ cat F >file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -F F &&
+
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >F0 &&
+
+ git checkout master &&
+
+ echo One >file1 &&
+ test_tick &&
+ git add file1 &&
+ git commit -m "Second commit"
+'
+
+test_expect_success rebase '
+
+ git rebase master side &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 &&
+
+ diff -u F0 F1 &&
+ diff -u F F0
+'
+
+test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index e83bbee074..d0a440feba 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -3,10 +3,10 @@
# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
#
-test_description='git-cherry should detect patches integrated upstream
+test_description='git cherry should detect patches integrated upstream
This test cherry-picks one local change of two into master branch, and
-checks that git-cherry only returns the second patch in the local branch
+checks that git cherry only returns the second patch in the local branch
'
. ./test-lib.sh
@@ -15,40 +15,40 @@ export GIT_AUTHOR_EMAIL=bogus_email_address
test_expect_success \
'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
'echo First > A &&
- git-update-index --add A &&
+ git update-index --add A &&
git-commit -m "Add A." &&
git-checkout -b my-topic-branch &&
echo Second > B &&
- git-update-index --add B &&
+ git update-index --add B &&
git-commit -m "Add B." &&
sleep 2 &&
echo AnotherSecond > C &&
- git-update-index --add C &&
+ git update-index --add C &&
git-commit -m "Add C." &&
git-checkout -f master &&
rm -f B C &&
echo Third >> A &&
- git-update-index A &&
+ git update-index A &&
git-commit -m "Modify A." &&
- expr "$(echo $(git-cherry master my-topic-branch) )" : "+ [^ ]* + .*"
+ expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* + .*"
'
test_expect_success \
'check that cherry with limit returns only the top patch'\
- 'expr "$(echo $(git-cherry master my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
+ 'expr "$(echo $(git cherry master my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
'
test_expect_success \
'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' \
- 'git-cherry-pick my-topic-branch^0 &&
- echo $(git-cherry master my-topic-branch) &&
- expr "$(echo $(git-cherry master my-topic-branch) )" : "+ [^ ]* - .*"
+ 'git cherry-pick my-topic-branch^0 &&
+ echo $(git cherry master my-topic-branch) &&
+ expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* - .*"
'
test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 0a97b75288..5c001aa489 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Carl D. Worth
#
-test_description='Test of the various options to git-rm.'
+test_description='Test of the various options to git rm.'
. ./test-lib.sh
@@ -11,13 +11,13 @@ test_description='Test of the various options to git-rm.'
test_expect_success \
'Initialize test directory' \
"touch -- foo bar baz 'space embedded' -q &&
- git-add -- foo bar baz 'space embedded' -q &&
+ git add -- foo bar baz 'space embedded' -q &&
git-commit -m 'add normal files' &&
test_tabs=y &&
if touch -- 'tab embedded' 'newline
embedded'
then
- git-add -- 'tab embedded' 'newline
+ git add -- 'tab embedded' 'newline
embedded' &&
git-commit -m 'add files with tabs and newlines'
else
@@ -26,7 +26,7 @@ embedded' &&
fi"
# Later we will try removing an unremovable path to make sure
-# git-rm barfs, but if the test is run as root that cannot be
+# git rm barfs, but if the test is run as root that cannot be
# arranged.
test_expect_success \
'Determine rm behavior' \
@@ -38,51 +38,85 @@ test_expect_success \
rm -f test-file'
test_expect_success \
- 'Pre-check that foo exists and is in index before git-rm foo' \
- '[ -f foo ] && git-ls-files --error-unmatch foo'
+ 'Pre-check that foo exists and is in index before git rm foo' \
+ '[ -f foo ] && git ls-files --error-unmatch foo'
test_expect_success \
- 'Test that git-rm foo succeeds' \
- 'git-rm --cached foo'
+ 'Test that git rm foo succeeds' \
+ 'git rm --cached foo'
test_expect_success \
- 'Post-check that foo exists but is not in index after git-rm foo' \
- '[ -f foo ] && ! git-ls-files --error-unmatch foo'
+ 'Test that git rm --cached foo succeeds if the index matches the file' \
+ 'echo content > foo
+ git add foo
+ git rm --cached foo'
test_expect_success \
- 'Pre-check that bar exists and is in index before "git-rm bar"' \
- '[ -f bar ] && git-ls-files --error-unmatch bar'
+ 'Test that git rm --cached foo succeeds if the index matches the file' \
+ 'echo content > foo
+ git add foo
+ git commit -m foo
+ echo "other content" > foo
+ git rm --cached foo'
+
+test_expect_failure \
+ 'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' \
+ 'echo content > foo
+ git add foo
+ git commit -m foo
+ echo "other content" > foo
+ git add foo
+ echo "yet another content" > foo
+ git rm --cached foo'
+
+test_expect_success \
+ 'Test that git rm --cached -f foo works in case where --cached only did not' \
+ 'echo content > foo
+ git add foo
+ git commit -m foo
+ echo "other content" > foo
+ git add foo
+ echo "yet another content" > foo
+ git rm --cached -f foo'
+
+test_expect_success \
+ 'Post-check that foo exists but is not in index after git rm foo' \
+ '[ -f foo ] && ! git ls-files --error-unmatch foo'
+
+test_expect_success \
+ 'Pre-check that bar exists and is in index before "git rm bar"' \
+ '[ -f bar ] && git ls-files --error-unmatch bar'
test_expect_success \
- 'Test that "git-rm bar" succeeds' \
- 'git-rm bar'
+ 'Test that "git rm bar" succeeds' \
+ 'git rm bar'
test_expect_success \
- 'Post-check that bar does not exist and is not in index after "git-rm -f bar"' \
- '! [ -f bar ] && ! git-ls-files --error-unmatch bar'
+ 'Post-check that bar does not exist and is not in index after "git rm -f bar"' \
+ '! [ -f bar ] && ! git ls-files --error-unmatch bar'
test_expect_success \
- 'Test that "git-rm -- -q" succeeds (remove a file that looks like an option)' \
- 'git-rm -- -q'
+ 'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' \
+ 'git rm -- -q'
test "$test_tabs" = y && test_expect_success \
- "Test that \"git-rm -f\" succeeds with embedded space, tab, or newline characters." \
- "git-rm -f 'space embedded' 'tab embedded' 'newline
+ "Test that \"git rm -f\" succeeds with embedded space, tab, or newline characters." \
+ "git rm -f 'space embedded' 'tab embedded' 'newline
embedded'"
if test "$test_failed_remove" = y; then
chmod a-w .
test_expect_failure \
- 'Test that "git-rm -f" fails if its rm fails' \
- 'git-rm -f baz'
+ 'Test that "git rm -f" fails if its rm fails' \
+ 'git rm -f baz'
chmod 775 .
else
test_expect_success 'skipping removal failure (perhaps running as root?)' :
fi
test_expect_success \
- 'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
- 'git-ls-files --error-unmatch baz'
+ 'When the rm in "git rm -f" fails, it should not remove the file from the index' \
+ 'git ls-files --error-unmatch baz'
test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
git rm --ignore-unmatch nonexistent
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index ad8cc7d4ae..a328bf57eb 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -3,72 +3,72 @@
# Copyright (c) 2006 Carl D. Worth
#
-test_description='Test of git-add, including the -- option.'
+test_description='Test of git add, including the -- option.'
. ./test-lib.sh
test_expect_success \
- 'Test of git-add' \
- 'touch foo && git-add foo'
+ 'Test of git add' \
+ 'touch foo && git add foo'
test_expect_success \
'Post-check that foo is in the index' \
- 'git-ls-files foo | grep foo'
+ 'git ls-files foo | grep foo'
test_expect_success \
- 'Test that "git-add -- -q" works' \
- 'touch -- -q && git-add -- -q'
+ 'Test that "git add -- -q" works' \
+ 'touch -- -q && git add -- -q'
test_expect_success \
- 'git-add: Test that executable bit is not used if core.filemode=0' \
+ 'git add: Test that executable bit is not used if core.filemode=0' \
'git config core.filemode 0 &&
echo foo >xfoo1 &&
chmod 755 xfoo1 &&
- git-add xfoo1 &&
- case "`git-ls-files --stage xfoo1`" in
+ git add xfoo1 &&
+ case "`git ls-files --stage xfoo1`" in
100644" "*xfoo1) echo ok;;
- *) echo fail; git-ls-files --stage xfoo1; (exit 1);;
+ *) echo fail; git ls-files --stage xfoo1; (exit 1);;
esac'
-test_expect_success 'git-add: filemode=0 should not get confused by symlink' '
+test_expect_success 'git add: filemode=0 should not get confused by symlink' '
rm -f xfoo1 &&
ln -s foo xfoo1 &&
- git-add xfoo1 &&
- case "`git-ls-files --stage xfoo1`" in
+ git add xfoo1 &&
+ case "`git ls-files --stage xfoo1`" in
120000" "*xfoo1) echo ok;;
- *) echo fail; git-ls-files --stage xfoo1; (exit 1);;
+ *) echo fail; git ls-files --stage xfoo1; (exit 1);;
esac
'
test_expect_success \
- 'git-update-index --add: Test that executable bit is not used...' \
+ 'git update-index --add: Test that executable bit is not used...' \
'git config core.filemode 0 &&
echo foo >xfoo2 &&
chmod 755 xfoo2 &&
- git-update-index --add xfoo2 &&
- case "`git-ls-files --stage xfoo2`" in
+ git update-index --add xfoo2 &&
+ case "`git ls-files --stage xfoo2`" in
100644" "*xfoo2) echo ok;;
- *) echo fail; git-ls-files --stage xfoo2; (exit 1);;
+ *) echo fail; git ls-files --stage xfoo2; (exit 1);;
esac'
-test_expect_success 'git-add: filemode=0 should not get confused by symlink' '
+test_expect_success 'git add: filemode=0 should not get confused by symlink' '
rm -f xfoo2 &&
ln -s foo xfoo2 &&
git update-index --add xfoo2 &&
- case "`git-ls-files --stage xfoo2`" in
+ case "`git ls-files --stage xfoo2`" in
120000" "*xfoo2) echo ok;;
- *) echo fail; git-ls-files --stage xfoo2; (exit 1);;
+ *) echo fail; git ls-files --stage xfoo2; (exit 1);;
esac
'
test_expect_success \
- 'git-update-index --add: Test that executable bit is not used...' \
+ 'git update-index --add: Test that executable bit is not used...' \
'git config core.filemode 0 &&
ln -s xfoo2 xfoo3 &&
- git-update-index --add xfoo3 &&
- case "`git-ls-files --stage xfoo3`" in
+ git update-index --add xfoo3 &&
+ case "`git ls-files --stage xfoo3`" in
120000" "*xfoo3) echo ok;;
- *) echo fail; git-ls-files --stage xfoo3; (exit 1);;
+ *) echo fail; git ls-files --stage xfoo3; (exit 1);;
esac'
test_expect_success '.gitignore test setup' '
@@ -80,28 +80,28 @@ test_expect_success '.gitignore test setup' '
'
test_expect_success '.gitignore is honored' '
- git-add . &&
- ! git-ls-files | grep "\\.ig"
+ git add . &&
+ ! git ls-files | grep "\\.ig"
'
test_expect_success 'error out when attempting to add ignored ones without -f' '
- ! git-add a.?? &&
- ! git-ls-files | grep "\\.ig"
+ ! git add a.?? &&
+ ! git ls-files | grep "\\.ig"
'
test_expect_success 'error out when attempting to add ignored ones without -f' '
- ! git-add d.?? &&
- ! git-ls-files | grep "\\.ig"
+ ! git add d.?? &&
+ ! git ls-files | grep "\\.ig"
'
test_expect_success 'add ignored ones with -f' '
- git-add -f a.?? &&
- git-ls-files --error-unmatch a.ig
+ git add -f a.?? &&
+ git ls-files --error-unmatch a.ig
'
test_expect_success 'add ignored ones with -f' '
- git-add -f d.??/* &&
- git-ls-files --error-unmatch d.ig/d.if d.ig/d.ig
+ git add -f d.??/* &&
+ git ls-files --error-unmatch d.ig/d.if d.ig/d.ig
'
mkdir 1 1/2 1/3
@@ -110,4 +110,49 @@ test_expect_success 'check correct prefix detection' '
git add 1/2/a 1/3/b 1/2/c
'
+test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries' '
+ for s in 1 2 3
+ do
+ echo $s > stage$s
+ echo "100755 $(git hash-object -w stage$s) $s file"
+ echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s symlink"
+ done | git update-index --index-info &&
+ git config core.filemode 0 &&
+ git config core.symlinks 0 &&
+ echo new > file &&
+ echo new > symlink &&
+ git add file symlink &&
+ git ls-files --stage | grep "^100755 .* 0 file$" &&
+ git ls-files --stage | grep "^120000 .* 0 symlink$"
+'
+
+test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over stage 1' '
+ git rm --cached -f file symlink &&
+ (
+ echo "100644 $(git hash-object -w stage1) 1 file"
+ echo "100755 $(git hash-object -w stage2) 2 file"
+ echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink"
+ echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink"
+ ) | git update-index --index-info &&
+ git config core.filemode 0 &&
+ git config core.symlinks 0 &&
+ echo new > file &&
+ echo new > symlink &&
+ git add file symlink &&
+ git ls-files --stage | grep "^100755 .* 0 file$" &&
+ git ls-files --stage | grep "^120000 .* 0 symlink$"
+'
+
+test_expect_success 'git add --refresh' '
+ >foo && git add foo && git commit -a -m "commit all" &&
+ test -z "`git diff-index HEAD -- foo`" &&
+ git read-tree HEAD &&
+ case "`git diff-index HEAD -- foo`" in
+ :100644" "*"M foo") echo ok;;
+ *) echo fail; (exit 1);;
+ esac &&
+ git add --refresh -- foo &&
+ test -z "`git diff-index HEAD -- foo`"
+'
+
test_done
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index 7c7e4335d6..261f199a0c 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -22,9 +22,9 @@ check_verify_failure () {
# first create a commit, so we have a valid object/type
# for the tag.
echo Hello >A
-git-update-index --add A
+git update-index --add A
git-commit -m "Initial commit"
-head=$(git-rev-parse --verify HEAD)
+head=$(git rev-parse --verify HEAD)
############################################################
# 1. length check
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index ffddb68db3..fcbabe8ec3 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -8,28 +8,28 @@ test_description='commit and log output encodings'
. ./test-lib.sh
compare_with () {
- git-show -s $1 | sed -e '1,/^$/d' -e 's/^ //' -e '$d' >current &&
+ git show -s $1 | sed -e '1,/^$/d' -e 's/^ //' -e '$d' >current &&
git diff current "$2"
}
test_expect_success setup '
: >F &&
- git-add F &&
- T=$(git-write-tree) &&
- C=$(git-commit-tree $T <../t3900/1-UTF-8.txt) &&
- git-update-ref HEAD $C &&
+ git add F &&
+ T=$(git write-tree) &&
+ C=$(git commit-tree $T <../t3900/1-UTF-8.txt) &&
+ git update-ref HEAD $C &&
git-tag C0
'
test_expect_success 'no encoding header for base case' '
- E=$(git-cat-file commit C0 | sed -ne "s/^encoding //p") &&
+ E=$(git cat-file commit C0 | sed -ne "s/^encoding //p") &&
test z = "z$E"
'
for H in ISO-8859-1 EUCJP ISO-2022-JP
do
test_expect_success "$H setup" '
- git-config i18n.commitencoding $H &&
+ git config i18n.commitencoding $H &&
git-checkout -b $H C0 &&
echo $H >F &&
git-commit -a -F ../t3900/$H.txt
@@ -39,21 +39,21 @@ done
for H in ISO-8859-1 EUCJP ISO-2022-JP
do
test_expect_success "check encoding header for $H" '
- E=$(git-cat-file commit '$H' | sed -ne "s/^encoding //p") &&
+ E=$(git cat-file commit '$H' | sed -ne "s/^encoding //p") &&
test "z$E" = "z'$H'"
'
done
test_expect_success 'config to remove customization' '
- git-config --unset-all i18n.commitencoding &&
- if Z=$(git-config --get-all i18n.commitencoding)
+ git config --unset-all i18n.commitencoding &&
+ if Z=$(git config --get-all i18n.commitencoding)
then
echo Oops, should have failed.
false
else
test z = "z$Z"
fi &&
- git-config i18n.commitencoding utf-8
+ git config i18n.commitencoding utf-8
'
test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
@@ -68,8 +68,8 @@ do
done
test_expect_success 'config to add customization' '
- git-config --unset-all i18n.commitencoding &&
- if Z=$(git-config --get-all i18n.commitencoding)
+ git config --unset-all i18n.commitencoding &&
+ if Z=$(git config --get-all i18n.commitencoding)
then
echo Oops, should have failed.
false
@@ -81,13 +81,13 @@ test_expect_success 'config to add customization' '
for H in ISO-8859-1 EUCJP ISO-2022-JP
do
test_expect_success "$H should be shown in itself now" '
- git-config i18n.commitencoding '$H' &&
+ git config i18n.commitencoding '$H' &&
compare_with '$H' ../t3900/'$H'.txt
'
done
test_expect_success 'config to tweak customization' '
- git-config i18n.logoutputencoding utf-8
+ git config i18n.logoutputencoding utf-8
'
test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
@@ -103,7 +103,7 @@ done
for J in EUCJP ISO-2022-JP
do
- git-config i18n.logoutputencoding $J
+ git config i18n.logoutputencoding $J
for H in EUCJP ISO-2022-JP
do
test_expect_success "$H should be shown in $J now" '
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 24bf0ee018..28e9e372f3 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -14,7 +14,7 @@ check_encoding () {
do
git format-patch --encoding=UTF-8 --stdout HEAD~$i..HEAD~$j |
grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" &&
- git-cat-file commit HEAD~$j |
+ git cat-file commit HEAD~$j |
case "$header" in
8859)
grep "^encoding ISO-8859-1" ;;
@@ -31,7 +31,7 @@ check_encoding () {
}
test_expect_success setup '
- git-config i18n.commitencoding UTF-8 &&
+ git config i18n.commitencoding UTF-8 &&
# use UTF-8 in author and committer name to match the
# i18n.commitencoding settings
@@ -55,7 +55,7 @@ test_expect_success setup '
git commit -s -m "Second on side" &&
# the second one on the side branch is ISO-8859-1
- git-config i18n.commitencoding ISO-8859-1 &&
+ git config i18n.commitencoding ISO-8859-1 &&
# use author and committer name in ISO-8859-1 to match it.
. ../t3901-8859-1.txt &&
test_tick &&
@@ -64,11 +64,11 @@ test_expect_success setup '
git commit -s -m "Third on side" &&
# Back to default
- git-config i18n.commitencoding UTF-8
+ git config i18n.commitencoding UTF-8
'
test_expect_success 'format-patch output (ISO-8859-1)' '
- git-config i18n.logoutputencoding ISO-8859-1 &&
+ git config i18n.logoutputencoding ISO-8859-1 &&
git format-patch --stdout master..HEAD^ >out-l1 &&
git format-patch --stdout HEAD^ >out-l2 &&
@@ -91,7 +91,7 @@ test_expect_success 'format-patch output (UTF-8)' '
test_expect_success 'rebase (U/U)' '
# We want the result of rebase in UTF-8
- git-config i18n.commitencoding UTF-8 &&
+ git config i18n.commitencoding UTF-8 &&
# The test is about logoutputencoding not affecting the
# final outcome -- it is used internally to generate the
@@ -109,7 +109,7 @@ test_expect_success 'rebase (U/U)' '
'
test_expect_success 'rebase (U/L)' '
- git-config i18n.commitencoding UTF-8 &&
+ git config i18n.commitencoding UTF-8 &&
git config i18n.logoutputencoding ISO-8859-1 &&
. ../t3901-utf8.txt &&
@@ -121,7 +121,7 @@ test_expect_success 'rebase (U/L)' '
test_expect_success 'rebase (L/L)' '
# In this test we want ISO-8859-1 encoded commits as the result
- git-config i18n.commitencoding ISO-8859-1 &&
+ git config i18n.commitencoding ISO-8859-1 &&
git config i18n.logoutputencoding ISO-8859-1 &&
. ../t3901-8859-1.txt &&
@@ -134,7 +134,7 @@ test_expect_success 'rebase (L/L)' '
test_expect_success 'rebase (L/U)' '
# This is pathological -- use UTF-8 as intermediate form
# to get ISO-8859-1 results.
- git-config i18n.commitencoding ISO-8859-1 &&
+ git config i18n.commitencoding ISO-8859-1 &&
git config i18n.logoutputencoding UTF-8 &&
. ../t3901-8859-1.txt &&
@@ -147,7 +147,7 @@ test_expect_success 'rebase (L/U)' '
test_expect_success 'cherry-pick(U/U)' '
# Both the commitencoding and logoutputencoding is set to UTF-8.
- git-config i18n.commitencoding UTF-8 &&
+ git config i18n.commitencoding UTF-8 &&
git config i18n.logoutputencoding UTF-8 &&
. ../t3901-utf8.txt &&
@@ -162,7 +162,7 @@ test_expect_success 'cherry-pick(U/U)' '
test_expect_success 'cherry-pick(L/L)' '
# Both the commitencoding and logoutputencoding is set to ISO-8859-1
- git-config i18n.commitencoding ISO-8859-1 &&
+ git config i18n.commitencoding ISO-8859-1 &&
git config i18n.logoutputencoding ISO-8859-1 &&
. ../t3901-8859-1.txt &&
@@ -177,7 +177,7 @@ test_expect_success 'cherry-pick(L/L)' '
test_expect_success 'cherry-pick(U/L)' '
# Commitencoding is set to UTF-8 but logoutputencoding is ISO-8859-1
- git-config i18n.commitencoding UTF-8 &&
+ git config i18n.commitencoding UTF-8 &&
git config i18n.logoutputencoding ISO-8859-1 &&
. ../t3901-utf8.txt &&
@@ -193,7 +193,7 @@ test_expect_success 'cherry-pick(L/U)' '
# Again, the commitencoding is set to ISO-8859-1 but
# logoutputencoding is set to UTF-8.
- git-config i18n.commitencoding ISO-8859-1 &&
+ git config i18n.commitencoding ISO-8859-1 &&
git config i18n.logoutputencoding UTF-8 &&
. ../t3901-8859-1.txt &&
@@ -206,7 +206,7 @@ test_expect_success 'cherry-pick(L/U)' '
'
test_expect_success 'rebase --merge (U/U)' '
- git-config i18n.commitencoding UTF-8 &&
+ git config i18n.commitencoding UTF-8 &&
git config i18n.logoutputencoding UTF-8 &&
. ../t3901-utf8.txt &&
@@ -217,7 +217,7 @@ test_expect_success 'rebase --merge (U/U)' '
'
test_expect_success 'rebase --merge (U/L)' '
- git-config i18n.commitencoding UTF-8 &&
+ git config i18n.commitencoding UTF-8 &&
git config i18n.logoutputencoding ISO-8859-1 &&
. ../t3901-utf8.txt &&
@@ -229,7 +229,7 @@ test_expect_success 'rebase --merge (U/L)' '
test_expect_success 'rebase --merge (L/L)' '
# In this test we want ISO-8859-1 encoded commits as the result
- git-config i18n.commitencoding ISO-8859-1 &&
+ git config i18n.commitencoding ISO-8859-1 &&
git config i18n.logoutputencoding ISO-8859-1 &&
. ../t3901-8859-1.txt &&
@@ -242,7 +242,7 @@ test_expect_success 'rebase --merge (L/L)' '
test_expect_success 'rebase --merge (L/U)' '
# This is pathological -- use UTF-8 as intermediate form
# to get ISO-8859-1 results.
- git-config i18n.commitencoding ISO-8859-1 &&
+ git config i18n.commitencoding ISO-8859-1 &&
git config i18n.logoutputencoding UTF-8 &&
. ../t3901-8859-1.txt &&
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
new file mode 100755
index 0000000000..245fb3babd
--- /dev/null
+++ b/t/t3902-quoted.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='quoted output'
+
+. ./test-lib.sh
+
+P1='pathname with HT'
+: >"$P1" 2>&1 && test -f "$P1" && rm -f "$P1" || {
+ echo >&2 'Filesystem does not support HT in names'
+ test_done
+}
+
+FN='濱野'
+GN='ç´”'
+HT=' '
+LF='
+'
+DQ='"'
+
+for_each_name () {
+ for name in \
+ Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \
+ "$FN$HT$GN" "$FN$LF$GN" "$FN $GN" "$FN$GN" "$FN$DQ$GN" \
+ "With SP in it"
+ do
+ eval "$1"
+ done
+}
+
+test_expect_success setup '
+
+ for_each_name "echo initial >\"\$name\""
+ git add . &&
+ git commit -q -m Initial &&
+
+ for_each_name "echo second >\"\$name\"" &&
+ git commit -a -m Second
+
+ for_each_name "echo modified >\"\$name\""
+
+'
+
+cat >expect.quoted <<\EOF
+Name
+"Name and a\nLF"
+"Name and an\tHT"
+"Name\""
+With SP in it
+"\346\277\261\351\207\216\t\347\264\224"
+"\346\277\261\351\207\216\n\347\264\224"
+"\346\277\261\351\207\216 \347\264\224"
+"\346\277\261\351\207\216\"\347\264\224"
+"\346\277\261\351\207\216\347\264\224"
+EOF
+
+cat >expect.raw <<\EOF
+Name
+"Name and a\nLF"
+"Name and an\tHT"
+"Name\""
+With SP in it
+"濱野\t純"
+"濱野\n純"
+濱野 純
+"濱野\"純"
+濱野純
+EOF
+
+test_expect_success 'check fully quoted output from ls-files' '
+
+ git ls-files >current && diff -u expect.quoted current
+
+'
+
+test_expect_success 'check fully quoted output from diff-files' '
+
+ git diff --name-only >current &&
+ diff -u expect.quoted current
+
+'
+
+test_expect_success 'check fully quoted output from diff-index' '
+
+ git diff --name-only HEAD >current &&
+ diff -u expect.quoted current
+
+'
+
+test_expect_success 'check fully quoted output from diff-tree' '
+
+ git diff --name-only HEAD^ HEAD >current &&
+ diff -u expect.quoted current
+
+'
+
+test_expect_success 'setting core.quotepath' '
+
+ git config --bool core.quotepath false
+
+'
+
+test_expect_success 'check fully quoted output from ls-files' '
+
+ git ls-files >current && diff -u expect.raw current
+
+'
+
+test_expect_success 'check fully quoted output from diff-files' '
+
+ git diff --name-only >current &&
+ diff -u expect.raw current
+
+'
+
+test_expect_success 'check fully quoted output from diff-index' '
+
+ git diff --name-only HEAD >current &&
+ diff -u expect.raw current
+
+'
+
+test_expect_success 'check fully quoted output from diff-tree' '
+
+ git diff --name-only HEAD^ HEAD >current &&
+ diff -u expect.raw current
+
+'
+
+test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
new file mode 100755
index 0000000000..9a9a250d2c
--- /dev/null
+++ b/t/t3903-stash.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E Schindelin
+#
+
+test_description='Test git-stash'
+
+. ./test-lib.sh
+
+test_expect_success 'stash some dirty working directory' '
+ echo 1 > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ echo 2 > file &&
+ git add file &&
+ echo 3 > file &&
+ test_tick &&
+ git stash &&
+ git diff-files --quiet &&
+ git diff-index --cached --quiet HEAD
+'
+
+cat > expect << EOF
+diff --git a/file b/file
+index 0cfbf08..00750ed 100644
+--- a/file
++++ b/file
+@@ -1 +1 @@
+-2
++3
+EOF
+
+test_expect_success 'parents of stash' '
+ test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
+ git diff stash^2..stash > output &&
+ diff -u output expect
+'
+
+test_expect_success 'apply needs clean working directory' '
+ echo 4 > other-file &&
+ git add other-file &&
+ echo 5 > other-file
+ ! git stash apply
+'
+
+test_expect_success 'apply stashed changes' '
+ git add other-file &&
+ test_tick &&
+ git commit -m other-file &&
+ git stash apply &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'apply stashed changes (including index)' '
+ git reset --hard HEAD^ &&
+ echo 6 > other-file &&
+ git add other-file &&
+ test_tick &&
+ git commit -m other-file &&
+ git stash apply --index &&
+ test 3 = $(cat file) &&
+ test 2 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'unstashing in a subdirectory' '
+ git reset --hard HEAD &&
+ mkdir subdir &&
+ cd subdir &&
+ git stash apply
+'
+
+test_done
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index 9c58d77cc2..7d92ae3e99 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -17,15 +17,15 @@ chmod +x path1
test_expect_success \
'update-cache --add two files with and without +x.' \
- 'git-update-index --add path0 path1'
+ 'git update-index --add path0 path1'
mv path0 path0-
sed -e 's/line/Line/' <path0- >path0
chmod +x path0
rm -f path1
test_expect_success \
- 'git-diff-files -p after editing work tree.' \
- 'git-diff-files -p >current'
+ 'git diff-files -p after editing work tree.' \
+ 'git diff-files -p >current'
# that's as far as it comes
if [ "$(git config --get core.filemode)" = false ]
@@ -56,7 +56,7 @@ deleted file mode 100755
EOF
test_expect_success \
- 'validate git-diff-files -p output.' \
+ 'validate git diff-files -p output.' \
'compare_diff_patch current expected'
test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 2e3c20d6b9..063e79257a 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -28,21 +28,21 @@ Line 15
test_expect_success \
'update-cache --add a file.' \
- 'git-update-index --add path0'
+ 'git update-index --add path0'
test_expect_success \
'write that tree.' \
- 'tree=$(git-write-tree) && echo $tree'
+ 'tree=$(git write-tree) && echo $tree'
sed -e 's/line/Line/' <path0 >path1
rm -f path0
test_expect_success \
'renamed and edited the file.' \
- 'git-update-index --add --remove path0 path1'
+ 'git update-index --add --remove path0 path1'
test_expect_success \
- 'git-diff-index -p -M after rename and editing.' \
- 'git-diff-index -p -M $tree >current'
+ 'git diff-index -p -M after rename and editing.' \
+ 'git diff-index -p -M $tree >current'
cat >expected <<\EOF
diff --git a/path0 b/path1
rename from path0
@@ -64,4 +64,17 @@ test_expect_success \
'validate the output.' \
'compare_diff_patch current expected'
+test_expect_success 'favour same basenames over different ones' '
+ cp path1 another-path &&
+ git add another-path &&
+ git commit -m 1 &&
+ git rm path1 &&
+ mkdir subdir &&
+ git mv another-path subdir/path1 &&
+ git runstatus | grep "renamed: .*path1 -> subdir/path1"'
+
+test_expect_success 'favour same basenames even with minor differences' '
+ git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
+ git runstatus | grep "renamed: .*path1 -> subdir/path1"'
+
test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index 56eda63fc2..a4cfde6b29 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -140,80 +140,80 @@ cmp_diff_files_output () {
test_expect_success \
'diff-tree of known trees.' \
- 'git-diff-tree $tree_O $tree_A >.test-a &&
+ 'git diff-tree $tree_O $tree_A >.test-a &&
cmp -s .test-a .test-plain-OA'
test_expect_success \
'diff-tree of known trees.' \
- 'git-diff-tree -r $tree_O $tree_A >.test-a &&
+ 'git diff-tree -r $tree_O $tree_A >.test-a &&
cmp -s .test-a .test-recursive-OA'
test_expect_success \
'diff-tree of known trees.' \
- 'git-diff-tree $tree_O $tree_B >.test-a &&
+ 'git diff-tree $tree_O $tree_B >.test-a &&
cmp -s .test-a .test-plain-OB'
test_expect_success \
'diff-tree of known trees.' \
- 'git-diff-tree -r $tree_O $tree_B >.test-a &&
+ 'git diff-tree -r $tree_O $tree_B >.test-a &&
cmp -s .test-a .test-recursive-OB'
test_expect_success \
'diff-tree of known trees.' \
- 'git-diff-tree $tree_A $tree_B >.test-a &&
+ 'git diff-tree $tree_A $tree_B >.test-a &&
cmp -s .test-a .test-plain-AB'
test_expect_success \
'diff-tree of known trees.' \
- 'git-diff-tree -r $tree_A $tree_B >.test-a &&
+ 'git diff-tree -r $tree_A $tree_B >.test-a &&
cmp -s .test-a .test-recursive-AB'
test_expect_success \
'diff-cache O with A in cache' \
- 'git-read-tree $tree_A &&
- git-diff-index --cached $tree_O >.test-a &&
+ 'git read-tree $tree_A &&
+ git diff-index --cached $tree_O >.test-a &&
cmp -s .test-a .test-recursive-OA'
test_expect_success \
'diff-cache O with B in cache' \
- 'git-read-tree $tree_B &&
- git-diff-index --cached $tree_O >.test-a &&
+ 'git read-tree $tree_B &&
+ git diff-index --cached $tree_O >.test-a &&
cmp -s .test-a .test-recursive-OB'
test_expect_success \
'diff-cache A with B in cache' \
- 'git-read-tree $tree_B &&
- git-diff-index --cached $tree_A >.test-a &&
+ 'git read-tree $tree_B &&
+ git diff-index --cached $tree_A >.test-a &&
cmp -s .test-a .test-recursive-AB'
test_expect_success \
'diff-files with O in cache and A checked out' \
'rm -fr Z [A-Z][A-Z] &&
- git-read-tree $tree_A &&
- git-checkout-index -f -a &&
- git-read-tree --reset $tree_O || return 1
- git-update-index --refresh >/dev/null ;# this can exit non-zero
- git-diff-files >.test-a &&
+ git read-tree $tree_A &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_O || return 1
+ git update-index --refresh >/dev/null ;# this can exit non-zero
+ git diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-OA'
test_expect_success \
'diff-files with O in cache and B checked out' \
'rm -fr Z [A-Z][A-Z] &&
- git-read-tree $tree_B &&
- git-checkout-index -f -a &&
- git-read-tree --reset $tree_O || return 1
- git-update-index --refresh >/dev/null ;# this can exit non-zero
- git-diff-files >.test-a &&
+ git read-tree $tree_B &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_O || return 1
+ git update-index --refresh >/dev/null ;# this can exit non-zero
+ git diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-OB'
test_expect_success \
'diff-files with A in cache and B checked out' \
'rm -fr Z [A-Z][A-Z] &&
- git-read-tree $tree_B &&
- git-checkout-index -f -a &&
- git-read-tree --reset $tree_A || return 1
- git-update-index --refresh >/dev/null ;# this can exit non-zero
- git-diff-files >.test-a &&
+ git read-tree $tree_B &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_A || return 1
+ git update-index --refresh >/dev/null ;# this can exit non-zero
+ git diff-files >.test-a &&
cmp_diff_files_output .test-a .test-recursive-AB'
################################################################
@@ -222,26 +222,26 @@ test_expect_success \
test_expect_success \
'diff-tree O A == diff-tree -R A O' \
- 'git-diff-tree $tree_O $tree_A >.test-a &&
- git-diff-tree -R $tree_A $tree_O >.test-b &&
+ 'git diff-tree $tree_O $tree_A >.test-a &&
+ git diff-tree -R $tree_A $tree_O >.test-b &&
cmp -s .test-a .test-b'
test_expect_success \
'diff-tree -r O A == diff-tree -r -R A O' \
- 'git-diff-tree -r $tree_O $tree_A >.test-a &&
- git-diff-tree -r -R $tree_A $tree_O >.test-b &&
+ 'git diff-tree -r $tree_O $tree_A >.test-a &&
+ git diff-tree -r -R $tree_A $tree_O >.test-b &&
cmp -s .test-a .test-b'
test_expect_success \
'diff-tree B A == diff-tree -R A B' \
- 'git-diff-tree $tree_B $tree_A >.test-a &&
- git-diff-tree -R $tree_A $tree_B >.test-b &&
+ 'git diff-tree $tree_B $tree_A >.test-a &&
+ git diff-tree -R $tree_A $tree_B >.test-b &&
cmp -s .test-a .test-b'
test_expect_success \
'diff-tree -r B A == diff-tree -r -R A B' \
- 'git-diff-tree -r $tree_B $tree_A >.test-a &&
- git-diff-tree -r -R $tree_A $tree_B >.test-b &&
+ 'git diff-tree -r $tree_B $tree_A >.test-a &&
+ git diff-tree -r -R $tree_A $tree_B >.test-b &&
cmp -s .test-a .test-b'
test_done
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index 27519704d4..8b1f875286 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -13,8 +13,8 @@ test_expect_success \
'prepare reference tree' \
'cat ../../COPYING >COPYING &&
echo frotz >rezrov &&
- git-update-index --add COPYING rezrov &&
- tree=$(git-write-tree) &&
+ git update-index --add COPYING rezrov &&
+ tree=$(git write-tree) &&
echo $tree'
test_expect_success \
@@ -22,14 +22,14 @@ test_expect_success \
'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
rm -f COPYING &&
- git-update-index --add --remove COPYING COPYING.?'
+ git update-index --add --remove COPYING COPYING.?'
# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
# both are slightly edited, and unchanged rezrov. So we say you
# copy-and-edit one, and rename-and-edit the other. We do not say
# anything about rezrov.
-GIT_DIFF_OPTS=--unified=0 git-diff-index -M -p $tree >current
+GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current
cat >expected <<\EOF
diff --git a/COPYING b/COPYING.1
copy from COPYING
@@ -62,14 +62,14 @@ test_expect_success \
test_expect_success \
'prepare work tree again' \
'mv COPYING.2 COPYING &&
- git-update-index --add --remove COPYING COPYING.1 COPYING.2'
+ git update-index --add --remove COPYING COPYING.1 COPYING.2'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
# both are slightly edited, and unchanged rezrov. So we say you
# edited one, and copy-and-edit the other. We do not say
# anything about rezrov.
-GIT_DIFF_OPTS=--unified=0 git-diff-index -C -p $tree >current
+GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current
cat >expected <<\EOF
diff --git a/COPYING b/COPYING
--- a/COPYING
@@ -100,16 +100,16 @@ test_expect_success \
test_expect_success \
'prepare work tree once again' \
'cat ../../COPYING >COPYING &&
- git-update-index --add --remove COPYING COPYING.1'
+ git update-index --add --remove COPYING COPYING.1'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
# but COPYING is not edited. We say you copy-and-edit COPYING.1; this
# is only possible because -C mode now reports the unmodified file to
# the diff-core. Unchanged rezrov, although being fed to
-# git-diff-index as well, should not be mentioned.
+# git diff-index as well, should not be mentioned.
GIT_DIFF_OPTS=--unified=0 \
- git-diff-index -C --find-copies-harder -p $tree >current
+ git diff-index -C --find-copies-harder -p $tree >current
cat >expected <<\EOF
diff --git a/COPYING b/COPYING.1
copy from COPYING
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index a23aaa0a94..3d25be7a67 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -16,8 +16,8 @@ test_expect_success \
'prepare reference tree' \
'echo xyzzy | tr -d '\\\\'012 >yomin &&
ln -s xyzzy frotz &&
- git-update-index --add frotz yomin &&
- tree=$(git-write-tree) &&
+ git update-index --add frotz yomin &&
+ tree=$(git write-tree) &&
echo $tree'
test_expect_success \
@@ -26,7 +26,7 @@ test_expect_success \
rm -f yomin &&
ln -s xyzzy nitfol &&
ln -s xzzzy bozbar &&
- git-update-index --add --remove frotz rezrov nitfol bozbar yomin'
+ git update-index --add --remove frotz rezrov nitfol bozbar yomin'
# tree has frotz pointing at xyzzy, and yomin that contains xyzzy to
# confuse things. work tree has rezrov (xyzzy) nitfol (xyzzy) and
@@ -34,7 +34,7 @@ test_expect_success \
# rezrov and nitfol are rename/copy of frotz and bozbar should be
# a new creation.
-GIT_DIFF_OPTS=--unified=0 git-diff-index -M -p $tree >current
+GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current
cat >expected <<\EOF
diff --git a/bozbar b/bozbar
new file mode 120000
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index 684fd23a41..6630017312 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -13,8 +13,8 @@ test_expect_success \
'prepare reference tree' \
'cat ../../COPYING >COPYING &&
echo frotz >rezrov &&
- git-update-index --add COPYING rezrov &&
- tree=$(git-write-tree) &&
+ git update-index --add COPYING rezrov &&
+ tree=$(git write-tree) &&
echo $tree'
test_expect_success \
@@ -22,14 +22,14 @@ test_expect_success \
'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
rm -f COPYING &&
- git-update-index --add --remove COPYING COPYING.?'
+ git update-index --add --remove COPYING COPYING.?'
# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
# both are slightly edited, and unchanged rezrov. We say COPYING.1
# and COPYING.2 are based on COPYING, and do not say anything about
# rezrov.
-git-diff-index -M $tree >current
+git diff-index -M $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
@@ -45,14 +45,14 @@ test_expect_success \
test_expect_success \
'prepare work tree again' \
'mv COPYING.2 COPYING &&
- git-update-index --add --remove COPYING COPYING.1 COPYING.2'
+ git update-index --add --remove COPYING COPYING.1 COPYING.2'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
# both are slightly edited, and unchanged rezrov. We say COPYING.1
# is based on COPYING and COPYING is still there, and do not say anything
# about rezrov.
-git-diff-index -C $tree >current
+git diff-index -C $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M COPYING
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
@@ -72,9 +72,9 @@ test_expect_success \
test_expect_success \
'prepare work tree once again' \
'cat ../../COPYING >COPYING &&
- git-update-index --add --remove COPYING COPYING.1'
+ git update-index --add --remove COPYING COPYING.1'
-git-diff-index -C --find-copies-harder $tree >current
+git diff-index -C --find-copies-harder $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
EOF
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
index e72c6fd1b4..ab5406dd9f 100755
--- a/t/t4006-diff-mode.sh
+++ b/t/t4006-diff-mode.sh
@@ -11,24 +11,24 @@ test_description='Test mode change diffs.
test_expect_success \
'setup' \
'echo frotz >rezrov &&
- git-update-index --add rezrov &&
- tree=`git-write-tree` &&
+ git update-index --add rezrov &&
+ tree=`git write-tree` &&
echo $tree'
if [ "$(git config --get core.filemode)" = false ]
then
say 'filemode disabled on the filesystem, using update-index --chmod=+x'
test_expect_success \
- 'git-update-index --chmod=+x' \
- 'git-update-index rezrov &&
- git-update-index --chmod=+x rezrov &&
- git-diff-index $tree >current'
+ 'git update-index --chmod=+x' \
+ 'git update-index rezrov &&
+ git update-index --chmod=+x rezrov &&
+ git diff-index $tree >current'
else
test_expect_success \
'chmod' \
'chmod +x rezrov &&
- git-update-index rezrov &&
- git-diff-index $tree >current'
+ git update-index rezrov &&
+ git diff-index $tree >current'
fi
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
@@ -41,4 +41,3 @@ test_expect_success \
'git diff expected check'
test_done
-
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
index bb6ba69258..104a4e1492 100755
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -13,20 +13,20 @@ test_expect_success \
'prepare reference tree' \
'mkdir path0 path1 &&
cp ../../COPYING path0/COPYING &&
- git-update-index --add path0/COPYING &&
- tree=$(git-write-tree) &&
+ git update-index --add path0/COPYING &&
+ tree=$(git write-tree) &&
echo $tree'
test_expect_success \
'prepare work tree' \
'cp path0/COPYING path1/COPYING &&
- git-update-index --add --remove path0/COPYING path1/COPYING'
+ git update-index --add --remove path0/COPYING path1/COPYING'
# In the tree, there is only path0/COPYING. In the cache, path0 and
# path1 both have COPYING and the latter is a copy of path0/COPYING.
# Comparing the full tree with cache should tell us so.
-git-diff-index -C --find-copies-harder $tree >current
+git diff-index -C --find-copies-harder $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100 path0/COPYING path1/COPYING
@@ -42,7 +42,7 @@ test_expect_success \
# path1/COPYING suddenly appearing from nowhere, not detected as
# a copy from path0/COPYING.
-git-diff-index -C $tree path1 >current
+git diff-index -C $tree path1 >current
cat >expected <<\EOF
:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
@@ -55,14 +55,14 @@ test_expect_success \
test_expect_success \
'tweak work tree' \
'rm -f path0/COPYING &&
- git-update-index --remove path0/COPYING'
+ git update-index --remove path0/COPYING'
# In the tree, there is only path0/COPYING. In the cache, path0 does
# not have COPYING anymore and path1 has COPYING which is a copy of
# path0/COPYING. Showing the full tree with cache should tell us about
# the rename.
-git-diff-index -C $tree >current
+git diff-index -C $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 path0/COPYING path1/COPYING
@@ -77,7 +77,7 @@ test_expect_success \
# path0/COPYING. When we say we care only about path1, we should just
# see path1/COPYING appearing from nowhere.
-git-diff-index -C $tree path1 >current
+git diff-index -C $tree path1 >current
cat >expected <<\EOF
:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 263ac1ebf7..5836e3a899 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -28,19 +28,19 @@ test_expect_success \
setup \
'cat ../../README >file0 &&
cat ../../COPYING >file1 &&
- git-update-index --add file0 file1 &&
- tree=$(git-write-tree) &&
+ git update-index --add file0 file1 &&
+ tree=$(git write-tree) &&
echo "$tree"'
test_expect_success \
'change file1 with copy-edit of file0 and remove file0' \
'sed -e "s/git/GIT/" file0 >file1 &&
rm -f file0 &&
- git-update-index --remove file0 file1'
+ git update-index --remove file0 file1'
test_expect_success \
'run diff with -B' \
- 'git-diff-index -B --cached "$tree" >current'
+ 'git diff-index -B --cached "$tree" >current'
cat >expected <<\EOF
:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D file0
@@ -53,7 +53,7 @@ test_expect_success \
test_expect_success \
'run diff with -B and -M' \
- 'git-diff-index -B -M "$tree" >current'
+ 'git diff-index -B -M "$tree" >current'
cat >expected <<\EOF
:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c R100 file0 file1
@@ -66,16 +66,16 @@ test_expect_success \
test_expect_success \
'swap file0 and file1' \
'rm -f file0 file1 &&
- git-read-tree -m $tree &&
- git-checkout-index -f -u -a &&
+ git read-tree -m $tree &&
+ git checkout-index -f -u -a &&
mv file0 tmp &&
mv file1 file0 &&
mv tmp file1 &&
- git-update-index file0 file1'
+ git update-index file0 file1'
test_expect_success \
'run diff with -B' \
- 'git-diff-index -B "$tree" >current'
+ 'git diff-index -B "$tree" >current'
cat >expected <<\EOF
:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 6ff87c4664981e4397625791c8ea3bbb5f2279a3 M100 file0
@@ -88,7 +88,7 @@ test_expect_success \
test_expect_success \
'run diff with -B and -M' \
- 'git-diff-index -B -M "$tree" >current'
+ 'git diff-index -B -M "$tree" >current'
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 file1 file0
@@ -103,11 +103,11 @@ test_expect_success \
'make file0 into something completely different' \
'rm -f file0 &&
ln -s frotz file0 &&
- git-update-index file0 file1'
+ git update-index file0 file1'
test_expect_success \
'run diff with -B' \
- 'git-diff-index -B "$tree" >current'
+ 'git diff-index -B "$tree" >current'
cat >expected <<\EOF
:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
@@ -120,7 +120,7 @@ test_expect_success \
test_expect_success \
'run diff with -B' \
- 'git-diff-index -B -M "$tree" >current'
+ 'git diff-index -B -M "$tree" >current'
# This should not mistake file0 as the copy source of new file1
# due to type differences.
@@ -135,7 +135,7 @@ test_expect_success \
test_expect_success \
'run diff with -M' \
- 'git-diff-index -M "$tree" >current'
+ 'git diff-index -M "$tree" >current'
# This should not mistake file0 as the copy source of new file1
# due to type differences.
@@ -151,16 +151,16 @@ test_expect_success \
test_expect_success \
'file1 edited to look like file0 and file0 rename-edited to file2' \
'rm -f file0 file1 &&
- git-read-tree -m $tree &&
- git-checkout-index -f -u -a &&
+ git read-tree -m $tree &&
+ git checkout-index -f -u -a &&
sed -e "s/git/GIT/" file0 >file1 &&
sed -e "s/git/GET/" file0 >file2 &&
rm -f file0
- git-update-index --add --remove file0 file1 file2'
+ git update-index --add --remove file0 file1 file2'
test_expect_success \
'run diff with -B' \
- 'git-diff-index -B "$tree" >current'
+ 'git diff-index -B "$tree" >current'
cat >expected <<\EOF
:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D file0
@@ -174,7 +174,7 @@ test_expect_success \
test_expect_success \
'run diff with -B -M' \
- 'git-diff-index -B -M "$tree" >current'
+ 'git diff-index -B -M "$tree" >current'
cat >expected <<\EOF
:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c C095 file0 file1
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index 2f2f8b1216..d2b45e7b8f 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -13,8 +13,8 @@ test_expect_success \
'prepare reference tree' \
'cat ../../COPYING >COPYING &&
echo frotz >rezrov &&
- git-update-index --add COPYING rezrov &&
- tree=$(git-write-tree) &&
+ git update-index --add COPYING rezrov &&
+ tree=$(git write-tree) &&
echo $tree'
test_expect_success \
@@ -22,14 +22,14 @@ test_expect_success \
'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
rm -f COPYING &&
- git-update-index --add --remove COPYING COPYING.?'
+ git update-index --add --remove COPYING COPYING.?'
# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
# both are slightly edited, and unchanged rezrov. We say COPYING.1
# and COPYING.2 are based on COPYING, and do not say anything about
# rezrov.
-git-diff-index -z -M $tree >current
+git diff-index -z -M $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
@@ -49,14 +49,14 @@ test_expect_success \
test_expect_success \
'prepare work tree again' \
'mv COPYING.2 COPYING &&
- git-update-index --add --remove COPYING COPYING.1 COPYING.2'
+ git update-index --add --remove COPYING COPYING.1 COPYING.2'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
# both are slightly edited, and unchanged rezrov. We say COPYING.1
# is based on COPYING and COPYING is still there, and do not say anything
# about rezrov.
-git-diff-index -z -C $tree >current
+git diff-index -z -C $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M
COPYING
@@ -79,9 +79,9 @@ test_expect_success \
test_expect_success \
'prepare work tree once again' \
'cat ../../COPYING >COPYING &&
- git-update-index --add --remove COPYING COPYING.1'
+ git update-index --add --remove COPYING COPYING.1'
-git-diff-index -z -C --find-copies-harder $tree >current
+git diff-index -z -C --find-copies-harder $tree >current
cat >expected <<\EOF
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
COPYING
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 9e1544df9d..ad3d9e4845 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -17,18 +17,18 @@ test_expect_success \
'echo frotz >file0 &&
mkdir path1 &&
echo rezrov >path1/file1 &&
- git-update-index --add file0 path1/file1 &&
- tree=`git-write-tree` &&
+ git update-index --add file0 path1/file1 &&
+ tree=`git write-tree` &&
echo "$tree" &&
echo nitfol >file0 &&
echo yomin >path1/file1 &&
- git-update-index file0 path1/file1'
+ git update-index file0 path1/file1'
cat >expected <<\EOF
EOF
test_expect_success \
'limit to path should show nothing' \
- 'git-diff-index --cached $tree -- path >current &&
+ 'git diff-index --cached $tree -- path >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -36,7 +36,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path1 should show path1/file1' \
- 'git-diff-index --cached $tree -- path1 >current &&
+ 'git diff-index --cached $tree -- path1 >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -44,7 +44,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path1/ should show path1/file1' \
- 'git-diff-index --cached $tree -- path1/ >current &&
+ 'git diff-index --cached $tree -- path1/ >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -52,14 +52,14 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to file0 should show file0' \
- 'git-diff-index --cached $tree -- file0 >current &&
+ 'git diff-index --cached $tree -- file0 >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
EOF
test_expect_success \
'limit to file0/ should emit nothing.' \
- 'git-diff-index --cached $tree -- file0/ >current &&
+ 'git diff-index --cached $tree -- file0/ >current &&
compare_diff_raw current expected'
test_done
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 379a831f0b..c6d13693ba 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -23,17 +23,17 @@ EOF
test_expect_success \
'diff new symlink' \
'ln -s xyzzy frotz &&
- git-update-index &&
- tree=$(git-write-tree) &&
- git-update-index --add frotz &&
- GIT_DIFF_OPTS=--unified=0 git-diff-index -M -p $tree > current &&
+ git update-index &&
+ tree=$(git write-tree) &&
+ git update-index --add frotz &&
+ GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree > current &&
compare_diff_patch current expected'
test_expect_success \
'diff unchanged symlink' \
- 'tree=$(git-write-tree) &&
- git-update-index frotz &&
- test -z "$(git-diff-index --name-only $tree)"'
+ 'tree=$(git write-tree) &&
+ git update-index frotz &&
+ test -z "$(git diff-index --name-only $tree)"'
cat > expected << EOF
diff --git a/frotz b/frotz
@@ -49,7 +49,7 @@ EOF
test_expect_success \
'diff removed symlink' \
'rm frotz &&
- git-diff-index -M -p $tree > current &&
+ git diff-index -M -p $tree > current &&
compare_diff_patch current expected'
cat > expected << EOF
@@ -60,7 +60,7 @@ test_expect_success \
'diff identical, but newly created symlink' \
'sleep 3 &&
ln -s xyzzy frotz &&
- git-diff-index -M -p $tree > current &&
+ git diff-index -M -p $tree > current &&
compare_diff_patch current expected'
cat > expected << EOF
@@ -79,7 +79,7 @@ test_expect_success \
'diff different symlink' \
'rm frotz &&
ln -s yxyyz frotz &&
- git-diff-index -M -p $tree > current &&
+ git diff-index -M -p $tree > current &&
compare_diff_patch current expected'
test_done
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index 323606c65c..eced1f30fb 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -10,7 +10,7 @@ test_description='Binary diff and apply
test_expect_success 'prepare repository' \
'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
- git-update-index --add a b c d &&
+ git update-index --add a b c d &&
echo git >a &&
cat ../test4012.png >b &&
echo git >c &&
@@ -24,18 +24,18 @@ cat > expected <<\EOF
4 files changed, 2 insertions(+), 2 deletions(-)
EOF
test_expect_success 'diff without --binary' \
- 'git-diff | git-apply --stat --summary >current &&
+ 'git diff | git apply --stat --summary >current &&
cmp current expected'
test_expect_success 'diff with --binary' \
- 'git-diff --binary | git-apply --stat --summary >current &&
+ 'git diff --binary | git apply --stat --summary >current &&
cmp current expected'
# apply needs to be able to skip the binary material correctly
# in order to report the line number of a corrupt patch.
test_expect_success 'apply detecting corrupt patch correctly' \
- 'git-diff | sed -e 's/-CIT/xCIT/' >broken &&
- if git-apply --stat --summary broken 2>detected
+ 'git diff | sed -e 's/-CIT/xCIT/' >broken &&
+ if git apply --stat --summary broken 2>detected
then
echo unhappy - should have detected an error
(exit 1)
@@ -48,8 +48,8 @@ test_expect_success 'apply detecting corrupt patch correctly' \
test "$detected" = xCIT'
test_expect_success 'apply detecting corrupt patch correctly' \
- 'git-diff --binary | sed -e 's/-CIT/xCIT/' >broken &&
- if git-apply --stat --summary broken 2>detected
+ 'git diff --binary | sed -e 's/-CIT/xCIT/' >broken &&
+ if git apply --stat --summary broken 2>detected
then
echo unhappy - should have detected an error
(exit 1)
@@ -66,15 +66,15 @@ test_expect_success 'initial commit' 'git-commit -a -m initial'
# Try removal (b), modification (d), and creation (e).
test_expect_success 'diff-index with --binary' \
'echo AIT >a && mv b e && echo CIT >c && cat e >d &&
- git-update-index --add --remove a b c d e &&
- tree0=`git-write-tree` &&
- git-diff --cached --binary >current &&
- git-apply --stat --summary current'
+ git update-index --add --remove a b c d e &&
+ tree0=`git write-tree` &&
+ git diff --cached --binary >current &&
+ git apply --stat --summary current'
test_expect_success 'apply binary patch' \
'git-reset --hard &&
- git-apply --binary --index <current &&
- tree1=`git-write-tree` &&
+ git apply --binary --index <current &&
+ tree1=`git write-tree` &&
test "$tree1" = "$tree0"'
test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 4e7d68dda2..9eec754221 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -243,6 +243,8 @@ format-patch --inline --stdout initial..side
format-patch --inline --stdout initial..master^
format-patch --inline --stdout initial..master
format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+config format.subjectprefix DIFFERENT_PREFIX
+format-patch --inline --stdout initial..master^^
diff --abbrev initial..side
diff -r initial..side
diff --git a/t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX b/t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX
new file mode 100644
index 0000000000..78f8970e2b
--- /dev/null
+++ b/t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX
@@ -0,0 +1,2 @@
+$ git config format.subjectprefix DIFFERENT_PREFIX
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
new file mode 100644
index 0000000000..b8e81e1552
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
@@ -0,0 +1,60 @@
+$ git format-patch --inline --stdout initial..master^^
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [DIFFERENT_PREFIX] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+
+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
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 930e209d31..79fdff3f3a 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -17,7 +17,7 @@ do {
} while (0);
EOF
-git-update-index --add x
+git update-index --add x
cat << EOF > x
do
@@ -42,13 +42,13 @@ index adf3937..6edc172 100644
+while (0);
EOF
-git-diff > out
+git diff > out
test_expect_success "Ray's example without options" 'git diff expect out'
-git-diff -w > out
+git diff -w > out
test_expect_success "Ray's example with -w" 'git diff expect out'
-git-diff -b > out
+git diff -b > out
test_expect_success "Ray's example with -b" 'git diff expect out'
tr 'Q' '\015' << EOF > x
@@ -60,7 +60,7 @@ unchanged line
CR at endQ
EOF
-git-update-index x
+git update-index x
cat << EOF > x
whitespace at beginning
@@ -89,14 +89,14 @@ index d99af23..8b32fb5 100644
-CR at endQ
+CR at end
EOF
-git-diff > out
+git diff > out
test_expect_success 'another test, without options' 'git diff expect out'
cat << EOF > expect
diff --git a/x b/x
index d99af23..8b32fb5 100644
EOF
-git-diff -w > out
+git diff -w > out
test_expect_success 'another test, with -w' 'git diff expect out'
tr 'Q' '\015' << EOF > expect
@@ -114,7 +114,7 @@ index d99af23..8b32fb5 100644
unchanged line
CR at endQ
EOF
-git-diff -b > out
+git diff -b > out
test_expect_success 'another test, with -b' 'git diff expect out'
test_done
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
new file mode 100644
index 0000000000..f9db81d3ab
--- /dev/null
+++ b/t/t4018-diff-funcname.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test custom diff function name patterns'
+
+. ./test-lib.sh
+
+LF='
+'
+
+cat > Beer.java << EOF
+public class Beer
+{
+ int special;
+ public static void main(String args[])
+ {
+ String s=" ";
+ for(int x = 99; x > 0; x--)
+ {
+ System.out.print(x + " bottles of beer on the wall "
+ + x + " bottles of beer\n"
+ + "Take one down, pass it around, " + (x - 1)
+ + " bottles of beer on the wall.\n");
+ }
+ System.out.print("Go to the store, buy some more,\n"
+ + "99 bottles of beer on the wall.\n");
+ }
+}
+EOF
+
+sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
+
+test_expect_success 'default behaviour' '
+ git diff Beer.java Beer-correct.java |
+ grep "^@@.*@@ public class Beer"
+'
+
+test_expect_success 'preset java pattern' '
+ echo "*.java diff=java" >.gitattributes &&
+ git diff Beer.java Beer-correct.java |
+ grep "^@@.*@@ public static void main("
+'
+
+git config diff.java.funcname '!static
+!String
+[^ ].*s.*'
+
+test_expect_success 'custom pattern' '
+ git diff Beer.java Beer-correct.java |
+ grep "^@@.*@@ int special;$"
+'
+
+test_expect_success 'last regexp must not be negated' '
+ git config diff.java.funcname "!static" &&
+ ! git diff Beer.java Beer-correct.java
+'
+
+test_done
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index f0045cd788..ed3bd5b3fe 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -94,4 +94,16 @@ test_expect_success 'diff attribute should apply only to diff' '
'
+test_expect_success 'no diff with -diff' '
+ echo >.gitattributes "file -diff" &&
+ git diff | grep Binary
+'
+
+echo NULZbetweenZwords | tr Z '\0' > file
+
+test_expect_success 'force diff with "diff"' '
+ echo >.gitattributes "file diff" &&
+ git diff | grep -a second
+'
+
test_done
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
index 7b81c32e57..435f65b370 100755
--- a/t/t4100-apply-stat.sh
+++ b/t/t4100-apply-stat.sh
@@ -3,45 +3,44 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply --stat --summary test.
+test_description='git apply --stat --summary test.
'
. ./test-lib.sh
test_expect_success \
'rename' \
- 'git-apply --stat --summary <../t4100/t-apply-1.patch >current &&
+ 'git apply --stat --summary <../t4100/t-apply-1.patch >current &&
git diff ../t4100/t-apply-1.expect current'
test_expect_success \
'copy' \
- 'git-apply --stat --summary <../t4100/t-apply-2.patch >current &&
+ 'git apply --stat --summary <../t4100/t-apply-2.patch >current &&
git diff ../t4100/t-apply-2.expect current'
test_expect_success \
'rewrite' \
- 'git-apply --stat --summary <../t4100/t-apply-3.patch >current &&
+ 'git apply --stat --summary <../t4100/t-apply-3.patch >current &&
git diff ../t4100/t-apply-3.expect current'
test_expect_success \
'mode' \
- 'git-apply --stat --summary <../t4100/t-apply-4.patch >current &&
+ 'git apply --stat --summary <../t4100/t-apply-4.patch >current &&
git diff ../t4100/t-apply-4.expect current'
test_expect_success \
'non git' \
- 'git-apply --stat --summary <../t4100/t-apply-5.patch >current &&
+ 'git apply --stat --summary <../t4100/t-apply-5.patch >current &&
git diff ../t4100/t-apply-5.expect current'
test_expect_success \
'non git' \
- 'git-apply --stat --summary <../t4100/t-apply-6.patch >current &&
+ 'git apply --stat --summary <../t4100/t-apply-6.patch >current &&
git diff ../t4100/t-apply-6.expect current'
test_expect_success \
'non git' \
- 'git-apply --stat --summary <../t4100/t-apply-7.patch >current &&
+ 'git apply --stat --summary <../t4100/t-apply-7.patch >current &&
git diff ../t4100/t-apply-7.expect current'
test_done
-
diff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh
index 026fac8c55..da8abcf364 100755
--- a/t/t4101-apply-nonl.sh
+++ b/t/t4101-apply-nonl.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply should handle files with incomplete lines.
+test_description='git apply should handle files with incomplete lines.
'
. ./test-lib.sh
@@ -23,7 +23,7 @@ do
cat frotz.$i >frotz
test_expect_success \
"apply diff between $i and $j" \
- "git-apply <../t4101/diff.$i-$j && diff frotz.$j frotz"
+ "git apply <../t4101/diff.$i-$j && diff frotz.$j frotz"
done
done
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index b4662b0364..d42abff1ad 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply handling copy/rename patch.
+test_description='git apply handling copy/rename patch.
'
. ./test-lib.sh
@@ -26,10 +26,10 @@ echo 'This is foo' >foo
chmod +x foo
test_expect_success setup \
- 'git-update-index --add foo'
+ 'git update-index --add foo'
test_expect_success apply \
- 'git-apply --index --stat --summary --apply test-patch'
+ 'git apply --index --stat --summary --apply test-patch'
if [ "$(git config --get core.filemode)" = false ]
then
@@ -40,7 +40,7 @@ else
fi
test_expect_success 'apply reverse' \
- 'git-apply -R --index --stat --summary --apply test-patch &&
+ 'git apply -R --index --stat --summary --apply test-patch &&
test "$(cat foo)" = "This is foo"'
cat >test-patch <<\EOF
@@ -56,7 +56,7 @@ copy to bar
EOF
test_expect_success 'apply copy' \
- 'git-apply --index --stat --summary --apply test-patch &&
+ 'git apply --index --stat --summary --apply test-patch &&
test "$(cat bar)" = "This is bar" -a "$(cat foo)" = "This is foo"'
test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index e2b1124c78..011126f336 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply handling binary patches
+test_description='git apply handling binary patches
'
. ./test-lib.sh
@@ -20,55 +20,55 @@ EOF
cat file1 >file2
cat file1 >file4
-git-update-index --add --remove file1 file2 file4
+git update-index --add --remove file1 file2 file4
git-commit -m 'Initial Version' 2>/dev/null
git-checkout -b binary
tr 'x' '\0' <file1 >file3
cat file3 >file4
-git-add file2
+git add file2
tr '\0' 'v' <file3 >file1
rm -f file2
-git-update-index --add --remove file1 file2 file3 file4
+git update-index --add --remove file1 file2 file3 file4
git-commit -m 'Second Version'
-git-diff-tree -p master binary >B.diff
-git-diff-tree -p -C master binary >C.diff
+git diff-tree -p master binary >B.diff
+git diff-tree -p -C master binary >C.diff
-git-diff-tree -p --binary master binary >BF.diff
-git-diff-tree -p --binary -C master binary >CF.diff
+git diff-tree -p --binary master binary >BF.diff
+git diff-tree -p --binary -C master binary >CF.diff
test_expect_success 'stat binary diff -- should not fail.' \
'git-checkout master
- git-apply --stat --summary B.diff'
+ git apply --stat --summary B.diff'
test_expect_success 'stat binary diff (copy) -- should not fail.' \
'git-checkout master
- git-apply --stat --summary C.diff'
+ git apply --stat --summary C.diff'
test_expect_failure 'check binary diff -- should fail.' \
'git-checkout master
- git-apply --check B.diff'
+ git apply --check B.diff'
test_expect_failure 'check binary diff (copy) -- should fail.' \
'git-checkout master
- git-apply --check C.diff'
+ git apply --check C.diff'
test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
'git-checkout master
- git-apply --check --allow-binary-replacement B.diff'
+ git apply --check --allow-binary-replacement B.diff'
test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
'git-checkout master
- git-apply --check --allow-binary-replacement C.diff'
+ git apply --check --allow-binary-replacement C.diff'
test_expect_success 'check binary diff with replacement.' \
'git-checkout master
- git-apply --check --allow-binary-replacement BF.diff'
+ git apply --check --allow-binary-replacement BF.diff'
test_expect_success 'check binary diff with replacement (copy).' \
'git-checkout master
- git-apply --check --allow-binary-replacement CF.diff'
+ git apply --check --allow-binary-replacement CF.diff'
# Now we start applying them.
@@ -80,36 +80,36 @@ do_reset () {
test_expect_failure 'apply binary diff -- should fail.' \
'do_reset
- git-apply B.diff'
+ git apply B.diff'
test_expect_failure 'apply binary diff -- should fail.' \
'do_reset
- git-apply --index B.diff'
+ git apply --index B.diff'
test_expect_failure 'apply binary diff (copy) -- should fail.' \
'do_reset
- git-apply C.diff'
+ git apply C.diff'
test_expect_failure 'apply binary diff (copy) -- should fail.' \
'do_reset
- git-apply --index C.diff'
+ git apply --index C.diff'
test_expect_success 'apply binary diff without replacement.' \
'do_reset
- git-apply BF.diff'
+ git apply BF.diff'
test_expect_success 'apply binary diff without replacement (copy).' \
'do_reset
- git-apply CF.diff'
+ git apply CF.diff'
test_expect_success 'apply binary diff.' \
'do_reset
- git-apply --allow-binary-replacement --index BF.diff &&
- test -z "$(git-diff --name-status binary)"'
+ git apply --allow-binary-replacement --index BF.diff &&
+ test -z "$(git diff --name-status binary)"'
test_expect_success 'apply binary diff (copy).' \
'do_reset
- git-apply --allow-binary-replacement --index CF.diff &&
- test -z "$(git-diff --name-status binary)"'
+ git apply --allow-binary-replacement --index CF.diff &&
+ test -z "$(git diff --name-status binary)"'
test_done
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
index a5fb3ea40e..64f34e3298 100755
--- a/t/t4104-apply-boundary.sh
+++ b/t/t4104-apply-boundary.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply boundary tests
+test_description='git apply boundary tests
'
. ./test-lib.sh
diff --git a/t/t4109-apply-multifrag.sh b/t/t4109-apply-multifrag.sh
index 5988e1ae4c..bd40a218cd 100755
--- a/t/t4109-apply-multifrag.sh
+++ b/t/t4109-apply-multifrag.sh
@@ -4,7 +4,7 @@
# Copyright (c) 2005 Robert Fitzsimons
#
-test_description='git-apply test patches with multiple fragments.
+test_description='git apply test patches with multiple fragments.
'
. ./test-lib.sh
@@ -138,8 +138,8 @@ diff --git a/main.c b/main.c
EOF
-test_expect_success "S = git-apply (1)" \
- 'git-apply patch1.patch patch2.patch'
+test_expect_success "S = git apply (1)" \
+ 'git apply patch1.patch patch2.patch'
mv main.c main.c.git
test_expect_success "S = patch (1)" \
@@ -150,8 +150,8 @@ test_expect_success "S = cmp (1)" \
rm -f main.c main.c.git
-test_expect_success "S = git-apply (2)" \
- 'git-apply patch1.patch patch2.patch patch3.patch'
+test_expect_success "S = git apply (2)" \
+ 'git apply patch1.patch patch2.patch patch3.patch'
mv main.c main.c.git
test_expect_success "S = patch (2)" \
@@ -162,8 +162,8 @@ test_expect_success "S = cmp (2)" \
rm -f main.c main.c.git
-test_expect_success "S = git-apply (3)" \
- 'git-apply patch1.patch patch4.patch'
+test_expect_success "S = git apply (3)" \
+ 'git apply patch1.patch patch4.patch'
mv main.c main.c.git
test_expect_success "S = patch (3)" \
diff --git a/t/t4110-apply-scan.sh b/t/t4110-apply-scan.sh
index 005f744816..db60652a37 100755
--- a/t/t4110-apply-scan.sh
+++ b/t/t4110-apply-scan.sh
@@ -4,7 +4,7 @@
# Copyright (c) 2005 Robert Fitzsimons
#
-test_description='git-apply test for patches which require scanning forwards and backwards.
+test_description='git apply test for patches which require scanning forwards and backwards.
'
. ./test-lib.sh
@@ -86,8 +86,8 @@ diff --git a/new.txt b/new.txt
+c2222
EOF
-test_expect_success "S = git-apply scan" \
- 'git-apply patch1.patch patch2.patch patch3.patch patch4.patch patch5.patch'
+test_expect_success "S = git apply scan" \
+ 'git apply patch1.patch patch2.patch patch3.patch patch4.patch patch5.patch'
mv new.txt apply.txt
test_expect_success "S = patch scan" \
@@ -98,4 +98,3 @@ test_expect_success "S = cmp" \
'cmp apply.txt patch.txt'
test_done
-
diff --git a/t/t4112-apply-renames.sh b/t/t4112-apply-renames.sh
index 69e9603c78..70a1859503 100755
--- a/t/t4112-apply-renames.sh
+++ b/t/t4112-apply-renames.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply should not get confused with rename/copy.
+test_description='git apply should not get confused with rename/copy.
'
@@ -49,10 +49,10 @@ copy to include/arch/cris/klibc/archsetjmp.h
- * arch/x86_64/include/klibc/archsetjmp.h
+ * arch/cris/include/klibc/archsetjmp.h
*/
-
+
#ifndef _KLIBC_ARCHSETJMP_H
#define _KLIBC_ARCHSETJMP_H
-
+
struct __jmp_buf {
- unsigned long __rbx;
- unsigned long __rsp;
@@ -74,9 +74,9 @@ copy to include/arch/cris/klibc/archsetjmp.h
+ unsigned long __sp;
+ unsigned long __srp;
};
-
+
typedef struct __jmp_buf jmp_buf[1];
-
+
-#endif /* _SETJMP_H */
+#endif /* _KLIBC_ARCHSETJMP_H */
diff --git a/klibc/arch/x86_64/include/klibc/archsetjmp.h b/include/arch/m32r/klibc/archsetjmp.h
@@ -90,10 +90,10 @@ rename to include/arch/m32r/klibc/archsetjmp.h
- * arch/x86_64/include/klibc/archsetjmp.h
+ * arch/m32r/include/klibc/archsetjmp.h
*/
-
+
#ifndef _KLIBC_ARCHSETJMP_H
#define _KLIBC_ARCHSETJMP_H
-
+
struct __jmp_buf {
- unsigned long __rbx;
- unsigned long __rsp;
@@ -108,17 +108,17 @@ rename to include/arch/m32r/klibc/archsetjmp.h
unsigned long __r15;
- unsigned long __rip;
};
-
+
typedef struct __jmp_buf jmp_buf[1];
-
+
-#endif /* _SETJMP_H */
+#endif /* _KLIBC_ARCHSETJMP_H */
EOF
-find klibc -type f -print | xargs git-update-index --add --
+find klibc -type f -print | xargs git update-index --add --
-test_expect_success 'check rename/copy patch' 'git-apply --check patch'
+test_expect_success 'check rename/copy patch' 'git apply --check patch'
-test_expect_success 'apply rename/copy patch' 'git-apply --index patch'
+test_expect_success 'apply rename/copy patch' 'git apply --index patch'
test_done
diff --git a/t/t4113-apply-ending.sh b/t/t4113-apply-ending.sh
index 7fd0cf62ec..1c6bec044a 100755
--- a/t/t4113-apply-ending.sh
+++ b/t/t4113-apply-ending.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Catalin Marinas
#
-test_description='git-apply trying to add an ending line.
+test_description='git apply trying to add an ending line.
'
. ./test-lib.sh
@@ -25,12 +25,12 @@ echo 'b' >>file
echo 'c' >>file
test_expect_success setup \
- 'git-update-index --add file'
+ 'git update-index --add file'
# test
test_expect_failure 'apply at the end' \
- 'git-apply --index test-patch'
+ 'git apply --index test-patch'
cat >test-patch <<\EOF
diff a/file b/file
@@ -45,9 +45,9 @@ EOF
echo >file 'a
b
c'
-git-update-index file
+git update-index file
test_expect_failure 'apply at the beginning' \
- 'git-apply --index test-patch'
+ 'git apply --index test-patch'
test_done
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
index ca81d72157..55334927ab 100755
--- a/t/t4114-apply-typechange.sh
+++ b/t/t4114-apply-typechange.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Eric Wong
#
-test_description='git-apply should not get confused with type changes.
+test_description='git apply should not get confused with type changes.
'
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index b947ed83bb..a07ff42c2f 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply symlinks and partial files
+test_description='git apply symlinks and partial files
'
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index 11e02807a5..9ae2b3a8ef 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply in reverse
+test_description='git apply in reverse
'
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
index 91931f0e3f..659e17c92e 100755
--- a/t/t4117-apply-reject.sh
+++ b/t/t4117-apply-reject.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-apply with rejects
+test_description='git apply with rejects
'
diff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh
index 27cc6f2b88..1d531caf79 100755
--- a/t/t4118-apply-empty-context.sh
+++ b/t/t4118-apply-empty-context.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Junio C Hamano
#
-test_description='git-apply with new style GNU diff with empty context
+test_description='git apply with new style GNU diff with empty context
'
@@ -53,4 +53,3 @@ test_expect_success 'apply --apply' '
'
test_done
-
diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh
index 620a9207bf..65571e0549 100755
--- a/t/t4119-apply-config.sh
+++ b/t/t4119-apply-config.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2007 Junio C Hamano
#
-test_description='git-apply --whitespace=strip and configuration file.
+test_description='git apply --whitespace=strip and configuration file.
'
@@ -24,7 +24,7 @@ cat >gpatch.file <<\EOF &&
+++ file1+ 2007-02-21 01:07:44.000000000 -0800
@@ -1 +1 @@
-A
-+B
++B
EOF
sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file &&
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
index 2f672f30d4..83d4ba6798 100755
--- a/t/t4120-apply-popt.sh
+++ b/t/t4120-apply-popt.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2007 Shawn O. Pearce
#
-test_description='git-apply -p handling.'
+test_description='git apply -p handling.'
. ./test-lib.sh
diff --git a/t/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
index 2b2f1eda21..aff551a1d7 100755
--- a/t/t4121-apply-diffs.sh
+++ b/t/t4121-apply-diffs.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='git-apply for contextually independent diffs'
+test_description='git apply for contextually independent diffs'
. ./test-lib.sh
echo '1
@@ -30,4 +30,3 @@ test_expect_success \
'( git diff test~2 test~1; git diff test~1 test~0 )| git apply'
test_done
-
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 3ddfe64b02..841773f75f 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -53,4 +53,3 @@ test_expect_success 'check result' '
'
test_done
-
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index c64ebbb2e9..cfcdb69dc8 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Johannes E. Schindelin
#
-test_description='git-rerere
+test_description='git rerere
'
. ./test-lib.sh
@@ -39,15 +39,32 @@ sed -e 's/To die, t/To die! T/' > a1
echo "* END *" >>a1
git commit -q -a -m second
-# activate rerere
-mkdir .git/rr-cache
+test_expect_success 'nothing recorded without rerere' '
+ (rm -rf .git/rr-cache; git config rerere.enabled false) &&
+ ! git merge first &&
+ ! test -d .git/rr-cache
+'
-test_expect_failure 'conflicting merge' 'git pull . first'
+# activate rerere, old style
+test_expect_success 'conflicting merge' '
+ git reset --hard &&
+ mkdir .git/rr-cache &&
+ git config --unset rerere.enabled &&
+ ! git merge first
+'
sha1=$(sed -e 's/ .*//' .git/rr-cache/MERGE_RR)
rr=.git/rr-cache/$sha1
test_expect_success 'recorded preimage' "grep ======= $rr/preimage"
+test_expect_success 'rerere.enabled works, too' '
+ rm -rf .git/rr-cache &&
+ git config rerere.enabled true &&
+ git reset --hard &&
+ ! git merge first &&
+ grep ======= $rr/preimage
+'
+
test_expect_success 'no postimage or thisimage yet' \
"test ! -f $rr/postimage -a ! -f $rr/thisimage"
@@ -99,11 +116,12 @@ test_expect_success 'commit succeeds' \
test_expect_success 'recorded postimage' "test -f $rr/postimage"
-git checkout -b third master
-git show second^:a1 | sed 's/To die: t/To die! T/' > a1
-git commit -q -a -m third
-
-test_expect_failure 'another conflicting merge' 'git pull . first'
+test_expect_success 'another conflicting merge' '
+ git checkout -b third master &&
+ git show second^:a1 | sed "s/To die: t/To die! T/" > a1 &&
+ git commit -q -a -m third &&
+ ! git pull . first
+'
git show first:a1 | sed 's/To die: t/To die! T/' > expect
test_expect_success 'rerere kicked in' "! grep ======= a1"
@@ -147,6 +165,37 @@ test_expect_success 'garbage collection (part2)' 'git rerere gc'
test_expect_success 'old records rest in peace' \
"test ! -f $rr/preimage && test ! -f $rr2/preimage"
-test_done
+test_expect_success 'file2 added differently in two branches' '
+ git reset --hard &&
+ git checkout -b fourth &&
+ echo Hallo > file2 &&
+ git add file2 &&
+ git commit -m version1 &&
+ git checkout third &&
+ echo Bello > file2 &&
+ git add file2 &&
+ git commit -m version2 &&
+ ! git merge fourth &&
+ sha1=$(sed -e "s/ .*//" .git/rr-cache/MERGE_RR) &&
+ rr=.git/rr-cache/$sha1 &&
+ echo Cello > file2 &&
+ git add file2 &&
+ git commit -m resolution
+'
+test_expect_success 'resolution was recorded properly' '
+ git reset --hard HEAD~2 &&
+ git checkout -b fifth &&
+ echo Hallo > file3 &&
+ git add file3 &&
+ git commit -m version1 &&
+ git checkout third &&
+ echo Bello > file3 &&
+ git add file3 &&
+ git commit -m version2 &&
+ ! git merge fifth &&
+ git diff-files -q &&
+ test Cello = "$(cat file3)"
+'
+test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index a48733cee0..10a5fa9a3a 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2006 Johannes E. Schindelin
#
-test_description='git-shortlog
+test_description='git shortlog
'
. ./test-lib.sh
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index e223c074f0..1a4c53a031 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -3,7 +3,7 @@
# Copyright (C) 2005 Rene Scharfe
#
-test_description='git-tar-tree and git-get-tar-commit-id test
+test_description='git tar-tree and git get-tar-commit-id test
This test covers the topics of file contents, commit date handling and
commit id embedding:
@@ -13,13 +13,13 @@ commit id embedding:
binary file (/bin/sh). Only paths shorter than 99 characters are
used.
- git-tar-tree applies the commit date to every file in the archive it
+ git tar-tree applies the commit date to every file in the archive it
creates. The test sets the commit date to a specific value and checks
if the tar archive contains that value.
- When giving git-tar-tree a commit id (in contrast to a tree id) it
+ When giving git tar-tree a commit id (in contrast to a tree id) it
embeds this commit id into the tar archive as a comment. The test
- checks the ability of git-get-tar-commit-id to figure it out from the
+ checks the ability of git get-tar-commit-id to figure it out from the
tar file.
'
@@ -42,23 +42,23 @@ test_expect_success \
test_expect_success \
'add files to repository' \
- 'find a -type f | xargs git-update-index --add &&
- find a -type l | xargs git-update-index --add &&
- treeid=`git-write-tree` &&
+ 'find a -type f | xargs git update-index --add &&
+ find a -type l | xargs git update-index --add &&
+ treeid=`git write-tree` &&
echo $treeid >treeid &&
- git-update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
- git-commit-tree $treeid </dev/null)'
+ git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
+ git commit-tree $treeid </dev/null)'
test_expect_success \
- 'git-archive' \
- 'git-archive HEAD >b.tar'
+ 'git archive' \
+ 'git archive HEAD >b.tar'
test_expect_success \
- 'git-tar-tree' \
- 'git-tar-tree HEAD >b2.tar'
+ 'git tar-tree' \
+ 'git tar-tree HEAD >b2.tar'
test_expect_success \
- 'git-archive vs. git-tar-tree' \
+ 'git archive vs. git tar-tree' \
'diff b.tar b2.tar'
test_expect_success \
@@ -70,9 +70,9 @@ test_expect_success \
diff expected.mtime b.mtime'
test_expect_success \
- 'git-get-tar-commit-id' \
- 'git-get-tar-commit-id <b.tar >b.commitid &&
- diff .git/$(git-symbolic-ref HEAD) b.commitid'
+ 'git get-tar-commit-id' \
+ 'git get-tar-commit-id <b.tar >b.commitid &&
+ diff .git/$(git symbolic-ref HEAD) b.commitid'
test_expect_success \
'extract tar archive' \
@@ -88,8 +88,8 @@ test_expect_success \
'diff -r a b/a'
test_expect_success \
- 'git-tar-tree with prefix' \
- 'git-tar-tree HEAD prefix >c.tar'
+ 'git tar-tree with prefix' \
+ 'git tar-tree HEAD prefix >c.tar'
test_expect_success \
'extract tar archive with prefix' \
@@ -105,8 +105,15 @@ test_expect_success \
'diff -r a c/prefix/a'
test_expect_success \
- 'git-archive --format=zip' \
- 'git-archive --format=zip HEAD >d.zip'
+ 'git archive --format=zip' \
+ 'git archive --format=zip HEAD >d.zip'
+
+$UNZIP -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+ echo "Skipping ZIP tests, because unzip was not found"
+ test_done
+ exit
+fi
test_expect_success \
'extract ZIP archive' \
@@ -122,8 +129,8 @@ test_expect_success \
'diff -r a d/a'
test_expect_success \
- 'git-archive --format=zip with prefix' \
- 'git-archive --format=zip --prefix=prefix/ HEAD >e.zip'
+ 'git archive --format=zip with prefix' \
+ 'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
test_expect_success \
'extract ZIP archive with prefix' \
@@ -139,7 +146,7 @@ test_expect_success \
'diff -r a e/prefix/a'
test_expect_success \
- 'git-archive --list outside of a git repo' \
- 'GIT_DIR=some/non-existing/directory git-archive --list'
+ 'git archive --list outside of a git repo' \
+ 'GIT_DIR=some/non-existing/directory git archive --list'
test_done
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index ca96918da2..9b1a74542a 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -3,12 +3,12 @@
# Copyright (c) 2005 Junio C Hamano
#
-test_description='git-mailinfo and git-mailsplit test'
+test_description='git mailinfo and git mailsplit test'
. ./test-lib.sh
test_expect_success 'split sample box' \
- 'git-mailsplit -o. ../t5100/sample.mbox >last &&
+ 'git mailsplit -o. ../t5100/sample.mbox >last &&
last=`cat last` &&
echo total is $last &&
test `cat last` = 8'
@@ -16,7 +16,7 @@ test_expect_success 'split sample box' \
for mail in `echo 00*`
do
test_expect_success "mailinfo $mail" \
- "git-mailinfo -u msg$mail patch$mail <$mail >info$mail &&
+ "git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
echo msg &&
diff ../t5100/msg$mail msg$mail &&
echo patch &&
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index f336769836..ba7579c251 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -16,26 +16,26 @@ test_expect_success \
for i in a b c
do
dd if=/dev/zero bs=4k count=1 | tr "\\0" $i >$i &&
- git-update-index --add $i || return 1
+ git update-index --add $i || return 1
done &&
- cat c >d && echo foo >>d && git-update-index --add d &&
- tree=`git-write-tree` &&
- commit=`git-commit-tree $tree </dev/null` && {
+ cat c >d && echo foo >>d && git update-index --add d &&
+ tree=`git write-tree` &&
+ commit=`git commit-tree $tree </dev/null` && {
echo $tree &&
echo $commit &&
- git-ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
} >obj-list && {
- git-diff-tree --root -p $commit &&
+ git diff-tree --root -p $commit &&
while read object
do
- t=`git-cat-file -t $object` &&
- git-cat-file $t $object || return 1
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
done <obj-list
} >expect'
test_expect_success \
'pack without delta' \
- 'packname_1=$(git-pack-objects --window=0 test-1 <obj-list)'
+ 'packname_1=$(git pack-objects --window=0 test-1 <obj-list)'
rm -fr .git2
mkdir .git2
@@ -44,9 +44,9 @@ test_expect_success \
'unpack without delta' \
"GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- git-init &&
- git-unpack-objects -n <test-1-${packname_1}.pack &&
- git-unpack-objects <test-1-${packname_1}.pack"
+ git init &&
+ git unpack-objects -n <test-1-${packname_1}.pack &&
+ git unpack-objects <test-1-${packname_1}.pack"
unset GIT_OBJECT_DIRECTORY
cd "$TRASH/.git2"
@@ -66,7 +66,7 @@ cd "$TRASH"
test_expect_success \
'pack with REF_DELTA' \
'pwd &&
- packname_2=$(git-pack-objects test-2 <obj-list)'
+ packname_2=$(git pack-objects test-2 <obj-list)'
rm -fr .git2
mkdir .git2
@@ -75,9 +75,9 @@ test_expect_success \
'unpack with REF_DELTA' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- git-init &&
- git-unpack-objects -n <test-2-${packname_2}.pack &&
- git-unpack-objects <test-2-${packname_2}.pack'
+ git init &&
+ git unpack-objects -n <test-2-${packname_2}.pack &&
+ git unpack-objects <test-2-${packname_2}.pack'
unset GIT_OBJECT_DIRECTORY
cd "$TRASH/.git2"
@@ -96,7 +96,7 @@ cd "$TRASH"
test_expect_success \
'pack with OFS_DELTA' \
'pwd &&
- packname_3=$(git-pack-objects --delta-base-offset test-3 <obj-list)'
+ packname_3=$(git pack-objects --delta-base-offset test-3 <obj-list)'
rm -fr .git2
mkdir .git2
@@ -105,9 +105,9 @@ test_expect_success \
'unpack with OFS_DELTA' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- git-init &&
- git-unpack-objects -n <test-3-${packname_3}.pack &&
- git-unpack-objects <test-3-${packname_3}.pack'
+ git init &&
+ git unpack-objects -n <test-3-${packname_3}.pack &&
+ git unpack-objects <test-3-${packname_3}.pack'
unset GIT_OBJECT_DIRECTORY
cd "$TRASH/.git2"
@@ -137,13 +137,13 @@ test_expect_success \
'use packed objects' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- git-init &&
+ git init &&
cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
- git-diff-tree --root -p $commit &&
+ git diff-tree --root -p $commit &&
while read object
do
- t=`git-cat-file -t $object` &&
- git-cat-file $t $object || return 1
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
done <obj-list
} >current &&
diff expect current'
@@ -154,11 +154,11 @@ test_expect_success \
export GIT_OBJECT_DIRECTORY &&
rm -f .git2/objects/pack/test-* &&
cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
- git-diff-tree --root -p $commit &&
+ git diff-tree --root -p $commit &&
while read object
do
- t=`git-cat-file -t $object` &&
- git-cat-file $t $object || return 1
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
done <obj-list
} >current &&
diff expect current'
@@ -169,11 +169,11 @@ test_expect_success \
export GIT_OBJECT_DIRECTORY &&
rm -f .git2/objects/pack/test-* &&
cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
- git-diff-tree --root -p $commit &&
+ git diff-tree --root -p $commit &&
while read object
do
- t=`git-cat-file -t $object` &&
- git-cat-file $t $object || return 1
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
done <obj-list
} >current &&
diff expect current'
@@ -182,7 +182,7 @@ unset GIT_OBJECT_DIRECTORY
test_expect_success \
'verify pack' \
- 'git-verify-pack test-1-${packname_1}.idx \
+ 'git verify-pack test-1-${packname_1}.idx \
test-2-${packname_2}.idx \
test-3-${packname_3}.idx'
@@ -190,7 +190,7 @@ test_expect_success \
'corrupt a pack and see if verify catches' \
'cat test-1-${packname_1}.idx >test-3.idx &&
cat test-2-${packname_2}.pack >test-3.pack &&
- if git-verify-pack test-3.idx
+ if git verify-pack test-3.idx
then false
else :;
fi &&
@@ -198,7 +198,7 @@ test_expect_success \
: PACK_SIGNATURE &&
cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
- if git-verify-pack test-3.idx
+ if git verify-pack test-3.idx
then false
else :;
fi &&
@@ -206,7 +206,7 @@ test_expect_success \
: PACK_VERSION &&
cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
- if git-verify-pack test-3.idx
+ if git verify-pack test-3.idx
then false
else :;
fi &&
@@ -214,7 +214,7 @@ test_expect_success \
: TYPE/SIZE byte of the first packed object data &&
cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
- if git-verify-pack test-3.idx
+ if git verify-pack test-3.idx
then false
else :;
fi &&
@@ -224,7 +224,7 @@ test_expect_success \
l=`expr $l - 20` &&
cat test-1-${packname_1}.pack >test-3.pack &&
dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
- if git-verify-pack test-3.pack
+ if git verify-pack test-3.pack
then false
else :;
fi &&
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index fce77f1255..073ac0c6f9 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -13,48 +13,48 @@ test_expect_success \
do
echo $i >$i &&
test-genrandom "$i" 32768 >>$i &&
- git-update-index --add $i || return 1
+ git update-index --add $i || return 1
done &&
- echo d >d && cat c >>d && git-update-index --add d &&
- tree=`git-write-tree` &&
- commit1=`git-commit-tree $tree </dev/null` &&
- git-update-ref HEAD $commit1 &&
+ echo d >d && cat c >>d && git update-index --add d &&
+ tree=`git write-tree` &&
+ commit1=`git commit-tree $tree </dev/null` &&
+ git update-ref HEAD $commit1 &&
git-repack -a -d &&
- test "`git-count-objects`" = "0 objects, 0 kilobytes" &&
+ test "`git count-objects`" = "0 objects, 0 kilobytes" &&
pack1=`ls .git/objects/pack/*.pack` &&
test -f "$pack1"'
test_expect_success \
'verify-pack -v, defaults' \
- 'git-verify-pack -v "$pack1"'
+ 'git verify-pack -v "$pack1"'
test_expect_success \
'verify-pack -v, packedGitWindowSize == 1 page' \
- 'git-config core.packedGitWindowSize 512 &&
- git-verify-pack -v "$pack1"'
+ 'git config core.packedGitWindowSize 512 &&
+ git verify-pack -v "$pack1"'
test_expect_success \
'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' \
- 'git-config core.packedGitWindowSize 512 &&
- git-config core.packedGitLimit 512 &&
- git-verify-pack -v "$pack1"'
+ 'git config core.packedGitWindowSize 512 &&
+ git config core.packedGitLimit 512 &&
+ git verify-pack -v "$pack1"'
test_expect_success \
'repack -a -d, packedGit{WindowSize,Limit} == 1 page' \
- 'git-config core.packedGitWindowSize 512 &&
- git-config core.packedGitLimit 512 &&
- commit2=`git-commit-tree $tree -p $commit1 </dev/null` &&
- git-update-ref HEAD $commit2 &&
+ 'git config core.packedGitWindowSize 512 &&
+ git config core.packedGitLimit 512 &&
+ commit2=`git commit-tree $tree -p $commit1 </dev/null` &&
+ git update-ref HEAD $commit2 &&
git-repack -a -d &&
- test "`git-count-objects`" = "0 objects, 0 kilobytes" &&
+ test "`git count-objects`" = "0 objects, 0 kilobytes" &&
pack2=`ls .git/objects/pack/*.pack` &&
test -f "$pack2"
test "$pack1" \!= "$pack2"'
test_expect_success \
'verify-pack -v, defaults' \
- 'git-config --unset core.packedGitWindowSize &&
- git-config --unset core.packedGitLimit &&
- git-verify-pack -v "$pack2"'
+ 'git config --unset core.packedGitWindowSize &&
+ git config --unset core.packedGitLimit &&
+ git verify-pack -v "$pack2"'
test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 4d06eca6a5..4f58c4c3f9 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -9,34 +9,34 @@ test_description='pack index with 64-bit offsets and object CRC'
test_expect_success \
'setup' \
'rm -rf .git
- git-init &&
+ git init &&
i=1 &&
while test $i -le 100
do
i=`printf '%03i' $i`
echo $i >file_$i &&
test-genrandom "$i" 8192 >>file_$i &&
- git-update-index --add file_$i &&
+ git update-index --add file_$i &&
i=`expr $i + 1` || return 1
done &&
{ echo 101 && test-genrandom 100 8192; } >file_101 &&
- git-update-index --add file_101 &&
- tree=`git-write-tree` &&
- commit=`git-commit-tree $tree </dev/null` && {
+ git update-index --add file_101 &&
+ tree=`git write-tree` &&
+ commit=`git commit-tree $tree </dev/null` && {
echo $tree &&
- git-ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
} >obj-list &&
- git-update-ref HEAD $commit'
+ git update-ref HEAD $commit'
test_expect_success \
'pack-objects with index version 1' \
- 'pack1=$(git-pack-objects --index-version=1 test-1 <obj-list) &&
- git-verify-pack -v "test-1-${pack1}.pack"'
+ 'pack1=$(git pack-objects --index-version=1 test-1 <obj-list) &&
+ git verify-pack -v "test-1-${pack1}.pack"'
test_expect_success \
'pack-objects with index version 2' \
- 'pack2=$(git-pack-objects --index-version=2 test-2 <obj-list) &&
- git-verify-pack -v "test-2-${pack2}.pack"'
+ 'pack2=$(git pack-objects --index-version=2 test-2 <obj-list) &&
+ git verify-pack -v "test-2-${pack2}.pack"'
test_expect_success \
'both packs should be identical' \
@@ -61,8 +61,8 @@ test_expect_success \
test_expect_success \
'index v2: force some 64-bit offsets with pack-objects' \
- 'pack3=$(git-pack-objects --index-version=2,0x40000 test-3 <obj-list) &&
- git-verify-pack -v "test-3-${pack3}.pack"'
+ 'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list) &&
+ git verify-pack -v "test-3-${pack3}.pack"'
test_expect_failure \
'64-bit offsets: should be different from previous index v2 results' \
@@ -79,22 +79,22 @@ test_expect_success \
test_expect_success \
'[index v1] 1) stream pack to repository' \
'git-index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
- git-prune-packed &&
- git-count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
+ git prune-packed &&
+ git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
cmp "test-1-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
test_expect_success \
'[index v1] 2) create a stealth corruption in a delta base reference' \
'# this test assumes a delta smaller than 16 bytes at the end of the pack
- git-show-index <1.idx | sort -n | tail -n 1 | (
+ git show-index <1.idx | sort -n | tail -n 1 | (
read delta_offs delta_sha1 &&
- git-cat-file blob "$delta_sha1" > blob_1 &&
+ git cat-file blob "$delta_sha1" > blob_1 &&
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
if=".git/objects/pack/pack-${pack1}.idx" skip=$((256 * 4 + 4)) \
bs=1 count=20 conv=notrunc &&
- git-cat-file blob "$delta_sha1" > blob_2 )'
+ git cat-file blob "$delta_sha1" > blob_2 )'
test_expect_failure \
'[index v1] 3) corrupted delta happily returned wrong data' \
@@ -102,37 +102,37 @@ test_expect_failure \
test_expect_failure \
'[index v1] 4) confirm that the pack is actually corrupted' \
- 'git-fsck --full $commit'
+ 'git fsck --full $commit'
test_expect_success \
'[index v1] 5) pack-objects happily reuses corrupted data' \
- 'pack4=$(git-pack-objects test-4 <obj-list) &&
+ 'pack4=$(git pack-objects test-4 <obj-list) &&
test -f "test-4-${pack1}.pack"'
test_expect_failure \
'[index v1] 6) newly created pack is BAD !' \
- 'git-verify-pack -v "test-4-${pack1}.pack"'
+ 'git verify-pack -v "test-4-${pack1}.pack"'
test_expect_success \
'[index v2] 1) stream pack to repository' \
'rm -f .git/objects/pack/* &&
git-index-pack --index-version=2,0x40000 --stdin < "test-1-${pack1}.pack" &&
- git-prune-packed &&
- git-count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
+ git prune-packed &&
+ git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
cmp "test-3-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
test_expect_success \
'[index v2] 2) create a stealth corruption in a delta base reference' \
'# this test assumes a delta smaller than 16 bytes at the end of the pack
- git-show-index <1.idx | sort -n | tail -n 1 | (
+ git show-index <1.idx | sort -n | tail -n 1 | (
read delta_offs delta_sha1 delta_crc &&
- git-cat-file blob "$delta_sha1" > blob_3 &&
+ git cat-file blob "$delta_sha1" > blob_3 &&
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
if=".git/objects/pack/pack-${pack1}.idx" skip=$((8 + 256 * 4)) \
bs=1 count=20 conv=notrunc &&
- git-cat-file blob "$delta_sha1" > blob_4 )'
+ git cat-file blob "$delta_sha1" > blob_4 )'
test_expect_failure \
'[index v2] 3) corrupted delta happily returned wrong data' \
@@ -140,10 +140,10 @@ test_expect_failure \
test_expect_failure \
'[index v2] 4) confirm that the pack is actually corrupted' \
- 'git-fsck --full $commit'
+ 'git fsck --full $commit'
test_expect_failure \
'[index v2] 5) pack-objects refuses to reuse corrupted data' \
- 'git-pack-objects test-5 <obj-list'
+ 'git pack-objects test-5 <obj-list'
test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 477b267599..6c8767e1df 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -13,9 +13,9 @@ test_expect_success setup '
test_tick &&
mkdir mozart mozart/is &&
echo "Commit #0" >mozart/is/pink &&
- git-update-index --add mozart/is/pink &&
- tree=$(git-write-tree) &&
- commit=$(echo "Commit #0" | git-commit-tree $tree) &&
+ git update-index --add mozart/is/pink &&
+ tree=$(git write-tree) &&
+ commit=$(echo "Commit #0" | git commit-tree $tree) &&
zero=$commit &&
parent=$zero &&
i=0 &&
@@ -24,18 +24,18 @@ test_expect_success setup '
i=$(($i+1)) &&
test_tick &&
echo "Commit #$i" >mozart/is/pink &&
- git-update-index --add mozart/is/pink &&
- tree=$(git-write-tree) &&
- commit=$(echo "Commit #$i" | git-commit-tree $tree -p $parent) &&
- git-update-ref refs/tags/commit$i $commit &&
+ git update-index --add mozart/is/pink &&
+ tree=$(git write-tree) &&
+ commit=$(echo "Commit #$i" | git commit-tree $tree -p $parent) &&
+ git update-ref refs/tags/commit$i $commit &&
parent=$commit || return 1
done &&
- git-update-ref HEAD "$commit" &&
+ git update-ref HEAD "$commit" &&
git-clone ./. victim &&
cd victim &&
- git-log &&
+ git log &&
cd .. &&
- git-update-ref HEAD "$zero" &&
+ git update-ref HEAD "$zero" &&
parent=$zero &&
i=0 &&
while test $i -le $cnt
@@ -43,15 +43,15 @@ test_expect_success setup '
i=$(($i+1)) &&
test_tick &&
echo "Rebase #$i" >mozart/is/pink &&
- git-update-index --add mozart/is/pink &&
- tree=$(git-write-tree) &&
- commit=$(echo "Rebase #$i" | git-commit-tree $tree -p $parent) &&
- git-update-ref refs/tags/rebase$i $commit &&
+ git update-index --add mozart/is/pink &&
+ tree=$(git write-tree) &&
+ commit=$(echo "Rebase #$i" | git commit-tree $tree -p $parent) &&
+ git update-ref refs/tags/rebase$i $commit &&
parent=$commit || return 1
done &&
- git-update-ref HEAD "$commit" &&
+ git update-ref HEAD "$commit" &&
echo Rebase &&
- git-log'
+ git log'
test_expect_success 'pack the source repository' '
git repack -a -d &&
@@ -66,7 +66,7 @@ test_expect_success 'pack the destination repository' '
'
test_expect_success \
- 'pushing rewound head should not barf but require --force' '
+ 'pushing rewound head should not barf but require --force' '
# should not fail but refuse to update.
if git-send-pack ./victim/.git/ master
then
@@ -106,9 +106,9 @@ export HOME ;# this way we force the victim/.git/config to be used.
test_expect_success \
'pushing with --force should be denied with denyNonFastforwards' '
cd victim &&
- git-config receive.denyNonFastforwards true &&
+ git config receive.denyNonFastforwards true &&
cd .. &&
- git-update-ref refs/heads/master master^ || return 1
+ git update-ref refs/heads/master master^ || return 1
git-send-pack --force ./victim/.git/ master && return 1
! git diff .git/refs/heads/master victim/.git/refs/heads/master
'
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index f1c7ff0c0a..c5dd30d0a4 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -8,19 +8,19 @@ test_description='Test the update hook infrastructure.'
test_expect_success setup '
echo This is a test. >a &&
- git-update-index --add a &&
- tree0=$(git-write-tree) &&
- commit0=$(echo setup | git-commit-tree $tree0) &&
+ git update-index --add a &&
+ tree0=$(git write-tree) &&
+ commit0=$(echo setup | git commit-tree $tree0) &&
echo We hope it works. >a &&
- git-update-index a &&
- tree1=$(git-write-tree) &&
- commit1=$(echo modify | git-commit-tree $tree1 -p $commit0) &&
- git-update-ref refs/heads/master $commit0 &&
- git-update-ref refs/heads/tofail $commit1 &&
+ git update-index a &&
+ tree1=$(git write-tree) &&
+ commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
+ git update-ref refs/heads/master $commit0 &&
+ git update-ref refs/heads/tofail $commit1 &&
git-clone ./. victim &&
- GIT_DIR=victim/.git git-update-ref refs/heads/tofail $commit1 &&
- git-update-ref refs/heads/master $commit1 &&
- git-update-ref refs/heads/tofail $commit0
+ GIT_DIR=victim/.git git update-ref refs/heads/tofail $commit1 &&
+ git update-ref refs/heads/master $commit1 &&
+ git update-ref refs/heads/tofail $commit0
'
cat >victim/.git/hooks/pre-receive <<'EOF'
@@ -65,8 +65,8 @@ test_expect_failure push '
'
test_expect_success 'updated as expected' '
- test $(GIT_DIR=victim/.git git-rev-parse master) = $commit1 &&
- test $(GIT_DIR=victim/.git git-rev-parse tofail) = $commit1
+ test $(GIT_DIR=victim/.git git rev-parse master) = $commit1 &&
+ test $(GIT_DIR=victim/.git git rev-parse tofail) = $commit1
'
test_expect_success 'hooks ran' '
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 48e3d1705f..7b6798d8b5 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -25,12 +25,12 @@ add () {
done
echo "$text" > test.txt
- git-update-index --add test.txt
- tree=$(git-write-tree)
+ git update-index --add test.txt
+ tree=$(git write-tree)
# make sure timestamps are in correct order
sec=$(($sec+1))
commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \
- git-commit-tree $tree $parents 2>>log2.txt)
+ git commit-tree $tree $parents 2>>log2.txt)
export $name=$commit
echo $commit > .git/refs/heads/$branch
eval ${branch}TIP=$commit
@@ -61,19 +61,19 @@ pull_to_client () {
"git-fetch-pack -k -v .. $heads"
case "$heads" in *A*) echo $ATIP > .git/refs/heads/A;; esac
case "$heads" in *B*) echo $BTIP > .git/refs/heads/B;; esac
- git-symbolic-ref HEAD refs/heads/`echo $heads | sed -e 's/^\(.\).*$/\1/'`
+ git symbolic-ref HEAD refs/heads/`echo $heads | sed -e 's/^\(.\).*$/\1/'`
- test_expect_success "fsck" 'git-fsck --full > fsck.txt 2>&1'
+ test_expect_success "fsck" 'git fsck --full > fsck.txt 2>&1'
test_expect_success 'check downloaded results' \
'mv .git/objects/pack/pack-* . &&
p=`ls -1 pack-*.pack` &&
- git-unpack-objects <$p &&
- git-fsck --full'
+ git unpack-objects <$p &&
+ git fsck --full'
test_expect_success "new object count after $number pull" \
'idx=`echo pack-*.idx` &&
- pack_count=`git-show-index <$idx | wc -l` &&
+ pack_count=`git show-index <$idx | wc -l` &&
test $pack_count = $count'
test -z "$pack_count" && pack_count=0
if [ -z "$no_strict_count_check" ]; then
@@ -97,7 +97,7 @@ pull_to_client () {
(
mkdir client &&
cd client &&
- git-init 2>> log2.txt &&
+ git init 2>> log2.txt &&
git config transfer.unpacklimit 0
)
@@ -113,7 +113,7 @@ add B1 $A1
echo $ATIP > .git/refs/heads/A
echo $BTIP > .git/refs/heads/B
-git-symbolic-ref HEAD refs/heads/B
+git symbolic-ref HEAD refs/heads/B
pull_to_client 1st "B A" $((11*3))
@@ -129,9 +129,9 @@ pull_to_client 2nd "B" $((64*3))
pull_to_client 3rd "A" $((1*3)) # old fails
-test_expect_success "clone shallow" "git-clone --depth 2 . shallow"
+test_expect_success "clone shallow" "git-clone --depth 2 file://`pwd`/. shallow"
-(cd shallow; git-count-objects -v) > count.shallow
+(cd shallow; git count-objects -v) > count.shallow
test_expect_success "clone shallow object count" \
"test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\""
@@ -145,7 +145,7 @@ test_expect_success "clone shallow object count (part 2)" '
'
test_expect_success "fsck in shallow repo" \
- "(cd shallow; git-fsck --full)"
+ "(cd shallow; git fsck --full)"
#test_done; exit
@@ -155,7 +155,7 @@ add B67 $B66
test_expect_success "pull in shallow repo" \
"(cd shallow; git pull .. B)"
-(cd shallow; git-count-objects -v) > count.shallow
+(cd shallow; git count-objects -v) > count.shallow
test_expect_success "clone shallow object count" \
"test \"count: 6\" = \"$(grep count count.shallow)\""
@@ -165,14 +165,14 @@ add B69 $B68
test_expect_success "deepening pull in shallow repo" \
"(cd shallow; git pull --depth 4 .. B)"
-(cd shallow; git-count-objects -v) > count.shallow
+(cd shallow; git count-objects -v) > count.shallow
test_expect_success "clone shallow object count" \
"test \"count: 12\" = \"$(grep count count.shallow)\""
test_expect_success "deepening fetch in shallow repo" \
"(cd shallow; git fetch --depth 4 .. A:A)"
-(cd shallow; git-count-objects -v) > count.shallow
+(cd shallow; git count-objects -v) > count.shallow
test_expect_success "clone shallow object count" \
"test \"count: 18\" = \"$(grep count count.shallow)\""
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 426017e1d0..439430f569 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -145,4 +145,12 @@ test_expect_success 'bundle does not prerequisite objects' '
test 4 = $(git verify-pack -v bundle.pack | wc -l)
'
+test_expect_success 'bundle should be able to create a full history' '
+
+ cd "$D" &&
+ git tag -a -m '1.0' v1.0 master &&
+ git bundle create bundle4 v1.0
+
+'
+
test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
new file mode 100755
index 0000000000..ca46aafe72
--- /dev/null
+++ b/t/t5516-fetch-push.sh
@@ -0,0 +1,247 @@
+#!/bin/sh
+
+test_description='fetching and pushing, with or without wildcard'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+mk_empty () {
+ rm -fr testrepo &&
+ mkdir testrepo &&
+ (
+ cd testrepo &&
+ git init &&
+ mv .git/hooks .git/hooks-disabled
+ )
+}
+
+mk_test () {
+ mk_empty &&
+ (
+ for ref in "$@"
+ do
+ git push testrepo $the_first_commit:refs/$ref || {
+ echo "Oops, push refs/$ref failure"
+ exit 1
+ }
+ done &&
+ cd testrepo &&
+ for ref in "$@"
+ do
+ r=$(git show-ref -s --verify refs/$ref) &&
+ test "z$r" = "z$the_first_commit" || {
+ echo "Oops, refs/$ref is wrong"
+ exit 1
+ }
+ done &&
+ git fsck --full
+ )
+}
+
+check_push_result () {
+ (
+ cd testrepo &&
+ it="$1" &&
+ shift
+ for ref in "$@"
+ do
+ r=$(git show-ref -s --verify refs/$ref) &&
+ test "z$r" = "z$it" || {
+ echo "Oops, refs/$ref is wrong"
+ exit 1
+ }
+ done &&
+ git fsck --full
+ )
+}
+
+test_expect_success setup '
+
+ : >path1 &&
+ git add path1 &&
+ test_tick &&
+ git commit -a -m repo &&
+ the_first_commit=$(git show-ref -s --verify refs/heads/master) &&
+
+ : >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -a -m second &&
+ the_commit=$(git show-ref -s --verify refs/heads/master)
+
+'
+
+test_expect_success 'fetch without wildcard' '
+ mk_empty &&
+ (
+ cd testrepo &&
+ git fetch .. refs/heads/master:refs/remotes/origin/master &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'fetch with wildcard' '
+ mk_empty &&
+ (
+ cd testrepo &&
+ git config remote.up.url .. &&
+ git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+ git fetch up &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push without wildcard' '
+ mk_empty &&
+
+ git push testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with wildcard' '
+ mk_empty &&
+
+ git push testrepo "refs/heads/*:refs/remotes/origin/*" &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with matching heads' '
+
+ mk_test heads/master &&
+ git push testrepo &&
+ check_push_result $the_commit heads/master
+
+'
+
+test_expect_success 'push with no ambiguity (1)' '
+
+ mk_test heads/master &&
+ git push testrepo master:master &&
+ check_push_result $the_commit heads/master
+
+'
+
+test_expect_success 'push with no ambiguity (2)' '
+
+ mk_test remotes/origin/master &&
+ git push testrepo master:master &&
+ check_push_result $the_commit remotes/origin/master
+
+'
+
+test_expect_success 'push with weak ambiguity (1)' '
+
+ mk_test heads/master remotes/origin/master &&
+ git push testrepo master:master &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_first_commit remotes/origin/master
+
+'
+
+test_expect_success 'push with weak ambiguity (2)' '
+
+ mk_test heads/master remotes/origin/master remotes/another/master &&
+ git push testrepo master:master &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_first_commit remotes/origin/master remotes/another/master
+
+'
+
+test_expect_success 'push with ambiguity (1)' '
+
+ mk_test remotes/origin/master remotes/frotz/master &&
+ if git push testrepo master:master
+ then
+ echo "Oops, should have failed"
+ false
+ else
+ check_push_result $the_first_commit remotes/origin/master remotes/frotz/master
+ fi
+'
+
+test_expect_success 'push with ambiguity (2)' '
+
+ mk_test heads/frotz tags/frotz &&
+ if git push testrepo master:frotz
+ then
+ echo "Oops, should have failed"
+ false
+ else
+ check_push_result $the_first_commit heads/frotz tags/frotz
+ fi
+
+'
+
+test_expect_success 'push with colon-less refspec (1)' '
+
+ mk_test heads/frotz tags/frotz &&
+ git branch -f frotz master &&
+ git push testrepo frotz &&
+ check_push_result $the_commit heads/frotz &&
+ check_push_result $the_first_commit tags/frotz
+
+'
+
+test_expect_success 'push with colon-less refspec (2)' '
+
+ mk_test heads/frotz tags/frotz &&
+ if git show-ref --verify -q refs/heads/frotz
+ then
+ git branch -D frotz
+ fi &&
+ git tag -f frotz &&
+ git push testrepo frotz &&
+ check_push_result $the_commit tags/frotz &&
+ check_push_result $the_first_commit heads/frotz
+
+'
+
+test_expect_success 'push with colon-less refspec (3)' '
+
+ mk_test &&
+ if git show-ref --verify -q refs/tags/frotz
+ then
+ git tag -d frotz
+ fi &&
+ git branch -f frotz master &&
+ git push testrepo frotz &&
+ check_push_result $the_commit heads/frotz &&
+ test 1 = $( cd testrepo && git show-ref | wc -l )
+'
+
+test_expect_success 'push with colon-less refspec (4)' '
+
+ mk_test &&
+ if git show-ref --verify -q refs/heads/frotz
+ then
+ git branch -D frotz
+ fi &&
+ git tag -f frotz &&
+ git push testrepo frotz &&
+ check_push_result $the_commit tags/frotz &&
+ test 1 = $( cd testrepo && git show-ref | wc -l )
+
+'
+
+test_done
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 243212d3da..93eaf2c154 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -54,4 +54,3 @@ test_expect_success 'the default remote . should not break explicit pull' '
'
test_done
-
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index 6d43252593..4e93aaab02 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -51,7 +51,7 @@ diff expected current'
cd "$base_dir"
test_expect_success 'cloning with reference (no -l -s)' \
-'git clone --reference B A D'
+'git clone --reference B file://`pwd`/A D'
cd "$base_dir"
diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh
index b0933274db..56f9d8ae73 100755
--- a/t/t5701-clone-local.sh
+++ b/t/t5701-clone-local.sh
@@ -43,4 +43,21 @@ test_expect_success 'local clone from x.git that does not exist' '
fi
'
+test_expect_success 'With -no-hardlinks, local will make a copy' '
+ cd "$D" &&
+ git clone --bare --no-hardlinks x w &&
+ cd w &&
+ linked=$(find objects -type f ! -links 1 | wc -l) &&
+ test 0 = $linked
+'
+
+test_expect_success 'Even without -l, local will make a hardlink' '
+ cd "$D" &&
+ rm -fr w &&
+ git clone -l --bare x w &&
+ cd w &&
+ copied=$(find objects -type f -links 1 | wc -l) &&
+ test 0 = $copied
+'
+
test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index 2f8e97cb7e..699df6ebd8 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -104,4 +104,3 @@ test_valid_repo'
cd "$base_dir"
test_done
-
diff --git a/t/t6000lib.sh b/t/t6000lib.sh
index d40262159b..180633e1e0 100755
--- a/t/t6000lib.sh
+++ b/t/t6000lib.sh
@@ -17,17 +17,17 @@ unique_commit()
_text=$1
_tree=$2
shift 2
- echo $_text | git-commit-tree $(tag $_tree) "$@"
+ echo $_text | git commit-tree $(tag $_tree) "$@"
}
# Save the output of a command into the tag specified. Prepend
# a substitution script for the tag onto the front of sed.script
save_tag()
{
- _tag=$1
+ _tag=$1
[ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
shift 1
- "$@" >.git/refs/tags/$_tag
+ "$@" >.git/refs/tags/$_tag
echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
cat sed.script >> sed.script.tmp
@@ -35,7 +35,7 @@ save_tag()
mv sed.script.tmp sed.script
}
-# Replace unhelpful sha1 hashses with their symbolic equivalents
+# Replace unhelpful sha1 hashses with their symbolic equivalents
entag()
{
sed -f sed.script
@@ -62,7 +62,7 @@ as_author()
commit_date()
{
_commit=$1
- git-cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+ git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
}
on_committer_date()
@@ -103,14 +103,14 @@ name_from_description()
# Execute the test described by the first argument, by eval'ing
# command line specified in the 2nd argument. Check the status code
-# is zero and that the output matches the stream read from
+# is zero and that the output matches the stream read from
# stdin.
test_output_expect_success()
-{
+{
_description=$1
_test=$2
[ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
_name=$(echo $_description | name_from_description)
cat > $_name.expected
- test_expect_success "$_description" "check_output $_name \"$_test\""
+ test_expect_success "$_description" "check_output $_name \"$_test\""
}
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index fcb3302764..8f5de097ec 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -2,7 +2,7 @@
#
# Copyright (c) 2005 Jon Seymour
#
-test_description='Tests git-rev-list --bisect functionality'
+test_description='Tests git rev-list --bisect functionality'
. ./test-lib.sh
. ../t6000lib.sh # t6xxx specific functions
@@ -16,17 +16,17 @@ test_bisection_diff()
_max_diff=$1
_bisect_option=$2
shift 2
- _bisection=$(git-rev-list $_bisect_option "$@")
- _list_size=$(git-rev-list "$@" | wc -l)
+ _bisection=$(git rev-list $_bisect_option "$@")
+ _list_size=$(git rev-list "$@" | wc -l)
_head=$1
shift 1
- _bisection_size=$(git-rev-list $_bisection "$@" | wc -l)
+ _bisection_size=$(git rev-list $_bisection "$@" | wc -l)
[ -n "$_list_size" -a -n "$_bisection_size" ] ||
error "test_bisection_diff failed"
# Test if bisection size is close to half of list size within
# tolerance.
- #
+ #
_bisect_err=`expr $_list_size - $_bisection_size \* 2`
test "$_bisect_err" -lt 0 && _bisect_err=`expr 0 - $_bisect_err`
_bisect_err=`expr $_bisect_err / 2` ; # floor
@@ -37,8 +37,8 @@ test_bisection_diff()
}
date >path0
-git-update-index --add path0
-save_tag tree git-write-tree
+git update-index --add path0
+save_tag tree git write-tree
on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
@@ -58,7 +58,7 @@ on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3
on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
-git-update-ref HEAD $(tag l5)
+git update-ref HEAD $(tag l5)
# E
@@ -116,8 +116,8 @@ on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p
test_sequence()
{
- _bisect_option=$1
-
+ _bisect_option=$1
+
test_bisection_diff 0 $_bisect_option l0 ^root
test_bisection_diff 0 $_bisect_option l1 ^root
test_bisection_diff 0 $_bisect_option l2 ^root
@@ -152,7 +152,7 @@ test_sequence()
test_bisection_diff 0 $_bisect_option u3 ^U
test_bisection_diff 0 $_bisect_option u4 ^U
test_bisection_diff 0 $_bisect_option u5 ^U
-
+
#
# the following illustrates Linus' binary bug blatt idea.
#
@@ -163,23 +163,23 @@ test_sequence()
# the bisection point is the head - this is the bad point.
#
-test_output_expect_success "$_bisect_option l5 ^root" 'git-rev-list $_bisect_option l5 ^root' <<EOF
+test_output_expect_success "$_bisect_option l5 ^root" 'git rev-list $_bisect_option l5 ^root' <<EOF
c3
EOF
-test_output_expect_success "$_bisect_option l5 ^root ^c3" 'git-rev-list $_bisect_option l5 ^root ^c3' <<EOF
+test_output_expect_success "$_bisect_option l5 ^root ^c3" 'git rev-list $_bisect_option l5 ^root ^c3' <<EOF
b4
EOF
-test_output_expect_success "$_bisect_option l5 ^root ^c3 ^b4" 'git-rev-list $_bisect_option l5 ^c3 ^b4' <<EOF
+test_output_expect_success "$_bisect_option l5 ^root ^c3 ^b4" 'git rev-list $_bisect_option l5 ^c3 ^b4' <<EOF
l3
EOF
-test_output_expect_success "$_bisect_option l3 ^root ^c3 ^b4" 'git-rev-list $_bisect_option l3 ^root ^c3 ^b4' <<EOF
+test_output_expect_success "$_bisect_option l3 ^root ^c3 ^b4" 'git rev-list $_bisect_option l3 ^root ^c3 ^b4' <<EOF
a4
EOF
-test_output_expect_success "$_bisect_option l5 ^b3 ^a3 ^b4 ^a4" 'git-rev-list $_bisect_option l3 ^b3 ^a3 ^a4' <<EOF
+test_output_expect_success "$_bisect_option l5 ^b3 ^a3 ^b4 ^a4" 'git rev-list $_bisect_option l3 ^b3 ^a3 ^a4' <<EOF
l3
EOF
@@ -187,11 +187,11 @@ EOF
# if l3 is bad, then l4 is bad too - so advance the bad pointer by making b4 the known bad head
#
-test_output_expect_success "$_bisect_option l4 ^a2 ^a3 ^b ^a4" 'git-rev-list $_bisect_option l4 ^a2 ^a3 ^a4' <<EOF
+test_output_expect_success "$_bisect_option l4 ^a2 ^a3 ^b ^a4" 'git rev-list $_bisect_option l4 ^a2 ^a3 ^a4' <<EOF
l3
EOF
-test_output_expect_success "$_bisect_option l3 ^a2 ^a3 ^b ^a4" 'git-rev-list $_bisect_option l3 ^a2 ^a3 ^a4' <<EOF
+test_output_expect_success "$_bisect_option l3 ^a2 ^a3 ^b ^a4" 'git rev-list $_bisect_option l3 ^a2 ^a3 ^a4' <<EOF
l3
EOF
@@ -201,15 +201,15 @@ EOF
# as another example, let's consider a4 to be the bad head, in which case
#
-test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
c2
EOF
-test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2' <<EOF
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2' <<EOF
c3
EOF
-test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3' <<EOF
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3' <<EOF
a4
EOF
@@ -219,11 +219,11 @@ EOF
# or consider c3 to be the bad head
#
-test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
c2
EOF
-test_output_expect_success "$_bisect_option c3 ^a2 ^a3 ^b4 ^c2" 'git-rev-list $_bisect_option c3 ^a2 ^a3 ^b4 ^c2' <<EOF
+test_output_expect_success "$_bisect_option c3 ^a2 ^a3 ^b4 ^c2" 'git rev-list $_bisect_option c3 ^a2 ^a3 ^b4 ^c2' <<EOF
c3
EOF
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index d99a9ad39e..5daa0be8cc 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Jon Seymour
#
-test_description='Tests git-rev-list --topo-order functionality'
+test_description='Tests git rev-list --topo-order functionality'
. ./test-lib.sh
. ../t6000lib.sh # t6xxx specific functions
@@ -14,8 +14,8 @@ list_duplicates()
}
date >path0
-git-update-index --add path0
-save_tag tree git-write-tree
+git update-index --add path0
+save_tag tree git write-tree
on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
@@ -77,13 +77,13 @@ save_tag h2 unique_commit g4 tree -p g2
save_tag g3 unique_commit g5 tree -p g2
save_tag g4 unique_commit g6 tree -p g3 -p h2
-git-update-ref HEAD $(tag l5)
+git update-ref HEAD $(tag l5)
-test_output_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -d \" \"' <<EOF
+test_output_expect_success 'rev-list has correct number of entries' 'git rev-list HEAD | wc -l | tr -d \" \"' <<EOF
19
EOF
-test_output_expect_success 'simple topo order' 'git-rev-list --topo-order HEAD' <<EOF
+test_output_expect_success 'simple topo order' 'git rev-list --topo-order HEAD' <<EOF
l5
l4
l3
@@ -105,7 +105,7 @@ l0
root
EOF
-test_output_expect_success 'two diamonds topo order (g6)' 'git-rev-list --topo-order g4' <<EOF
+test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order g4' <<EOF
g4
h2
g3
@@ -115,7 +115,7 @@ g1
g0
EOF
-test_output_expect_success 'multiple heads' 'git-rev-list --topo-order a3 b3 c3' <<EOF
+test_output_expect_success 'multiple heads' 'git rev-list --topo-order a3 b3 c3' <<EOF
a3
a2
a1
@@ -132,7 +132,7 @@ l0
root
EOF
-test_output_expect_success 'multiple heads, prune at a1' 'git-rev-list --topo-order a3 b3 c3 ^a1' <<EOF
+test_output_expect_success 'multiple heads, prune at a1' 'git rev-list --topo-order a3 b3 c3 ^a1' <<EOF
a3
a2
c3
@@ -143,7 +143,7 @@ b2
b1
EOF
-test_output_expect_success 'multiple heads, prune at l1' 'git-rev-list --topo-order a3 b3 c3 ^l1' <<EOF
+test_output_expect_success 'multiple heads, prune at l1' 'git rev-list --topo-order a3 b3 c3 ^l1' <<EOF
a3
a2
a1
@@ -157,7 +157,7 @@ a0
l2
EOF
-test_output_expect_success 'cross-epoch, head at l5, prune at l1' 'git-rev-list --topo-order l5 ^l1' <<EOF
+test_output_expect_success 'cross-epoch, head at l5, prune at l1' 'git rev-list --topo-order l5 ^l1' <<EOF
l5
l4
l3
@@ -176,7 +176,7 @@ a0
l2
EOF
-test_output_expect_success 'duplicated head arguments' 'git-rev-list --topo-order l5 l5 ^l1' <<EOF
+test_output_expect_success 'duplicated head arguments' 'git rev-list --topo-order l5 l5 ^l1' <<EOF
l5
l4
l3
@@ -195,7 +195,7 @@ a0
l2
EOF
-test_output_expect_success 'prune near topo' 'git-rev-list --topo-order a4 ^c3' <<EOF
+test_output_expect_success 'prune near topo' 'git rev-list --topo-order a4 ^c3' <<EOF
a4
b4
a3
@@ -204,52 +204,52 @@ a1
b3
EOF
-test_output_expect_success "head has no parent" 'git-rev-list --topo-order root' <<EOF
+test_output_expect_success "head has no parent" 'git rev-list --topo-order root' <<EOF
root
EOF
-test_output_expect_success "two nodes - one head, one base" 'git-rev-list --topo-order l0' <<EOF
+test_output_expect_success "two nodes - one head, one base" 'git rev-list --topo-order l0' <<EOF
l0
root
EOF
-test_output_expect_success "three nodes one head, one internal, one base" 'git-rev-list --topo-order l1' <<EOF
+test_output_expect_success "three nodes one head, one internal, one base" 'git rev-list --topo-order l1' <<EOF
l1
l0
root
EOF
-test_output_expect_success "linear prune l2 ^root" 'git-rev-list --topo-order l2 ^root' <<EOF
+test_output_expect_success "linear prune l2 ^root" 'git rev-list --topo-order l2 ^root' <<EOF
l2
l1
l0
EOF
-test_output_expect_success "linear prune l2 ^l0" 'git-rev-list --topo-order l2 ^l0' <<EOF
+test_output_expect_success "linear prune l2 ^l0" 'git rev-list --topo-order l2 ^l0' <<EOF
l2
l1
EOF
-test_output_expect_success "linear prune l2 ^l1" 'git-rev-list --topo-order l2 ^l1' <<EOF
+test_output_expect_success "linear prune l2 ^l1" 'git rev-list --topo-order l2 ^l1' <<EOF
l2
EOF
-test_output_expect_success "linear prune l5 ^a4" 'git-rev-list --topo-order l5 ^a4' <<EOF
+test_output_expect_success "linear prune l5 ^a4" 'git rev-list --topo-order l5 ^a4' <<EOF
l5
l4
l3
EOF
-test_output_expect_success "linear prune l5 ^l3" 'git-rev-list --topo-order l5 ^l3' <<EOF
+test_output_expect_success "linear prune l5 ^l3" 'git rev-list --topo-order l5 ^l3' <<EOF
l5
l4
EOF
-test_output_expect_success "linear prune l5 ^l4" 'git-rev-list --topo-order l5 ^l4' <<EOF
+test_output_expect_success "linear prune l5 ^l4" 'git rev-list --topo-order l5 ^l4' <<EOF
l5
EOF
-test_output_expect_success "max-count 10 - topo order" 'git-rev-list --topo-order --max-count=10 l5' <<EOF
+test_output_expect_success "max-count 10 - topo order" 'git rev-list --topo-order --max-count=10 l5' <<EOF
l5
l4
l3
@@ -262,7 +262,7 @@ a3
a2
EOF
-test_output_expect_success "max-count 10 - non topo order" 'git-rev-list --max-count=10 l5' <<EOF
+test_output_expect_success "max-count 10 - non topo order" 'git rev-list --max-count=10 l5' <<EOF
l5
l4
l3
@@ -275,7 +275,7 @@ c2
b3
EOF
-test_output_expect_success '--max-age=c3, no --topo-order' "git-rev-list --max-age=$(commit_date c3) l5" <<EOF
+test_output_expect_success '--max-age=c3, no --topo-order' "git rev-list --max-age=$(commit_date c3) l5" <<EOF
l5
l4
l3
@@ -289,7 +289,7 @@ EOF
#
# this test fails on --topo-order - a fix is required
#
-#test_output_expect_success '--max-age=c3, --topo-order' "git-rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
+#test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
#l5
#l4
#l3
@@ -300,31 +300,31 @@ EOF
#a2
#EOF
-test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git-rev-list --topo-order a4 c3" <<EOF
+test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git rev-list --topo-order a4 c3" <<EOF
EOF
-test_output_expect_success 'one specified head reachable from another c3, a4, --topo-order' "list_duplicates git-rev-list --topo-order c3 a4" <<EOF
+test_output_expect_success 'one specified head reachable from another c3, a4, --topo-order' "list_duplicates git rev-list --topo-order c3 a4" <<EOF
EOF
-test_output_expect_success 'one specified head reachable from another a4, c3, no --topo-order' "list_duplicates git-rev-list a4 c3" <<EOF
+test_output_expect_success 'one specified head reachable from another a4, c3, no --topo-order' "list_duplicates git rev-list a4 c3" <<EOF
EOF
-test_output_expect_success 'one specified head reachable from another c3, a4, no --topo-order' "list_duplicates git-rev-list c3 a4" <<EOF
+test_output_expect_success 'one specified head reachable from another c3, a4, no --topo-order' "list_duplicates git rev-list c3 a4" <<EOF
EOF
-test_output_expect_success 'graph with c3 and a4 parents of head' "list_duplicates git-rev-list m1" <<EOF
+test_output_expect_success 'graph with c3 and a4 parents of head' "list_duplicates git rev-list m1" <<EOF
EOF
-test_output_expect_success 'graph with a4 and c3 parents of head' "list_duplicates git-rev-list m2" <<EOF
+test_output_expect_success 'graph with a4 and c3 parents of head' "list_duplicates git rev-list m2" <<EOF
EOF
-test_expect_success "head ^head --topo-order" 'git-rev-list --topo-order a3 ^a3' <<EOF
+test_expect_success "head ^head --topo-order" 'git rev-list --topo-order a3 ^a3' <<EOF
EOF
-test_expect_success "head ^head no --topo-order" 'git-rev-list a3 ^a3' <<EOF
+test_expect_success "head ^head no --topo-order" 'git rev-list a3 ^a3' <<EOF
EOF
-test_output_expect_success 'simple topo order (l5r1)' 'git-rev-list --topo-order l5r1' <<EOF
+test_output_expect_success 'simple topo order (l5r1)' 'git rev-list --topo-order l5r1' <<EOF
l5r1
r1
r0
@@ -350,7 +350,7 @@ l0
root
EOF
-test_output_expect_success 'simple topo order (r1l5)' 'git-rev-list --topo-order r1l5' <<EOF
+test_output_expect_success 'simple topo order (r1l5)' 'git rev-list --topo-order r1l5' <<EOF
r1l5
l5
l4
@@ -376,13 +376,13 @@ r0
alt_root
EOF
-test_output_expect_success "don't print things unreachable from one branch" "git-rev-list a3 ^b3 --topo-order" <<EOF
+test_output_expect_success "don't print things unreachable from one branch" "git rev-list a3 ^b3 --topo-order" <<EOF
a3
a2
a1
EOF
-test_output_expect_success "--topo-order a4 l3" "git-rev-list --topo-order a4 l3" <<EOF
+test_output_expect_success "--topo-order a4 l3" "git rev-list --topo-order a4 l3" <<EOF
l3
a4
c3
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
index 761f09b1e5..80d71988b8 100755
--- a/t/t6004-rev-list-path-optim.sh
+++ b/t/t6004-rev-list-path-optim.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='git-rev-list trivial path optimization test'
+test_description='git rev-list trivial path optimization test'
. ./test-lib.sh
@@ -12,9 +12,9 @@ initial=$(git rev-parse --verify HEAD)
'
test_expect_success path-optimization '
- commit=$(echo "Unchanged tree" | git-commit-tree "HEAD^{tree}" -p HEAD) &&
- test $(git-rev-list $commit | wc -l) = 2 &&
- test $(git-rev-list $commit -- . | wc -l) = 1
+ commit=$(echo "Unchanged tree" | git commit-tree "HEAD^{tree}" -p HEAD) &&
+ test $(git rev-list $commit | wc -l) = 2 &&
+ test $(git rev-list $commit -- . | wc -l) = 1
'
test_expect_success 'further setup' '
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index 334fccf58c..0b64822bf6 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='git-rev-list --max-count and --skip test'
+test_description='git rev-list --max-count and --skip test'
. ./test-lib.sh
@@ -13,39 +13,39 @@ test_expect_success 'setup' '
'
test_expect_success 'no options' '
- test $(git-rev-list HEAD | wc -l) = 5
+ test $(git rev-list HEAD | wc -l) = 5
'
test_expect_success '--max-count' '
- test $(git-rev-list HEAD --max-count=0 | wc -l) = 0 &&
- test $(git-rev-list HEAD --max-count=3 | wc -l) = 3 &&
- test $(git-rev-list HEAD --max-count=5 | wc -l) = 5 &&
- test $(git-rev-list HEAD --max-count=10 | wc -l) = 5
+ test $(git rev-list HEAD --max-count=0 | wc -l) = 0 &&
+ test $(git rev-list HEAD --max-count=3 | wc -l) = 3 &&
+ test $(git rev-list HEAD --max-count=5 | wc -l) = 5 &&
+ test $(git rev-list HEAD --max-count=10 | wc -l) = 5
'
test_expect_success '--max-count all forms' '
- test $(git-rev-list HEAD --max-count=1 | wc -l) = 1 &&
- test $(git-rev-list HEAD -1 | wc -l) = 1 &&
- test $(git-rev-list HEAD -n1 | wc -l) = 1 &&
- test $(git-rev-list HEAD -n 1 | wc -l) = 1
+ test $(git rev-list HEAD --max-count=1 | wc -l) = 1 &&
+ test $(git rev-list HEAD -1 | wc -l) = 1 &&
+ test $(git rev-list HEAD -n1 | wc -l) = 1 &&
+ test $(git rev-list HEAD -n 1 | wc -l) = 1
'
test_expect_success '--skip' '
- test $(git-rev-list HEAD --skip=0 | wc -l) = 5 &&
- test $(git-rev-list HEAD --skip=3 | wc -l) = 2 &&
- test $(git-rev-list HEAD --skip=5 | wc -l) = 0 &&
- test $(git-rev-list HEAD --skip=10 | wc -l) = 0
+ test $(git rev-list HEAD --skip=0 | wc -l) = 5 &&
+ test $(git rev-list HEAD --skip=3 | wc -l) = 2 &&
+ test $(git rev-list HEAD --skip=5 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=10 | wc -l) = 0
'
test_expect_success '--skip --max-count' '
- test $(git-rev-list HEAD --skip=0 --max-count=0 | wc -l) = 0 &&
- test $(git-rev-list HEAD --skip=0 --max-count=10 | wc -l) = 5 &&
- test $(git-rev-list HEAD --skip=3 --max-count=0 | wc -l) = 0 &&
- test $(git-rev-list HEAD --skip=3 --max-count=1 | wc -l) = 1 &&
- test $(git-rev-list HEAD --skip=3 --max-count=2 | wc -l) = 2 &&
- test $(git-rev-list HEAD --skip=3 --max-count=10 | wc -l) = 2 &&
- test $(git-rev-list HEAD --skip=5 --max-count=10 | wc -l) = 0 &&
- test $(git-rev-list HEAD --skip=10 --max-count=10 | wc -l) = 0
+ test $(git rev-list HEAD --skip=0 --max-count=0 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=0 --max-count=10 | wc -l) = 5 &&
+ test $(git rev-list HEAD --skip=3 --max-count=0 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=3 --max-count=1 | wc -l) = 1 &&
+ test $(git rev-list HEAD --skip=3 --max-count=2 | wc -l) = 2 &&
+ test $(git rev-list HEAD --skip=3 --max-count=10 | wc -l) = 2 &&
+ test $(git rev-list HEAD --skip=5 --max-count=10 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=10 --max-count=10 | wc -l) = 0
'
test_done
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index aab17face8..ad6d0b8c9d 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -1,12 +1,12 @@
#!/bin/sh
-test_description='git-rev-list --pretty=format test'
+test_description='git rev-list --pretty=format test'
. ./test-lib.sh
test_tick
test_expect_success 'setup' '
-touch foo && git-add foo && git-commit -m "added foo" &&
+touch foo && git add foo && git-commit -m "added foo" &&
echo changed >foo && git-commit -a -m "changed foo"
'
@@ -14,8 +14,8 @@ touch foo && git-add foo && git-commit -m "added foo" &&
test_format() {
cat >expect.$1
test_expect_success "format $1" "
-git-rev-list --pretty=format:$2 master >output.$1 &&
-git-diff expect.$1 output.$1
+git rev-list --pretty=format:$2 master >output.$1 &&
+git diff expect.$1 output.$1
"
}
@@ -113,7 +113,7 @@ and it will be encoded in iso8859-1. We should therefore
include an iso8859 character: ¡bueno!
EOF
test_expect_success 'setup complex body' '
-git-config i18n.commitencoding iso8859-1 &&
+git config i18n.commitencoding iso8859-1 &&
echo change2 >foo && git-commit -a -F commit-msg
'
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
new file mode 100755
index 0000000000..3faeae6c01
--- /dev/null
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='test git rev-list --cherry-pick -- file'
+
+. ./test-lib.sh
+
+# A---B
+# \
+# \
+# C
+#
+# B changes a file foo.c, adding a line of text. C changes foo.c as
+# well as bar.c, but the change in foo.c was identical to change B.
+
+test_expect_success setup '
+ echo Hallo > foo &&
+ git add foo &&
+ test_tick &&
+ git commit -m "A" &&
+ git tag A &&
+ git checkout -b branch &&
+ echo Bello > foo &&
+ echo Cello > bar &&
+ git add foo bar &&
+ test_tick &&
+ git commit -m "C" &&
+ git tag C &&
+ git checkout master &&
+ git checkout branch foo &&
+ test_tick &&
+ git commit -m "B" &&
+ git tag B
+'
+
+test_expect_success '--cherry-pick foo comes up empty' '
+ test -z "$(git rev-list --left-right --cherry-pick B...C -- foo)"
+'
+
+test_expect_success '--cherry-pick bar does not come up empty' '
+ ! test -z "$(git rev-list --left-right --cherry-pick B...C -- bar)"
+'
+
+test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 22e0893056..96f3d35530 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -8,7 +8,7 @@ test_description='Merge base computation.
. ./test-lib.sh
-T=$(git-write-tree)
+T=$(git write-tree)
M=1130000000
Z=+0000
@@ -29,7 +29,7 @@ doit() {
GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
- commit=$(echo $NAME | git-commit-tree $T $PARENTS)
+ commit=$(echo $NAME | git commit-tree $T $PARENTS)
echo $commit >.git/refs/tags/$NAME
echo $commit
}
@@ -51,16 +51,16 @@ G=$(doit 7 G $B $E)
H=$(doit 8 H $A $F)
test_expect_success 'compute merge-base (single)' \
- 'MB=$(git-merge-base G H) &&
- expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
+ 'MB=$(git merge-base G H) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
test_expect_success 'compute merge-base (all)' \
- 'MB=$(git-merge-base --all G H) &&
- expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
+ 'MB=$(git merge-base --all G H) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
test_expect_success 'compute merge-base with show-branch' \
- 'MB=$(git-show-branch --merge-base G H) &&
- expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
+ 'MB=$(git show-branch --merge-base G H) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
# Setup for second test to demonstrate that relying on timestamps in a
# distributed SCM to provide a _consistent_ partial ordering of commits
@@ -100,11 +100,11 @@ PL=$(doit 4 PL $L2 $C2)
PR=$(doit 4 PR $C2 $R2)
test_expect_success 'compute merge-base (single)' \
- 'MB=$(git-merge-base PL PR) &&
- expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+ 'MB=$(git merge-base PL PR) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
test_expect_success 'compute merge-base (all)' \
- 'MB=$(git-merge-base --all PL PR) &&
- expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+ 'MB=$(git merge-base --all PL PR) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index 499cafb882..0ab14a6e81 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -20,7 +20,7 @@ test_expect_success 'prepare repository' \
7
8
9" > file &&
-git add file &&
+git add file &&
git commit -m "Initial commit" file &&
git branch A &&
git branch B &&
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index c76fccfb5a..ae3b6f2831 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -56,18 +56,18 @@ printf "propter nomen suum." >> new4.txt
cp new1.txt test.txt
test_expect_success "merge without conflict" \
- "git-merge-file test.txt orig.txt new2.txt"
+ "git merge-file test.txt orig.txt new2.txt"
cp new1.txt test2.txt
test_expect_success "merge without conflict (missing LF at EOF)" \
- "git-merge-file test2.txt orig.txt new2.txt"
+ "git merge-file test2.txt orig.txt new2.txt"
test_expect_success "merge result added missing LF" \
"git diff test.txt test2.txt"
cp test.txt backup.txt
test_expect_failure "merge with conflicts" \
- "git-merge-file test.txt orig.txt new3.txt"
+ "git merge-file test.txt orig.txt new3.txt"
cat > expect.txt << EOF
<<<<<<< test.txt
@@ -90,7 +90,7 @@ test_expect_success "expected conflict markers" "git diff test.txt expect.txt"
cp backup.txt test.txt
test_expect_failure "merge with conflicts, using -L" \
- "git-merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
+ "git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
cat > expect.txt << EOF
<<<<<<< 1
@@ -114,7 +114,7 @@ test_expect_success "expected conflict markers, with -L" \
sed "s/ tu / TU /" < new1.txt > new5.txt
test_expect_failure "conflict in removed tail" \
- "git-merge-file -p orig.txt new1.txt new5.txt > out"
+ "git merge-file -p orig.txt new1.txt new5.txt > out"
cat > expect << EOF
Dominus regit me,
@@ -134,5 +134,9 @@ EOF
test_expect_success "expected conflict markers" "git diff expect out"
-test_done
+test_expect_success 'binary files cannot be merged' '
+ ! git merge-file -p orig.txt ../test4012.png new1.txt 2> merge.err &&
+ grep "Cannot merge binary files" merge.err
+'
+test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 058db9cc52..c154f03cf5 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -28,7 +28,7 @@ echo B > a1 &&
GIT_AUTHOR_DATE="2006-12-12 23:00:02" git commit -m B a1 &&
git checkout -b D A &&
-git-rev-parse B > .git/MERGE_HEAD &&
+git rev-parse B > .git/MERGE_HEAD &&
echo D > a1 &&
git update-index a1 &&
GIT_AUTHOR_DATE="2006-12-12 23:00:03" git commit -m D &&
@@ -42,19 +42,19 @@ echo C > a1 &&
GIT_AUTHOR_DATE="2006-12-12 23:00:05" git commit -m C a1 &&
git checkout -b E C &&
-git-rev-parse B > .git/MERGE_HEAD &&
+git rev-parse B > .git/MERGE_HEAD &&
echo E > a1 &&
git update-index a1 &&
GIT_AUTHOR_DATE="2006-12-12 23:00:06" git commit -m E &&
git checkout -b G E &&
-git-rev-parse A > .git/MERGE_HEAD &&
+git rev-parse A > .git/MERGE_HEAD &&
echo G > a1 &&
git update-index a1 &&
GIT_AUTHOR_DATE="2006-12-12 23:00:07" git commit -m G &&
git checkout -b F D &&
-git-rev-parse C > .git/MERGE_HEAD &&
+git rev-parse C > .git/MERGE_HEAD &&
echo F > a1 &&
git update-index a1 &&
GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
diff --git a/t/t6025-merge-symlinks.sh b/t/t6025-merge-symlinks.sh
index 3c1a6972bd..950c2e9b63 100755
--- a/t/t6025-merge-symlinks.sh
+++ b/t/t6025-merge-symlinks.sh
@@ -12,22 +12,22 @@ if core.symlinks is false.'
test_expect_success \
'setup' '
-git-config core.symlinks false &&
+git config core.symlinks false &&
> file &&
-git-add file &&
+git add file &&
git-commit -m initial &&
-git-branch b-symlink &&
-git-branch b-file &&
+git branch b-symlink &&
+git branch b-file &&
l=$(echo -n file | git-hash-object -t blob -w --stdin) &&
-echo "120000 $l symlink" | git-update-index --index-info &&
+echo "120000 $l symlink" | git update-index --index-info &&
git-commit -m master &&
git-checkout b-symlink &&
l=$(echo -n file-different | git-hash-object -t blob -w --stdin) &&
-echo "120000 $l symlink" | git-update-index --index-info &&
+echo "120000 $l symlink" | git update-index --index-info &&
git-commit -m b-symlink &&
git-checkout b-file &&
echo plain-file > symlink &&
-git-add symlink &&
+git add symlink &&
git-commit -m b-file'
test_expect_failure \
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
new file mode 100755
index 0000000000..a7358f75b1
--- /dev/null
+++ b/t/t6027-merge-binary.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='ask merge-recursive to merge binary files'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ cat ../test4012.png >m &&
+ git add m &&
+ git ls-files -s | sed -e "s/ 0 / 1 /" >E1 &&
+ test_tick &&
+ git commit -m "initial" &&
+
+ git branch side &&
+ echo frotz >a &&
+ git add a &&
+ echo nitfol >>m &&
+ git add a m &&
+ git ls-files -s a >E0 &&
+ git ls-files -s m | sed -e "s/ 0 / 3 /" >E3 &&
+ test_tick &&
+ git commit -m "master adds some" &&
+
+ git checkout side &&
+ echo rezrov >>m &&
+ git add m &&
+ git ls-files -s m | sed -e "s/ 0 / 2 /" >E2 &&
+ test_tick &&
+ git commit -m "side modifies" &&
+
+ git tag anchor &&
+
+ cat E0 E1 E2 E3 >expect
+'
+
+test_expect_success resolve '
+
+ rm -f a* m* &&
+ git reset --hard anchor &&
+
+ if git merge -s resolve master
+ then
+ echo Oops, should not have succeeded
+ false
+ else
+ git ls-files -s >current
+ diff -u current expect
+ fi
+'
+
+test_expect_success recursive '
+
+ rm -f a* m* &&
+ git reset --hard anchor &&
+
+ if git merge -s recursive master
+ then
+ echo Oops, should not have succeeded
+ false
+ else
+ git ls-files -s >current
+ diff -u current expect
+ fi
+'
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 30f6ade13f..03cdba5808 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -102,4 +102,3 @@ test_expect_success \
#
#
test_done
-
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 7d354a1fae..0724864e56 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -3,31 +3,40 @@
# Copyright (c) 2005 Johannes Schindelin
#
-test_description='Test git-rev-parse with different parent options'
+test_description='Test git rev-parse with different parent options'
. ./test-lib.sh
. ../t6000lib.sh # t6xxx specific functions
date >path0
-git-update-index --add path0
-save_tag tree git-write-tree
+git update-index --add path0
+save_tag tree git write-tree
hide_error save_tag start unique_commit "start" tree
save_tag second unique_commit "second" tree -p start
hide_error save_tag start2 unique_commit "start2" tree
save_tag two_parents unique_commit "next" tree -p second -p start2
save_tag final unique_commit "final" tree -p two_parents
-test_expect_success 'start is valid' 'git-rev-parse start | grep "^[0-9a-f]\{40\}$"'
-test_expect_success 'start^0' "test $(cat .git/refs/tags/start) = $(git-rev-parse start^0)"
-test_expect_success 'start^1 not valid' "if git-rev-parse --verify start^1; then false; else :; fi"
-test_expect_success 'second^1 = second^' "test $(git-rev-parse second^1) = $(git-rev-parse second^)"
-test_expect_success 'final^1^1^1' "test $(git-rev-parse start) = $(git-rev-parse final^1^1^1)"
-test_expect_success 'final^1^1^1 = final^^^' "test $(git-rev-parse final^1^1^1) = $(git-rev-parse final^^^)"
-test_expect_success 'final^1^2' "test $(git-rev-parse start2) = $(git-rev-parse final^1^2)"
-test_expect_success 'final^1^2 != final^1^1' "test $(git-rev-parse final^1^2) != $(git-rev-parse final^1^1)"
-test_expect_success 'final^1^3 not valid' "if git-rev-parse --verify final^1^3; then false; else :; fi"
-test_expect_failure '--verify start2^1' 'git-rev-parse --verify start2^1'
-test_expect_success '--verify start2^0' 'git-rev-parse --verify start2^0'
+test_expect_success 'start is valid' 'git rev-parse start | grep "^[0-9a-f]\{40\}$"'
+test_expect_success 'start^0' "test $(cat .git/refs/tags/start) = $(git rev-parse start^0)"
+test_expect_success 'start^1 not valid' "if git rev-parse --verify start^1; then false; else :; fi"
+test_expect_success 'second^1 = second^' "test $(git rev-parse second^1) = $(git rev-parse second^)"
+test_expect_success 'final^1^1^1' "test $(git rev-parse start) = $(git rev-parse final^1^1^1)"
+test_expect_success 'final^1^1^1 = final^^^' "test $(git rev-parse final^1^1^1) = $(git rev-parse final^^^)"
+test_expect_success 'final^1^2' "test $(git rev-parse start2) = $(git rev-parse final^1^2)"
+test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)"
+test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi"
+test_expect_failure '--verify start2^1' 'git rev-parse --verify start2^1'
+test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0'
-test_done
+test_expect_success 'repack for next test' 'git repack -a -d'
+test_expect_success 'short SHA-1 works' '
+ start=`git rev-parse --verify start` &&
+ echo $start &&
+ abbrv=`echo $start | sed s/.\$//` &&
+ echo $abbrv &&
+ abbrv=`git rev-parse --verify $abbrv` &&
+ echo $abbrv &&
+ test $start = $abbrv'
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 3e9edda1ca..ae8ee11183 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -28,39 +28,39 @@ check_describe () {
test_expect_success setup '
test_tick &&
- echo one >file && git-add file && git-commit -m initial &&
- one=$(git-rev-parse HEAD) &&
+ echo one >file && git add file && git-commit -m initial &&
+ one=$(git rev-parse HEAD) &&
test_tick &&
- echo two >file && git-add file && git-commit -m second &&
- two=$(git-rev-parse HEAD) &&
+ echo two >file && git add file && git-commit -m second &&
+ two=$(git rev-parse HEAD) &&
test_tick &&
- echo three >file && git-add file && git-commit -m third &&
+ echo three >file && git add file && git-commit -m third &&
test_tick &&
- echo A >file && git-add file && git-commit -m A &&
+ echo A >file && git add file && git-commit -m A &&
test_tick &&
git-tag -a -m A A &&
test_tick &&
- echo c >file && git-add file && git-commit -m c &&
+ echo c >file && git add file && git-commit -m c &&
test_tick &&
git-tag c &&
git reset --hard $two &&
test_tick &&
- echo B >side && git-add side && git-commit -m B &&
+ echo B >side && git add side && git-commit -m B &&
test_tick &&
git-tag -a -m B B &&
test_tick &&
git-merge -m Merged c &&
- merged=$(git-rev-parse HEAD) &&
+ merged=$(git rev-parse HEAD) &&
git reset --hard $two &&
test_tick &&
- echo D >another && git-add another && git-commit -m D &&
+ echo D >another && git add another && git-commit -m D &&
test_tick &&
git-tag -a -m D D &&
@@ -77,7 +77,7 @@ test_expect_success setup '
git-merge -m Merged $merged &&
test_tick &&
- echo X >file && echo X >side && git-add file side &&
+ echo X >file && echo X >side && git add file side &&
git-commit -m x
'
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 344033249c..8b43fb5a27 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -1,18 +1,18 @@
#!/bin/sh
-test_description='git-mv in subdirs'
+test_description='git mv in subdirs'
. ./test-lib.sh
test_expect_success \
'prepare reference tree' \
'mkdir path0 path1 &&
cp ../../COPYING path0/COPYING &&
- git-add path0/COPYING &&
+ git add path0/COPYING &&
git-commit -m add -a'
test_expect_success \
'moving the file out of subdirectory' \
- 'cd path0 && git-mv COPYING ../path1/COPYING'
+ 'cd path0 && git mv COPYING ../path1/COPYING'
# in path0 currently
test_expect_success \
@@ -21,12 +21,12 @@ test_expect_success \
test_expect_success \
'checking the commit' \
- 'git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
grep -E "^R100.+path0/COPYING.+path1/COPYING"'
test_expect_success \
'moving the file back into subdirectory' \
- 'cd path0 && git-mv ../path1/COPYING COPYING'
+ 'cd path0 && git mv ../path1/COPYING COPYING'
# in path0 currently
test_expect_success \
@@ -35,18 +35,18 @@ test_expect_success \
test_expect_success \
'checking the commit' \
- 'git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
grep -E "^R100.+path1/COPYING.+path0/COPYING"'
test_expect_success \
'adding another file' \
'cp ../../README path0/README &&
- git-add path0/README &&
+ git add path0/README &&
git-commit -m add2 -a'
test_expect_success \
'moving whole subdirectory' \
- 'git-mv path0 path2'
+ 'git mv path0 path2'
test_expect_success \
'commiting the change' \
@@ -54,18 +54,18 @@ test_expect_success \
test_expect_success \
'checking the commit' \
- 'git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
grep -E "^R100.+path0/COPYING.+path2/COPYING" &&
- git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ git diff-tree -r -M --name-status HEAD^ HEAD | \
grep -E "^R100.+path0/README.+path2/README"'
test_expect_success \
'succeed when source is a prefix of destination' \
- 'git-mv path2/COPYING path2/COPYING-renamed'
+ 'git mv path2/COPYING path2/COPYING-renamed'
test_expect_success \
'moving whole subdirectory into subdirectory' \
- 'git-mv path2 path1'
+ 'git mv path2 path1'
test_expect_success \
'commiting the change' \
@@ -73,18 +73,18 @@ test_expect_success \
test_expect_success \
'checking the commit' \
- 'git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
grep -E "^R100.+path2/COPYING.+path1/path2/COPYING" &&
- git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ git diff-tree -r -M --name-status HEAD^ HEAD | \
grep -E "^R100.+path2/README.+path1/path2/README"'
test_expect_failure \
'do not move directory over existing directory' \
- 'mkdir path0 && mkdir path0/path2 && git-mv path2 path0'
+ 'mkdir path0 && mkdir path0/path2 && git mv path2 path0'
test_expect_success \
'move into "."' \
- 'git-mv path1/path2/ .'
+ 'git mv path1/path2/ .'
test_expect_success "Michael Cassar's test case" '
rm -fr .git papers partA &&
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
new file mode 100755
index 0000000000..bc6e2ddb19
--- /dev/null
+++ b/t/t7003-filter-branch.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+
+test_description='git-filter-branch'
+. ./test-lib.sh
+
+make_commit () {
+ lower=$(echo $1 | tr A-Z a-z)
+ echo $lower > $lower
+ git add $lower
+ test_tick
+ git commit -m $1
+ git tag $1
+}
+
+test_expect_success 'setup' '
+ make_commit A
+ make_commit B
+ git checkout -b branch B
+ make_commit D
+ make_commit E
+ git checkout master
+ make_commit C
+ git checkout branch
+ git merge C
+ git tag F
+ make_commit G
+ make_commit H
+'
+
+H=$(git rev-parse H)
+
+test_expect_success 'rewrite identically' '
+ git-filter-branch branch
+'
+test_expect_success 'result is really identical' '
+ test $H = $(git rev-parse HEAD)
+'
+
+test_expect_success 'rewrite, renaming a specific file' '
+ git-filter-branch -f --tree-filter "mv d doh || :" HEAD
+'
+
+test_expect_success 'test that the file was renamed' '
+ test d = $(git show HEAD:doh)
+'
+
+git tag oldD HEAD~4
+test_expect_success 'rewrite one branch, keeping a side branch' '
+ git branch modD oldD &&
+ git-filter-branch -f --tree-filter "mv b boh || :" D..modD
+'
+
+test_expect_success 'common ancestor is still common (unchanged)' '
+ test "$(git merge-base modD D)" = "$(git rev-parse B)"
+'
+
+test_expect_success 'filter subdirectory only' '
+ mkdir subdir &&
+ touch subdir/new &&
+ git add subdir/new &&
+ test_tick &&
+ git commit -m "subdir" &&
+ echo H > a &&
+ test_tick &&
+ git commit -m "not subdir" a &&
+ echo A > subdir/new &&
+ test_tick &&
+ git commit -m "again subdir" subdir/new &&
+ git rm a &&
+ test_tick &&
+ git commit -m "again not subdir" &&
+ git branch sub &&
+ git-filter-branch -f --subdirectory-filter subdir refs/heads/sub
+'
+
+test_expect_success 'subdirectory filter result looks okay' '
+ test 2 = $(git rev-list sub | wc -l) &&
+ git show sub:new &&
+ ! git show sub:subdir
+'
+
+test_expect_success 'setup and filter history that requires --full-history' '
+ git checkout master &&
+ mkdir subdir &&
+ echo A > subdir/new &&
+ git add subdir/new &&
+ test_tick &&
+ git commit -m "subdir on master" subdir/new &&
+ git rm a &&
+ test_tick &&
+ git commit -m "again subdir on master" &&
+ git merge branch &&
+ git branch sub-master &&
+ git-filter-branch -f --subdirectory-filter subdir sub-master
+'
+
+test_expect_success 'subdirectory filter result looks okay' '
+ test 3 = $(git rev-list -1 --parents sub-master | wc -w) &&
+ git show sub-master^:new &&
+ git show sub-master^2:new &&
+ ! git show sub:subdir
+'
+
+test_expect_success 'use index-filter to move into a subdirectory' '
+ git branch directorymoved &&
+ git-filter-branch -f --index-filter \
+ "git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
+ GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
+ git update-index --index-info &&
+ mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" directorymoved &&
+ test -z "$(git diff HEAD directorymoved:newsubdir)"'
+
+test_expect_success 'stops when msg filter fails' '
+ old=$(git rev-parse HEAD) &&
+ ! git-filter-branch -f --msg-filter false &&
+ test $old = $(git rev-parse HEAD) &&
+ rm -rf .git-rewrite
+'
+
+test_expect_success 'author information is preserved' '
+ : > i &&
+ git add i &&
+ test_tick &&
+ GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
+ git branch preserved-author &&
+ git-filter-branch -f --msg-filter "cat; \
+ test \$GIT_COMMIT != $(git rev-parse master) || \
+ echo Hallo" \
+ preserved-author &&
+ test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l)
+'
+
+test_expect_success "remove a certain author's commits" '
+ echo i > i &&
+ test_tick &&
+ git commit -m i i &&
+ git branch removed-author &&
+ git-filter-branch -f --commit-filter "\
+ if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\
+ then\
+ shift;\
+ while [ -n \"\$1\" ];\
+ do\
+ shift;\
+ echo \"\$1\";\
+ shift;\
+ done;\
+ else\
+ git commit-tree \"\$@\";\
+ fi" removed-author &&
+ cnt1=$(git rev-list master | wc -l) &&
+ cnt2=$(git rev-list removed-author | wc -l) &&
+ test $cnt1 -eq $(($cnt2 + 1)) &&
+ test 0 = $(git rev-list --author="B V Uips" removed-author | wc -l)
+'
+
+test_expect_success 'barf on invalid name' '
+ ! git filter-branch -f master xy-problem &&
+ ! git filter-branch -f HEAD^
+'
+
+test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
new file mode 100755
index 0000000000..c4fa4461f7
--- /dev/null
+++ b/t/t7004-tag.sh
@@ -0,0 +1,1002 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carlos Rica
+#
+
+test_description='git-tag
+
+Tests for operations with tags.'
+
+. ./test-lib.sh
+
+# creating and listing lightweight tags:
+
+tag_exists () {
+ git show-ref --quiet --verify refs/tags/"$1"
+}
+
+# todo: git tag -l now returns always zero, when fixed, change this test
+test_expect_success 'listing all tags in an empty tree should succeed' '
+ git tag -l &&
+ git tag
+'
+
+test_expect_success 'listing all tags in an empty tree should output nothing' '
+ test `git-tag -l | wc -l` -eq 0 &&
+ test `git-tag | wc -l` -eq 0
+'
+
+test_expect_failure 'looking for a tag in an empty tree should fail' \
+ 'tag_exists mytag'
+
+test_expect_success 'creating a tag in an empty tree should fail' '
+ ! git-tag mynotag &&
+ ! tag_exists mynotag
+'
+
+test_expect_success 'creating a tag for HEAD in an empty tree should fail' '
+ ! git-tag mytaghead HEAD &&
+ ! tag_exists mytaghead
+'
+
+test_expect_success 'creating a tag for an unknown revision should fail' '
+ ! git-tag mytagnorev aaaaaaaaaaa &&
+ ! tag_exists mytagnorev
+'
+
+# commit used in the tests, test_tick is also called here to freeze the date:
+test_expect_success 'creating a tag using default HEAD should succeed' '
+ test_tick &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -m Foo &&
+ git tag mytag
+'
+
+test_expect_success 'listing all tags if one exists should succeed' '
+ git-tag -l &&
+ git-tag
+'
+
+test_expect_success 'listing all tags if one exists should output that tag' '
+ test `git-tag -l` = mytag &&
+ test `git-tag` = mytag
+'
+
+# pattern matching:
+
+test_expect_success 'listing a tag using a matching pattern should succeed' \
+ 'git-tag -l mytag'
+
+test_expect_success \
+ 'listing a tag using a matching pattern should output that tag' \
+ 'test `git-tag -l mytag` = mytag'
+
+# todo: git tag -l now returns always zero, when fixed, change this test
+test_expect_success \
+ 'listing tags using a non-matching pattern should suceed' \
+ 'git-tag -l xxx'
+
+test_expect_success \
+ 'listing tags using a non-matching pattern should output nothing' \
+ 'test `git-tag -l xxx | wc -l` -eq 0'
+
+# special cases for creating tags:
+
+test_expect_failure \
+ 'trying to create a tag with the name of one existing should fail' \
+ 'git tag mytag'
+
+test_expect_success \
+ 'trying to create a tag with a non-valid name should fail' '
+ test `git-tag -l | wc -l` -eq 1 &&
+ ! git tag "" &&
+ ! git tag .othertag &&
+ ! git tag "other tag" &&
+ ! git tag "othertag^" &&
+ ! git tag "other~tag" &&
+ test `git-tag -l | wc -l` -eq 1
+'
+
+test_expect_success 'creating a tag using HEAD directly should succeed' '
+ git tag myhead HEAD &&
+ tag_exists myhead
+'
+
+# deleting tags:
+
+test_expect_success 'trying to delete an unknown tag should fail' '
+ ! tag_exists unknown-tag &&
+ ! git-tag -d unknown-tag
+'
+
+cat >expect <<EOF
+myhead
+mytag
+EOF
+test_expect_success \
+ 'trying to delete tags without params should succeed and do nothing' '
+ git tag -l > actual && git diff expect actual &&
+ git-tag -d &&
+ git tag -l > actual && git diff expect actual
+'
+
+test_expect_success \
+ 'deleting two existing tags in one command should succeed' '
+ tag_exists mytag &&
+ tag_exists myhead &&
+ git-tag -d mytag myhead &&
+ ! tag_exists mytag &&
+ ! tag_exists myhead
+'
+
+test_expect_success \
+ 'creating a tag with the name of another deleted one should succeed' '
+ ! tag_exists mytag &&
+ git-tag mytag &&
+ tag_exists mytag
+'
+
+test_expect_success \
+ 'trying to delete two tags, existing and not, should fail in the 2nd' '
+ tag_exists mytag &&
+ ! tag_exists myhead &&
+ ! git-tag -d mytag anothertag &&
+ ! tag_exists mytag &&
+ ! tag_exists myhead
+'
+
+test_expect_failure 'trying to delete an already deleted tag should fail' \
+ 'git-tag -d mytag'
+
+# listing various tags with pattern matching:
+
+cat >expect <<EOF
+a1
+aa1
+cba
+t210
+t211
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+EOF
+test_expect_success 'listing all tags should print them ordered' '
+ git tag v1.0.1 &&
+ git tag t211 &&
+ git tag aa1 &&
+ git tag v0.2.1 &&
+ git tag v1.1.3 &&
+ git tag cba &&
+ git tag a1 &&
+ git tag v1.0 &&
+ git tag t210 &&
+ git tag -l > actual &&
+ git diff expect actual &&
+ git tag > actual &&
+ git diff expect actual
+'
+
+cat >expect <<EOF
+a1
+aa1
+cba
+EOF
+test_expect_success \
+ 'listing tags with substring as pattern must print those matching' '
+ git-tag -l a > actual &&
+ git diff expect actual
+'
+
+cat >expect <<EOF
+v0.2.1
+v1.0.1
+v1.1.3
+EOF
+test_expect_success \
+ 'listing tags with substring as pattern must print those matching' '
+ git-tag -l .1 > actual &&
+ git diff expect actual
+'
+
+cat >expect <<EOF
+t210
+t211
+EOF
+test_expect_success \
+ 'listing tags with substring as pattern must print those matching' '
+ git-tag -l t21 > actual &&
+ git diff expect actual
+'
+
+cat >expect <<EOF
+a1
+aa1
+EOF
+test_expect_success \
+ 'listing tags using a name as pattern must print those matching' '
+ git-tag -l a1 > actual &&
+ git diff expect actual
+'
+
+cat >expect <<EOF
+v1.0
+v1.0.1
+EOF
+test_expect_success \
+ 'listing tags using a name as pattern must print those matching' '
+ git-tag -l v1.0 > actual &&
+ git diff expect actual
+'
+
+cat >expect <<EOF
+v1.1.3
+EOF
+test_expect_success \
+ 'listing tags with ? in the pattern should print those matching' '
+ git-tag -l "1.1?" > actual &&
+ git diff expect actual
+'
+
+>expect
+test_expect_success \
+ 'listing tags using v.* should print nothing because none have v.' '
+ git-tag -l "v.*" > actual &&
+ git diff expect actual
+'
+
+cat >expect <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+EOF
+test_expect_success \
+ 'listing tags using v* should print only those having v' '
+ git-tag -l "v*" > actual &&
+ git diff expect actual
+'
+
+# creating and verifying lightweight tags:
+
+test_expect_success \
+ 'a non-annotated tag created without parameters should point to HEAD' '
+ git-tag non-annotated-tag &&
+ test $(git cat-file -t non-annotated-tag) = commit &&
+ test $(git rev-parse non-annotated-tag) = $(git rev-parse HEAD)
+'
+
+test_expect_failure 'trying to verify an unknown tag should fail' \
+ 'git-tag -v unknown-tag'
+
+test_expect_failure \
+ 'trying to verify a non-annotated and non-signed tag should fail' \
+ 'git-tag -v non-annotated-tag'
+
+test_expect_failure \
+ 'trying to verify many non-annotated or unknown tags, should fail' \
+ 'git-tag -v unknown-tag1 non-annotated-tag unknown-tag2'
+
+# creating annotated tags:
+
+get_tag_msg () {
+ git cat-file tag "$1" | sed -e "/BEGIN PGP/q"
+}
+
+# run test_tick before committing always gives the time in that timezone
+get_tag_header () {
+cat <<EOF
+object $2
+type $3
+tag $1
+tagger C O Mitter <committer@example.com> $4 -0700
+
+EOF
+}
+
+commit=$(git rev-parse HEAD)
+time=$test_tick
+
+get_tag_header annotated-tag $commit commit $time >expect
+echo "A message" >>expect
+test_expect_success \
+ 'creating an annotated tag with -m message should succeed' '
+ git-tag -m "A message" annotated-tag &&
+ get_tag_msg annotated-tag >actual &&
+ git diff expect actual
+'
+
+cat >msgfile <<EOF
+Another message
+in a file.
+EOF
+get_tag_header file-annotated-tag $commit commit $time >expect
+cat msgfile >>expect
+test_expect_success \
+ 'creating an annotated tag with -F messagefile should succeed' '
+ git-tag -F msgfile file-annotated-tag &&
+ get_tag_msg file-annotated-tag >actual &&
+ git diff expect actual
+'
+
+cat >inputmsg <<EOF
+A message from the
+standard input
+EOF
+get_tag_header stdin-annotated-tag $commit commit $time >expect
+cat inputmsg >>expect
+test_expect_success 'creating an annotated tag with -F - should succeed' '
+ git-tag -F - stdin-annotated-tag <inputmsg &&
+ get_tag_msg stdin-annotated-tag >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'trying to create a tag with a non-existing -F file should fail' '
+ ! test -f nonexistingfile &&
+ ! tag_exists notag &&
+ ! git-tag -F nonexistingfile notag &&
+ ! tag_exists notag
+'
+
+test_expect_success \
+ 'trying to create tags giving many -m or -F options should fail' '
+ echo "message file 1" >msgfile1 &&
+ echo "message file 2" >msgfile2 &&
+ ! tag_exists msgtag &&
+ ! git-tag -m "message 1" -m "message 2" msgtag &&
+ ! tag_exists msgtag &&
+ ! git-tag -F msgfile1 -F msgfile2 msgtag &&
+ ! tag_exists msgtag &&
+ ! git-tag -m "message 1" -F msgfile1 msgtag &&
+ ! tag_exists msgtag &&
+ ! git-tag -F msgfile1 -m "message 1" msgtag &&
+ ! tag_exists msgtag &&
+ ! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag &&
+ ! tag_exists msgtag &&
+ ! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag &&
+ ! tag_exists msgtag
+'
+
+# blank and empty messages:
+
+get_tag_header empty-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with an empty -m message should succeed' '
+ git-tag -m "" empty-annotated-tag &&
+ get_tag_msg empty-annotated-tag >actual &&
+ git diff expect actual
+'
+
+>emptyfile
+get_tag_header emptyfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with an empty -F messagefile should succeed' '
+ git-tag -F emptyfile emptyfile-annotated-tag &&
+ get_tag_msg emptyfile-annotated-tag >actual &&
+ git diff expect actual
+'
+
+printf '\n\n \n\t\nLeading blank lines\n' >blanksfile
+printf '\n\t \t \nRepeated blank lines\n' >>blanksfile
+printf '\n\n\nTrailing spaces \t \n' >>blanksfile
+printf '\nTrailing blank lines\n\n\t \n\n' >>blanksfile
+get_tag_header blanks-annotated-tag $commit commit $time >expect
+cat >>expect <<EOF
+Leading blank lines
+
+Repeated blank lines
+
+Trailing spaces
+
+Trailing blank lines
+EOF
+test_expect_success \
+ 'extra blanks in the message for an annotated tag should be removed' '
+ git-tag -F blanksfile blanks-annotated-tag &&
+ get_tag_msg blanks-annotated-tag >actual &&
+ git diff expect actual
+'
+
+get_tag_header blank-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with blank -m message with spaces should succeed' '
+ git-tag -m " " blank-annotated-tag &&
+ get_tag_msg blank-annotated-tag >actual &&
+ git diff expect actual
+'
+
+echo ' ' >blankfile
+echo '' >>blankfile
+echo ' ' >>blankfile
+get_tag_header blankfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with blank -F messagefile with spaces should succeed' '
+ git-tag -F blankfile blankfile-annotated-tag &&
+ get_tag_msg blankfile-annotated-tag >actual &&
+ git diff expect actual
+'
+
+printf ' ' >blanknonlfile
+get_tag_header blanknonlfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with -F file of spaces and no newline should succeed' '
+ git-tag -F blanknonlfile blanknonlfile-annotated-tag &&
+ get_tag_msg blanknonlfile-annotated-tag >actual &&
+ git diff expect actual
+'
+
+# messages with commented lines:
+
+cat >commentsfile <<EOF
+# A comment
+
+############
+The message.
+############
+One line.
+
+
+# commented lines
+# commented lines
+
+Another line.
+# comments
+
+Last line.
+EOF
+get_tag_header comments-annotated-tag $commit commit $time >expect
+cat >>expect <<EOF
+The message.
+One line.
+
+Another line.
+
+Last line.
+EOF
+test_expect_success \
+ 'creating a tag using a -F messagefile with #comments should succeed' '
+ git-tag -F commentsfile comments-annotated-tag &&
+ get_tag_msg comments-annotated-tag >actual &&
+ git diff expect actual
+'
+
+get_tag_header comment-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with a #comment in the -m message should succeed' '
+ git-tag -m "#comment" comment-annotated-tag &&
+ get_tag_msg comment-annotated-tag >actual &&
+ git diff expect actual
+'
+
+echo '#comment' >commentfile
+echo '' >>commentfile
+echo '####' >>commentfile
+get_tag_header commentfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with #comments in the -F messagefile should succeed' '
+ git-tag -F commentfile commentfile-annotated-tag &&
+ get_tag_msg commentfile-annotated-tag >actual &&
+ git diff expect actual
+'
+
+printf '#comment' >commentnonlfile
+get_tag_header commentnonlfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with a file of #comment and no newline should succeed' '
+ git-tag -F commentnonlfile commentnonlfile-annotated-tag &&
+ get_tag_msg commentnonlfile-annotated-tag >actual &&
+ git diff expect actual
+'
+
+# listing messages for annotated non-signed tags:
+
+test_expect_success \
+ 'listing the one-line message of a non-signed tag should succeed' '
+ git-tag -m "A msg" tag-one-line &&
+
+ echo "tag-one-line" >expect &&
+ git-tag -l | grep "^tag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l | grep "^tag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l tag-one-line >actual &&
+ git diff expect actual &&
+
+ echo "tag-one-line A msg" >expect &&
+ git-tag -n xxx -l | grep "^tag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n "" -l | grep "^tag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l | grep "^tag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n -l | grep "^tag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l tag-one-line >actual &&
+ git diff expect actual &&
+ git-tag -n 2 -l tag-one-line >actual &&
+ git diff expect actual &&
+ git-tag -n 999 -l tag-one-line >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'listing the zero-lines message of a non-signed tag should succeed' '
+ git-tag -m "" tag-zero-lines &&
+
+ echo "tag-zero-lines" >expect &&
+ git-tag -l | grep "^tag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l | grep "^tag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l tag-zero-lines >actual &&
+ git diff expect actual &&
+
+ echo "tag-zero-lines " >expect &&
+ git-tag -n 1 -l | grep "^tag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n -l | grep "^tag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l tag-zero-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 2 -l tag-zero-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 999 -l tag-zero-lines >actual &&
+ git diff expect actual
+'
+
+echo 'tag line one' >annotagmsg
+echo 'tag line two' >>annotagmsg
+echo 'tag line three' >>annotagmsg
+test_expect_success \
+ 'listing many message lines of a non-signed tag should succeed' '
+ git-tag -F annotagmsg tag-lines &&
+
+ echo "tag-lines" >expect &&
+ git-tag -l | grep "^tag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l | grep "^tag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l tag-lines >actual &&
+ git diff expect actual &&
+
+ echo "tag-lines tag line one" >expect &&
+ git-tag -n 1 -l | grep "^tag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n -l | grep "^tag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l tag-lines >actual &&
+ git diff expect actual &&
+
+ echo " tag line two" >>expect &&
+ git-tag -n 2 -l | grep "^ *tag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 2 -l tag-lines >actual &&
+ git diff expect actual &&
+
+ echo " tag line three" >>expect &&
+ git-tag -n 3 -l | grep "^ *tag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 3 -l tag-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 4 -l | grep "^ *tag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 4 -l tag-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 99 -l | grep "^ *tag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 99 -l tag-lines >actual &&
+ git diff expect actual
+'
+
+# trying to verify annotated non-signed tags:
+
+test_expect_success \
+ 'trying to verify an annotated non-signed tag should fail' '
+ tag_exists annotated-tag &&
+ ! git-tag -v annotated-tag
+'
+
+test_expect_success \
+ 'trying to verify a file-annotated non-signed tag should fail' '
+ tag_exists file-annotated-tag &&
+ ! git-tag -v file-annotated-tag
+'
+
+test_expect_success \
+ 'trying to verify two annotated non-signed tags should fail' '
+ tag_exists annotated-tag file-annotated-tag &&
+ ! git-tag -v annotated-tag file-annotated-tag
+'
+
+# creating and verifying signed tags:
+
+gpg --version >/dev/null
+if [ $? -eq 127 ]; then
+ echo "Skipping signed tags tests, because gpg was not found"
+ test_done
+ exit
+fi
+
+# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+# the gpg version 1.0.6 didn't parse trust packets correctly, so for
+# that version, creation of signed tags using the generated key fails.
+case "$(gpg --version)" in
+'gpg (GnuPG) 1.0.6'*)
+ echo "Skipping signed tag tests, because a bug in 1.0.6 version"
+ test_done
+ exit
+ ;;
+esac
+
+# key generation info: gpg --homedir t/t7004 --gen-key
+# Type DSA and Elgamal, size 2048 bits, no expiration date.
+# Name and email: C O Mitter <committer@example.com>
+# No password given, to enable non-interactive operation.
+
+cp -R ../t7004 ./gpghome
+chmod 0700 gpghome
+export GNUPGHOME="$(pwd)/gpghome"
+
+get_tag_header signed-tag $commit commit $time >expect
+echo 'A signed tag message' >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success 'creating a signed tag with -m message should succeed' '
+ git-tag -s -m "A signed tag message" signed-tag &&
+ get_tag_msg signed-tag >actual &&
+ git diff expect actual
+'
+
+cat >sigmsgfile <<EOF
+Another signed tag
+message in a file.
+EOF
+get_tag_header file-signed-tag $commit commit $time >expect
+cat sigmsgfile >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with -F messagefile should succeed' '
+ git-tag -s -F sigmsgfile file-signed-tag &&
+ get_tag_msg file-signed-tag >actual &&
+ git diff expect actual
+'
+
+cat >siginputmsg <<EOF
+A signed tag message from
+the standard input
+EOF
+get_tag_header stdin-signed-tag $commit commit $time >expect
+cat siginputmsg >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success 'creating a signed tag with -F - should succeed' '
+ git-tag -s -F - stdin-signed-tag <siginputmsg &&
+ get_tag_msg stdin-signed-tag >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'trying to create a signed tag with non-existing -F file should fail' '
+ ! test -f nonexistingfile &&
+ ! tag_exists nosigtag &&
+ ! git-tag -s -F nonexistingfile nosigtag &&
+ ! tag_exists nosigtag
+'
+
+test_expect_success 'verifying a signed tag should succeed' \
+ 'git-tag -v signed-tag'
+
+test_expect_success 'verifying two signed tags in one command should succeed' \
+ 'git-tag -v signed-tag file-signed-tag'
+
+test_expect_success \
+ 'verifying many signed and non-signed tags should fail' '
+ ! git-tag -v signed-tag annotated-tag &&
+ ! git-tag -v file-annotated-tag file-signed-tag &&
+ ! git-tag -v annotated-tag file-signed-tag file-annotated-tag &&
+ ! git-tag -v signed-tag annotated-tag file-signed-tag
+'
+
+test_expect_success 'verifying a forged tag should fail' '
+ forged=$(git cat-file tag signed-tag |
+ sed -e "s/signed-tag/forged-tag/" |
+ git mktag) &&
+ git tag forged-tag $forged &&
+ ! git-tag -v forged-tag
+'
+
+# blank and empty messages for signed tags:
+
+get_tag_header empty-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with an empty -m message should succeed' '
+ git-tag -s -m "" empty-signed-tag &&
+ get_tag_msg empty-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v empty-signed-tag
+'
+
+>sigemptyfile
+get_tag_header emptyfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with an empty -F messagefile should succeed' '
+ git-tag -s -F sigemptyfile emptyfile-signed-tag &&
+ get_tag_msg emptyfile-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v emptyfile-signed-tag
+'
+
+printf '\n\n \n\t\nLeading blank lines\n' > sigblanksfile
+printf '\n\t \t \nRepeated blank lines\n' >>sigblanksfile
+printf '\n\n\nTrailing spaces \t \n' >>sigblanksfile
+printf '\nTrailing blank lines\n\n\t \n\n' >>sigblanksfile
+get_tag_header blanks-signed-tag $commit commit $time >expect
+cat >>expect <<EOF
+Leading blank lines
+
+Repeated blank lines
+
+Trailing spaces
+
+Trailing blank lines
+EOF
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'extra blanks in the message for a signed tag should be removed' '
+ git-tag -s -F sigblanksfile blanks-signed-tag &&
+ get_tag_msg blanks-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v blanks-signed-tag
+'
+
+get_tag_header blank-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with a blank -m message should succeed' '
+ git-tag -s -m " " blank-signed-tag &&
+ get_tag_msg blank-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v blank-signed-tag
+'
+
+echo ' ' >sigblankfile
+echo '' >>sigblankfile
+echo ' ' >>sigblankfile
+get_tag_header blankfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with blank -F file with spaces should succeed' '
+ git-tag -s -F sigblankfile blankfile-signed-tag &&
+ get_tag_msg blankfile-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v blankfile-signed-tag
+'
+
+printf ' ' >sigblanknonlfile
+get_tag_header blanknonlfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with spaces and no newline should succeed' '
+ git-tag -s -F sigblanknonlfile blanknonlfile-signed-tag &&
+ get_tag_msg blanknonlfile-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v signed-tag
+'
+
+# messages with commented lines for signed tags:
+
+cat >sigcommentsfile <<EOF
+# A comment
+
+############
+The message.
+############
+One line.
+
+
+# commented lines
+# commented lines
+
+Another line.
+# comments
+
+Last line.
+EOF
+get_tag_header comments-signed-tag $commit commit $time >expect
+cat >>expect <<EOF
+The message.
+One line.
+
+Another line.
+
+Last line.
+EOF
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with a -F file with #comments should succeed' '
+ git-tag -s -F sigcommentsfile comments-signed-tag &&
+ get_tag_msg comments-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v comments-signed-tag
+'
+
+get_tag_header comment-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with #commented -m message should succeed' '
+ git-tag -s -m "#comment" comment-signed-tag &&
+ get_tag_msg comment-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v comment-signed-tag
+'
+
+echo '#comment' >sigcommentfile
+echo '' >>sigcommentfile
+echo '####' >>sigcommentfile
+get_tag_header commentfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with #commented -F messagefile should succeed' '
+ git-tag -s -F sigcommentfile commentfile-signed-tag &&
+ get_tag_msg commentfile-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v commentfile-signed-tag
+'
+
+printf '#comment' >sigcommentnonlfile
+get_tag_header commentnonlfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag with a #comment and no newline should succeed' '
+ git-tag -s -F sigcommentnonlfile commentnonlfile-signed-tag &&
+ get_tag_msg commentnonlfile-signed-tag >actual &&
+ git diff expect actual &&
+ git-tag -v commentnonlfile-signed-tag
+'
+
+# listing messages for signed tags:
+
+test_expect_success \
+ 'listing the one-line message of a signed tag should succeed' '
+ git-tag -s -m "A message line signed" stag-one-line &&
+
+ echo "stag-one-line" >expect &&
+ git-tag -l | grep "^stag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l | grep "^stag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l stag-one-line >actual &&
+ git diff expect actual &&
+
+ echo "stag-one-line A message line signed" >expect &&
+ git-tag -n xxx -l | grep "^stag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n "" -l | grep "^stag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l | grep "^stag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n -l | grep "^stag-one-line" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l stag-one-line >actual &&
+ git diff expect actual &&
+ git-tag -n 2 -l stag-one-line >actual &&
+ git diff expect actual &&
+ git-tag -n 999 -l stag-one-line >actual &&
+ git diff expect actual
+'
+
+test_expect_success \
+ 'listing the zero-lines message of a signed tag should succeed' '
+ git-tag -s -m "" stag-zero-lines &&
+
+ echo "stag-zero-lines" >expect &&
+ git-tag -l | grep "^stag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l | grep "^stag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l stag-zero-lines >actual &&
+ git diff expect actual &&
+
+ echo "stag-zero-lines " >expect &&
+ git-tag -n 1 -l | grep "^stag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n -l | grep "^stag-zero-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l stag-zero-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 2 -l stag-zero-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 999 -l stag-zero-lines >actual &&
+ git diff expect actual
+'
+
+echo 'stag line one' >sigtagmsg
+echo 'stag line two' >>sigtagmsg
+echo 'stag line three' >>sigtagmsg
+test_expect_success \
+ 'listing many message lines of a signed tag should succeed' '
+ git-tag -s -F sigtagmsg stag-lines &&
+
+ echo "stag-lines" >expect &&
+ git-tag -l | grep "^stag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l | grep "^stag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 0 -l stag-lines >actual &&
+ git diff expect actual &&
+
+ echo "stag-lines stag line one" >expect &&
+ git-tag -n 1 -l | grep "^stag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n -l | grep "^stag-lines" >actual &&
+ git diff expect actual &&
+ git-tag -n 1 -l stag-lines >actual &&
+ git diff expect actual &&
+
+ echo " stag line two" >>expect &&
+ git-tag -n 2 -l | grep "^ *stag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 2 -l stag-lines >actual &&
+ git diff expect actual &&
+
+ echo " stag line three" >>expect &&
+ git-tag -n 3 -l | grep "^ *stag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 3 -l stag-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 4 -l | grep "^ *stag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 4 -l stag-lines >actual &&
+ git diff expect actual &&
+ git-tag -n 99 -l | grep "^ *stag.line" >actual &&
+ git diff expect actual &&
+ git-tag -n 99 -l stag-lines >actual &&
+ git diff expect actual
+'
+
+# tags pointing to objects different from commits:
+
+tree=$(git rev-parse HEAD^{tree})
+blob=$(git rev-parse HEAD:foo)
+tag=$(git rev-parse signed-tag)
+
+get_tag_header tree-signed-tag $tree tree $time >expect
+echo "A message for a tree" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag pointing to a tree should succeed' '
+ git-tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} &&
+ get_tag_msg tree-signed-tag >actual &&
+ git diff expect actual
+'
+
+get_tag_header blob-signed-tag $blob blob $time >expect
+echo "A message for a blob" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag pointing to a blob should succeed' '
+ git-tag -s -m "A message for a blob" blob-signed-tag HEAD:foo &&
+ get_tag_msg blob-signed-tag >actual &&
+ git diff expect actual
+'
+
+get_tag_header tag-signed-tag $tag tag $time >expect
+echo "A message for another tag" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success \
+ 'creating a signed tag pointing to another tag should succeed' '
+ git-tag -s -m "A message for another tag" tag-signed-tag signed-tag &&
+ get_tag_msg tag-signed-tag >actual &&
+ git diff expect actual
+'
+
+# try to verify without gpg:
+
+rm -rf gpghome
+test_expect_failure \
+ 'verify signed tag fails when public key is not present' \
+ 'git-tag -v signed-tag'
+
+test_done
diff --git a/t/t7004/pubring.gpg b/t/t7004/pubring.gpg
new file mode 100644
index 0000000000..83855fa4e1
--- /dev/null
+++ b/t/t7004/pubring.gpg
Binary files differ
diff --git a/t/t7004/random_seed b/t/t7004/random_seed
new file mode 100644
index 0000000000..8fed1339ed
--- /dev/null
+++ b/t/t7004/random_seed
Binary files differ
diff --git a/t/t7004/secring.gpg b/t/t7004/secring.gpg
new file mode 100644
index 0000000000..d831cd9eb3
--- /dev/null
+++ b/t/t7004/secring.gpg
Binary files differ
diff --git a/t/t7004/trustdb.gpg b/t/t7004/trustdb.gpg
new file mode 100644
index 0000000000..abace962b8
--- /dev/null
+++ b/t/t7004/trustdb.gpg
Binary files differ
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
new file mode 100755
index 0000000000..28643b0da4
--- /dev/null
+++ b/t/t7005-editor.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+test_description='GIT_EDITOR, core.editor, and stuff'
+
+. ./test-lib.sh
+
+for i in GIT_EDITOR core_editor EDITOR VISUAL vi
+do
+ cat >e-$i.sh <<-EOF
+ echo "Edited by $i" >"\$1"
+ EOF
+ chmod +x e-$i.sh
+done
+unset vi
+mv e-vi.sh vi
+PATH=".:$PATH"
+unset EDITOR VISUAL GIT_EDITOR
+
+test_expect_success setup '
+
+ msg="Hand edited" &&
+ echo "$msg" >expect &&
+ git add vi &&
+ test_tick &&
+ git commit -m "$msg" &&
+ git show -s --pretty=oneline |
+ sed -e "s/^[0-9a-f]* //" >actual &&
+ diff actual expect
+
+'
+
+TERM=dumb
+export TERM
+test_expect_success 'dumb should error out when falling back on vi' '
+
+ if git commit --amend
+ then
+ echo "Oops?"
+ exit 1
+ else
+ : happy
+ fi
+'
+
+TERM=vt100
+export TERM
+for i in vi EDITOR VISUAL core_editor GIT_EDITOR
+do
+ echo "Edited by $i" >expect
+ unset EDITOR VISUAL GIT_EDITOR
+ git config --unset-all core.editor
+ case "$i" in
+ core_editor)
+ git config core.editor ./e-core_editor.sh
+ ;;
+ [A-Z]*)
+ eval "$i=./e-$i.sh"
+ export $i
+ ;;
+ esac
+ test_expect_success "Using $i" '
+ git commit --amend &&
+ git show -s --pretty=oneline |
+ sed -e "s/^[0-9a-f]* //" >actual &&
+ diff actual expect
+ '
+done
+
+unset EDITOR VISUAL GIT_EDITOR
+git config --unset-all core.editor
+for i in vi EDITOR VISUAL core_editor GIT_EDITOR
+do
+ echo "Edited by $i" >expect
+ case "$i" in
+ core_editor)
+ git config core.editor ./e-core_editor.sh
+ ;;
+ [A-Z]*)
+ eval "$i=./e-$i.sh"
+ export $i
+ ;;
+ esac
+ test_expect_success "Using $i (override)" '
+ git commit --amend &&
+ git show -s --pretty=oneline |
+ sed -e "s/^[0-9a-f]* //" >actual &&
+ diff actual expect
+ '
+done
+
+test_done
diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh
index a9191407f2..66d40430b2 100755
--- a/t/t7101-reset.sh
+++ b/t/t7101-reset.sh
@@ -10,7 +10,7 @@ test_expect_success \
'creating initial files' \
'mkdir path0 &&
cp ../../COPYING path0/COPYING &&
- git-add path0/COPYING &&
+ git add path0/COPYING &&
git-commit -m add -a'
test_expect_success \
@@ -21,10 +21,10 @@ test_expect_success \
cp ../../COPYING path1/COPYING &&
cp ../../COPYING COPYING &&
cp ../../COPYING path0/COPYING-TOO &&
- git-add path1/path2/COPYING &&
- git-add path1/COPYING &&
- git-add COPYING &&
- git-add path0/COPYING-TOO &&
+ git add path1/path2/COPYING &&
+ git add path1/COPYING &&
+ git add COPYING &&
+ git add path0/COPYING-TOO &&
git-commit -m change -a'
test_expect_success \
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index de70b38d1c..eb0847afe9 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -13,10 +13,10 @@ test_expect_success 'setup' '
touch src/part1.c Makefile &&
echo build >.gitignore &&
echo \*.o >>.gitignore &&
- git-add . &&
+ git add . &&
git-commit -m setup &&
touch src/part2.c README &&
- git-add .
+ git add .
'
@@ -141,7 +141,7 @@ test_expect_success 'git-clean -d -X' '
test_expect_success 'clean.requireForce' '
- git-config clean.requireForce true &&
+ git config clean.requireForce true &&
! git-clean
'
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
new file mode 100755
index 0000000000..e8ce7cdb83
--- /dev/null
+++ b/t/t7400-submodule-basic.sh
@@ -0,0 +1,178 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Lars Hjemli
+#
+
+test_description='Basic porcelain support for submodules
+
+This test tries to verify basic sanity of the init, update and status
+subcommands of git-submodule.
+'
+
+. ./test-lib.sh
+
+#
+# Test setup:
+# -create a repository in directory lib
+# -add a couple of files
+# -add directory lib to 'superproject', this creates a DIRLINK entry
+# -add a couple of regular files to enable testing of submodule filtering
+# -mv lib subrepo
+# -add an entry to .gitmodules for submodule 'example'
+#
+test_expect_success 'Prepare submodule testing' '
+ : > t &&
+ git-add t &&
+ git-commit -m "initial commit" &&
+ git branch initial HEAD &&
+ mkdir lib &&
+ cd lib &&
+ git init &&
+ echo a >a &&
+ git add a &&
+ git-commit -m "submodule commit 1" &&
+ git-tag -a -m "rev-1" rev-1 &&
+ rev1=$(git rev-parse HEAD) &&
+ if test -z "$rev1"
+ then
+ echo "[OOPS] submodule git rev-parse returned nothing"
+ false
+ fi &&
+ cd .. &&
+ echo a >a &&
+ echo z >z &&
+ git add a lib z &&
+ git-commit -m "super commit 1" &&
+ mv lib .subrepo &&
+ GIT_CONFIG=.gitmodules git config submodule.example.url git://example.com/lib.git
+'
+
+test_expect_success 'status should fail for unmapped paths' '
+ if git-submodule status
+ then
+ echo "[OOPS] submodule status succeeded"
+ false
+ elif ! GIT_CONFIG=.gitmodules git config submodule.example.path lib
+ then
+ echo "[OOPS] git config failed to update .gitmodules"
+ false
+ fi
+'
+
+test_expect_success 'status should only print one line' '
+ lines=$(git-submodule status | wc -l) &&
+ test $lines = 1
+'
+
+test_expect_success 'status should initially be "missing"' '
+ git-submodule status | grep "^-$rev1"
+'
+
+test_expect_success 'init should register submodule url in .git/config' '
+ git-submodule init &&
+ url=$(git config submodule.example.url) &&
+ if test "$url" != "git://example.com/lib.git"
+ then
+ echo "[OOPS] init succeeded but submodule url is wrong"
+ false
+ elif ! git config submodule.example.url ./.subrepo
+ then
+ echo "[OOPS] init succeeded but update of url failed"
+ false
+ fi
+'
+
+test_expect_success 'update should fail when path is used by a file' '
+ echo "hello" >lib &&
+ if git-submodule update
+ then
+ echo "[OOPS] update should have failed"
+ false
+ elif test "$(cat lib)" != "hello"
+ then
+ echo "[OOPS] update failed but lib file was molested"
+ false
+ else
+ rm lib
+ fi
+'
+
+test_expect_success 'update should fail when path is used by a nonempty directory' '
+ mkdir lib &&
+ echo "hello" >lib/a &&
+ if git-submodule update
+ then
+ echo "[OOPS] update should have failed"
+ false
+ elif test "$(cat lib/a)" != "hello"
+ then
+ echo "[OOPS] update failed but lib/a was molested"
+ false
+ else
+ rm lib/a
+ fi
+'
+
+test_expect_success 'update should work when path is an empty dir' '
+ rm -rf lib &&
+ mkdir lib &&
+ git-submodule update &&
+ head=$(cd lib && git rev-parse HEAD) &&
+ if test -z "$head"
+ then
+ echo "[OOPS] Failed to obtain submodule head"
+ false
+ elif test "$head" != "$rev1"
+ then
+ echo "[OOPS] Submodule head is $head but should have been $rev1"
+ false
+ fi
+'
+
+test_expect_success 'status should be "up-to-date" after update' '
+ git-submodule status | grep "^ $rev1"
+'
+
+test_expect_success 'status should be "modified" after submodule commit' '
+ cd lib &&
+ echo b >b &&
+ git add b &&
+ git-commit -m "submodule commit 2" &&
+ rev2=$(git rev-parse HEAD) &&
+ cd .. &&
+ if test -z "$rev2"
+ then
+ echo "[OOPS] submodule git rev-parse returned nothing"
+ false
+ fi &&
+ git-submodule status | grep "^+$rev2"
+'
+
+test_expect_success 'the --cached sha1 should be rev1' '
+ git-submodule --cached status | grep "^+$rev1"
+'
+
+test_expect_success 'update should checkout rev1' '
+ git-submodule update &&
+ head=$(cd lib && git rev-parse HEAD) &&
+ if test -z "$head"
+ then
+ echo "[OOPS] submodule git rev-parse returned nothing"
+ false
+ elif test "$head" != "$rev1"
+ then
+ echo "[OOPS] init did not checkout correct head"
+ false
+ fi
+'
+
+test_expect_success 'status should be "up-to-date" after update' '
+ git-submodule status | grep "^ $rev1"
+'
+
+test_expect_success 'checkout superproject with subproject already present' '
+ git-checkout initial &&
+ git-checkout master
+'
+
+test_done
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
new file mode 100755
index 0000000000..f11ada8617
--- /dev/null
+++ b/t/t7500-commit.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Steven Grimm
+#
+
+test_description='git-commit
+
+Tests for selected commit options.'
+
+. ./test-lib.sh
+
+commit_msg_is () {
+ test "`git log --pretty=format:%s%b -1`" = "$1"
+}
+
+# A sanity check to see if commit is working at all.
+test_expect_success 'a basic commit in an empty tree should succeed' '
+ echo content > foo &&
+ git add foo &&
+ git commit -m "initial commit"
+'
+
+test_expect_success 'nonexistent template file should return error' '
+ echo changes >> foo &&
+ git add foo &&
+ ! git commit --template "$PWD"/notexist
+'
+
+test_expect_success 'nonexistent template file in config should return error' '
+ git config commit.template "$PWD"/notexist &&
+ ! git commit &&
+ git config --unset commit.template
+'
+
+# From now on we'll use a template file that exists.
+TEMPLATE="$PWD"/template
+
+test_expect_success 'unedited template should not commit' '
+ echo "template line" > "$TEMPLATE" &&
+ ! git commit --template "$TEMPLATE"
+'
+
+test_expect_success 'unedited template with comments should not commit' '
+ echo "# comment in template" >> "$TEMPLATE" &&
+ ! git commit --template "$TEMPLATE"
+'
+
+test_expect_success 'a Signed-off-by line by itself should not commit' '
+ ! GIT_EDITOR=../t7500/add-signed-off git commit --template "$TEMPLATE"
+'
+
+test_expect_success 'adding comments to a template should not commit' '
+ ! GIT_EDITOR=../t7500/add-comments git commit --template "$TEMPLATE"
+'
+
+test_expect_success 'adding real content to a template should commit' '
+ GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" &&
+ commit_msg_is "template linecommit message"
+'
+
+test_expect_success '-t option should be short for --template' '
+ echo "short template" > "$TEMPLATE" &&
+ echo "new content" >> foo &&
+ git add foo &&
+ GIT_EDITOR=../t7500/add-content git commit -t "$TEMPLATE" &&
+ commit_msg_is "short templatecommit message"
+'
+
+test_expect_success 'config-specified template should commit' '
+ echo "new template" > "$TEMPLATE" &&
+ git config commit.template "$TEMPLATE" &&
+ echo "more content" >> foo &&
+ git add foo &&
+ GIT_EDITOR=../t7500/add-content git commit &&
+ git config --unset commit.template &&
+ commit_msg_is "new templatecommit message"
+'
+
+test_expect_success 'explicit commit message should override template' '
+ echo "still more content" >> foo &&
+ git add foo &&
+ GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \
+ -m "command line msg" &&
+ commit_msg_is "command line msg<unknown>"
+'
+
+test_expect_success 'commit message from file should override template' '
+ echo "content galore" >> foo &&
+ git add foo &&
+ echo "standard input msg" |
+ GIT_EDITOR=../t7500/add-content git commit \
+ --template "$TEMPLATE" --file - &&
+ commit_msg_is "standard input msg<unknown>"
+'
+
+test_done
diff --git a/t/t7500/add-comments b/t/t7500/add-comments
new file mode 100755
index 0000000000..a72e65c891
--- /dev/null
+++ b/t/t7500/add-comments
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "# this is a new comment" >> "$1"
+echo "# and so is this" >> "$1"
+exit 0
diff --git a/t/t7500/add-content b/t/t7500/add-content
new file mode 100755
index 0000000000..2fa3d86a10
--- /dev/null
+++ b/t/t7500/add-content
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "commit message" >> "$1"
+exit 0
diff --git a/t/t7500/add-signed-off b/t/t7500/add-signed-off
new file mode 100755
index 0000000000..e1d856af6d
--- /dev/null
+++ b/t/t7500/add-signed-off
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "Signed-off-by: foo <bar@frotz>" >> "$1"
+exit 0
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
new file mode 100644
index 0000000000..6bd3c9e3e0
--- /dev/null
+++ b/t/t7501-commit.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+#
+
+# FIXME: Test the various index usages, -i and -o, test reflog,
+# signoff, hooks
+
+test_description='git-commit'
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success \
+ "initial status" \
+ "echo 'bongo bongo' >file &&
+ git-add file && \
+ git-status | grep 'Initial commit'"
+
+test_expect_failure \
+ "fail initial amend" \
+ "git-commit --amend"
+
+test_expect_success \
+ "initial commit" \
+ "git-commit -m initial"
+
+test_expect_failure \
+ "invalid options 1" \
+ "git-commit -m foo -m bar -F file"
+
+test_expect_failure \
+ "invalid options 2" \
+ "git-commit -C HEAD -m illegal"
+
+test_expect_failure \
+ "using invalid commit with -C" \
+ "git-commit -C bogus"
+
+test_expect_failure \
+ "testing nothing to commit" \
+ "git-commit -m initial"
+
+test_expect_success \
+ "next commit" \
+ "echo 'bongo bongo bongo' >file \
+ git-commit -m next -a"
+
+test_expect_failure \
+ "commit message from non-existing file" \
+ "echo 'more bongo: bongo bongo bongo bongo' >file && \
+ git-commit -F gah -a"
+
+# Empty except stray tabs and spaces on a few lines.
+sed -e 's/@$//' >msg <<EOF
+ @
+
+ @
+Signed-off-by: hula
+EOF
+test_expect_failure \
+ "empty commit message" \
+ "git-commit -F msg -a"
+
+test_expect_success \
+ "commit message from file" \
+ "echo 'this is the commit message, coming from a file' >msg && \
+ git-commit -F msg -a"
+
+cat >editor <<\EOF
+#!/bin/sh
+sed -i -e "s/a file/an amend commit/g" $1
+EOF
+chmod 755 editor
+
+test_expect_success \
+ "amend commit" \
+ "VISUAL=./editor git-commit --amend"
+
+test_expect_failure \
+ "passing -m and -F" \
+ "echo 'enough with the bongos' >file && \
+ git-commit -F msg -m amending ."
+
+test_expect_success \
+ "using message from other commit" \
+ "git-commit -C HEAD^ ."
+
+cat >editor <<\EOF
+#!/bin/sh
+sed -i -e "s/amend/older/g" $1
+EOF
+chmod 755 editor
+
+test_expect_success \
+ "editing message from other commit" \
+ "echo 'hula hula' >file && \
+ VISUAL=./editor git-commit -c HEAD^ -a"
+
+test_expect_success \
+ "message from stdin" \
+ "echo 'silly new contents' >file && \
+ echo commit message from stdin | git-commit -F - -a"
+
+test_expect_success \
+ "overriding author from command line" \
+ "echo 'gak' >file && \
+ git-commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a"
+
+test_expect_success \
+ "interactive add" \
+ "echo 7 | git-commit --interactive | grep 'What now'"
+
+test_expect_success \
+ "showing committed revisions" \
+ "git-rev-list HEAD >current"
+
+# We could just check the head sha1, but checking each commit makes it
+# easier to isolate bugs.
+
+cat >expected <<\EOF
+72c0dc9855b0c9dadcbfd5a31cab072e0cb774ca
+9b88fc14ce6b32e3d9ee021531a54f18a5cf38a2
+3536bbb352c3a1ef9a420f5b4242d48578b92aa7
+d381ac431806e53f3dd7ac2f1ae0534f36d738b9
+4fd44095ad6334f3ef72e4c5ec8ddf108174b54a
+402702b49136e7587daa9280e91e4bb7cb2179f7
+EOF
+
+test_expect_success \
+ 'validate git-rev-list output.' \
+ 'diff current expected'
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 3a6490e8f8..eabec2e06e 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='git-annotate'
+test_description='git annotate'
. ./test-lib.sh
PROG='git annotate'
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 9777393996..92ece30fa9 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='git-blame'
+test_description='git blame'
. ./test-lib.sh
PROG='git blame -c'
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 70c3669ee8..614cf50d19 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -86,10 +86,10 @@ test_expect_failure "$name" "
rm -f '$GIT_DIR'/index &&
git checkout -f -b mybranch3 remotes/git-svn &&
rm bar/zzz &&
- git-update-index --remove bar/zzz &&
+ git update-index --remove bar/zzz &&
mkdir bar/zzz &&
echo yyy > bar/zzz/yyy &&
- git-update-index --add bar/zzz/yyy &&
+ git update-index --add bar/zzz/yyy &&
git commit -m '$name' &&
git-svn set-tree --find-copies-harder --rmdir \
remotes/git-svn..mybranch3" || true
@@ -191,8 +191,8 @@ 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 rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a &&
+ git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
git diff a b"
name='check imported tree checksums expected tree checksums'
@@ -214,7 +214,7 @@ EOF
test_expect_success "$name" "git diff a expected"
test_expect_failure 'exit if remote refs are ambigious' "
- git-config --add svn-remote.svn.fetch \
+ git config --add svn-remote.svn.fetch \
bar:refs/remotes/git-svn &&
git-svn migrate
"
@@ -222,7 +222,7 @@ test_expect_failure 'exit if remote refs are ambigious' "
test_expect_failure 'exit if init-ing a would clobber a URL' "
svnadmin create ${PWD}/svnrepo2 &&
svn mkdir -m 'mkdir bar' ${svnrepo}2/bar &&
- git-config --unset svn-remote.svn.fetch \
+ git config --unset svn-remote.svn.fetch \
'^bar:refs/remotes/git-svn$' &&
git-svn init ${svnrepo}2/bar
"
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 35aa45cb9a..d8f9cab35d 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -30,31 +30,31 @@ test_expect_success 'initialize repo' "
test_expect_success 'init and fetch a moved directory' "
git-svn init --minimize-url -i thunk $svnrepo/thunk &&
git-svn fetch -i thunk &&
- test \"\`git-rev-parse --verify refs/remotes/thunk@2\`\" \
- = \"\`git-rev-parse --verify refs/remotes/thunk~1\`\" &&
- test \"\`git-cat-file blob refs/remotes/thunk:readme |\
+ test \"\`git rev-parse --verify refs/remotes/thunk@2\`\" \
+ = \"\`git rev-parse --verify refs/remotes/thunk~1\`\" &&
+ test \"\`git cat-file blob refs/remotes/thunk:readme |\
sed -n -e '3p'\`\" = goodbye &&
- test -z \"\`git-config --get svn-remote.svn.fetch \
+ test -z \"\`git config --get svn-remote.svn.fetch \
'^trunk:refs/remotes/thunk@2$'\`\"
"
test_expect_success 'init and fetch from one svn-remote' "
- git-config svn-remote.svn.url $svnrepo &&
- git-config --add svn-remote.svn.fetch \
+ git config svn-remote.svn.url $svnrepo &&
+ git config --add svn-remote.svn.fetch \
trunk:refs/remotes/svn/trunk &&
- git-config --add svn-remote.svn.fetch \
+ git config --add svn-remote.svn.fetch \
thunk:refs/remotes/svn/thunk &&
git-svn fetch -i svn/thunk &&
- test \"\`git-rev-parse --verify refs/remotes/svn/trunk\`\" \
- = \"\`git-rev-parse --verify refs/remotes/svn/thunk~1\`\" &&
- test \"\`git-cat-file blob refs/remotes/svn/thunk:readme |\
+ test \"\`git rev-parse --verify refs/remotes/svn/trunk\`\" \
+ = \"\`git rev-parse --verify refs/remotes/svn/thunk~1\`\" &&
+ test \"\`git cat-file blob refs/remotes/svn/thunk:readme |\
sed -n -e '3p'\`\" = goodbye
"
test_expect_success 'follow deleted parent' "
svn cp -m 'resurrecting trunk as junk' \
-r2 $svnrepo/trunk $svnrepo/junk &&
- git-config --add svn-remote.svn.fetch \
+ git config --add svn-remote.svn.fetch \
junk:refs/remotes/svn/junk &&
git-svn fetch -i svn/thunk &&
git-svn fetch -i svn/junk &&
@@ -71,13 +71,13 @@ test_expect_success 'follow larger parent' "
git-svn init --minimize-url -i larger \
$svnrepo/another-larger/trunk/thunk/bump/thud &&
git-svn fetch -i larger &&
- git-rev-parse --verify refs/remotes/larger &&
- git-rev-parse --verify \
+ git rev-parse --verify refs/remotes/larger &&
+ git rev-parse --verify \
refs/remotes/larger-parent/trunk/thunk/bump/thud &&
- test \"\`git-merge-base \
+ test \"\`git merge-base \
refs/remotes/larger-parent/trunk/thunk/bump/thud \
refs/remotes/larger\`\" = \
- \"\`git-rev-parse refs/remotes/larger\`\"
+ \"\`git rev-parse refs/remotes/larger\`\"
true
"
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index dc2afdaa45..67fdf7023f 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -19,9 +19,9 @@ test_expect_success 'setup old-looking metadata' "
mv $GIT_DIR/svn/* $GIT_DIR/ &&
mv $GIT_DIR/svn/.metadata $GIT_DIR/ &&
rmdir $GIT_DIR/svn &&
- git-update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn &&
- git-update-ref refs/heads/svn-HEAD refs/remotes/git-svn &&
- git-update-ref -d refs/remotes/git-svn refs/remotes/git-svn
+ git update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn &&
+ git update-ref refs/heads/svn-HEAD refs/remotes/git-svn &&
+ git update-ref -d refs/remotes/git-svn refs/remotes/git-svn
"
head=`git rev-parse --verify refs/heads/git-svn-HEAD^0`
@@ -33,8 +33,8 @@ test_expect_success 'initialize old-style (v0) git-svn layout' "
echo $svnrepo > $GIT_DIR/svn/info/url &&
git-svn migrate &&
! test -d $GIT_DIR/git-svn &&
- git-rev-parse --verify refs/remotes/git-svn^0 &&
- git-rev-parse --verify refs/remotes/svn^0 &&
+ git rev-parse --verify refs/remotes/git-svn^0 &&
+ git rev-parse --verify refs/remotes/svn^0 &&
test \`git config --get svn-remote.svn.url\` = '$svnrepo' &&
test \`git config --get svn-remote.svn.fetch\` = \
':refs/remotes/git-svn'
@@ -42,20 +42,20 @@ test_expect_success 'initialize old-style (v0) git-svn layout' "
test_expect_success 'initialize a multi-repository repo' "
git-svn init $svnrepo -T trunk -t tags -b branches &&
- git-config --get-all svn-remote.svn.fetch > fetch.out &&
+ git config --get-all svn-remote.svn.fetch > fetch.out &&
grep '^trunk:refs/remotes/trunk$' fetch.out &&
- test -n \"\`git-config --get svn-remote.svn.branches \
+ test -n \"\`git config --get svn-remote.svn.branches \
'^branches/\*:refs/remotes/\*$'\`\" &&
- test -n \"\`git-config --get svn-remote.svn.tags \
+ test -n \"\`git config --get svn-remote.svn.tags \
'^tags/\*:refs/remotes/tags/\*$'\`\" &&
git config --unset svn-remote.svn.branches \
'^branches/\*:refs/remotes/\*$' &&
git config --unset svn-remote.svn.tags \
'^tags/\*:refs/remotes/tags/\*$' &&
- git-config --add svn-remote.svn.fetch 'branches/a:refs/remotes/a' &&
- git-config --add svn-remote.svn.fetch 'branches/b:refs/remotes/b' &&
+ git config --add svn-remote.svn.fetch 'branches/a:refs/remotes/a' &&
+ git config --add svn-remote.svn.fetch 'branches/b:refs/remotes/b' &&
for i in tags/0.1 tags/0.2 tags/0.3; do
- git-config --add svn-remote.svn.fetch \
+ git config --add svn-remote.svn.fetch \
\$i:refs/remotes/\$i || exit 1; done
"
@@ -86,8 +86,8 @@ test_expect_success 'migrate --minimize on old inited layout' "
echo $svnrepo\$path > $GIT_DIR/svn/\$ref/info/url ) || exit 1;
done &&
git-svn migrate --minimize &&
- test -z \"\`git-config -l |grep -v '^svn-remote\.git-svn\.'\`\" &&
- git-config --get-all svn-remote.svn.fetch > fetch.out &&
+ test -z \"\`git config -l |grep -v '^svn-remote\.git-svn\.'\`\" &&
+ git config --get-all svn-remote.svn.fetch > fetch.out &&
grep '^trunk:refs/remotes/trunk$' fetch.out &&
grep '^branches/a:refs/remotes/a$' fetch.out &&
grep '^branches/b:refs/remotes/b$' fetch.out &&
@@ -109,4 +109,3 @@ test_expect_success ".rev_db auto-converted to .rev_db.UUID" "
"
test_done
-
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
index 59e17f2663..6235af4db8 100755
--- a/t/t9110-git-svn-use-svm-props.sh
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -13,7 +13,7 @@ test_expect_success 'load svm repo' "
git-svn init --minimize-url -R argh -i dir $svnrepo/mirror/argh &&
git-svn init --minimize-url -R argh -i e \
$svnrepo/mirror/argh/a/b/c/d/e &&
- git-config svn.useSvmProps true &&
+ git config svn.useSvmProps true &&
git-svn fetch --all
"
@@ -21,31 +21,31 @@ uuid=161ce429-a9dd-4828-af4a-52023f968c89
bar_url=http://mayonaise/svnrepo/bar
test_expect_success 'verify metadata for /bar' "
- git-cat-file commit refs/remotes/bar | \
+ git cat-file commit refs/remotes/bar | \
grep '^git-svn-id: $bar_url@12 $uuid$' &&
- git-cat-file commit refs/remotes/bar~1 | \
+ git cat-file commit refs/remotes/bar~1 | \
grep '^git-svn-id: $bar_url@11 $uuid$' &&
- git-cat-file commit refs/remotes/bar~2 | \
+ git cat-file commit refs/remotes/bar~2 | \
grep '^git-svn-id: $bar_url@10 $uuid$' &&
- git-cat-file commit refs/remotes/bar~3 | \
+ git cat-file commit refs/remotes/bar~3 | \
grep '^git-svn-id: $bar_url@9 $uuid$' &&
- git-cat-file commit refs/remotes/bar~4 | \
+ git cat-file commit refs/remotes/bar~4 | \
grep '^git-svn-id: $bar_url@6 $uuid$' &&
- git-cat-file commit refs/remotes/bar~5 | \
+ git cat-file commit refs/remotes/bar~5 | \
grep '^git-svn-id: $bar_url@1 $uuid$'
"
e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
- git-cat-file commit refs/remotes/e | \
+ git cat-file commit refs/remotes/e | \
grep '^git-svn-id: $e_url@1 $uuid$'
"
dir_url=http://mayonaise/svnrepo/dir
test_expect_success 'verify metadata for /dir' "
- git-cat-file commit refs/remotes/dir | \
+ git cat-file commit refs/remotes/dir | \
grep '^git-svn-id: $dir_url@2 $uuid$' &&
- git-cat-file commit refs/remotes/dir~1 | \
+ git cat-file commit refs/remotes/dir~1 | \
grep '^git-svn-id: $dir_url@1 $uuid$'
"
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh
index e52321471a..ec7dedd48b 100755
--- a/t/t9111-git-svn-use-svnsync-props.sh
+++ b/t/t9111-git-svn-use-svnsync-props.sh
@@ -12,7 +12,7 @@ test_expect_success 'load svnsync repo' "
git-svn init --minimize-url -R arr -i bar $svnrepo/bar &&
git-svn init --minimize-url -R argh -i dir $svnrepo/dir &&
git-svn init --minimize-url -R argh -i e $svnrepo/dir/a/b/c/d/e &&
- git-config svn.useSvnsyncProps true &&
+ git config svn.useSvnsyncProps true &&
git-svn fetch --all
"
@@ -20,31 +20,31 @@ uuid=161ce429-a9dd-4828-af4a-52023f968c89
bar_url=http://mayonaise/svnrepo/bar
test_expect_success 'verify metadata for /bar' "
- git-cat-file commit refs/remotes/bar | \
+ git cat-file commit refs/remotes/bar | \
grep '^git-svn-id: $bar_url@12 $uuid$' &&
- git-cat-file commit refs/remotes/bar~1 | \
+ git cat-file commit refs/remotes/bar~1 | \
grep '^git-svn-id: $bar_url@11 $uuid$' &&
- git-cat-file commit refs/remotes/bar~2 | \
+ git cat-file commit refs/remotes/bar~2 | \
grep '^git-svn-id: $bar_url@10 $uuid$' &&
- git-cat-file commit refs/remotes/bar~3 | \
+ git cat-file commit refs/remotes/bar~3 | \
grep '^git-svn-id: $bar_url@9 $uuid$' &&
- git-cat-file commit refs/remotes/bar~4 | \
+ git cat-file commit refs/remotes/bar~4 | \
grep '^git-svn-id: $bar_url@6 $uuid$' &&
- git-cat-file commit refs/remotes/bar~5 | \
+ git cat-file commit refs/remotes/bar~5 | \
grep '^git-svn-id: $bar_url@1 $uuid$'
"
e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
- git-cat-file commit refs/remotes/e | \
+ git cat-file commit refs/remotes/e | \
grep '^git-svn-id: $e_url@1 $uuid$'
"
dir_url=http://mayonaise/svnrepo/dir
test_expect_success 'verify metadata for /dir' "
- git-cat-file commit refs/remotes/dir | \
+ git cat-file commit refs/remotes/dir | \
grep '^git-svn-id: $dir_url@2 $uuid$' &&
- git-cat-file commit refs/remotes/dir~1 | \
+ git cat-file commit refs/remotes/dir~1 | \
grep '^git-svn-id: $dir_url@1 $uuid$'
"
diff --git a/t/t9111/svnsync.dump b/t/t9111/svnsync.dump
index a9a46eeb29..499fa9594f 100644
--- a/t/t9111/svnsync.dump
+++ b/t/t9111/svnsync.dump
@@ -558,5 +558,3 @@ Text-content-md5: 7abb78de7f2756ca8b511cbc879fd5e7
Content-length: 4
cba
-
-
diff --git a/t/t9113-git-svn-dcommit-new-file.sh b/t/t9113-git-svn-dcommit-new-file.sh
new file mode 100755
index 0000000000..9ef0db9044
--- /dev/null
+++ b/t/t9113-git-svn-dcommit-new-file.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+# Don't run this test by default unless the user really wants it
+# I don't like the idea of taking a port and possibly leaving a
+# daemon running on a users system if the test fails.
+# Not all git users will need to interact with SVN.
+test -z "$SVNSERVE_PORT" && exit 0
+
+test_description='git-svn dcommit new files over svn:// test'
+
+. ./lib-git-svn.sh
+
+start_svnserve () {
+ svnserve --listen-port $SVNSERVE_PORT \
+ --root $rawsvnrepo \
+ --listen-once \
+ --listen-host 127.0.0.1 &
+}
+
+test_expect_success 'start tracking an empty repo' "
+ svn mkdir -m 'empty dir' $svnrepo/empty-dir &&
+ echo anon-access = write >> $rawsvnrepo/conf/svnserve.conf &&
+ start_svnserve &&
+ git svn init svn://127.0.0.1:$SVNSERVE_PORT &&
+ git svn fetch
+ "
+
+test_expect_success 'create files in new directory with dcommit' "
+ mkdir git-new-dir &&
+ echo hello > git-new-dir/world &&
+ git update-index --add git-new-dir/world &&
+ git commit -m hello &&
+ start_svnserve &&
+ git svn dcommit
+ "
+
+test_done
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
new file mode 100755
index 0000000000..d6ca955081
--- /dev/null
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+# Based on a script by Joakim Tjernlund <joakim.tjernlund@transmode.se>
+
+test_description='git-svn dcommit handles merges'
+
+. ./lib-git-svn.sh
+
+big_text_block () {
+cat << EOF
+#
+# (C) Copyright 2000 - 2005
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+EOF
+}
+
+test_expect_success 'setup svn repository' "
+ svn co $svnrepo mysvnwork &&
+ mkdir -p mysvnwork/trunk &&
+ cd mysvnwork &&
+ big_text_block >> trunk/README &&
+ svn add trunk &&
+ svn ci -m 'first commit' trunk &&
+ cd ..
+ "
+
+test_expect_success 'setup git mirror and merge' "
+ git svn init $svnrepo -t tags -T trunk -b branches &&
+ git svn fetch &&
+ git checkout --track -b svn remotes/trunk &&
+ git checkout -b merge &&
+ echo new file > new_file &&
+ git add new_file &&
+ git commit -a -m 'New file' &&
+ echo hello >> README &&
+ git commit -a -m 'hello' &&
+ echo add some stuff >> new_file &&
+ git commit -a -m 'add some stuff' &&
+ git checkout svn &&
+ mv -f README tmp &&
+ echo friend > README &&
+ cat tmp >> README &&
+ git commit -a -m 'friend' &&
+ git pull . merge
+ "
+
+test_debug 'gitk --all & sleep 1'
+
+test_expect_success 'verify pre-merge ancestry' "
+ test x\`git rev-parse --verify refs/heads/svn^2\` = \
+ x\`git rev-parse --verify refs/heads/merge\` &&
+ git cat-file commit refs/heads/svn^ | grep '^friend$'
+ "
+
+test_expect_success 'git svn dcommit merges' "
+ git svn dcommit
+ "
+
+test_debug 'gitk --all & sleep 1'
+
+test_expect_success 'verify post-merge ancestry' "
+ test x\`git rev-parse --verify refs/heads/svn\` = \
+ x\`git rev-parse --verify refs/remotes/trunk \` &&
+ test x\`git rev-parse --verify refs/heads/svn^2\` = \
+ x\`git rev-parse --verify refs/heads/merge\` &&
+ git cat-file commit refs/heads/svn^ | grep '^friend$'
+ "
+
+test_done
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
new file mode 100755
index 0000000000..182299cbb5
--- /dev/null
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+
+
+test_description='git-svn dcommit can commit renames of files with ugly names'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load repository with strange names' "
+ svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump &&
+ start_httpd
+ "
+
+test_expect_success 'init and fetch repository' "
+ git svn init $svnrepo &&
+ git svn fetch &&
+ git reset --hard git-svn
+ "
+
+test_expect_success 'create file in existing ugly and empty dir' '
+ mkdir "#{bad_directory_name}" &&
+ echo hi > "#{bad_directory_name}/ foo" &&
+ git update-index --add "#{bad_directory_name}/ foo" &&
+ git commit -m "new file in ugly parent" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename ugly file' '
+ git mv "#{bad_directory_name}/ foo" "file name with feces" &&
+ git commit -m "rename ugly file" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename pretty file' '
+ echo :x > pretty &&
+ git update-index --add pretty &&
+ git commit -m "pretty :x" &&
+ git svn dcommit &&
+ mkdir regular_dir_name &&
+ git mv pretty regular_dir_name/pretty &&
+ git commit -m "moved pretty file" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename pretty file into ugly one' '
+ git mv regular_dir_name/pretty "#{bad_directory_name}/ booboo" &&
+ git commit -m booboo &&
+ git svn dcommit
+ '
+
+stop_httpd
+
+test_done
diff --git a/t/t9115/funky-names.dump b/t/t9115/funky-names.dump
new file mode 100644
index 0000000000..42422f791e
--- /dev/null
+++ b/t/t9115/funky-names.dump
@@ -0,0 +1,103 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 819c44fe-2bcc-4066-88e4-985e2bc0b418
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2007-07-12T07:54:26.062914Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 152
+Content-length: 152
+
+K 7
+svn:log
+V 44
+what will those wacky people think of next?
+
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2007-07-12T08:00:05.011573Z
+PROPS-END
+
+Node-path: leading space
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: leading space file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 5
+Text-content-md5: e4fa20c67542cdc21271e08d329397ab
+Content-length: 15
+
+PROPS-END
+ugly
+
+
+Node-path: #{bad_directory_name}
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: #{cool_name}
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 18
+Text-content-md5: 87dac40ca337dfa3dcc8911388c3ddda
+Content-length: 28
+
+PROPS-END
+strange name here
+
+
+Node-path: dir name with spaces
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: file name with spaces
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: c1f10cfd640618484a2a475c11410fd3
+Content-length: 17
+
+PROPS-END
+spaces
+
+
+Node-path: regular_dir_name
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
new file mode 100755
index 0000000000..0d4e6b3f04
--- /dev/null
+++ b/t/t9116-git-svn-log.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git-svn log tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup repository and import' "
+ mkdir import &&
+ cd import &&
+ for i in trunk branches/a branches/b \
+ tags/0.1 tags/0.2 tags/0.3; do
+ mkdir -p \$i && \
+ echo hello >> \$i/README || exit 1
+ done && \
+ svn import -m test . $svnrepo
+ cd .. &&
+ git-svn init $svnrepo -T trunk -b branches -t tags &&
+ git-svn fetch &&
+ git reset --hard trunk &&
+ echo bye >> README &&
+ git commit -a -m bye &&
+ git svn dcommit &&
+ git reset --hard a &&
+ echo why >> FEEDME &&
+ git update-index --add FEEDME &&
+ git commit -m feedme &&
+ git svn dcommit &&
+ git reset --hard trunk &&
+ echo aye >> README &&
+ git commit -a -m aye &&
+ git svn dcommit
+ "
+
+test_expect_success 'run log' "
+ git reset --hard a &&
+ git svn log -r2 trunk | grep ^r2 &&
+ git svn log -r4 trunk | grep ^r4 &&
+ git svn log -r3 | grep ^r3
+ "
+
+test_expect_success 'run log against a from trunk' "
+ git reset --hard trunk &&
+ git svn log -r3 a | grep ^r3
+ "
+
+test_done
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 4efa0c926c..910c584f24 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -28,6 +28,18 @@ git add empty &&
git commit -q -a -m "Initial" 2>/dev/null ||
exit 1
+check_entries () {
+ # $1 == directory, $2 == expected
+ grep '^/' "$1/CVS/Entries" | sort | cut -d/ -f2,3,5 >actual
+ if test -z "$2"
+ then
+ >expected
+ else
+ printf '%s\n' "$2" | tr '|' '\012' >expected
+ fi
+ diff -u expected actual
+}
+
test_expect_success \
'New file' \
'mkdir A B C D E F &&
@@ -43,10 +55,10 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
- test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.1/" &&
- test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "newfile2.txt/1.1/" &&
- test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "newfile3.png/1.1/-kb" &&
- test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.1/-kb" &&
+ check_entries A "newfile1.txt/1.1/" &&
+ check_entries B "newfile2.txt/1.1/" &&
+ check_entries C "newfile3.png/1.1/-kb" &&
+ check_entries D "newfile4.png/1.1/-kb" &&
diff A/newfile1.txt ../A/newfile1.txt &&
diff B/newfile2.txt ../B/newfile2.txt &&
diff C/newfile3.png ../C/newfile3.png &&
@@ -67,12 +79,12 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
- test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
- test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.2/-kb" &&
- test "$(echo $(sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
- test "$(echo $(sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
+ check_entries A "newfile1.txt/1.2/" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "newfile4.png/1.2/-kb" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
diff A/newfile1.txt ../A/newfile1.txt &&
diff D/newfile4.png ../D/newfile4.png &&
diff E/newfile5.txt ../E/newfile5.txt &&
@@ -115,12 +127,12 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
- test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
- test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
- test "$(echo $(sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
+ check_entries A "newfile1.txt/1.2/" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
diff A/newfile1.txt ../A/newfile1.txt &&
diff E/newfile5.txt ../E/newfile5.txt &&
diff F/newfile6.png ../F/newfile6.png
@@ -133,12 +145,12 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
- test "$(echo $(sort A/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
- test "$(echo $(sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
- test "$(echo $(sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
+ check_entries A "" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
diff E/newfile5.txt ../E/newfile5.txt &&
diff F/newfile6.png ../F/newfile6.png
)'
@@ -154,7 +166,7 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git-cvsexportcommit -c $id &&
- test "$(echo $(sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.1/-kb with spaces.txt/1.1/"
+ check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
)'
test_expect_success \
@@ -166,7 +178,7 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git-cvsexportcommit -c $id
- test "$(echo $(sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.2/-kb with spaces.txt/1.2/"
+ check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
)'
# Some filesystems mangle pathnames with UTF-8 characters --
@@ -191,7 +203,9 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git-cvsexportcommit -v -c $id &&
- test "$(echo $(sort Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/CVS/Entries|cut -d/ -f2,3,5))" = "gårdetsågårdet.png/1.1/-kb gårdetsågårdet.txt/1.1/"
+ check_entries \
+ "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
+ "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
)'
fi
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 72e49f5d3b..6f95305bf4 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -60,10 +60,10 @@ INPUT_END
test_expect_success \
'A: create pack from stdin' \
'git-fast-import --export-marks=marks.out <input &&
- git-whatchanged master'
+ git whatchanged master'
test_expect_success \
'A: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
cat >expect <<EOF
author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -73,7 +73,7 @@ initial
EOF
test_expect_success \
'A: verify commit' \
- 'git-cat-file commit master | sed 1d >actual &&
+ 'git cat-file commit master | sed 1d >actual &&
git diff expect actual'
cat >expect <<EOF
@@ -83,29 +83,29 @@ cat >expect <<EOF
EOF
test_expect_success \
'A: verify tree' \
- 'git-cat-file -p master^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ 'git cat-file -p master^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
git diff expect actual'
echo "$file2_data" >expect
test_expect_success \
'A: verify file2' \
- 'git-cat-file blob master:file2 >actual && git diff expect actual'
+ 'git cat-file blob master:file2 >actual && git diff expect actual'
echo "$file3_data" >expect
test_expect_success \
'A: verify file3' \
- 'git-cat-file blob master:file3 >actual && git diff expect actual'
+ 'git cat-file blob master:file3 >actual && git diff expect actual'
printf "$file4_data" >expect
test_expect_success \
'A: verify file4' \
- 'git-cat-file blob master:file4 >actual && git diff expect actual'
+ 'git cat-file blob master:file4 >actual && git diff expect actual'
cat >expect <<EOF
-:2 `git-rev-parse --verify master:file2`
-:3 `git-rev-parse --verify master:file3`
-:4 `git-rev-parse --verify master:file4`
-:5 `git-rev-parse --verify master^0`
+:2 `git rev-parse --verify master:file2`
+:3 `git rev-parse --verify master:file3`
+:4 `git rev-parse --verify master:file4`
+:5 `git rev-parse --verify master^0`
EOF
test_expect_success \
'A: verify marks output' \
@@ -134,19 +134,19 @@ INPUT_END
test_expect_success \
'A: verify marks import does not crash' \
'git-fast-import --import-marks=marks.out <input &&
- git-whatchanged verify--import-marks'
+ git whatchanged verify--import-marks'
test_expect_success \
'A: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
cat >expect <<EOF
:000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A copy-of-file2
EOF
-git-diff-tree -M -r master verify--import-marks >actual
+git diff-tree -M -r master verify--import-marks >actual
test_expect_success \
'A: verify diff' \
'compare_diff_raw expect actual &&
- test `git-rev-parse --verify master:file2` \
- = `git-rev-parse --verify verify--import-marks:copy-of-file2`'
+ test `git rev-parse --verify master:file2` \
+ = `git rev-parse --verify verify--import-marks:copy-of-file2`'
###
### series B
@@ -175,7 +175,7 @@ rm -f .git/objects/pack_* .git/objects/index_*
###
newf=`echo hi newf | git-hash-object -w --stdin`
-oldf=`git-rev-parse --verify master:file2`
+oldf=`git rev-parse --verify master:file2`
test_tick
cat >input <<INPUT_END
commit refs/heads/branch
@@ -193,17 +193,17 @@ INPUT_END
test_expect_success \
'C: incremental import create pack from stdin' \
'git-fast-import <input &&
- git-whatchanged branch'
+ git whatchanged branch'
test_expect_success \
'C: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
test_expect_success \
'C: validate reuse existing blob' \
- 'test $newf = `git-rev-parse --verify branch:file2/newf`
- test $oldf = `git-rev-parse --verify branch:file2/oldf`'
+ 'test $newf = `git rev-parse --verify branch:file2/newf`
+ test $oldf = `git rev-parse --verify branch:file2/oldf`'
cat >expect <<EOF
-parent `git-rev-parse --verify master^0`
+parent `git rev-parse --verify master^0`
author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -211,7 +211,7 @@ second
EOF
test_expect_success \
'C: verify commit' \
- 'git-cat-file commit branch | sed 1d >actual &&
+ 'git cat-file commit branch | sed 1d >actual &&
git diff expect actual'
cat >expect <<EOF
@@ -219,7 +219,7 @@ cat >expect <<EOF
:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100 file2 file2/oldf
:100644 000000 0d92e9f3374ae2947c23aa477cbc68ce598135f1 0000000000000000000000000000000000000000 D file3
EOF
-git-diff-tree -M -r master branch >actual
+git diff-tree -M -r master branch >actual
test_expect_success \
'C: validate rename result' \
'compare_diff_raw expect actual'
@@ -251,16 +251,16 @@ INPUT_END
test_expect_success \
'D: inline data in commit' \
'git-fast-import <input &&
- git-whatchanged branch'
+ git whatchanged branch'
test_expect_success \
'D: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
cat >expect <<EOF
:000000 100755 0000000000000000000000000000000000000000 35a59026a33beac1569b1c7f66f3090ce9c09afc A newdir/exec.sh
:000000 100644 0000000000000000000000000000000000000000 046d0371e9220107917db0d0e030628de8a1de9b A newdir/interesting
EOF
-git-diff-tree -M -r branch^ branch >actual
+git diff-tree -M -r branch^ branch >actual
test_expect_success \
'D: validate new files added' \
'compare_diff_raw expect actual'
@@ -268,13 +268,13 @@ test_expect_success \
echo "$file5_data" >expect
test_expect_success \
'D: verify file5' \
- 'git-cat-file blob branch:newdir/interesting >actual &&
+ 'git cat-file blob branch:newdir/interesting >actual &&
git diff expect actual'
echo "$file6_data" >expect
test_expect_success \
'D: verify file6' \
- 'git-cat-file blob branch:newdir/exec.sh >actual &&
+ 'git cat-file blob branch:newdir/exec.sh >actual &&
git diff expect actual'
###
@@ -300,7 +300,7 @@ test_expect_success \
'git-fast-import --date-format=rfc2822 <input'
test_expect_success \
'E: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
cat >expect <<EOF
author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 1170778938 -0500
@@ -310,14 +310,14 @@ RFC 2822 type date
EOF
test_expect_success \
'E: verify commit' \
- 'git-cat-file commit branch | sed 1,2d >actual &&
+ 'git cat-file commit branch | sed 1,2d >actual &&
git diff expect actual'
###
### series F
###
-old_branch=`git-rev-parse --verify branch^0`
+old_branch=`git rev-parse --verify branch^0`
test_tick
cat >input <<INPUT_END
commit refs/heads/branch
@@ -339,7 +339,7 @@ test_expect_success \
echo BAD gfi did not fail
return 1
else
- if test $old_branch = `git-rev-parse --verify branch^0`
+ if test $old_branch = `git rev-parse --verify branch^0`
then
: branch unaffected and failure returned
return 0
@@ -351,11 +351,11 @@ test_expect_success \
'
test_expect_success \
'F: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
cat >expect <<EOF
-tree `git-rev-parse branch~1^{tree}`
-parent `git-rev-parse branch~1`
+tree `git rev-parse branch~1^{tree}`
+parent `git rev-parse branch~1`
author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -363,14 +363,14 @@ losing things already?
EOF
test_expect_success \
'F: verify other commit' \
- 'git-cat-file commit other >actual &&
+ 'git cat-file commit other >actual &&
git diff expect actual'
###
### series G
###
-old_branch=`git-rev-parse --verify branch^0`
+old_branch=`git rev-parse --verify branch^0`
test_tick
cat >input <<INPUT_END
commit refs/heads/branch
@@ -387,11 +387,11 @@ test_expect_success \
'git-fast-import --force <input'
test_expect_success \
'G: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
test_expect_success \
'G: branch changed, but logged' \
- 'test $old_branch != `git-rev-parse --verify branch^0` &&
- test $old_branch = `git-rev-parse --verify branch@{1}`'
+ 'test $old_branch != `git rev-parse --verify branch^0` &&
+ test $old_branch = `git rev-parse --verify branch@{1}`'
###
### series H
@@ -421,10 +421,10 @@ INPUT_END
test_expect_success \
'H: deletall, add 1' \
'git-fast-import <input &&
- git-whatchanged H'
+ git whatchanged H'
test_expect_success \
'H: verify pack' \
- 'for p in .git/objects/pack/*.pack;do git-verify-pack $p||exit;done'
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
cat >expect <<EOF
:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file2/newf
@@ -433,7 +433,7 @@ cat >expect <<EOF
:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100 newdir/interesting h/e/l/lo
:100755 000000 e74b7d465e52746be2b4bae983670711e6e66657 0000000000000000000000000000000000000000 D newdir/exec.sh
EOF
-git-diff-tree -M -r H^ H >actual
+git diff-tree -M -r H^ H >actual
test_expect_success \
'H: validate old files removed, new files added' \
'compare_diff_raw expect actual'
@@ -441,7 +441,7 @@ test_expect_success \
echo "$file5_data" >expect
test_expect_success \
'H: verify file' \
- 'git-cat-file blob H:h/e/l/lo >actual &&
+ 'git cat-file blob H:h/e/l/lo >actual &&
git diff expect actual'
###
@@ -463,7 +463,7 @@ test_expect_success \
'git-fast-import --export-pack-edges=edges.list <input'
cat >expect <<EOF
-.git/objects/pack/pack-.pack: `git-rev-parse --verify export-boundary`
+.git/objects/pack/pack-.pack: `git rev-parse --verify export-boundary`
EOF
test_expect_success \
'I: verify edge list' \
@@ -497,7 +497,7 @@ test_expect_success \
'git-fast-import <input'
test_expect_success \
'J: branch has 1 commit, empty tree' \
- 'test 1 = `git-rev-list J | wc -l` &&
+ 'test 1 = `git rev-list J | wc -l` &&
test 0 = `git ls-tree J | wc -l`'
###
@@ -527,8 +527,8 @@ test_expect_success \
'git-fast-import <input'
test_expect_success \
'K: verify K^1 = branch^1' \
- 'test `git-rev-parse --verify branch^1` \
- = `git-rev-parse --verify K^1`'
+ 'test `git rev-parse --verify branch^1` \
+ = `git rev-parse --verify K^1`'
###
### series L
@@ -577,7 +577,158 @@ EXPECT_END
test_expect_success \
'L: verify internal tree sorting' \
'git-fast-import <input &&
- git-diff --raw L^ L >output &&
+ git diff-tree --abbrev --raw L^ L >output &&
git diff expect output'
+###
+### series M
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/M1
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file rename
+COMMIT
+
+from refs/heads/branch^0
+R file2/newf file2/n.e.w.f
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 file2/newf file2/n.e.w.f
+EOF
+test_expect_success \
+ 'M: rename file in same subdirectory' \
+ 'git-fast-import <input &&
+ git diff-tree -M -r M1^ M1 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/M2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file rename
+COMMIT
+
+from refs/heads/branch^0
+R file2/newf i/am/new/to/you
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 file2/newf i/am/new/to/you
+EOF
+test_expect_success \
+ 'M: rename file to new subdirectory' \
+ 'git-fast-import <input &&
+ git diff-tree -M -r M2^ M2 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/M3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file rename
+COMMIT
+
+from refs/heads/M2^0
+R i other/sub
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 i/am/new/to/you other/sub/am/new/to/you
+EOF
+test_expect_success \
+ 'M: rename subdirectory to new subdirectory' \
+ 'git-fast-import <input &&
+ git diff-tree -M -r M3^ M3 >actual &&
+ compare_diff_raw expect actual'
+
+###
+### series N
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/N1
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file copy
+COMMIT
+
+from refs/heads/branch^0
+C file2/newf file2/n.e.w.f
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file2/n.e.w.f
+EOF
+test_expect_success \
+ 'N: copy file in same subdirectory' \
+ 'git-fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/N2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+clean directory copy
+COMMIT
+
+from refs/heads/branch^0
+C file2 file3
+
+commit refs/heads/N2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+modify directory copy
+COMMIT
+
+M 644 inline file3/file5
+data <<EOF
+$file5_data
+EOF
+
+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
+EOF
+test_expect_success \
+ 'N: copy then modify subdirectory' \
+ 'git-fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/N3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+dirty directory copy
+COMMIT
+
+from refs/heads/branch^0
+M 644 inline file2/file5
+data <<EOF
+$file5_data
+EOF
+
+C file2 file3
+D file2/file5
+
+INPUT_END
+
+test_expect_success \
+ 'N: copy dirty subdirectory' \
+ 'git-fast-import <input &&
+ test `git-rev-parse N2^{tree}` = `git-rev-parse N3^{tree}`'
+
test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index d406a8824a..641303e0a1 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -38,7 +38,7 @@ echo >empty &&
git commit -q -m "First Commit" &&
git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
- GIT_DIR="$SERVERDIR" git config --bool gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
+ GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
exit 1
# note that cvs doesn't accept absolute pathnames
@@ -47,6 +47,217 @@ test_expect_success 'basic checkout' \
'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master &&
test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5))" = "empty/1.1/"'
+#------------------------
+# PSERVER AUTHENTICATION
+#------------------------
+
+cat >request-anonymous <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+anonymous
+
+END AUTH REQUEST
+EOF
+
+cat >request-git <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+git
+
+END AUTH REQUEST
+EOF
+
+cat >login-anonymous <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+anonymous
+
+END VERIFICATION REQUEST
+EOF
+
+cat >login-git <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+git
+
+END VERIFICATION REQUEST
+EOF
+
+test_expect_success 'pserver authentication' \
+ 'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+ tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_success 'pserver authentication failure (non-anonymous user)' \
+ 'if cat request-git | git-cvsserver pserver >log 2>&1
+ then
+ false
+ else
+ true
+ fi &&
+ tail -n1 log | grep -q "^I HATE YOU$"'
+
+test_expect_success 'pserver authentication (login)' \
+ 'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+ tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
+ 'if cat login-git | git-cvsserver pserver >log 2>&1
+ then
+ false
+ else
+ true
+ fi &&
+ tail -n1 log | grep -q "^I HATE YOU$"'
+
+
+# misuse pserver authentication for testing of req_Root
+
+cat >request-relative <<EOF
+BEGIN AUTH REQUEST
+gitcvs.git
+anonymous
+
+END AUTH REQUEST
+EOF
+
+cat >request-conflict <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+anonymous
+
+END AUTH REQUEST
+Root $WORKDIR
+EOF
+
+test_expect_success 'req_Root failure (relative pathname)' \
+ 'if cat request-relative | git-cvsserver pserver >log 2>&1
+ then
+ echo unexpected success
+ false
+ else
+ true
+ fi &&
+ tail log | grep -q "^error 1 Root must be an absolute pathname$"'
+
+test_expect_success 'req_Root failure (conflicting roots)' \
+ 'cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+ tail log | grep -q "^error 1 Conflicting roots specified$"'
+
+test_expect_success 'req_Root (strict paths)' \
+ 'cat request-anonymous | git-cvsserver --strict-paths pserver $SERVERDIR >log 2>&1 &&
+ tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_failure 'req_Root failure (strict-paths)' \
+ 'cat request-anonymous | git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1'
+
+test_expect_success 'req_Root (w/o strict-paths)' \
+ 'cat request-anonymous | git-cvsserver pserver $WORKDIR/ >log 2>&1 &&
+ tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_failure 'req_Root failure (w/o strict-paths)' \
+ 'cat request-anonymous | git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1'
+
+cat >request-base <<EOF
+BEGIN AUTH REQUEST
+/gitcvs.git
+anonymous
+
+END AUTH REQUEST
+Root /gitcvs.git
+EOF
+
+test_expect_success 'req_Root (base-path)' \
+ 'cat request-base | git-cvsserver --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
+ tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_failure 'req_Root failure (base-path)' \
+ 'cat request-anonymous | git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1'
+
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
+
+test_expect_success 'req_Root (export-all)' \
+ 'cat request-anonymous | git-cvsserver --export-all pserver $WORKDIR >log 2>&1 &&
+ tail -n1 log | grep -q "^I LOVE YOU$"'
+
+test_expect_failure 'req_Root failure (export-all w/o whitelist)' \
+ 'cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 ||
+ false'
+
+test_expect_success 'req_Root (everything together)' \
+ 'cat request-base | git-cvsserver --export-all --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
+ tail -n1 log | grep -q "^I LOVE YOU$"'
+
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
+
+#--------------
+# CONFIG TESTS
+#--------------
+
+test_expect_success 'gitcvs.enabled = false' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+ if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+ then
+ echo unexpected cvs success
+ false
+ else
+ true
+ fi &&
+ cat cvs.log | grep -q "GITCVS emulation disabled" &&
+ test ! -d cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.enabled = true' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+ diff -q cvswork cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.enabled = false' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+ if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+ then
+ echo unexpected cvs success
+ false
+ else
+ true
+ fi &&
+ cat cvs.log | grep -q "GITCVS emulation disabled" &&
+ test ! -d cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.dbname' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+ diff -q cvswork cvswork2 &&
+ test -f "$SERVERDIR/gitcvs.ext.master.sqlite" &&
+ cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.dbname' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+ diff -q cvswork cvswork2 &&
+ test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" &&
+ test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" &&
+ cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"'
+
+
+#------------
+# CVS UPDATE
+#------------
+
+rm -fr "$SERVERDIR"
+cd "$WORKDIR" &&
+git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
+exit 1
+
test_expect_success 'cvs update (create new file)' \
'echo testfile1 >testfile1 &&
git add testfile1 &&
@@ -123,4 +334,75 @@ test_expect_success 'cvs update (re-add deleted file)' \
test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
diff -q testfile1 ../testfile1'
+cd "$WORKDIR"
+test_expect_success 'cvs update (merge)' \
+ 'echo Line 0 >expected &&
+ for i in 1 2 3 4 5 6 7
+ do
+ echo Line $i >>merge
+ echo Line $i >>expected
+ done &&
+ echo Line 8 >>expected &&
+ git add merge &&
+ git commit -q -m "Merge test (pre-merge)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
+ diff -q merge ../merge &&
+ ( echo Line 0; cat merge ) >merge.tmp &&
+ mv merge.tmp merge &&
+ cd "$WORKDIR" &&
+ echo Line 8 >>merge &&
+ git add merge &&
+ git commit -q -m "Merge test (merge)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ sleep 1 && touch merge &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ diff -q merge ../expected'
+
+cd "$WORKDIR"
+
+cat >expected.C <<EOF
+<<<<<<< merge.mine
+Line 0
+=======
+LINE 0
+>>>>>>> merge.3
+EOF
+
+for i in 1 2 3 4 5 6 7 8
+do
+ echo Line $i >>expected.C
+done
+
+test_expect_success 'cvs update (conflict merge)' \
+ '( echo LINE 0; cat merge ) >merge.tmp &&
+ mv merge.tmp merge &&
+ git add merge &&
+ git commit -q -m "Merge test (conflict)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ diff -q merge ../expected.C'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (-C)' \
+ 'cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update -C &&
+ diff -q merge ../merge'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (merge no-op)' \
+ 'echo Line 9 >>merge &&
+ cp merge cvswork/merge &&
+ git add merge &&
+ git commit -q -m "Merge test (no-op)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ sleep 1 && touch merge &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ diff -q merge ../merge'
+
test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
new file mode 100755
index 0000000000..fa32598b0c
--- /dev/null
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -0,0 +1,552 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+test_description='gitweb as standalone script (basic tests).
+
+This test runs gitweb (git web interface) as CGI script from
+commandline, and checks that it would not write any errors
+or warnings to log.'
+
+gitweb_init () {
+ cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = "current";
+our \$GIT = "git";
+our \$projectroot = "$(pwd)";
+our \$home_link_str = "projects";
+our \$site_name = "[localhost]";
+our \$site_header = "";
+our \$site_footer = "";
+our \$home_text = "indextext.html";
+our @stylesheets = ("file:///$(pwd)/../../gitweb/gitweb.css");
+our \$logo = "file:///$(pwd)/../../gitweb/git-logo.png";
+our \$favicon = "file:///$(pwd)/../../gitweb/git-favicon.png";
+our \$projects_list = "";
+our \$export_ok = "";
+our \$strict_export = "";
+
+CGI::Carp::set_programname("gitweb/gitweb.cgi");
+EOF
+
+ cat >.git/description <<EOF
+$0 test repository
+EOF
+}
+
+gitweb_run () {
+ export GATEWAY_INTERFACE="CGI/1.1"
+ export HTTP_ACCEPT="*/*"
+ export REQUEST_METHOD="GET"
+ export QUERY_STRING=""$1""
+ export PATH_INFO=""$2""
+
+ export GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+
+ # some of git commands write to STDERR on error, but this is not
+ # written to web server logs, so we are not interested in that:
+ # we are interested only in properly formatted errors/warnings
+ rm -f gitweb.log &&
+ perl -- $(pwd)/../../gitweb/gitweb.perl \
+ >/dev/null 2>gitweb.log &&
+ if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
+
+ # gitweb.log is left for debugging
+}
+
+. ./test-lib.sh
+
+perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
+ test_expect_success 'skipping gitweb tests, perl version is too old' :
+ test_done
+ exit
+}
+
+gitweb_init
+
+# ----------------------------------------------------------------------
+# no commits (empty, just initialized repository)
+
+test_expect_success \
+ 'no commits: projects_list (implicit)' \
+ 'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: projects_index' \
+ 'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git summary (implicit)' \
+ 'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git commit (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git commitdiff (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git tree (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git heads' \
+ 'gitweb_run "p=.git;a=heads"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git tags' \
+ 'gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# initial commit
+
+test_expect_success \
+ 'Make initial commit' \
+ 'echo "Not an empty file." > file &&
+ git add file &&
+ git commit -a -m "Initial commit." &&
+ git branch b'
+
+test_expect_success \
+ 'projects_list (implicit)' \
+ 'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'projects_index' \
+ 'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git summary (implicit)' \
+ 'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commit (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff (implicit HEAD, root commit)' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff_plain (implicit HEAD, root commit)' \
+ 'gitweb_run "p=.git;a=commitdiff_plain"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commit (HEAD)' \
+ 'gitweb_run "p=.git;a=commit;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git tree (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob (file)' \
+ 'gitweb_run "p=.git;a=blob;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob_plain (file)' \
+ 'gitweb_run "p=.git;a=blob_plain;f=file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# nonexistent objects
+
+test_expect_success \
+ '.git commit (non-existent)' \
+ 'gitweb_run "p=.git;a=commit;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff (non-existent)' \
+ 'gitweb_run "p=.git;a=commitdiff;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff (non-existent vs HEAD)' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=non-existent;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git tree (0000000000000000000000000000000000000000)' \
+ 'gitweb_run "p=.git;a=tree;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git tag (0000000000000000000000000000000000000000)' \
+ 'gitweb_run "p=.git;a=tag;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob (non-existent)' \
+ 'gitweb_run "p=.git;a=blob;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob_plain (non-existent)' \
+ 'gitweb_run "p=.git;a=blob_plain;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# commitdiff testing (implicit, one implicit tree-ish)
+
+test_expect_success \
+ 'commitdiff(0): root' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file added' \
+ 'echo "New file" > new_file &&
+ git add new_file &&
+ git commit -a -m "File added." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): mode change' \
+ 'chmod a+x new_file &&
+ git commit -a -m "Mode changed." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file renamed' \
+ 'git mv new_file renamed_file &&
+ git commit -a -m "File renamed." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file to symlink' \
+ 'rm renamed_file &&
+ ln -s file renamed_file &&
+ git commit -a -m "File to symlink." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file deleted' \
+ 'git rm renamed_file &&
+ rm -f renamed_file &&
+ git commit -a -m "File removed." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file copied / new file' \
+ 'cp file file2 &&
+ git add file2 &&
+ git commit -a -m "File copied." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): mode change and modified' \
+ 'echo "New line" >> file2 &&
+ chmod a+x file2 &&
+ git commit -a -m "Mode change and modification." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): renamed and modified' \
+ 'cat >file2<<EOF &&
+Dominus regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+EOF
+ git commit -a -m "File added." &&
+ git mv file2 file3 &&
+ echo "Propter nomen suum." >> file3 &&
+ git commit -a -m "File rename and modification." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): renamed, mode change and modified' \
+ 'git mv file3 file2 &&
+ echo "Propter nomen suum." >> file2 &&
+ chmod a+x file2 &&
+ git commit -a -m "File rename, mode change and modification." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commitdiff testing (taken from t4114-apply-typechange.sh)
+
+test_expect_success 'setup typechange commits' '
+ echo "hello world" > foo &&
+ echo "hi planet" > bar &&
+ git update-index --add foo bar &&
+ git commit -m initial &&
+ git branch initial &&
+ rm -f foo &&
+ ln -s bar foo &&
+ git update-index foo &&
+ git commit -m "foo symlinked to bar" &&
+ git branch foo-symlinked-to-bar &&
+ rm -f foo &&
+ echo "how far is the sun?" > foo &&
+ git update-index foo &&
+ git commit -m "foo back to file" &&
+ git branch foo-back-to-file &&
+ rm -f foo &&
+ git update-index --remove foo &&
+ mkdir foo &&
+ echo "if only I knew" > foo/baz &&
+ git update-index --add foo/baz &&
+ git commit -m "foo becomes a directory" &&
+ git branch "foo-becomes-a-directory" &&
+ echo "hello world" > foo/baz &&
+ git update-index foo/baz &&
+ git commit -m "foo/baz is the original foo" &&
+ git branch foo-baz-renamed-from-foo
+ '
+
+test_expect_success \
+ 'commitdiff(2): file renamed from foo to foo/baz' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-baz-renamed-from-foo"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): file renamed from foo/baz to foo' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-baz-renamed-from-foo;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): directory becomes file' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): file becomes directory' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): file becomes symlink' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): symlink becomes file' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-back-to-file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): symlink becomes directory' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): directory becomes symlink' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commit, commitdiff: merge, large
+test_expect_success \
+ 'Create a merge' \
+ 'git checkout b &&
+ echo "Branch" >> b &&
+ git add b &&
+ git commit -a -m "On branch" &&
+ git checkout master &&
+ git pull . b'
+
+test_expect_success \
+ 'commit(0): merge commit' \
+ 'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): merge commit' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'Prepare large commit' \
+ 'git checkout b &&
+ echo "To be changed" > 01-change &&
+ echo "To be renamed" > 02-pure-rename-from &&
+ echo "To be deleted" > 03-delete &&
+ echo "To be renamed and changed" > 04-rename-from &&
+ echo "To have mode changed" > 05-mode-change &&
+ echo "File to symlink" > 06-file-or-symlink &&
+ echo "To be changed and have mode changed" > 07-change-mode-change &&
+ git add 0* &&
+ git commit -a -m "Prepare large commit" &&
+ echo "Changed" > 01-change &&
+ git mv 02-pure-rename-from 02-pure-rename-to &&
+ git rm 03-delete && rm -f 03-delete &&
+ echo "A new file" > 03-new &&
+ git add 03-new &&
+ git mv 04-rename-from 04-rename-to &&
+ echo "Changed" >> 04-rename-to &&
+ chmod a+x 05-mode-change &&
+ rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink &&
+ echo "Changed and have mode changed" > 07-change-mode-change &&
+ chmod a+x 07-change-mode-change &&
+ git commit -a -m "Large commit" &&
+ git checkout master'
+
+test_expect_success \
+ 'commit(1): large commit' \
+ 'gitweb_run "p=.git;a=commit;h=b"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(1): large commit' \
+ 'gitweb_run "p=.git;a=commitdiff;h=b"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# tags testing
+
+test_expect_success \
+ 'tags: list of different types of tags' \
+ 'git checkout master &&
+ git tag -a -m "Tag commit object" tag-commit HEAD &&
+ git tag -a -m "" tag-commit-nomessage HEAD &&
+ git tag -a -m "Tag tag object" tag-tag tag-commit &&
+ git tag -a -m "Tag tree object" tag-tree HEAD^{tree} &&
+ git tag -a -m "Tag blob object" tag-blob HEAD:file &&
+ git tag lightweight/tag-commit HEAD &&
+ git tag lightweight/tag-tag tag-commit &&
+ git tag lightweight/tag-tree HEAD^{tree} &&
+ git tag lightweight/tag-blob HEAD:file &&
+ gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'tag: Tag to commit object' \
+ 'gitweb_run "p=.git;a=tag;h=tag-commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'tag: on lightweight tag (invalid)' \
+ 'gitweb_run "p=.git;a=tag;h=lightweight/tag-commit"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# logs
+
+test_expect_success \
+ 'logs: log (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=log"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'logs: shortlog (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=shortlog"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'logs: history (implicit HEAD, file)' \
+ 'gitweb_run "p=.git;a=history;f=file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# feed generation
+
+test_expect_success \
+ 'feeds: OPML' \
+ 'gitweb_run "a=opml"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'feed: RSS' \
+ 'gitweb_run "p=.git;a=rss"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'feed: Atom' \
+ 'gitweb_run "p=.git;a=atom"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# encoding/decoding
+
+test_expect_success \
+ 'encode(commit): utf8' \
+ '. ../t3901-utf8.txt &&
+ echo "UTF-8" >> file &&
+ git add file &&
+ git commit -F ../t3900/1-UTF-8.txt &&
+ gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'encode(commit): iso-8859-1' \
+ '. ../t3901-8859-1.txt &&
+ echo "ISO-8859-1" >> file &&
+ git add file &&
+ git config i18n.commitencoding ISO-8859-1 &&
+ git commit -F ../t3900/ISO-8859-1.txt &&
+ git config --unset i18n.commitencoding &&
+ gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'encode(log): utf-8 and iso-8859-1' \
+ 'gitweb_run "p=.git;a=log"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# extra options
+
+test_expect_success \
+ 'opt: log --no-merges' \
+ 'gitweb_run "p=.git;a=log;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: atom --no-merges' \
+ 'gitweb_run "p=.git;a=log;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: "file" history --no-merges' \
+ 'gitweb_run "p=.git;a=history;f=file;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: log --no-such-option (invalid option)' \
+ 'gitweb_run "p=.git;a=log;opt=--no-such-option"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: tree --no-merges (invalid option for action)' \
+ 'gitweb_run "p=.git;a=tree;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index dee3ad7621..cc1253ccab 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -11,6 +11,7 @@ TZ=UTC
export LANG LC_ALL PAGER TZ
EDITOR=:
VISUAL=:
+unset GIT_EDITOR
unset AUTHOR_DATE
unset AUTHOR_EMAIL
unset AUTHOR_NAME
@@ -26,6 +27,7 @@ GIT_COMMITTER_EMAIL=committer@example.com
GIT_COMMITTER_NAME='C O Mitter'
unset GIT_DIFF_OPTS
unset GIT_DIR
+unset GIT_WORK_TREE
unset GIT_EXTERNAL_DIFF
unset GIT_INDEX_FILE
unset GIT_OBJECT_DIRECTORY
@@ -232,7 +234,7 @@ test_create_repo () {
mv .git/hooks .git/hooks-disabled
cd "$owd"
}
-
+
test_done () {
trap - exit
case "$test_failure" in
diff --git a/templates/Makefile b/templates/Makefile
index b8352e731b..6f4dbd362f 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -6,8 +6,9 @@ endif
INSTALL ?= install
TAR ?= tar
+RM ?= rm -f
prefix ?= $(HOME)
-template_dir ?= $(prefix)/share/git-core/templates/
+template_dir ?= $(prefix)/share/git-core/templates
# DESTDIR=
# Shell quote (do not use $(call) to accommodate ancient setups);
@@ -42,7 +43,7 @@ custom:
$(QUIET): no custom templates yet
clean:
- rm -rf blt boilerplates.made
+ $(RM) -r blt boilerplates.made
install: all
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(template_dir_SQ)'
diff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg
index 9b04f2d69c..c5cdb9d7ee 100644
--- a/templates/hooks--commit-msg
+++ b/templates/hooks--commit-msg
@@ -19,4 +19,3 @@ test "" = "$(grep '^Signed-off-by: ' "$1" |
echo >&2 Duplicate Signed-off-by lines.
exit 1
}
-
diff --git a/templates/hooks--post-receive b/templates/hooks--post-receive
index 190de2688c..b70c8fd364 100644
--- a/templates/hooks--post-receive
+++ b/templates/hooks--post-receive
@@ -14,4 +14,3 @@
#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
-
diff --git a/templates/hooks--pre-applypatch b/templates/hooks--pre-applypatch
index 5f56ce8053..eeccc934ca 100644
--- a/templates/hooks--pre-applypatch
+++ b/templates/hooks--pre-applypatch
@@ -12,4 +12,3 @@
test -x "$GIT_DIR/hooks/pre-commit" &&
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
:
-
diff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit
index 723a9ef210..18b87309f6 100644
--- a/templates/hooks--pre-commit
+++ b/templates/hooks--pre-commit
@@ -68,4 +68,3 @@ perl -e '
}
exit($found_bad);
'
-
diff --git a/test-absolute-path.c b/test-absolute-path.c
new file mode 100644
index 0000000000..c959ea20d3
--- /dev/null
+++ b/test-absolute-path.c
@@ -0,0 +1,11 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+ while (argc > 1) {
+ puts(make_absolute_path(argv[1]));
+ argc--;
+ argv++;
+ }
+ return 0;
+}
diff --git a/tree-diff.c b/tree-diff.c
index 852498eb49..26bdbdd2bf 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -3,6 +3,7 @@
*/
#include "cache.h"
#include "diff.h"
+#include "diffcore.h"
#include "tree.h"
static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
@@ -290,6 +291,78 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru
return 0;
}
+/*
+ * Does it look like the resulting diff might be due to a rename?
+ * - single entry
+ * - not a valid previous file
+ */
+static inline int diff_might_be_rename(void)
+{
+ return diff_queued_diff.nr == 1 &&
+ !DIFF_FILE_VALID(diff_queued_diff.queue[0]->one);
+}
+
+static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
+{
+ struct diff_options diff_opts;
+ struct diff_queue_struct *q = &diff_queued_diff;
+ struct diff_filepair *choice;
+ const char *paths[1];
+ int i;
+
+ /* Remove the file creation entry from the diff queue, and remember it */
+ choice = q->queue[0];
+ q->nr = 0;
+
+ diff_setup(&diff_opts);
+ diff_opts.recursive = 1;
+ diff_opts.detect_rename = DIFF_DETECT_RENAME;
+ diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_opts.single_follow = opt->paths[0];
+ paths[0] = NULL;
+ diff_tree_setup_paths(paths, &diff_opts);
+ if (diff_setup_done(&diff_opts) < 0)
+ die("unable to set up diff options to follow renames");
+ diff_tree(t1, t2, base, &diff_opts);
+ diffcore_std(&diff_opts);
+
+ /* Go through the new set of filepairing, and see if we find a more interesting one */
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+
+ /*
+ * Found a source? Not only do we use that for the new
+ * diff_queued_diff, we will also use that as the path in
+ * the future!
+ */
+ if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) {
+ /* Switch the file-pairs around */
+ q->queue[i] = choice;
+ choice = p;
+
+ /* Update the path we use from now on.. */
+ opt->paths[0] = xstrdup(p->one->path);
+ diff_tree_setup_paths(opt->paths, opt);
+ break;
+ }
+ }
+
+ /*
+ * Then, discard all the non-relevane file pairs...
+ */
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ diff_free_filepair(p);
+ }
+
+ /*
+ * .. and re-instate the one we want (which might be either the
+ * original one, or the rename/copy we found)
+ */
+ q->queue[0] = choice;
+ q->nr = 1;
+}
+
int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt)
{
void *tree1, *tree2;
@@ -306,6 +379,11 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
init_tree_desc(&t1, tree1, size1);
init_tree_desc(&t2, tree2, size2);
retval = diff_tree(&t1, &t2, base, opt);
+ if (opt->follow_renames && diff_might_be_rename()) {
+ init_tree_desc(&t1, tree1, size1);
+ init_tree_desc(&t2, tree2, size2);
+ try_to_follow_renames(&t1, &t2, base, opt);
+ }
free(tree1);
free(tree2);
return retval;
diff --git a/tree-walk.c b/tree-walk.c
index cbb24eb3f6..8d4b67317f 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -206,4 +206,3 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
free(tree);
return retval;
}
-
diff --git a/tree-walk.h b/tree-walk.h
index ee747aba0b..db0fbdc701 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -22,7 +22,7 @@ static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, co
static inline int tree_entry_len(const char *name, const unsigned char *sha1)
{
- return (char *)sha1 - (char *)name - 1;
+ return (const char *)sha1 - name - 1;
}
void update_tree_entry(struct tree_desc *);
diff --git a/tree.c b/tree.c
index e946dac069..8c0819fa72 100644
--- a/tree.c
+++ b/tree.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "cache-tree.h"
#include "tree.h"
#include "blob.h"
#include "commit.h"
@@ -7,7 +8,7 @@
const char *tree_type = "tree";
-static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+static int read_one_entry_opt(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, int opt)
{
int len;
unsigned int size;
@@ -25,7 +26,23 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel
memcpy(ce->name, base, baselen);
memcpy(ce->name + baselen, pathname, len+1);
hashcpy(ce->sha1, sha1);
- return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+ return add_cache_entry(ce, opt);
+}
+
+static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+{
+ return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
+ ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+}
+
+/*
+ * This is used when the caller knows there is no existing entries at
+ * the stage that will conflict with the entry being added.
+ */
+static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+{
+ return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
+ ADD_CACHE_JUST_APPEND);
}
static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths)
@@ -119,9 +136,55 @@ int read_tree_recursive(struct tree *tree,
return 0;
}
+static int cmp_cache_name_compare(const void *a_, const void *b_)
+{
+ const struct cache_entry *ce1, *ce2;
+
+ ce1 = *((const struct cache_entry **)a_);
+ ce2 = *((const struct cache_entry **)b_);
+ return cache_name_compare(ce1->name, ntohs(ce1->ce_flags),
+ ce2->name, ntohs(ce2->ce_flags));
+}
+
int read_tree(struct tree *tree, int stage, const char **match)
{
- return read_tree_recursive(tree, "", 0, stage, match, read_one_entry);
+ read_tree_fn_t fn = NULL;
+ int i, err;
+
+ /*
+ * Currently the only existing callers of this function all
+ * call it with stage=1 and after making sure there is nothing
+ * at that stage; we could always use read_one_entry_quick().
+ *
+ * But when we decide to straighten out git-read-tree not to
+ * use unpack_trees() in some cases, this will probably start
+ * to matter.
+ */
+
+ /*
+ * See if we have cache entry at the stage. If so,
+ * do it the original slow way, otherwise, append and then
+ * sort at the end.
+ */
+ for (i = 0; !fn && i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (ce_stage(ce) == stage)
+ fn = read_one_entry;
+ }
+
+ if (!fn)
+ fn = read_one_entry_quick;
+ err = read_tree_recursive(tree, "", 0, stage, match, fn);
+ if (fn == read_one_entry || err)
+ return err;
+
+ /*
+ * Sort the cache entry -- we need to nuke the cache tree, though.
+ */
+ cache_tree_free(&active_cache_tree);
+ qsort(active_cache, active_nr, sizeof(active_cache[0]),
+ cmp_cache_name_compare);
+ return 0;
}
struct tree *lookup_tree(const unsigned char *sha1)
@@ -157,7 +220,7 @@ static void track_tree_refs(struct tree *item)
/* Count how many entries there are.. */
init_tree_desc(&desc, item->buffer, item->size);
while (tree_entry(&desc, &entry)) {
- if (S_ISDIRLNK(entry.mode))
+ if (S_ISGITLINK(entry.mode))
continue;
n_refs++;
}
@@ -169,7 +232,7 @@ static void track_tree_refs(struct tree *item)
while (tree_entry(&desc, &entry)) {
struct object *obj;
- if (S_ISDIRLNK(entry.mode))
+ if (S_ISGITLINK(entry.mode))
continue;
if (S_ISDIR(entry.mode))
obj = &lookup_tree(entry.sha1)->object;
diff --git a/unpack-file.c b/unpack-file.c
index 25c56b374a..65c66eb0bf 100644
--- a/unpack-file.c
+++ b/unpack-file.c
@@ -14,9 +14,7 @@ static char *create_temp_file(unsigned char *sha1)
die("unable to read blob object %s", sha1_to_hex(sha1));
strcpy(path, ".merge_file_XXXXXX");
- fd = mkstemp(path);
- if (fd < 0)
- die("unable to create temp-file");
+ fd = xmkstemp(path);
if (write_in_full(fd, buf, size) != size)
die("unable to write temp-file");
close(fd);
diff --git a/unpack-trees.c b/unpack-trees.c
index 89dd279f89..ccfeb6e245 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -5,41 +5,30 @@
#include "cache-tree.h"
#include "unpack-trees.h"
#include "progress.h"
+#include "refs.h"
#define DBRT_DEBUG 1
struct tree_entry_list {
struct tree_entry_list *next;
- unsigned directory : 1;
- unsigned executable : 1;
- unsigned symlink : 1;
unsigned int mode;
const char *name;
const unsigned char *sha1;
};
-static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
+static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
{
- struct tree_desc desc;
struct name_entry one;
struct tree_entry_list *ret = NULL;
struct tree_entry_list **list_p = &ret;
- if (!tree->object.parsed)
- parse_tree(tree);
-
- init_tree_desc(&desc, tree->buffer, tree->size);
-
- while (tree_entry(&desc, &one)) {
+ while (tree_entry(desc, &one)) {
struct tree_entry_list *entry;
entry = xmalloc(sizeof(struct tree_entry_list));
entry->name = one.path;
entry->sha1 = one.sha1;
entry->mode = one.mode;
- entry->directory = S_ISDIR(one.mode) != 0;
- entry->executable = (one.mode & S_IXUSR) != 0;
- entry->symlink = S_ISLNK(one.mode) != 0;
entry->next = NULL;
*list_p = entry;
@@ -69,10 +58,17 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
return ret;
}
+static inline void remove_entry(int remove)
+{
+ if (remove >= 0)
+ remove_cache_entry_at(remove);
+}
+
static int unpack_trees_rec(struct tree_entry_list **posns, int len,
const char *base, struct unpack_trees_options *o,
struct tree_entry_list *df_conflict_list)
{
+ int remove;
int baselen = strlen(base);
int src_size = len + 1;
int i_stk = i_stk;
@@ -140,9 +136,9 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
#endif
if (!first || entcmp(first, firstdir,
posns[i]->name,
- posns[i]->directory) > 0) {
+ S_ISDIR(posns[i]->mode)) > 0) {
first = posns[i]->name;
- firstdir = posns[i]->directory;
+ firstdir = S_ISDIR(posns[i]->mode);
}
}
/* No name means we're done */
@@ -156,10 +152,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
subposns = xcalloc(len, sizeof(struct tree_list_entry *));
+ remove = -1;
if (cache_name && !strcmp(cache_name, first)) {
any_files = 1;
src[0] = active_cache[o->pos];
- remove_cache_entry_at(o->pos);
+ remove = o->pos;
}
for (i = 0; i < len; i++) {
@@ -176,11 +173,13 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
continue;
}
- if (posns[i]->directory) {
+ if (S_ISDIR(posns[i]->mode)) {
struct tree *tree = lookup_tree(posns[i]->sha1);
+ struct tree_desc t;
any_dirs = 1;
parse_tree(tree);
- subposns[i] = create_tree_entry_list(tree);
+ init_tree_desc(&t, tree->buffer, tree->size);
+ subposns[i] = create_tree_entry_list(&t);
posns[i] = posns[i]->next;
src[i + o->merge] = o->df_conflict_entry;
continue;
@@ -223,13 +222,14 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
printf("\n");
}
#endif
- ret = o->fn(src, o);
+ ret = o->fn(src, o, remove);
#if DBRT_DEBUG > 1
printf("Added %d entries\n", ret);
#endif
o->pos += ret;
} else {
+ remove_entry(remove);
for (i = 0; i < src_size; i++) {
if (src[i]) {
add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
@@ -336,12 +336,10 @@ static void check_updates(struct cache_entry **src, int nr,
stop_progress(&progress);;
}
-int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
+int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
{
- unsigned len = object_list_length(trees);
struct tree_entry_list **posns;
int i;
- struct object_list *posn = trees;
struct tree_entry_list df_conflict_list;
static struct cache_entry *dfc;
@@ -361,10 +359,9 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
if (len) {
posns = xmalloc(len * sizeof(struct tree_entry_list *));
- for (i = 0; i < len; i++) {
- posns[i] = create_tree_entry_list((struct tree *) posn->item);
- posn = posn->next;
- }
+ for (i = 0; i < len; i++)
+ posns[i] = create_tree_entry_list(t+i);
+
if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
o, &df_conflict_list))
return -1;
@@ -412,6 +409,15 @@ static void verify_uptodate(struct cache_entry *ce,
unsigned changed = ce_match_stat(ce, &st, 1);
if (!changed)
return;
+ /*
+ * NEEDSWORK: the current default policy is to allow
+ * submodule to be out of sync wrt the supermodule
+ * index. This needs to be tightened later for
+ * submodules that are marked to be automatically
+ * checked out.
+ */
+ if (S_ISGITLINK(ntohl(ce->ce_mode)))
+ return;
errno = 0;
}
if (errno == ENOENT)
@@ -425,11 +431,24 @@ static void invalidate_ce_path(struct cache_entry *ce)
cache_tree_invalidate_path(active_cache_tree, ce->name);
}
-static int verify_clean_subdirectory(const char *path, const char *action,
+/*
+ * Check that checking out ce->sha1 in subdir ce->name is not
+ * going to overwrite any working files.
+ *
+ * Currently, git does not checkout subprojects during a superproject
+ * checkout, so it is not going to overwrite anything.
+ */
+static int verify_clean_submodule(struct cache_entry *ce, const char *action,
+ struct unpack_trees_options *o)
+{
+ return 0;
+}
+
+static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
struct unpack_trees_options *o)
{
/*
- * we are about to extract "path"; we would not want to lose
+ * we are about to extract "ce->name"; we would not want to lose
* anything in the existing directory there.
*/
int namelen;
@@ -437,13 +456,24 @@ static int verify_clean_subdirectory(const char *path, const char *action,
struct dir_struct d;
char *pathbuf;
int cnt = 0;
+ unsigned char sha1[20];
+
+ if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
+ resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
+ /* If we are not going to update the submodule, then
+ * we don't care.
+ */
+ if (!hashcmp(sha1, ce->sha1))
+ return 0;
+ return verify_clean_submodule(ce, action, o);
+ }
/*
* First let's make sure we do not have a local modification
* in that directory.
*/
- namelen = strlen(path);
- pos = cache_name_pos(path, namelen);
+ namelen = strlen(ce->name);
+ pos = cache_name_pos(ce->name, namelen);
if (0 <= pos)
return cnt; /* we have it as nondirectory */
pos = -pos - 1;
@@ -451,7 +481,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
struct cache_entry *ce = active_cache[i];
int len = ce_namelen(ce);
if (len < namelen ||
- strncmp(path, ce->name, namelen) ||
+ strncmp(ce->name, ce->name, namelen) ||
ce->name[namelen] != '/')
break;
/*
@@ -469,16 +499,16 @@ static int verify_clean_subdirectory(const char *path, const char *action,
* present file that is not ignored.
*/
pathbuf = xmalloc(namelen + 2);
- memcpy(pathbuf, path, namelen);
+ memcpy(pathbuf, ce->name, namelen);
strcpy(pathbuf+namelen, "/");
memset(&d, 0, sizeof(d));
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
- i = read_directory(&d, path, pathbuf, namelen+1, NULL);
+ i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
if (i)
die("Updating '%s' would lose untracked files in it",
- path);
+ ce->name);
free(pathbuf);
return cnt;
}
@@ -487,7 +517,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
* We do not want to remove or overwrite a working tree file that
* is not tracked, unless it is ignored.
*/
-static void verify_absent(const char *path, const char *action,
+static void verify_absent(struct cache_entry *ce, const char *action,
struct unpack_trees_options *o)
{
struct stat st;
@@ -495,15 +525,15 @@ static void verify_absent(const char *path, const char *action,
if (o->index_only || o->reset || !o->update)
return;
- if (has_symlink_leading_path(path, NULL))
+ if (has_symlink_leading_path(ce->name, NULL))
return;
- if (!lstat(path, &st)) {
+ if (!lstat(ce->name, &st)) {
int cnt;
- if (o->dir && excluded(o->dir, path))
+ if (o->dir && excluded(o->dir, ce->name))
/*
- * path is explicitly excluded, so it is Ok to
+ * ce->name is explicitly excluded, so it is Ok to
* overwrite it.
*/
return;
@@ -515,7 +545,7 @@ static void verify_absent(const char *path, const char *action,
* files that are in "foo/" we would lose
* it.
*/
- cnt = verify_clean_subdirectory(path, action, o);
+ cnt = verify_clean_subdirectory(ce, action, o);
/*
* If this removed entries from the index,
@@ -543,7 +573,7 @@ static void verify_absent(const char *path, const char *action,
* delete this path, which is in a subdirectory that
* is being replaced with a blob.
*/
- cnt = cache_name_pos(path, strlen(path));
+ cnt = cache_name_pos(ce->name, strlen(ce->name));
if (0 <= cnt) {
struct cache_entry *ce = active_cache[cnt];
if (!ce_stage(ce) && !ce->ce_mode)
@@ -551,7 +581,7 @@ static void verify_absent(const char *path, const char *action,
}
die("Untracked working tree file '%s' "
- "would be %s by merge.", path, action);
+ "would be %s by merge.", ce->name, action);
}
}
@@ -575,7 +605,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
}
}
else {
- verify_absent(merge->name, "overwritten", o);
+ verify_absent(merge, "overwritten", o);
invalidate_ce_path(merge);
}
@@ -590,7 +620,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
if (old)
verify_uptodate(old, o);
else
- verify_absent(ce->name, "removed", o);
+ verify_absent(ce, "removed", o);
ce->ce_mode = 0;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
invalidate_ce_path(ce);
@@ -620,7 +650,8 @@ static void show_stage_entry(FILE *o,
#endif
int threeway_merge(struct cache_entry **stages,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *index;
struct cache_entry *head;
@@ -698,8 +729,10 @@ int threeway_merge(struct cache_entry **stages,
}
/* #1 */
- if (!head && !remote && any_anc_missing)
+ if (!head && !remote && any_anc_missing) {
+ remove_entry(remove);
return 0;
+ }
/* Under the new "aggressive" rule, we resolve mostly trivial
* cases that we historically had git-merge-one-file resolve.
@@ -707,18 +740,18 @@ int threeway_merge(struct cache_entry **stages,
if (o->aggressive) {
int head_deleted = !head && !df_conflict_head;
int remote_deleted = !remote && !df_conflict_remote;
- const char *path = NULL;
+ struct cache_entry *ce = NULL;
if (index)
- path = index->name;
+ ce = index;
else if (head)
- path = head->name;
+ ce = head;
else if (remote)
- path = remote->name;
+ ce = remote;
else {
for (i = 1; i < o->head_idx; i++) {
if (stages[i] && stages[i] != o->df_conflict_entry) {
- path = stages[i]->name;
+ ce = stages[i];
break;
}
}
@@ -731,10 +764,11 @@ int threeway_merge(struct cache_entry **stages,
if ((head_deleted && remote_deleted) ||
(head_deleted && remote && remote_match) ||
(remote_deleted && head && head_match)) {
+ remove_entry(remove);
if (index)
return deleted_entry(index, index, o);
- else if (path && !head_deleted)
- verify_absent(path, "removed", o);
+ else if (ce && !head_deleted)
+ verify_absent(ce, "removed", o);
return 0;
}
/*
@@ -753,6 +787,7 @@ int threeway_merge(struct cache_entry **stages,
verify_uptodate(index, o);
}
+ remove_entry(remove);
o->nontrivial_merge = 1;
/* #2, #3, #4, #6, #7, #9, #10, #11. */
@@ -788,7 +823,8 @@ int threeway_merge(struct cache_entry **stages,
*
*/
int twoway_merge(struct cache_entry **src,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *current = src[0];
struct cache_entry *oldtree = src[1];
@@ -816,6 +852,7 @@ int twoway_merge(struct cache_entry **src,
}
else if (oldtree && !newtree && same(current, oldtree)) {
/* 10 or 11 */
+ remove_entry(remove);
return deleted_entry(oldtree, current, o);
}
else if (oldtree && newtree &&
@@ -825,6 +862,7 @@ int twoway_merge(struct cache_entry **src,
}
else {
/* all other failures */
+ remove_entry(remove);
if (oldtree)
reject_merge(oldtree);
if (current)
@@ -836,8 +874,8 @@ int twoway_merge(struct cache_entry **src,
}
else if (newtree)
return merged_entry(newtree, current, o);
- else
- return deleted_entry(oldtree, current, o);
+ remove_entry(remove);
+ return deleted_entry(oldtree, current, o);
}
/*
@@ -847,7 +885,8 @@ int twoway_merge(struct cache_entry **src,
* stage0 does not have anything there.
*/
int bind_merge(struct cache_entry **src,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -870,7 +909,8 @@ int bind_merge(struct cache_entry **src,
* - take the stat information from stage0, take the data from stage1
*/
int oneway_merge(struct cache_entry **src,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -879,8 +919,10 @@ int oneway_merge(struct cache_entry **src,
return error("Cannot do a oneway merge of %d trees",
o->merge_size);
- if (!a)
+ if (!a) {
+ remove_entry(remove);
return deleted_entry(old, old, o);
+ }
if (old && same(old, a)) {
if (o->reset) {
struct stat st;
diff --git a/unpack-trees.h b/unpack-trees.h
index fee7da4382..5517faafad 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -4,7 +4,8 @@
struct unpack_trees_options;
typedef int (*merge_fn_t)(struct cache_entry **src,
- struct unpack_trees_options *options);
+ struct unpack_trees_options *options,
+ int remove);
struct unpack_trees_options {
int reset;
@@ -26,12 +27,12 @@ struct unpack_trees_options {
struct cache_entry *df_conflict_entry;
};
-extern int unpack_trees(struct object_list *trees,
+extern int unpack_trees(unsigned n, struct tree_desc *t,
struct unpack_trees_options *options);
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int);
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
+int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
#endif
diff --git a/upload-pack.c b/upload-pack.c
index d3a09e78d5..fe96ef15c4 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -62,7 +62,7 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
return safe_write(fd, data, sz);
}
-FILE *pack_pipe = NULL;
+static FILE *pack_pipe = NULL;
static void show_commit(struct commit *commit)
{
if (commit->object.flags & BOUNDARY)
@@ -678,7 +678,7 @@ int main(int argc, char **argv)
break;
}
}
-
+
if (i != argc-1)
usage(upload_pack_usage);
dir = argv[i];
diff --git a/var.c b/var.c
index e585e59d31..4127031910 100644
--- a/var.c
+++ b/var.c
@@ -67,8 +67,8 @@ int main(int argc, char **argv)
val = read_var(argv[1]);
if (!val)
usage(var_usage);
-
+
printf("%s\n", val);
-
+
return 0;
}
diff --git a/write_or_die.c b/write_or_die.c
index 5c4bc8515a..e125e11d3b 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -1,5 +1,45 @@
#include "cache.h"
+/*
+ * Some cases use stdio, but want to flush after the write
+ * to get error handling (and to get better interactive
+ * behaviour - not buffering excessively).
+ *
+ * Of course, if the flush happened within the write itself,
+ * we've already lost the error code, and cannot report it any
+ * more. So we just ignore that case instead (and hope we get
+ * the right error code on the flush).
+ *
+ * If the file handle is stdout, and stdout is a file, then skip the
+ * flush entirely since it's not needed.
+ */
+void maybe_flush_or_die(FILE *f, const char *desc)
+{
+ static int skip_stdout_flush = -1;
+ struct stat st;
+ char *cp;
+
+ if (f == stdout) {
+ if (skip_stdout_flush < 0) {
+ cp = getenv("GIT_FLUSH");
+ if (cp)
+ skip_stdout_flush = (atoi(cp) == 0);
+ else if ((fstat(fileno(stdout), &st) == 0) &&
+ S_ISREG(st.st_mode))
+ skip_stdout_flush = 1;
+ else
+ skip_stdout_flush = 0;
+ }
+ if (skip_stdout_flush && !ferror(f))
+ return;
+ }
+ if (fflush(f)) {
+ if (errno == EPIPE)
+ exit(0);
+ die("write failure on %s: %s", desc, strerror(errno));
+ }
+}
+
int read_in_full(int fd, void *buf, size_t count)
{
char *p = buf;
diff --git a/wt-status.c b/wt-status.c
index 4bfe8f15d8..52054201c2 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -198,7 +198,7 @@ static void wt_read_cache(struct wt_status *s)
read_cache();
}
-void wt_status_print_initial(struct wt_status *s)
+static void wt_status_print_initial(struct wt_status *s)
{
int i;
char buf[PATH_MAX];
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 963bb89b08..be866d12d3 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -130,4 +130,73 @@ int buffer_is_binary(const char *ptr, unsigned long size)
return !!memchr(ptr, 0, size);
}
+struct ff_regs {
+ int nr;
+ struct ff_reg {
+ regex_t re;
+ int negate;
+ } *array;
+};
+static long ff_regexp(const char *line, long len,
+ char *buffer, long buffer_size, void *priv)
+{
+ char *line_buffer = xstrndup(line, len); /* make NUL terminated */
+ struct ff_regs *regs = priv;
+ regmatch_t pmatch[2];
+ int result = 0, i;
+
+ for (i = 0; i < regs->nr; i++) {
+ struct ff_reg *reg = regs->array + i;
+ if (reg->negate ^ !!regexec(&reg->re,
+ line_buffer, 2, pmatch, 0)) {
+ free(line_buffer);
+ return -1;
+ }
+ }
+ i = pmatch[1].rm_so >= 0 ? 1 : 0;
+ line += pmatch[i].rm_so;
+ result = pmatch[i].rm_eo - pmatch[i].rm_so;
+ if (result > buffer_size)
+ result = buffer_size;
+ else
+ while (result > 0 && (isspace(line[result - 1]) ||
+ line[result - 1] == '\n'))
+ result--;
+ memcpy(buffer, line, result);
+ free(line_buffer);
+ return result;
+}
+
+void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value)
+{
+ int i;
+ struct ff_regs *regs;
+
+ xecfg->find_func = ff_regexp;
+ regs = xecfg->find_func_priv = xmalloc(sizeof(struct ff_regs));
+ for (i = 0, regs->nr = 1; value[i]; i++)
+ if (value[i] == '\n')
+ regs->nr++;
+ regs->array = xmalloc(regs->nr * sizeof(struct ff_reg));
+ for (i = 0; i < regs->nr; i++) {
+ struct ff_reg *reg = regs->array + i;
+ const char *ep = strchr(value, '\n'), *expression;
+ char *buffer = NULL;
+
+ reg->negate = (*value == '!');
+ if (reg->negate && i == regs->nr - 1)
+ die("Last expression must not be negated: %s", value);
+ if (*value == '!')
+ value++;
+ if (ep)
+ expression = buffer = xstrndup(value, ep - value);
+ else
+ expression = value;
+ if (regcomp(&reg->re, expression, 0))
+ die("Invalid regexp to look for hunk header: %s", expression);
+ if (buffer)
+ free(buffer);
+ value = ep + 1;
+ }
+}
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 536f4e4d97..fb742dbb6b 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -20,4 +20,6 @@ int parse_hunk_header(char *line, int len,
int read_mmfile(mmfile_t *ptr, const char *filename);
int buffer_is_binary(const char *ptr, unsigned long size);
+extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line);
+
#endif
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index e874a7c46a..c00ddaa6e9 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -73,9 +73,13 @@ typedef struct s_xdemitcb {
int (*outf)(void *, mmbuffer_t *, int);
} xdemitcb_t;
+typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
+
typedef struct s_xdemitconf {
long ctxlen;
unsigned long flags;
+ find_func_t find_func;
+ void *find_func_priv;
} xdemitconf_t;
typedef struct s_bdiffparam {
@@ -103,4 +107,3 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
#endif /* #ifdef __cplusplus */
#endif /* #if !defined(XDIFF_H) */
-
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 9aeebc473b..5cb7171a8f 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -565,4 +565,3 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
return 0;
}
-
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index 472aeaecfa..3e099dc445 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -57,4 +57,3 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
#endif /* #if !defined(XDIFFI_H) */
-
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index e291dc7608..d3d9c845c6 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -69,7 +69,24 @@ static xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
}
-static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll) {
+static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+{
+ if (len > 0 &&
+ (isalpha((unsigned char)*rec) || /* identifier? */
+ *rec == '_' || /* also identifier? */
+ *rec == '$')) { /* identifiers from VMS and other esoterico */
+ if (len > sz)
+ len = sz;
+ while (0 < len && isspace((unsigned char)rec[len - 1]))
+ len--;
+ memcpy(buf, rec, len);
+ return len;
+ }
+ return -1;
+}
+
+static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll,
+ find_func_t ff, void *ff_priv) {
/*
* Be quite stupid about this for now. Find a line in the old file
@@ -80,27 +97,17 @@ static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll) {
const char *rec;
long len;
- *ll = 0;
while (i-- > 0) {
len = xdl_get_rec(xf, i, &rec);
- if (len > 0 &&
- (isalpha((unsigned char)*rec) || /* identifier? */
- *rec == '_' || /* also identifier? */
- *rec == '$')) { /* mysterious GNU diff's invention */
- if (len > sz)
- len = sz;
- while (0 < len && isspace((unsigned char)rec[len - 1]))
- len--;
- memcpy(buf, rec, len);
- *ll = len;
+ if ((*ll = ff(rec, len, buf, sz, ff_priv)) >= 0)
return;
- }
}
+ *ll = 0;
}
-int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg) {
+static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg) {
xdfile_t *xdf = &xe->xdf1;
const char *rchg = xdf->rchg;
long ix;
@@ -120,6 +127,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdchange_t *xch, *xche;
char funcbuf[80];
long funclen = 0;
+ find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
if (xecfg->flags & XDL_EMIT_COMMON)
return xdl_emit_common(xe, xscr, ecb, xecfg);
@@ -143,7 +151,8 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
xdl_find_func(&xe->xdf1, s1, funcbuf,
- sizeof(funcbuf), &funclen);
+ sizeof(funcbuf), &funclen,
+ ff, xecfg->find_func_priv);
}
if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
funcbuf, funclen, ecb) < 0)
@@ -194,4 +203,3 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
return 0;
}
-
diff --git a/xdiff/xemit.h b/xdiff/xemit.h
index e629417dd2..440a7390fa 100644
--- a/xdiff/xemit.h
+++ b/xdiff/xemit.h
@@ -31,4 +31,3 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
#endif /* #if !defined(XEMIT_H) */
-
diff --git a/xdiff/xinclude.h b/xdiff/xinclude.h
index 04a9da82c9..526ccb344d 100644
--- a/xdiff/xinclude.h
+++ b/xdiff/xinclude.h
@@ -40,4 +40,3 @@
#endif /* #if !defined(XINCLUDE_H) */
-
diff --git a/xdiff/xmacros.h b/xdiff/xmacros.h
index e2cd2023b3..8ef232cfad 100644
--- a/xdiff/xmacros.h
+++ b/xdiff/xmacros.h
@@ -51,4 +51,3 @@ do { \
#endif /* #if !defined(XMACROS_H) */
-
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index 1be7b31950..e87ab57c65 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -466,4 +466,3 @@ static int xdl_optimize_ctxs(xdfile_t *xdf1, xdfile_t *xdf2) {
return 0;
}
-
diff --git a/xdiff/xprepare.h b/xdiff/xprepare.h
index 344c569e8b..8fb06a5374 100644
--- a/xdiff/xprepare.h
+++ b/xdiff/xprepare.h
@@ -32,4 +32,3 @@ void xdl_free_env(xdfenv_t *xe);
#endif /* #if !defined(XPREPARE_H) */
-
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index 3593a664fc..2511aef8d8 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -65,4 +65,3 @@ typedef struct s_xdfenv {
#endif /* #if !defined(XTYPES_H) */
-
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index bf91c0f73c..2ade97b257 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -380,4 +380,3 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
return 0;
}
-
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index 70d8b9838a..d5de8292e0 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -45,4 +45,3 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
#endif /* #if !defined(XUTILS_H) */
-