summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Documentation/Makefile1
-rw-r--r--Documentation/RelNotes/1.7.6.5.txt26
-rw-r--r--Documentation/RelNotes/1.7.7.4.txt14
-rw-r--r--Documentation/RelNotes/1.7.7.5.txt14
-rw-r--r--Documentation/RelNotes/1.7.8.1.txt17
-rw-r--r--Documentation/RelNotes/1.7.8.txt17
-rw-r--r--Documentation/RelNotes/1.7.9.txt113
-rw-r--r--Documentation/config.txt43
-rw-r--r--Documentation/git-branch.txt5
-rw-r--r--Documentation/git-cherry-pick.txt3
-rw-r--r--Documentation/git-commit-tree.txt16
-rw-r--r--Documentation/git-credential-cache--daemon.txt26
-rw-r--r--Documentation/git-credential-cache.txt77
-rw-r--r--Documentation/git-credential-store.txt75
-rw-r--r--Documentation/git-difftool.txt2
-rw-r--r--Documentation/git-fsck.txt11
-rw-r--r--Documentation/git-mv.txt8
-rw-r--r--Documentation/git-pull.txt2
-rw-r--r--Documentation/git-reset.txt8
-rw-r--r--Documentation/git-revert.txt3
-rw-r--r--Documentation/git-stripspace.txt69
-rw-r--r--Documentation/git-symbolic-ref.txt9
-rw-r--r--Documentation/git-tag.txt7
-rw-r--r--Documentation/git.txt14
-rw-r--r--Documentation/gitattributes.txt2
-rw-r--r--Documentation/gitcredentials.txt183
-rw-r--r--Documentation/merge-options.txt1
-rw-r--r--Documentation/sequencer.txt13
-rw-r--r--Documentation/technical/api-credentials.txt245
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile63
l---------RelNotes2
-rw-r--r--archive.c18
-rw-r--r--branch.c42
-rw-r--r--branch.h8
-rw-r--r--builtin.h9
-rw-r--r--builtin/add.c5
-rw-r--r--builtin/apply.c13
-rw-r--r--builtin/archive.c6
-rw-r--r--builtin/blame.c2
-rw-r--r--builtin/branch.c87
-rw-r--r--builtin/checkout.c103
-rw-r--r--builtin/commit-tree.c74
-rw-r--r--builtin/commit.c39
-rw-r--r--builtin/diff.c2
-rw-r--r--builtin/fetch-pack.c15
-rw-r--r--builtin/fetch.c90
-rw-r--r--builtin/fmt-merge-msg.c190
-rw-r--r--builtin/for-each-ref.c7
-rw-r--r--builtin/fsck.c80
-rw-r--r--builtin/gc.c4
-rw-r--r--builtin/grep.c11
-rw-r--r--builtin/index-pack.c4
-rw-r--r--builtin/log.c71
-rw-r--r--builtin/merge.c242
-rw-r--r--builtin/mv.c6
-rw-r--r--builtin/name-rev.c2
-rw-r--r--builtin/notes.c9
-rw-r--r--builtin/pack-objects.c119
-rw-r--r--builtin/prune.c13
-rw-r--r--builtin/receive-pack.c6
-rw-r--r--builtin/reflog.c2
-rw-r--r--builtin/remote.c10
-rw-r--r--builtin/replace.c4
-rw-r--r--builtin/reset.c7
-rw-r--r--builtin/revert.c312
-rw-r--r--builtin/send-pack.c3
-rw-r--r--builtin/show-branch.c6
-rw-r--r--builtin/show-ref.c2
-rw-r--r--builtin/stripspace.c4
-rw-r--r--builtin/symbolic-ref.c2
-rw-r--r--builtin/tag.c145
-rw-r--r--builtin/upload-archive.c63
-rw-r--r--builtin/verify-tag.c35
-rw-r--r--bulk-checkin.c275
-rw-r--r--bulk-checkin.h16
-rw-r--r--bundle.c2
-rw-r--r--cache-tree.c19
-rw-r--r--cache-tree.h4
-rw-r--r--cache.h15
-rw-r--r--commit.c180
-rw-r--r--commit.h37
-rw-r--r--compat/mingw.c2
-rw-r--r--compat/mingw.h4
-rw-r--r--compat/msvc.h1
-rw-r--r--compat/snprintf.c9
-rw-r--r--compat/strtoimax.c10
-rw-r--r--compat/vcbuild/include/arpa/inet.h1
-rw-r--r--compat/vcbuild/include/grp.h1
-rw-r--r--compat/vcbuild/include/inttypes.h1
-rw-r--r--compat/vcbuild/include/netdb.h1
-rw-r--r--compat/vcbuild/include/netinet/in.h1
-rw-r--r--compat/vcbuild/include/netinet/tcp.h1
-rw-r--r--compat/vcbuild/include/pwd.h1
-rw-r--r--compat/vcbuild/include/sys/ioctl.h1
-rw-r--r--compat/vcbuild/include/sys/select.h1
-rw-r--r--compat/vcbuild/include/sys/socket.h1
-rw-r--r--compat/vcbuild/include/sys/wait.h1
-rw-r--r--compat/vcbuild/include/termios.h1
-rw-r--r--config.c49
-rw-r--r--connect.c37
-rwxr-xr-xcontrib/completion/git-completion.bash201
-rwxr-xr-xcontrib/fast-import/git-p472
-rw-r--r--contrib/fast-import/git-p4.txt19
-rw-r--r--convert.c56
-rw-r--r--credential-cache--daemon.c269
-rw-r--r--credential-cache.c120
-rw-r--r--credential-store.c157
-rw-r--r--credential.c365
-rw-r--r--credential.h33
-rw-r--r--csum-file.c20
-rw-r--r--csum-file.h9
-rw-r--r--dir.c50
-rw-r--r--environment.c3
-rw-r--r--fast-import.c53
-rw-r--r--fmt-merge-msg.h7
-rwxr-xr-xgit-am.sh7
-rw-r--r--git-compat-util.h18
-rw-r--r--git-gui/.gitattributes1
-rwxr-xr-xgit-gui/GIT-VERSION-GEN2
-rwxr-xr-xgit-gui/git-gui.sh56
-rw-r--r--git-gui/lib/blame.tcl29
-rw-r--r--git-gui/lib/browser.tcl8
-rw-r--r--git-gui/lib/choose_rev.tcl1
-rw-r--r--git-gui/lib/class.tcl1
-rw-r--r--git-gui/lib/commit.tcl4
-rw-r--r--git-gui/lib/diff.tcl5
-rw-r--r--git-gui/lib/index.tcl24
-rw-r--r--git-gui/lib/line.tcl2
-rw-r--r--git-gui/lib/option.tcl20
-rw-r--r--git-gui/lib/search.tcl123
-rw-r--r--git-gui/lib/sshkey.tcl2
-rw-r--r--git-gui/lib/themed.tcl99
-rw-r--r--git-gui/lib/tools.tcl10
-rw-r--r--git-gui/lib/transport.tcl1
-rwxr-xr-xgit-pull.sh4
-rw-r--r--git-rebase--interactive.sh36
-rwxr-xr-xgit-request-pull.sh87
-rwxr-xr-xgit-svn.perl2
-rw-r--r--git.c1
-rwxr-xr-xgitk-git/gitk110
-rwxr-xr-xgitweb/gitweb.perl339
-rw-r--r--gitweb/static/gitweb.css30
-rw-r--r--gpg-interface.c138
-rw-r--r--gpg-interface.h10
-rw-r--r--http-fetch.c2
-rw-r--r--http-push.c2
-rw-r--r--http.c175
-rw-r--r--http.h5
-rw-r--r--imap-send.c1
-rw-r--r--list-objects.c9
-rw-r--r--mailmap.c5
-rw-r--r--merge-recursive.c15
-rw-r--r--notes-merge.c106
-rw-r--r--object.c2
-rw-r--r--pack-check.c28
-rw-r--r--pack-write.c57
-rw-r--r--pack.h15
-rw-r--r--perl/.gitignore1
-rw-r--r--reachable.c55
-rw-r--r--reachable.h3
-rw-r--r--read-cache.c125
-rw-r--r--reflog-walk.c16
-rw-r--r--refs.c76
-rw-r--r--remote-curl.c4
-rw-r--r--remote.c10
-rw-r--r--sequencer.c10
-rw-r--r--sequencer.h12
-rw-r--r--sha1_file.c72
-rw-r--r--strbuf.c51
-rw-r--r--strbuf.h13
-rw-r--r--submodule.c6
-rwxr-xr-xt/lib-credential.sh254
-rw-r--r--t/lib-httpd/apache.conf3
-rwxr-xr-xt/t0090-cache-tree.sh95
-rwxr-xr-xt/t0300-credentials.sh279
-rwxr-xr-xt/t0301-credential-cache.sh23
-rwxr-xr-xt/t0302-credential-store.sh9
-rwxr-xr-xt/t0303-credential-external.sh39
-rwxr-xr-xt/t1007-hash-object.sh2
-rwxr-xr-xt/t1013-loose-object-format.sh2
-rwxr-xr-xt/t1050-large.sh94
-rwxr-xr-xt/t1300-repo-config.sh2
-rwxr-xr-xt/t1412-reflog-loop.sh2
-rwxr-xr-xt/t1501-worktree.sh6
-rwxr-xr-xt/t1510-repo-setup.sh4
-rwxr-xr-xt/t1511-rev-parse-caret.sh2
-rwxr-xr-xt/t2018-checkout-branch.sh9
-rwxr-xr-xt/t2023-checkout-m.sh47
-rwxr-xr-xt/t3030-merge-recursive.sh72
-rwxr-xr-xt/t3040-subprojects-basic.sh144
-rwxr-xr-xt/t3200-branch.sh20
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh10
-rwxr-xr-xt/t3400-rebase.sh4
-rwxr-xr-xt/t3401-rebase-partial.sh62
-rwxr-xr-xt/t3418-rebase-continue.sh4
-rwxr-xr-xt/t3419-rebase-patch-id.sh2
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh54
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh285
-rwxr-xr-xt/t4018-diff-funcname.sh2
-rwxr-xr-xt/t4034-diff-words.sh1
-rw-r--r--t/t4034/matlab/expect14
-rw-r--r--t/t4034/matlab/post9
-rw-r--r--t/t4034/matlab/pre9
-rwxr-xr-xt/t4131-apply-fake-ancestor.sh2
-rwxr-xr-xt/t4136-apply-check.sh19
-rwxr-xr-xt/t4202-log.sh4
-rwxr-xr-xt/t5000-tar-tree.sh8
-rwxr-xr-xt/t5150-request-pull.sh10
-rwxr-xr-xt/t5500-fetch-pack.sh6
-rwxr-xr-xt/t5501-fetch-push-alternates.sh2
-rwxr-xr-xt/t5510-fetch.sh5
-rw-r--r--t/t5515/fetch.br-branches-default6
-rw-r--r--t/t5515/fetch.br-branches-default-merge6
-rw-r--r--t/t5515/fetch.br-branches-default-merge_branches-default6
-rw-r--r--t/t5515/fetch.br-branches-default-octopus6
-rw-r--r--t/t5515/fetch.br-branches-default-octopus_branches-default6
-rw-r--r--t/t5515/fetch.br-branches-default_branches-default6
-rw-r--r--t/t5515/fetch.br-branches-one6
-rw-r--r--t/t5515/fetch.br-branches-one-merge6
-rw-r--r--t/t5515/fetch.br-branches-one-merge_branches-one6
-rw-r--r--t/t5515/fetch.br-branches-one-octopus6
-rw-r--r--t/t5515/fetch.br-branches-one-octopus_branches-one6
-rw-r--r--t/t5515/fetch.br-branches-one_branches-one6
-rw-r--r--t/t5515/fetch.br-config-explicit6
-rw-r--r--t/t5515/fetch.br-config-explicit-merge6
-rw-r--r--t/t5515/fetch.br-config-explicit-merge_config-explicit6
-rw-r--r--t/t5515/fetch.br-config-explicit-octopus6
-rw-r--r--t/t5515/fetch.br-config-explicit-octopus_config-explicit6
-rw-r--r--t/t5515/fetch.br-config-explicit_config-explicit6
-rw-r--r--t/t5515/fetch.br-config-glob6
-rw-r--r--t/t5515/fetch.br-config-glob-merge6
-rw-r--r--t/t5515/fetch.br-config-glob-merge_config-glob6
-rw-r--r--t/t5515/fetch.br-config-glob-octopus6
-rw-r--r--t/t5515/fetch.br-config-glob-octopus_config-glob6
-rw-r--r--t/t5515/fetch.br-config-glob_config-glob6
-rw-r--r--t/t5515/fetch.br-remote-explicit6
-rw-r--r--t/t5515/fetch.br-remote-explicit-merge6
-rw-r--r--t/t5515/fetch.br-remote-explicit-merge_remote-explicit6
-rw-r--r--t/t5515/fetch.br-remote-explicit-octopus6
-rw-r--r--t/t5515/fetch.br-remote-explicit-octopus_remote-explicit6
-rw-r--r--t/t5515/fetch.br-remote-explicit_remote-explicit6
-rw-r--r--t/t5515/fetch.br-remote-glob6
-rw-r--r--t/t5515/fetch.br-remote-glob-merge6
-rw-r--r--t/t5515/fetch.br-remote-glob-merge_remote-glob6
-rw-r--r--t/t5515/fetch.br-remote-glob-octopus6
-rw-r--r--t/t5515/fetch.br-remote-glob-octopus_remote-glob6
-rw-r--r--t/t5515/fetch.br-remote-glob_remote-glob6
-rw-r--r--t/t5515/fetch.br-unconfig6
-rw-r--r--t/t5515/fetch.br-unconfig_--tags_.._.git6
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three6
-rw-r--r--t/t5515/fetch.br-unconfig_branches-default6
-rw-r--r--t/t5515/fetch.br-unconfig_branches-one6
-rw-r--r--t/t5515/fetch.br-unconfig_config-explicit6
-rw-r--r--t/t5515/fetch.br-unconfig_config-glob6
-rw-r--r--t/t5515/fetch.br-unconfig_remote-explicit6
-rw-r--r--t/t5515/fetch.br-unconfig_remote-glob6
-rw-r--r--t/t5515/fetch.master6
-rw-r--r--t/t5515/fetch.master_--tags_.._.git6
-rw-r--r--t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file6
-rw-r--r--t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three6
-rw-r--r--t/t5515/fetch.master_branches-default6
-rw-r--r--t/t5515/fetch.master_branches-one6
-rw-r--r--t/t5515/fetch.master_config-explicit6
-rw-r--r--t/t5515/fetch.master_config-glob6
-rw-r--r--t/t5515/fetch.master_remote-explicit6
-rw-r--r--t/t5515/fetch.master_remote-glob6
-rwxr-xr-xt/t5520-pull.sh23
-rwxr-xr-xt/t5527-fetch-odd-refs.sh29
-rwxr-xr-xt/t5540-http-push.sh34
-rwxr-xr-xt/t5550-http-fetch.sh68
-rwxr-xr-xt/t5700-clone-reference.sh7
-rwxr-xr-xt/t7106-reset-sequence.sh44
-rwxr-xr-xt/t7501-commit.sh319
-rwxr-xr-xt/t7600-merge.sh12
-rwxr-xr-xt/t7604-merge-custom-message.sh2
-rwxr-xr-xt/t7608-merge-messages.sh4
-rwxr-xr-xt/t9301-fast-import-notes.sh63
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh73
-rwxr-xr-xt/t9805-skip-submit-edit.sh104
-rwxr-xr-xt/t9807-submit.sh38
-rwxr-xr-xt/t9808-chdir.sh49
-rw-r--r--t/test-lib.sh5
-rw-r--r--tag.c5
-rw-r--r--test-credential.c38
-rw-r--r--test-dump-cache-tree.c2
-rw-r--r--test-scrap-cache-tree.c17
-rw-r--r--transport.c9
-rw-r--r--tree-diff.c22
-rw-r--r--tree-walk.c75
-rw-r--r--tree-walk.h18
-rw-r--r--tree.c11
-rw-r--r--unix-socket.c56
-rw-r--r--unix-socket.h7
-rw-r--r--unpack-trees.c6
-rw-r--r--userdiff.c5
-rw-r--r--wt-status.c4
-rw-r--r--zlib.c9
302 files changed, 7936 insertions, 2310 deletions
diff --git a/.gitignore b/.gitignore
index 8572c8c0b0..3b7680ea1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,9 @@
/git-commit-tree
/git-config
/git-count-objects
+/git-credential-cache
+/git-credential-cache--daemon
+/git-credential-store
/git-cvsexportcommit
/git-cvsimport
/git-cvsserver
@@ -167,10 +170,12 @@
/gitweb/static/gitweb.js
/gitweb/static/gitweb.min.*
/test-chmtime
+/test-credential
/test-ctype
/test-date
/test-delta
/test-dump-cache-tree
+/test-scrap-cache-tree
/test-genrandom
/test-index-version
/test-line-buffer
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 304b31edee..116f17587e 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -7,6 +7,7 @@ MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt \
MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
gitdiffcore.txt gitnamespaces.txt gitrevisions.txt gitworkflows.txt
+MAN7_TXT += gitcredentials.txt
MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
diff --git a/Documentation/RelNotes/1.7.6.5.txt b/Documentation/RelNotes/1.7.6.5.txt
new file mode 100644
index 0000000000..6713132a9e
--- /dev/null
+++ b/Documentation/RelNotes/1.7.6.5.txt
@@ -0,0 +1,26 @@
+Git v1.7.6.5 Release Notes
+==========================
+
+Fixes since v1.7.6.4
+--------------------
+
+ * The date parser did not accept timezone designators that lack minutes
+ part and also has a colon between "hh:mm".
+
+ * After fetching from a remote that has very long refname, the reporting
+ output could have corrupted by overrunning a static buffer.
+
+ * "git mergetool" did not use its arguments as pathspec, but as a path to
+ the file that may not even have any conflict.
+
+ * "git name-rev --all" tried to name all _objects_, naturally failing to
+ describe many blobs and trees, instead of showing only commits as
+ advertised in its documentation.
+
+ * "git remote rename $a $b" were not careful to match the remote name
+ against $a (i.e. source side of the remote nickname).
+
+ * "gitweb" used to produce a non-working link while showing the contents
+ of a blob, when JavaScript actions are enabled.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.7.4.txt b/Documentation/RelNotes/1.7.7.4.txt
new file mode 100644
index 0000000000..e5234485e7
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.4.txt
@@ -0,0 +1,14 @@
+Git v1.7.7.4 Release Notes
+==========================
+
+Fixes since v1.7.7.3
+--------------------
+
+ * A few header dependencies were missing from the Makefile.
+
+ * Some newer parts of the code used C99 __VA_ARGS__ while we still
+ try to cater to older compilers.
+
+ * "git name-rev --all" tried to name all _objects_, naturally failing to
+ describe many blobs and trees, instead of showing only commits as
+ advertised in its documentation.
diff --git a/Documentation/RelNotes/1.7.7.5.txt b/Documentation/RelNotes/1.7.7.5.txt
new file mode 100644
index 0000000000..7b0931987b
--- /dev/null
+++ b/Documentation/RelNotes/1.7.7.5.txt
@@ -0,0 +1,14 @@
+Git v1.7.7.5 Release Notes
+==========================
+
+Fixes since v1.7.7.4
+--------------------
+
+ * After fetching from a remote that has very long refname, the reporting
+ output could have corrupted by overrunning a static buffer.
+
+ * "git checkout" and "git merge" treated in-tree .gitignore and exclude
+ file in $GIT_DIR/info/ directory inconsistently when deciding which
+ untracked files are ignored and expendable.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.1.txt b/Documentation/RelNotes/1.7.8.1.txt
new file mode 100644
index 0000000000..0e8bd9f5ee
--- /dev/null
+++ b/Documentation/RelNotes/1.7.8.1.txt
@@ -0,0 +1,17 @@
+Git v1.7.8.1 Release Notes
+==========================
+
+Fixes since v1.7.8.1
+--------------------
+
+ * In some codepaths (notably, checkout and merge), the ignore patterns
+ recorded in $GIT_DIR/info/exclude were not honored. They now are.
+
+ * After fetching from a remote that has very long refname, the reporting
+ output could have corrupted by overrunning a static buffer.
+
+ * "git checkout" and "git merge" treated in-tree .gitignore and exclude
+ file in $GIT_DIR/info/ directory inconsistently when deciding which
+ untracked files are ignored and expendable.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.txt b/Documentation/RelNotes/1.7.8.txt
index b9bb32492f..b4d90bba0f 100644
--- a/Documentation/RelNotes/1.7.8.txt
+++ b/Documentation/RelNotes/1.7.8.txt
@@ -1,5 +1,5 @@
-Git v1.7.8 Release Notes (draft)
-================================
+Git v1.7.8 Release Notes
+========================
Updates since v1.7.7
--------------------
@@ -29,7 +29,7 @@ Updates since v1.7.7
files from the index, not from the working tree.
* Variants of "git cherry-pick" and "git revert" that take multiple
- commits learned to "--continue".
+ commits learned to "--continue" and "--abort".
* "git daemon" gives more human readble error messages to clients
using ERR packets when appropriate.
@@ -113,9 +113,6 @@ Updates since v1.7.7
between commits in the superproject that has and does not have the
submodule in the tree without re-cloning.
- * "git upload-archive" (hence "git archive --remote") can be built
- and used on mingw port of Git.
-
* "gitweb" leaked unescaped control characters from syntax hiliter
outputs.
@@ -162,11 +159,3 @@ included in this release.
were misspelled.
(merge c49904e fc/remote-seturl-usage-fix later to maint).
(merge 656cdf0 jc/remote-setbranches-usage-fix later to maint).
-
----
-exec >/var/tmp/1
-O=v1.7.8-rc1-23-gdc865af
-echo O=$(git describe --always master)
-git log --first-parent --oneline --reverse ^$O master
-echo
-git shortlog --no-merges ^$O master
diff --git a/Documentation/RelNotes/1.7.9.txt b/Documentation/RelNotes/1.7.9.txt
new file mode 100644
index 0000000000..ee72a845fe
--- /dev/null
+++ b/Documentation/RelNotes/1.7.9.txt
@@ -0,0 +1,113 @@
+Git v1.7.9 Release Notes (draft)
+========================
+
+Updates since v1.7.8
+--------------------
+
+ * gitk updates accumulated since early 2011.
+
+ * git-gui updated to 0.16.0.
+
+ * git-p4 (in contrib/) updates.
+
+ * Porcelain commands like "git reset" did not distinguish deletions
+ and type-changes from ordinary modification, and reported them with
+ the same 'M' moniker. They now use 'D' (for deletion) and 'T' (for
+ type-change) to match "git status -s" and "git diff --name-status".
+
+ * "git add" learned to stream large files directly into a packfile
+ instead of writing them into individual loose object files.
+
+ * "git branch -m <current branch> HEAD" is an obvious no-op and is
+ now allowed.
+
+ * "git checkout -B <current branch> <elsewhere>" is a more intuitive
+ way to spell "git reset --keep <elsewhere>".
+
+ * "git checkout" and "git merge" learned "--no-overwrite-ignore" option
+ to tell Git that untracked and ignored files are not expendable.
+
+ * "git commit --amend" learned "--no-edit" option to say that the
+ user is amending the tree being recorded, without updating the
+ commit log message.
+
+ * fsck and prune are relatively lengthy operations that still go
+ silent while making the end-user wait. They learned to give progress
+ output like other slow operations.
+
+ * The set of built-in function-header patterns for various languages
+ knows MATLAB.
+
+ * "git pull" can be used to fetch and merge an annotated/signed tag,
+ instead of the tip of a topic branch. The GPG signature from the
+ signed tag is recorded in the resulting merge commit for later
+ auditing.
+
+ * "git branch --edit-description" can be used to add descriptive text
+ to explain what a topic branch is about.
+
+ * "git fmt-merge-msg" learned to take the branch description into
+ account when preparing a merge summary that "git merge" records
+ when merging a local branch.
+
+ * "git request-pull" has been updated to convey more information
+ useful for integrators to decide if a topic is worth merging and
+ what is pulled is indeed what the requestor asked to pull,
+ including:
+
+ - the tip of the branch being requested to be merged;
+ - the branch description describing what the topic is about;
+ - the contents of the annotated tag, when requesting to pull a tag.
+
+ * "git pull" learned to notice 'pull.rebase' configuration variable,
+ which serves as a global fallback for setting 'branch.<name>.rebase'
+ configuration variable per branch.
+
+ * "git tag" learned "--cleanup" option to control how the whitespaces
+ and empty lines in tag message are cleaned up.
+
+ * "gitweb" learned to show side-by-side diff.
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.7.8
+------------------
+
+ * The function header pattern for files with "diff=cpp" attribute did
+ not consider "type *funcname(type param1,..." as the beginning of a
+ function.
+ (merge 37e7793 tr/userdiff-c-returns-pointer later to maint).
+
+ * LF-to-CRLF streaming filter used when checking out a large-ish blob
+ fell into an infinite loop with a rare input.
+ (merge 284e3d2 cn/maint-lf-to-crlf-filter later to maint).
+
+ * "git archive" mistakenly allowed remote clients to ask for commits
+ that are not at the tip of any ref.
+ (merge 7b51c33 jk/maint-upload-archive later to maint).
+
+ * "git apply --check" did not error out when given an empty input
+ without any patch.
+ (merge cc64b31 bc/maint-apply-check-no-patch later to maint).
+
+ * The error message from "git diff" and "git status" when they fail
+ to inspect changes in submodules did not report which submodule they
+ had trouble with.
+ (merge 6a5ceda jl/submodule-status-failure-report later to maint).
+
+ * "fast-import" did not correctly update an existing notes tree,
+ possibly corrupting the fan-out.
+
+ * When a "reword" action in "git rebase -i" failed to run "commit --amend",
+ we did not give the control back to the user to resolve the situation, and
+ instead kept the original commit log message.
+ (merge 0becb3e aw/rebase-i-stop-on-failure-to-amend later to maint).
+
+--
+exec >/var/tmp/1
+O=v1.7.8-282-ga2add85
+echo O=$(git describe master)
+git log --first-parent --oneline --reverse ^$O master
+echo
+git shortlog --no-merges ^$O ^maint master
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5a841da6d4..c6630c73e8 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -677,10 +677,12 @@ branch.<name>.mergeoptions::
branch.<name>.rebase::
When true, rebase the branch <name> on top of the fetched branch,
instead of merging the default branch from the default remote when
- "git pull" is run.
- *NOTE*: this is a possibly dangerous operation; do *not* use
- it unless you understand the implications (see linkgit:git-rebase[1]
- for details).
+ "git pull" is run. See "pull.rebase" for doing this in a non
+ branch-specific manner.
++
+*NOTE*: this is a possibly dangerous operation; do *not* use
+it unless you understand the implications (see linkgit:git-rebase[1]
+for details).
browser.<tool>.cmd::
Specify the command to invoke the specified browser. The
@@ -832,6 +834,29 @@ commit.template::
"{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
specified user's home directory.
+credential.helper::
+ Specify an external helper to be called when a username or
+ password credential is needed; the helper may consult external
+ storage to avoid prompting the user for the credentials. See
+ linkgit:gitcredentials[7] for details.
+
+credential.useHttpPath::
+ When acquiring credentials, consider the "path" component of an http
+ or https URL to be important. Defaults to false. See
+ linkgit:gitcredentials[7] for more information.
+
+credential.username::
+ If no username is set for a network authentication, use this username
+ by default. See credential.<context>.* below, and
+ linkgit:gitcredentials[7].
+
+credential.<url>.*::
+ Any of the credential.* options above can be applied selectively to
+ some credentials. For example "credential.https://example.com.username"
+ would set the default username only for https connections to
+ example.com. See linkgit:gitcredentials[7] for details on how URLs are
+ matched.
+
include::diff-config.txt[]
difftool.<tool>.path::
@@ -1590,6 +1615,16 @@ pretty.<name>::
Note that an alias with the same name as a built-in format
will be silently ignored.
+pull.rebase::
+ When true, rebase branches on top of the fetched branch, instead
+ of merging the default branch from the default remote when "git
+ pull" is run. See "branch.<name>.rebase" for setting this on a
+ per-branch basis.
++
+*NOTE*: this is a possibly dangerous operation; do *not* use
+it unless you understand the implications (see linkgit:git-rebase[1]
+for details).
+
pull.octopus::
The default merge strategy to use when pulling multiple branches
at once.
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index f46013c91f..0427e80a35 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -14,6 +14,7 @@ SYNOPSIS
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (-m | -M) [<oldbranch>] <newbranch>
'git branch' (-d | -D) [-r] <branchname>...
+'git branch' --edit-description [<branchname>]
DESCRIPTION
-----------
@@ -158,6 +159,10 @@ start-point is either a local or remote-tracking branch.
like '--track' would when creating the branch, except that where
branch points to is not changed.
+--edit-description::
+ Open an editor and edit the text to explain what the branch is
+ for, to be used by various other commands (e.g. `request-pull`).
+
--contains <commit>::
Only list branches which contain the specified commit.
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 2660a842fc..fed5097e00 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -9,8 +9,9 @@ SYNOPSIS
--------
[verse]
'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
-'git cherry-pick' --reset
'git cherry-pick' --continue
+'git cherry-pick' --quit
+'git cherry-pick' --abort
DESCRIPTION
-----------
diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt
index 02133d5fc9..cfb9906bb5 100644
--- a/Documentation/git-commit-tree.txt
+++ b/Documentation/git-commit-tree.txt
@@ -9,7 +9,8 @@ git-commit-tree - Create a new commit object
SYNOPSIS
--------
[verse]
-'git commit-tree' <tree> [(-p <parent commit>)...] < changelog
+'git commit-tree' <tree> [(-p <parent>)...] < changelog
+'git commit-tree' [(-p <parent>)...] [(-m <message>)...] [(-F <file>)...] <tree>
DESCRIPTION
-----------
@@ -17,7 +18,8 @@ This is usually not what an end user wants to run directly. See
linkgit:git-commit[1] instead.
Creates a new commit object based on the provided tree object and
-emits the new commit object id on stdout.
+emits the new commit object id on stdout. The log message is read
+from the standard input, unless `-m` or `-F` options are given.
A commit object may have any number of parents. With exactly one
parent, it is an ordinary commit. Having more than one parent makes
@@ -39,9 +41,17 @@ OPTIONS
<tree>::
An existing tree object
--p <parent commit>::
+-p <parent>::
Each '-p' indicates the id of a parent commit object.
+-m <message>::
+ A paragraph in the commig log message. This can be given more than
+ once and each <message> becomes its own paragraph.
+
+-F <file>::
+ Read the commit log message from the given file. Use `-` to read
+ from the standard input.
+
Commit Information
------------------
diff --git a/Documentation/git-credential-cache--daemon.txt b/Documentation/git-credential-cache--daemon.txt
new file mode 100644
index 0000000000..11edc5a173
--- /dev/null
+++ b/Documentation/git-credential-cache--daemon.txt
@@ -0,0 +1,26 @@
+git-credential-cache--daemon(1)
+===============================
+
+NAME
+----
+git-credential-cache--daemon - temporarily store user credentials in memory
+
+SYNOPSIS
+--------
+[verse]
+git credential-cache--daemon <socket>
+
+DESCRIPTION
+-----------
+
+NOTE: You probably don't want to invoke this command yourself; it is
+started automatically when you use linkgit:git-credential-cache[1].
+
+This command listens on the Unix domain socket specified by `<socket>`
+for `git-credential-cache` clients. Clients may store and retrieve
+credentials. Each credential is held for a timeout specified by the
+client; once no credentials are held, the daemon exits.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-credential-cache.txt b/Documentation/git-credential-cache.txt
new file mode 100644
index 0000000000..f3d09c5d51
--- /dev/null
+++ b/Documentation/git-credential-cache.txt
@@ -0,0 +1,77 @@
+git-credential-cache(1)
+=======================
+
+NAME
+----
+git-credential-cache - helper to temporarily store passwords in memory
+
+SYNOPSIS
+--------
+-----------------------------
+git config credential.helper 'cache [options]'
+-----------------------------
+
+DESCRIPTION
+-----------
+
+This command caches credentials in memory for use by future git
+programs. The stored credentials never touch the disk, and are forgotten
+after a configurable timeout. The cache is accessible over a Unix
+domain socket, restricted to the current user by filesystem permissions.
+
+You probably don't want to invoke this command directly; it is meant to
+be used as a credential helper by other parts of git. See
+linkgit:gitcredentials[7] or `EXAMPLES` below.
+
+OPTIONS
+-------
+
+--timeout <seconds>::
+
+ Number of seconds to cache credentials (default: 900).
+
+--socket <path>::
+
+ Use `<path>` to contact a running cache daemon (or start a new
+ cache daemon if one is not started). Defaults to
+ `~/.git-credential-cache/socket`. If your home directory is on a
+ network-mounted filesystem, you may need to change this to a
+ local filesystem.
+
+CONTROLLING THE DAEMON
+----------------------
+
+If you would like the daemon to exit early, forgetting all cached
+credentials before their timeout, you can issue an `exit` action:
+
+--------------------------------------
+git credential-cache exit
+--------------------------------------
+
+EXAMPLES
+--------
+
+The point of this helper is to reduce the number of times you must type
+your username or password. For example:
+
+------------------------------------
+$ git config credential.helper cache
+$ git push http://example.com/repo.git
+Username: <type your username>
+Password: <type your password>
+
+[work for 5 more minutes]
+$ git push http://example.com/repo.git
+[your credentials are used automatically]
+------------------------------------
+
+You can provide options via the credential.helper configuration
+variable (this example drops the cache time to 5 minutes):
+
+-------------------------------------------------------
+$ git config credential.helper 'cache --timeout=300'
+-------------------------------------------------------
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
new file mode 100644
index 0000000000..31093467d1
--- /dev/null
+++ b/Documentation/git-credential-store.txt
@@ -0,0 +1,75 @@
+git-credential-store(1)
+=======================
+
+NAME
+----
+git-credential-store - helper to store credentials on disk
+
+SYNOPSIS
+--------
+-------------------
+git config credential.helper 'store [options]'
+-------------------
+
+DESCRIPTION
+-----------
+
+NOTE: Using this helper will store your passwords unencrypted on disk,
+protected only by filesystem permissions. If this is not an acceptable
+security tradeoff, try linkgit:git-credential-cache[1], or find a helper
+that integrates with secure storage provided by your operating system.
+
+This command stores credentials indefinitely on disk for use by future
+git programs.
+
+You probably don't want to invoke this command directly; it is meant to
+be used as a credential helper by other parts of git. See
+linkgit:gitcredentials[7] or `EXAMPLES` below.
+
+OPTIONS
+-------
+
+--store=<path>::
+
+ Use `<path>` to store credentials. The file will have its
+ filesystem permissions set to prevent other users on the system
+ from reading it, but will not be encrypted or otherwise
+ protected. Defaults to `~/.git-credentials`.
+
+EXAMPLES
+--------
+
+The point of this helper is to reduce the number of times you must type
+your username or password. For example:
+
+------------------------------------------
+$ git config credential.helper store
+$ git push http://example.com/repo.git
+Username: <type your username>
+Password: <type your password>
+
+[several days later]
+$ git push http://example.com/repo.git
+[your credentials are used automatically]
+------------------------------------------
+
+STORAGE FORMAT
+--------------
+
+The `.git-credentials` file is stored in plaintext. Each credential is
+stored on its own line as a URL like:
+
+------------------------------
+https://user:pass@example.com
+------------------------------
+
+When git needs authentication for a particular URL context,
+credential-store will consider that context a pattern to match against
+each entry in the credentials file. If the protocol, hostname, and
+username (if we already have one) match, then the password is returned
+to git. See the discussion of configuration in linkgit:gitcredentials[7]
+for more information.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index a03515f1ec..19d473c070 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -31,7 +31,7 @@ OPTIONS
-t <tool>::
--tool=<tool>::
Use the diff tool specified by <tool>.
- Valid merge tools are:
+ Valid diff tools are:
araxis, bc3, diffuse, emerge, ecmerge, gvimdiff, kdiff3,
kompare, meld, opendiff, p4merge, tkdiff, vimdiff and xxdiff.
+
diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt
index a2a508dc28..0a17b4258e 100644
--- a/Documentation/git-fsck.txt
+++ b/Documentation/git-fsck.txt
@@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
- [--[no-]full] [--strict] [--verbose] [--lost-found] [<object>*]
+ [--[no-]full] [--strict] [--verbose] [--lost-found]
+ [--[no-]progress] [<object>*]
DESCRIPTION
-----------
@@ -72,6 +73,14 @@ index file, all SHA1 references in .git/refs/*, and all reflogs (unless
a blob, the contents are written into the file, rather than
its object name.
+--progress::
+--no-progress::
+ Progress status is reported on the standard error stream by
+ default when it is attached to a terminal, unless
+ --no-progress or --verbose is specified. --progress forces
+ progress status even if the standard error stream is not
+ directed to a terminal.
+
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
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index b8db373964..e3c8448614 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -15,8 +15,8 @@ DESCRIPTION
-----------
This script is used to move or rename a file, directory or symlink.
- git mv [-f] [-n] <source> <destination>
- git mv [-f] [-n] [-k] <source> ... <destination directory>
+ git mv [-v] [-f] [-n] [-k] <source> <destination>
+ git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
In the first form, it renames <source>, which must exist and be either
a file, symlink or directory, to <destination>.
@@ -40,6 +40,10 @@ OPTIONS
--dry-run::
Do nothing; only show what would happen
+-v::
+--verbose::
+ Report the names of files as they are moved.
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index e1da468766..0f18ec891a 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -108,7 +108,7 @@ include::merge-options.txt[]
fetched, the rebase uses that information to avoid rebasing
non-local changes.
+
-See `branch.<name>.rebase` and `branch.autosetuprebase` in
+See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
linkgit:git-config[1] if you want to make `git pull` always use
`{litdd}rebase` instead of merging.
+
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index b2832fc7eb..b674866e6d 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -9,8 +9,8 @@ SYNOPSIS
--------
[verse]
'git reset' [-q] [<commit>] [--] <paths>...
-'git reset' [--patch|-p] [<commit>] [--] [<paths>...]
-'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
+'git reset' (--patch | -p) [<commit>] [--] [<paths>...]
+'git reset' (--soft | --mixed | --hard | --merge | --keep) [-q] [<commit>]
DESCRIPTION
-----------
@@ -34,7 +34,7 @@ Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
can copy the contents of a path out of a commit to the index and to the
working tree in one go.
-'git reset' --patch|-p [<commit>] [--] [<paths>...]::
+'git reset' (--patch | -p) [<commit>] [--] [<paths>...]::
Interactively select hunks in the difference between the index
and <commit> (defaults to HEAD). The chosen hunks are applied
in reverse to the index.
@@ -43,7 +43,7 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
you can use it to selectively reset hunks. See the ``Interactive Mode''
section of linkgit:git-add[1] to learn how to operate the `\--patch` mode.
-'git reset' [--<mode>] [<commit>]::
+'git reset' --<mode> [<commit>]::
This form resets the current branch head to <commit> and
possibly updates the index (resetting it to the tree of <commit>) and
the working tree depending on <mode>, which
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index f3519413e7..b699a3458e 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -9,8 +9,9 @@ SYNOPSIS
--------
[verse]
'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
-'git revert' --reset
'git revert' --continue
+'git revert' --quit
+'git revert' --abort
DESCRIPTION
-----------
diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt
index b78f031cd4..a80d94650d 100644
--- a/Documentation/git-stripspace.txt
+++ b/Documentation/git-stripspace.txt
@@ -3,26 +3,83 @@ git-stripspace(1)
NAME
----
-git-stripspace - Filter out empty lines
+git-stripspace - Remove unnecessary whitespace
SYNOPSIS
--------
[verse]
-'git stripspace' [-s | --strip-comments] < <stream>
+'git stripspace' [-s | --strip-comments] < input
DESCRIPTION
-----------
-Remove multiple empty lines, and empty lines at beginning and end.
+
+Clean the input in the manner used by 'git' for text such as commit
+messages, notes, tags and branch descriptions.
+
+With no arguments, this will:
+
+- remove trailing whitespace from all lines
+- collapse multiple consecutive empty lines into one empty line
+- remove empty lines from the beginning and end of the input
+- add a missing '\n' to the last line if necessary.
+
+In the case where the input consists entirely of whitespace characters, no
+output will be produced.
+
+*NOTE*: This is intended for cleaning metadata, prefer the `--whitespace=fix`
+mode of linkgit:git-apply[1] for correcting whitespace of patches or files in
+the repository.
OPTIONS
-------
-s::
--strip-comments::
- In addition to empty lines, also strip lines starting with '#'.
+ Skip and remove all lines starting with '#'.
+
+EXAMPLES
+--------
+
+Given the following noisy input with '$' indicating the end of a line:
-<stream>::
- Byte stream to act on.
+--------
+|A brief introduction $
+| $
+|$
+|A new paragraph$
+|# with a commented-out line $
+|explaining lots of stuff.$
+|$
+|# An old paragraph, also commented-out. $
+| $
+|The end.$
+| $
+---------
+
+Use 'git stripspace' with no arguments to obtain:
+
+--------
+|A brief introduction$
+|$
+|A new paragraph$
+|# with a commented-out line$
+|explaining lots of stuff.$
+|$
+|# An old paragraph, also commented-out.$
+|$
+|The end.$
+---------
+
+Use 'git stripspace --strip-comments' to obtain:
+
+--------
+|A brief introduction$
+|$
+|A new paragraph$
+|explaining lots of stuff.$
+|$
+|The end.$
+---------
GIT
---
diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt
index 75b1ae5061..a45d4c4f29 100644
--- a/Documentation/git-symbolic-ref.txt
+++ b/Documentation/git-symbolic-ref.txt
@@ -43,12 +43,9 @@ In the past, `.git/HEAD` was a symbolic link pointing at
`refs/heads/master`. When we wanted to switch to another branch,
we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we wanted
to find out which branch we are on, we did `readlink .git/HEAD`.
-This was fine, and internally that is what still happens by
-default, but on platforms that do not have working symlinks,
-or that do not have the `readlink(1)` command, this was a bit
-cumbersome. On some platforms, `ln -sf` does not even work as
-advertised (horrors). Therefore symbolic links are now deprecated
-and symbolic refs are used by default.
+But symbolic links are not entirely portable, so they are now
+deprecated and symbolic refs (as described above) are used by
+default.
'git symbolic-ref' will exit with status 0 if the contents of the
symbolic ref were printed correctly, with status 1 if the requested
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index c83cb13de6..622a019eb0 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -99,6 +99,13 @@ OPTIONS
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
+--cleanup=<mode>::
+ This option sets how the tag message is cleaned up.
+ The '<mode>' can be one of 'verbatim', 'whitespace' and 'strip'. The
+ 'strip' mode is default. The 'verbatim' mode does not change message at
+ all, 'whitespace' removes just leading/trailing whitespace lines and
+ 'strip' removes both whitespace and commentary.
+
<tagname>::
The name of the tag to create, delete, or describe.
The new tag name must pass all checks defined by
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 5e80cfd71a..da7d48787e 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -44,15 +44,25 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.7.7.1/git.html[documentation for release 1.7.7.1]
+* link:v1.7.8/git.html[documentation for release 1.7.8]
* release notes for
+ link:RelNotes/1.7.8.txt[1.7.8].
+
+* link:v1.7.7.5/git.html[documentation for release 1.7.7.5]
+
+* release notes for
+ link:RelNotes/1.7.7.5.txt[1.7.7.5],
+ link:RelNotes/1.7.7.4.txt[1.7.7.4],
+ link:RelNotes/1.7.7.3.txt[1.7.7.3],
+ link:RelNotes/1.7.7.2.txt[1.7.7.2],
link:RelNotes/1.7.7.1.txt[1.7.7.1],
link:RelNotes/1.7.7.txt[1.7.7].
-* link:v1.7.6.4/git.html[documentation for release 1.7.6.4]
+* link:v1.7.6.5/git.html[documentation for release 1.7.6.5]
* release notes for
+ link:RelNotes/1.7.6.5.txt[1.7.6.5],
link:RelNotes/1.7.6.4.txt[1.7.6.4],
link:RelNotes/1.7.6.3.txt[1.7.6.3],
link:RelNotes/1.7.6.2.txt[1.7.6.2],
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 25e46aeb7a..a85b187e04 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -500,6 +500,8 @@ patterns are available:
- `java` suitable for source code in the Java language.
+- `matlab` suitable for source code in the MATLAB language.
+
- `objc` suitable for source code in the Objective-C language.
- `pascal` suitable for source code in the Pascal/Delphi language.
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
new file mode 100644
index 0000000000..066f825f2e
--- /dev/null
+++ b/Documentation/gitcredentials.txt
@@ -0,0 +1,183 @@
+gitcredentials(7)
+=================
+
+NAME
+----
+gitcredentials - providing usernames and passwords to git
+
+SYNOPSIS
+--------
+------------------
+git config credential.https://example.com.username myusername
+git config credential.helper "$helper $options"
+------------------
+
+DESCRIPTION
+-----------
+
+Git will sometimes need credentials from the user in order to perform
+operations; for example, it may need to ask for a username and password
+in order to access a remote repository over HTTP. This manual describes
+the mechanisms git uses to request these credentials, as well as some
+features to avoid inputting these credentials repeatedly.
+
+REQUESTING CREDENTIALS
+----------------------
+
+Without any credential helpers defined, git will try the following
+strategies to ask the user for usernames and passwords:
+
+1. If the `GIT_ASKPASS` environment variable is set, the program
+ specified by the variable is invoked. A suitable prompt is provided
+ to the program on the command line, and the user's input is read
+ from its standard output.
+
+2. Otherwise, if the `core.askpass` configuration variable is set, its
+ value is used as above.
+
+3. Otherwise, if the `SSH_ASKPASS` environment variable is set, its
+ value is used as above.
+
+4. Otherwise, the user is prompted on the terminal.
+
+AVOIDING REPETITION
+-------------------
+
+It can be cumbersome to input the same credentials over and over. Git
+provides two methods to reduce this annoyance:
+
+1. Static configuration of usernames for a given authentication context.
+
+2. Credential helpers to cache or store passwords, or to interact with
+ a system password wallet or keychain.
+
+The first is simple and appropriate if you do not have secure storage available
+for a password. It is generally configured by adding this to your config:
+
+---------------------------------------
+[credential "https://example.com"]
+ username = me
+---------------------------------------
+
+Credential helpers, on the other hand, are external programs from which git can
+request both usernames and passwords; they typically interface with secure
+storage provided by the OS or other programs.
+
+To use a helper, you must first select one to use. Git currently
+includes the following helpers:
+
+cache::
+
+ Cache credentials in memory for a short period of time. See
+ linkgit:git-credential-cache[1] for details.
+
+store::
+
+ Store credentials indefinitely on disk. See
+ linkgit:git-credential-store[1] for details.
+
+You may also have third-party helpers installed; search for
+`credential-*` in the output of `git help -a`, and consult the
+documentation of individual helpers. Once you have selected a helper,
+you can tell git to use it by putting its name into the
+credential.helper variable.
+
+1. Find a helper.
++
+-------------------------------------------
+$ git help -a | grep credential-
+credential-foo
+-------------------------------------------
+
+2. Read its description.
++
+-------------------------------------------
+$ git help credential-foo
+-------------------------------------------
+
+3. Tell git to use it.
++
+-------------------------------------------
+$ git config --global credential.helper foo
+-------------------------------------------
+
+If there are multiple instances of the `credential.helper` configuration
+variable, each helper will be tried in turn, and may provide a username,
+password, or nothing. Once git has acquired both a username and a
+password, no more helpers will be tried.
+
+
+CREDENTIAL CONTEXTS
+-------------------
+
+Git considers each credential to have a context defined by a URL. This context
+is used to look up context-specific configuration, and is passed to any
+helpers, which may use it as an index into secure storage.
+
+For instance, imagine we are accessing `https://example.com/foo.git`. When git
+looks into a config file to see if a section matches this context, it will
+consider the two a match if the context is a more-specific subset of the
+pattern in the config file. For example, if you have this in your config file:
+
+--------------------------------------
+[credential "https://example.com"]
+ username = foo
+--------------------------------------
+
+then we will match: both protocols are the same, both hosts are the same, and
+the "pattern" URL does not care about the path component at all. However, this
+context would not match:
+
+--------------------------------------
+[credential "https://kernel.org"]
+ username = foo
+--------------------------------------
+
+because the hostnames differ. Nor would it match `foo.example.com`; git
+compares hostnames exactly, without considering whether two hosts are part of
+the same domain. Likewise, a config entry for `http://example.com` would not
+match: git compares the protocols exactly.
+
+
+CONFIGURATION OPTIONS
+---------------------
+
+Options for a credential context can be configured either in
+`credential.\*` (which applies to all credentials), or
+`credential.<url>.\*`, where <url> matches the context as described
+above.
+
+The following options are available in either location:
+
+helper::
+
+ The name of an external credential helper, and any associated options.
+ If the helper name is not an absolute path, then the string `git
+ credential-` is prepended. The resulting string is executed by the
+ shell (so, for example, setting this to `foo --option=bar` will execute
+ `git credential-foo --option=bar` via the shell. See the manual of
+ specific helpers for examples of their use.
+
+username::
+
+ A default username, if one is not provided in the URL.
+
+useHttpPath::
+
+ By default, git does not consider the "path" component of an http URL
+ to be worth matching via external helpers. This means that a credential
+ stored for `https://example.com/foo.git` will also be used for
+ `https://example.com/bar.git`. If you do want to distinguish these
+ cases, set this option to `true`.
+
+
+CUSTOM HELPERS
+--------------
+
+You can write your own custom helpers to interface with any system in
+which you keep credentials. See the documentation for git's
+link:technical/api-credentials.html[credentials API] for details.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 6bd0b041c3..1a5c12e317 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -9,7 +9,6 @@ inspect and further tweak the merge result before committing.
--edit::
-e::
-+
Invoke editor before committing successful merge to further
edit the default merge message.
diff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt
index 3e6df338be..5747f442f2 100644
--- a/Documentation/sequencer.txt
+++ b/Documentation/sequencer.txt
@@ -1,9 +1,12 @@
---reset::
- Forget about the current operation in progress. Can be used
- to clear the sequencer state after a failed cherry-pick or
- revert.
-
--continue::
Continue the operation in progress using the information in
'.git/sequencer'. Can be used to continue after resolving
conflicts in a failed cherry-pick or revert.
+
+--quit::
+ Forget about the current operation in progress. Can be used
+ to clear the sequencer state after a failed cherry-pick or
+ revert.
+
+--abort::
+ Cancel the operation and return to the pre-sequence state.
diff --git a/Documentation/technical/api-credentials.txt b/Documentation/technical/api-credentials.txt
new file mode 100644
index 0000000000..21ca6a2553
--- /dev/null
+++ b/Documentation/technical/api-credentials.txt
@@ -0,0 +1,245 @@
+credentials API
+===============
+
+The credentials API provides an abstracted way of gathering username and
+password credentials from the user (even though credentials in the wider
+world can take many forms, in this document the word "credential" always
+refers to a username and password pair).
+
+Data Structures
+---------------
+
+`struct credential`::
+
+ This struct represents a single username/password combination
+ along with any associated context. All string fields should be
+ heap-allocated (or NULL if they are not known or not applicable).
+ The meaning of the individual context fields is the same as
+ their counterparts in the helper protocol; see the section below
+ for a description of each field.
++
+The `helpers` member of the struct is a `string_list` of helpers. Each
+string specifies an external helper which will be run, in order, to
+either acquire or store credentials. See the section on credential
+helpers below.
++
+This struct should always be initialized with `CREDENTIAL_INIT` or
+`credential_init`.
+
+
+Functions
+---------
+
+`credential_init`::
+
+ Initialize a credential structure, setting all fields to empty.
+
+`credential_clear`::
+
+ Free any resources associated with the credential structure,
+ returning it to a pristine initialized state.
+
+`credential_fill`::
+
+ Instruct the credential subsystem to fill the username and
+ password fields of the passed credential struct by first
+ consulting helpers, then asking the user. After this function
+ returns, the username and password fields of the credential are
+ guaranteed to be non-NULL. If an error occurs, the function will
+ die().
+
+`credential_reject`::
+
+ Inform the credential subsystem that the provided credentials
+ have been rejected. This will cause the credential subsystem to
+ notify any helpers of the rejection (which allows them, for
+ example, to purge the invalid credentials from storage). It
+ will also free() the username and password fields of the
+ credential and set them to NULL (readying the credential for
+ another call to `credential_fill`). Any errors from helpers are
+ ignored.
+
+`credential_approve`::
+
+ Inform the credential subsystem that the provided credentials
+ were successfully used for authentication. This will cause the
+ credential subsystem to notify any helpers of the approval, so
+ that they may store the result to be used again. Any errors
+ from helpers are ignored.
+
+`credential_from_url`::
+
+ Parse a URL into broken-down credential fields.
+
+Example
+-------
+
+The example below shows how the functions of the credential API could be
+used to login to a fictitious "foo" service on a remote host:
+
+-----------------------------------------------------------------------
+int foo_login(struct foo_connection *f)
+{
+ int status;
+ /*
+ * Create a credential with some context; we don't yet know the
+ * username or password.
+ */
+
+ struct credential c = CREDENTIAL_INIT;
+ c.protocol = xstrdup("foo");
+ c.host = xstrdup(f->hostname);
+
+ /*
+ * Fill in the username and password fields by contacting
+ * helpers and/or asking the user. The function will die if it
+ * fails.
+ */
+ credential_fill(&c);
+
+ /*
+ * Otherwise, we have a username and password. Try to use it.
+ */
+ status = send_foo_login(f, c.username, c.password);
+ switch (status) {
+ case FOO_OK:
+ /* It worked. Store the credential for later use. */
+ credential_accept(&c);
+ break;
+ case FOO_BAD_LOGIN:
+ /* Erase the credential from storage so we don't try it
+ * again. */
+ credential_reject(&c);
+ break;
+ default:
+ /*
+ * Some other error occured. We don't know if the
+ * credential is good or bad, so report nothing to the
+ * credential subsystem.
+ */
+ }
+
+ /* Free any associated resources. */
+ credential_clear(&c);
+
+ return status;
+}
+-----------------------------------------------------------------------
+
+
+Credential Helpers
+------------------
+
+Credential helpers are programs executed by git to fetch or save
+credentials from and to long-term storage (where "long-term" is simply
+longer than a single git process; e.g., credentials may be stored
+in-memory for a few minutes, or indefinitely on disk).
+
+Each helper is specified by a single string. The string is transformed
+by git into a command to be executed using these rules:
+
+ 1. If the helper string begins with "!", it is considered a shell
+ snippet, and everything after the "!" becomes the command.
+
+ 2. Otherwise, if the helper string begins with an absolute path, the
+ verbatim helper string becomes the command.
+
+ 3. Otherwise, the string "git credential-" is prepended to the helper
+ string, and the result becomes the command.
+
+The resulting command then has an "operation" argument appended to it
+(see below for details), and the result is executed by the shell.
+
+Here are some example specifications:
+
+----------------------------------------------------
+# run "git credential-foo"
+foo
+
+# same as above, but pass an argument to the helper
+foo --bar=baz
+
+# the arguments are parsed by the shell, so use shell
+# quoting if necessary
+foo --bar="whitespace arg"
+
+# you can also use an absolute path, which will not use the git wrapper
+/path/to/my/helper --with-arguments
+
+# or you can specify your own shell snippet
+!f() { echo "password=`cat $HOME/.secret`"; }; f
+----------------------------------------------------
+
+Generally speaking, rule (3) above is the simplest for users to specify.
+Authors of credential helpers should make an effort to assist their
+users by naming their program "git-credential-$NAME", and putting it in
+the $PATH or $GIT_EXEC_PATH during installation, which will allow a user
+to enable it with `git config credential.helper $NAME`.
+
+When a helper is executed, it will have one "operation" argument
+appended to its command line, which is one of:
+
+`get`::
+
+ Return a matching credential, if any exists.
+
+`store`::
+
+ Store the credential, if applicable to the helper.
+
+`erase`::
+
+ Remove a matching credential, if any, from the helper's storage.
+
+The details of the credential will be provided on the helper's stdin
+stream. The credential is split into a set of named attributes.
+Attributes are provided to the helper, one per line. Each attribute is
+specified by a key-value pair, separated by an `=` (equals) sign,
+followed by a newline. The key may contain any bytes except `=`,
+newline, or NUL. The value may contain any bytes except newline or NUL.
+In both cases, all bytes are treated as-is (i.e., there is no quoting,
+and one cannot transmit a value with newline or NUL in it). The list of
+attributes is terminated by a blank line or end-of-file.
+
+Git will send the following attributes (but may not send all of
+them for a given credential; for example, a `host` attribute makes no
+sense when dealing with a non-network protocol):
+
+`protocol`::
+
+ The protocol over which the credential will be used (e.g.,
+ `https`).
+
+`host`::
+
+ The remote hostname for a network credential.
+
+`path`::
+
+ The path with which the credential will be used. E.g., for
+ accessing a remote https repository, this will be the
+ repository's path on the server.
+
+`username`::
+
+ The credential's username, if we already have one (e.g., from a
+ URL, from the user, or from a previously run helper).
+
+`password`::
+
+ The credential's password, if we are asking it to be stored.
+
+For a `get` operation, the helper should produce a list of attributes
+on stdout in the same format. A helper is free to produce a subset, or
+even no values at all if it has nothing useful to provide. Any provided
+attributes will overwrite those already known about by git.
+
+For a `store` or `erase` operation, the helper's output is ignored.
+If it fails to perform the requested operation, it may complain to
+stderr to inform the user. If it does not support the requested
+operation (e.g., a read-only store), it should silently ignore the
+request.
+
+If a helper receives any other operation, it should silently ignore the
+request. This leaves room for future operations to be added (older
+helpers will just ignore the new requests).
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index c3435b0c4f..e18a30a16a 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.8-rc2
+DEF_VER=v1.7.8.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 08b9297084..9470a10343 100644
--- a/Makefile
+++ b/Makefile
@@ -73,8 +73,8 @@ all::
#
# Define NO_STRLCPY if you don't have strlcpy.
#
-# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
-# If your compiler also does not support long long or does not have
+# Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the
+# C library. If your compiler also does not support long long or does not have
# strtoull, define NO_STRTOULL.
#
# Define NO_SETENV if you don't have setenv in the C library.
@@ -159,6 +159,8 @@ all::
#
# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
#
+# Define NO_UNIX_SOCKETS if your system does not offer unix sockets.
+#
# Define NO_SOCKADDR_STORAGE if your platform does not have struct
# sockaddr_storage.
#
@@ -266,6 +268,12 @@ all::
# DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
# DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
#
+# Define COMPUTE_HEADER_DEPENDENCIES to "yes" if you want dependencies on
+# header files to be automatically computed, to avoid rebuilding objects when
+# an unrelated header file changes. Define it to "no" to use the hard-coded
+# dependency rules. The default is "auto", which means to use computed header
+# dependencies if your compiler is detected to support it.
+#
# Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded
# dependency rules.
#
@@ -439,14 +447,17 @@ PROGRAM_OBJS += show-index.o
PROGRAM_OBJS += upload-pack.o
PROGRAM_OBJS += http-backend.o
PROGRAM_OBJS += sh-i18n--envsubst.o
+PROGRAM_OBJS += credential-store.o
PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
TEST_PROGRAMS_NEED_X += test-chmtime
+TEST_PROGRAMS_NEED_X += test-credential
TEST_PROGRAMS_NEED_X += test-ctype
TEST_PROGRAMS_NEED_X += test-date
TEST_PROGRAMS_NEED_X += test-delta
TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
TEST_PROGRAMS_NEED_X += test-genrandom
TEST_PROGRAMS_NEED_X += test-index-version
TEST_PROGRAMS_NEED_X += test-line-buffer
@@ -523,6 +534,7 @@ LIB_H += argv-array.h
LIB_H += attr.h
LIB_H += blob.h
LIB_H += builtin.h
+LIB_H += bulk-checkin.h
LIB_H += cache.h
LIB_H += cache-tree.h
LIB_H += color.h
@@ -536,6 +548,8 @@ LIB_H += compat/win32/syslog.h
LIB_H += compat/win32/poll.h
LIB_H += compat/win32/dirent.h
LIB_H += connected.h
+LIB_H += convert.h
+LIB_H += credential.h
LIB_H += csum-file.h
LIB_H += decorate.h
LIB_H += delta.h
@@ -543,9 +557,11 @@ LIB_H += diffcore.h
LIB_H += diff.h
LIB_H += dir.h
LIB_H += exec_cmd.h
+LIB_H += fmt-merge-msg.h
LIB_H += fsck.h
LIB_H += gettext.h
LIB_H += git-compat-util.h
+LIB_H += gpg-interface.h
LIB_H += graph.h
LIB_H += grep.h
LIB_H += hash.h
@@ -609,6 +625,7 @@ LIB_OBJS += base85.o
LIB_OBJS += bisect.o
LIB_OBJS += blob.o
LIB_OBJS += branch.o
+LIB_OBJS += bulk-checkin.o
LIB_OBJS += bundle.o
LIB_OBJS += cache-tree.o
LIB_OBJS += color.o
@@ -620,6 +637,7 @@ LIB_OBJS += connect.o
LIB_OBJS += connected.o
LIB_OBJS += convert.o
LIB_OBJS += copy.o
+LIB_OBJS += credential.o
LIB_OBJS += csum-file.o
LIB_OBJS += ctype.o
LIB_OBJS += date.o
@@ -639,6 +657,7 @@ LIB_OBJS += entry.o
LIB_OBJS += environment.o
LIB_OBJS += exec_cmd.o
LIB_OBJS += fsck.o
+LIB_OBJS += gpg-interface.o
LIB_OBJS += gettext.o
LIB_OBJS += graph.o
LIB_OBJS += grep.o
@@ -1114,6 +1133,7 @@ ifeq ($(uname_S),Windows)
NO_SYS_POLL_H = YesPlease
NO_SYMLINK_HEAD = YesPlease
NO_IPV6 = YesPlease
+ NO_UNIX_SOCKETS = YesPlease
NO_SETENV = YesPlease
NO_UNSETENV = YesPlease
NO_STRCASESTR = YesPlease
@@ -1207,6 +1227,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_LIBGEN_H = YesPlease
NO_SYS_POLL_H = YesPlease
NO_SYMLINK_HEAD = YesPlease
+ NO_UNIX_SOCKETS = YesPlease
NO_SETENV = YesPlease
NO_UNSETENV = YesPlease
NO_STRCASESTR = YesPlease
@@ -1269,21 +1290,32 @@ endif
endif
ifdef CHECK_HEADER_DEPENDENCIES
-COMPUTE_HEADER_DEPENDENCIES =
+COMPUTE_HEADER_DEPENDENCIES = no
USE_COMPUTED_HEADER_DEPENDENCIES =
-else
+endif
+
ifndef COMPUTE_HEADER_DEPENDENCIES
+COMPUTE_HEADER_DEPENDENCIES = auto
+endif
+
+ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto)
dep_check = $(shell $(CC) $(ALL_CFLAGS) \
-c -MF /dev/null -MMD -MP -x c /dev/null -o /dev/null 2>&1; \
echo $$?)
ifeq ($(dep_check),0)
-COMPUTE_HEADER_DEPENDENCIES=YesPlease
-endif
+override COMPUTE_HEADER_DEPENDENCIES = yes
+else
+override COMPUTE_HEADER_DEPENDENCIES = no
endif
endif
-ifdef COMPUTE_HEADER_DEPENDENCIES
+ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
USE_COMPUTED_HEADER_DEPENDENCIES = YesPlease
+else
+ifneq ($(COMPUTE_HEADER_DEPENDENCIES),no)
+$(error please set COMPUTE_HEADER_DEPENDENCIES to yes, no, or auto \
+(not "$(COMPUTE_HEADER_DEPENDENCIES)"))
+endif
endif
ifdef SANE_TOOL_PATH
@@ -1491,7 +1523,7 @@ ifdef NO_STRLCPY
endif
ifdef NO_STRTOUMAX
COMPAT_CFLAGS += -DNO_STRTOUMAX
- COMPAT_OBJS += compat/strtoumax.o
+ COMPAT_OBJS += compat/strtoumax.o compat/strtoimax.o
endif
ifdef NO_STRTOULL
COMPAT_CFLAGS += -DNO_STRTOULL
@@ -1581,6 +1613,12 @@ ifdef NO_INET_PTON
LIB_OBJS += compat/inet_pton.o
BASIC_CFLAGS += -DNO_INET_PTON
endif
+ifndef NO_UNIX_SOCKETS
+ LIB_OBJS += unix-socket.o
+ LIB_H += unix-socket.h
+ PROGRAM_OBJS += credential-cache.o
+ PROGRAM_OBJS += credential-cache--daemon.o
+endif
ifdef NO_ICONV
BASIC_CFLAGS += -DNO_ICONV
@@ -1948,7 +1986,7 @@ OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)
dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))
-ifdef COMPUTE_HEADER_DEPENDENCIES
+ifeq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
$(dep_dirs):
@mkdir -p $@
@@ -1961,7 +1999,7 @@ Please unset CHECK_HEADER_DEPENDENCIES and try again)
endif
endif
-ifndef COMPUTE_HEADER_DEPENDENCIES
+ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
ifndef CHECK_HEADER_DEPENDENCIES
dep_dirs =
missing_dep_dirs =
@@ -2051,13 +2089,13 @@ builtin/branch.o builtin/checkout.o builtin/clone.o builtin/reset.o branch.o tra
builtin/bundle.o bundle.o transport.o: bundle.h
builtin/bisect--helper.o builtin/rev-list.o bisect.o: bisect.h
builtin/clone.o builtin/fetch-pack.o transport.o: fetch-pack.h
-builtin/grep.o builtin/pack-objects.o transport-helper.o: thread-utils.h
+builtin/grep.o builtin/pack-objects.o transport-helper.o thread-utils.o: thread-utils.h
builtin/send-pack.o transport.o: send-pack.h
builtin/log.o builtin/shortlog.o: shortlog.h
builtin/prune.o builtin/reflog.o reachable.o: reachable.h
builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
builtin/tar-tree.o archive-tar.o: tar.h
-connect.o transport.o http-backend.o: url.h
+connect.o transport.o url.o http-backend.o: url.h
http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
@@ -2252,6 +2290,7 @@ ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
endif
@echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@
@echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@
+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
### Detect Tck/Tk interpreter path changes
ifndef NO_TCLTK
diff --git a/RelNotes b/RelNotes
index 7d9276973a..766bbaf8f5 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.7.8.txt \ No newline at end of file
+Documentation/RelNotes/1.7.9.txt \ No newline at end of file
diff --git a/archive.c b/archive.c
index 2ae740a71e..164bbd014a 100644
--- a/archive.c
+++ b/archive.c
@@ -247,7 +247,8 @@ static void parse_pathspec_arg(const char **pathspec,
}
static void parse_treeish_arg(const char **argv,
- struct archiver_args *ar_args, const char *prefix)
+ struct archiver_args *ar_args, const char *prefix,
+ int remote)
{
const char *name = argv[0];
const unsigned char *commit_sha1;
@@ -256,8 +257,17 @@ static void parse_treeish_arg(const char **argv,
const struct commit *commit;
unsigned char sha1[20];
- if (get_sha1(name, sha1))
- die("Not a valid object name");
+ /* Remotes are only allowed to fetch actual refs */
+ if (remote) {
+ char *ref = NULL;
+ if (!dwim_ref(name, strlen(name), sha1, &ref))
+ die("no such ref: %s", name);
+ free(ref);
+ }
+ else {
+ if (get_sha1(name, sha1))
+ die("Not a valid object name");
+ }
commit = lookup_commit_reference_gently(sha1, 1);
if (commit) {
@@ -414,7 +424,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
setup_git_directory();
}
- parse_treeish_arg(argv, &args, prefix);
+ parse_treeish_arg(argv, &args, prefix, remote);
parse_pathspec_arg(argv + 1, &args);
return ar->write_archive(ar, &args);
diff --git a/branch.c b/branch.c
index d8098762f6..9971820a18 100644
--- a/branch.c
+++ b/branch.c
@@ -3,7 +3,6 @@
#include "refs.h"
#include "remote.h"
#include "commit.h"
-#include "sequencer.h"
struct tracking {
struct refspec spec;
@@ -136,6 +135,37 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
return 0;
}
+struct branch_desc_cb {
+ const char *config_name;
+ const char *value;
+};
+
+static int read_branch_desc_cb(const char *var, const char *value, void *cb)
+{
+ struct branch_desc_cb *desc = cb;
+ if (strcmp(desc->config_name, var))
+ return 0;
+ free((char *)desc->value);
+ return git_config_string(&desc->value, var, value);
+}
+
+int read_branch_desc(struct strbuf *buf, const char *branch_name)
+{
+ struct branch_desc_cb cb;
+ struct strbuf name = STRBUF_INIT;
+ strbuf_addf(&name, "branch.%s.description", branch_name);
+ cb.config_name = name.buf;
+ cb.value = NULL;
+ if (git_config(read_branch_desc_cb, &cb) < 0) {
+ strbuf_release(&name);
+ return -1;
+ }
+ if (cb.value)
+ strbuf_addstr(buf, cb.value);
+ strbuf_release(&name);
+ return 0;
+}
+
int validate_new_branchname(const char *name, struct strbuf *ref,
int force, int attr_only)
{
@@ -151,7 +181,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
const char *head;
unsigned char sha1[20];
- head = resolve_ref("HEAD", sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die("Cannot force update the current branch.");
}
@@ -160,7 +190,8 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
void create_branch(const char *head,
const char *name, const char *start_name,
- int force, int reflog, enum branch_track track)
+ int force, int reflog, int clobber_head,
+ enum branch_track track)
{
struct ref_lock *lock = NULL;
struct commit *commit;
@@ -175,7 +206,8 @@ void create_branch(const char *head,
explicit_tracking = 1;
if (validate_new_branchname(name, &ref, force,
- track == BRANCH_TRACK_OVERRIDE)) {
+ track == BRANCH_TRACK_OVERRIDE ||
+ clobber_head)) {
if (!force)
dont_change_ref = 1;
else
@@ -241,10 +273,10 @@ void create_branch(const char *head,
void remove_branch_state(void)
{
unlink(git_path("CHERRY_PICK_HEAD"));
+ unlink(git_path("REVERT_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_RR"));
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
unlink(git_path("SQUASH_MSG"));
- remove_sequencer_state(0);
}
diff --git a/branch.h b/branch.h
index 1285158dd4..b99c5a369e 100644
--- a/branch.h
+++ b/branch.h
@@ -13,7 +13,8 @@
* branch for (if any).
*/
void create_branch(const char *head, const char *name, const char *start_name,
- int force, int reflog, enum branch_track track);
+ int force, int reflog,
+ int clobber_head, enum branch_track track);
/*
* Validates that the requested branch may be created, returning the
@@ -46,4 +47,9 @@ void remove_branch_state(void);
#define BRANCH_CONFIG_VERBOSE 01
extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);
+/*
+ * Read branch description
+ */
+extern int read_branch_desc(struct strbuf *, const char *branch_name);
+
#endif
diff --git a/builtin.h b/builtin.h
index 0e9da90834..857b9c8aa8 100644
--- a/builtin.h
+++ b/builtin.h
@@ -14,8 +14,14 @@ extern const char git_usage_string[];
extern const char git_more_info_string[];
extern void prune_packed_objects(int);
+
+struct fmt_merge_msg_opts {
+ unsigned add_title:1;
+ int shortlog_len;
+};
+
extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
- int merge_title, int shortlog_len);
+ struct fmt_merge_msg_opts *);
extern void commit_notes(struct notes_tree *t, const char *msg);
struct notes_rewrite_cfg {
@@ -133,6 +139,7 @@ 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_update_server_info(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_archive_writer(int argc, const char **argv, const char *prefix);
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
extern int cmd_var(int argc, const char **argv, const char *prefix);
extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index c59b0c98fe..1c42900ff8 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -13,6 +13,7 @@
#include "diff.h"
#include "diffcore.h"
#include "revision.h"
+#include "bulk-checkin.h"
static const char * const builtin_add_usage[] = {
"git add [options] [--] <filepattern>...",
@@ -458,11 +459,15 @@ int cmd_add(int argc, const char **argv, const char *prefix)
free(seen);
}
+ plug_bulk_checkin();
+
exit_status |= add_files_to_cache(prefix, pathspec, flags);
if (add_new_files)
exit_status |= add_files(&dir, flags);
+ unplug_bulk_checkin();
+
finish:
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
diff --git a/builtin/apply.c b/builtin/apply.c
index 84a8a0b521..c24dc546d0 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -250,9 +250,6 @@ static int fuzzy_matchlines(const char *s1, size_t n1,
const char *last2 = s2 + n2 - 1;
int result = 0;
- if (n1 < 0 || n2 < 0)
- return 0;
-
/* ignore line endings */
while ((*last1 == '\r') || (*last1 == '\n'))
last1--;
@@ -3590,15 +3587,12 @@ static int write_out_one_reject(struct patch *patch)
return -1;
}
-static int write_out_results(struct patch *list, int skipped_patch)
+static int write_out_results(struct patch *list)
{
int phase;
int errs = 0;
struct patch *l;
- if (!list && !skipped_patch)
- return error("No changes");
-
for (phase = 0; phase < 2; phase++) {
l = list;
while (l) {
@@ -3724,6 +3718,9 @@ static int apply_patch(int fd, const char *filename, int options)
offset += nr;
}
+ if (!list && !skipped_patch)
+ die("unrecognized input");
+
if (whitespace_error && (ws_error_action == die_on_ws_error))
apply = 0;
@@ -3741,7 +3738,7 @@ static int apply_patch(int fd, const char *filename, int options)
!apply_with_reject)
exit(1);
- if (apply && write_out_results(list, skipped_patch))
+ if (apply && write_out_results(list))
exit(1);
if (fake_ancestor)
diff --git a/builtin/archive.c b/builtin/archive.c
index e405566c5e..931956def9 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -87,7 +87,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
const char *exec = "git-upload-archive";
const char *output = NULL;
const char *remote = NULL;
- int is_remote = 0;
struct option local_opts[] = {
OPT_STRING('o', "output", &output, "file",
"write the archive to this file"),
@@ -95,9 +94,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
"retrieve the archive from remote repository <repo>"),
OPT_STRING(0, "exec", &exec, "cmd",
"path to the remote git-upload-archive command"),
- { OPTION_BOOLEAN, 0, "remote-request", &is_remote, NULL,
- "indicate we are serving a remote request",
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
OPT_END()
};
@@ -112,5 +108,5 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
- return write_archive(argc, argv, prefix, 1, output, is_remote);
+ return write_archive(argc, argv, prefix, 1, output, 0);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 80febbe420..5a67c202f0 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1598,7 +1598,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
int tz;
if (show_raw_time) {
- sprintf(time_buf, "%lu %s", time, tz_str);
+ snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
}
else {
tz = atoi(tz_str);
diff --git a/builtin/branch.c b/builtin/branch.c
index 51ca6a02d7..7095718c13 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -104,6 +104,7 @@ static int branch_merged(int kind, const char *name,
*/
struct commit *reference_rev = NULL;
const char *reference_name = NULL;
+ void *reference_name_to_free = NULL;
int merged;
if (kind == REF_LOCAL_BRANCH) {
@@ -114,8 +115,8 @@ static int branch_merged(int kind, const char *name,
branch->merge &&
branch->merge[0] &&
branch->merge[0]->dst &&
- (reference_name =
- resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+ (reference_name = reference_name_to_free =
+ resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
if (!reference_rev)
@@ -141,6 +142,7 @@ static int branch_merged(int kind, const char *name,
" '%s', even though it is merged to HEAD."),
name, reference_name);
}
+ free(reference_name_to_free);
return merged;
}
@@ -186,7 +188,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
free(name);
name = xstrdup(mkpath(fmt, bname.buf));
- if (!resolve_ref(name, sha1, 1, NULL)) {
+ if (read_ref(name, sha1)) {
error(_("%sbranch '%s' not found."),
remote, bname.buf);
ret = 1;
@@ -250,7 +252,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst, *cp;
- dst = resolve_ref(src, sha1, 0, &flag);
+ dst = resolve_ref_unsafe(src, sha1, 0, &flag);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
if (prefix && (cp = skip_prefix(dst, prefix)))
@@ -565,9 +567,9 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
static void rename_branch(const char *oldname, const char *newname, int force)
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
- unsigned char sha1[20];
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
int recovery = 0;
+ int clobber_head_ok;
if (!oldname)
die(_("cannot rename the current branch while not on any."));
@@ -577,13 +579,19 @@ static void rename_branch(const char *oldname, const char *newname, int force)
* Bad name --- this could be an attempt to rename a
* ref that we used to allow to be created by accident.
*/
- if (resolve_ref(oldref.buf, sha1, 1, NULL))
+ if (ref_exists(oldref.buf))
recovery = 1;
else
die(_("Invalid branch name: '%s'"), oldname);
}
- validate_new_branchname(newname, &newref, force, 0);
+ /*
+ * A command like "git branch -M currentbranch currentbranch" cannot
+ * cause the worktree to become inconsistent with HEAD, so allow it.
+ */
+ clobber_head_ok = !strcmp(oldname, newname);
+
+ validate_new_branchname(newname, &newref, force, clobber_head_ok);
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
@@ -623,11 +631,49 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int
return 0;
}
+static const char edit_description[] = "BRANCH_DESCRIPTION";
+
+static int edit_branch_description(const char *branch_name)
+{
+ FILE *fp;
+ int status;
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf name = STRBUF_INIT;
+
+ read_branch_desc(&buf, branch_name);
+ if (!buf.len || buf.buf[buf.len-1] != '\n')
+ strbuf_addch(&buf, '\n');
+ strbuf_addf(&buf,
+ "# Please edit the description for the branch\n"
+ "# %s\n"
+ "# Lines starting with '#' will be stripped.\n",
+ branch_name);
+ fp = fopen(git_path(edit_description), "w");
+ if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
+ strbuf_release(&buf);
+ return error(_("could not write branch description template: %s\n"),
+ strerror(errno));
+ }
+ strbuf_reset(&buf);
+ if (launch_editor(git_path(edit_description), &buf, NULL)) {
+ strbuf_release(&buf);
+ return -1;
+ }
+ stripspace(&buf, 1);
+
+ strbuf_addf(&name, "branch.%s.description", branch_name);
+ status = git_config_set(name.buf, buf.buf);
+ strbuf_release(&name);
+ strbuf_release(&buf);
+
+ return status;
+}
+
int cmd_branch(int argc, const char **argv, const char *prefix)
{
int delete = 0, rename = 0, force_create = 0, list = 0;
int verbose = 0, abbrev = -1, detached = 0;
- int reflog = 0;
+ int reflog = 0, edit_description = 0;
enum branch_track track;
int kinds = REF_LOCAL_BRANCH;
struct commit_list *with_commit = NULL;
@@ -666,6 +712,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
OPT_BOOLEAN(0, "list", &list, "list branch names"),
OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
+ OPT_BOOLEAN(0, "edit-description", &edit_description,
+ "edit the description for the branch"),
OPT__FORCE(&force_create, "force creation (when already exists)"),
{
OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
@@ -689,10 +737,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_ref("HEAD", head_sha1, 0, NULL);
+ head = resolve_refdup("HEAD", head_sha1, 0, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
- head = xstrdup(head);
if (!strcmp(head, "HEAD")) {
detached = 1;
} else {
@@ -705,7 +752,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
- if (!delete && !rename && !force_create && argc == 0)
+ if (!delete && !rename && !edit_description && argc == 0)
list = 1;
if (!!delete + !!rename + !!force_create + !!list > 1)
@@ -719,18 +766,30 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
else if (list)
return print_ref_list(kinds, detached, verbose, abbrev,
with_commit, argv);
- else if (rename) {
+ else if (edit_description) {
+ const char *branch_name;
+ if (detached)
+ die("Cannot give description to detached HEAD");
+ if (!argc)
+ branch_name = head;
+ else if (argc == 1)
+ branch_name = argv[0];
+ else
+ usage_with_options(builtin_branch_usage, options);
+ if (edit_branch_description(branch_name))
+ return 1;
+ } else if (rename) {
if (argc == 1)
rename_branch(head, argv[0], rename > 1);
else if (argc == 2)
rename_branch(argv[0], argv[1], rename > 1);
else
usage_with_options(builtin_branch_usage, options);
- } else if (argc <= 2) {
+ } else if (argc > 0 && argc <= 2) {
if (kinds != REF_LOCAL_BRANCH)
die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
- force_create, reflog, track);
+ force_create, reflog, 0, track);
} else
usage_with_options(builtin_branch_usage, options);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 2a80772425..f1984d9933 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -34,6 +34,7 @@ struct checkout_opts {
int force_detach;
int writeout_stage;
int writeout_error;
+ int overwrite_ignore;
/* not set by parse_options */
int branch_exists;
@@ -114,16 +115,21 @@ static int check_stage(int stage, struct cache_entry *ce, int pos)
return error(_("path '%s' does not have their version"), ce->name);
}
-static int check_all_stages(struct cache_entry *ce, int pos)
+static int check_stages(unsigned stages, struct cache_entry *ce, int pos)
{
- if (ce_stage(ce) != 1 ||
- active_nr <= pos + 2 ||
- strcmp(active_cache[pos+1]->name, ce->name) ||
- ce_stage(active_cache[pos+1]) != 2 ||
- strcmp(active_cache[pos+2]->name, ce->name) ||
- ce_stage(active_cache[pos+2]) != 3)
- return error(_("path '%s' does not have all three versions"),
- ce->name);
+ unsigned seen = 0;
+ const char *name = ce->name;
+
+ while (pos < active_nr) {
+ ce = active_cache[pos];
+ if (strcmp(name, ce->name))
+ break;
+ seen |= (1 << ce_stage(ce));
+ pos++;
+ }
+ if ((stages & seen) != stages)
+ return error(_("path '%s' does not have all necessary versions"),
+ name);
return 0;
}
@@ -150,18 +156,27 @@ static int checkout_merged(int pos, struct checkout *state)
int status;
unsigned char sha1[20];
mmbuffer_t result_buf;
+ unsigned char threeway[3][20];
+ unsigned mode = 0;
+
+ memset(threeway, 0, sizeof(threeway));
+ while (pos < active_nr) {
+ int stage;
+ stage = ce_stage(ce);
+ if (!stage || strcmp(path, ce->name))
+ break;
+ hashcpy(threeway[stage - 1], ce->sha1);
+ if (stage == 2)
+ mode = create_ce_mode(ce->ce_mode);
+ pos++;
+ ce = active_cache[pos];
+ }
+ if (is_null_sha1(threeway[1]) || is_null_sha1(threeway[2]))
+ return error(_("path '%s' does not have necessary versions"), path);
- if (ce_stage(ce) != 1 ||
- active_nr <= pos + 2 ||
- strcmp(active_cache[pos+1]->name, path) ||
- ce_stage(active_cache[pos+1]) != 2 ||
- strcmp(active_cache[pos+2]->name, path) ||
- ce_stage(active_cache[pos+2]) != 3)
- return error(_("path '%s' does not have all 3 versions"), path);
-
- read_mmblob(&ancestor, active_cache[pos]->sha1);
- read_mmblob(&ours, active_cache[pos+1]->sha1);
- read_mmblob(&theirs, active_cache[pos+2]->sha1);
+ read_mmblob(&ancestor, threeway[0]);
+ read_mmblob(&ours, threeway[1]);
+ read_mmblob(&theirs, threeway[2]);
/*
* NEEDSWORK: re-create conflicts from merges with
@@ -192,9 +207,7 @@ static int checkout_merged(int pos, struct checkout *state)
if (write_sha1_file(result_buf.ptr, result_buf.size,
blob_type, sha1))
die(_("Unable to add merge result for '%s'"), path);
- ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
- sha1,
- path, 2, 0);
+ ce = make_cache_entry(mode, sha1, path, 2, 0);
if (!ce)
die(_("make_cache_entry failed for path '%s'"), path);
status = checkout_entry(ce, state, NULL);
@@ -252,7 +265,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
} else if (stage) {
errs |= check_stage(stage, ce, pos);
} else if (opts->merge) {
- errs |= check_all_stages(ce, pos);
+ errs |= check_stages((1<<2) | (1<<3), ce, pos);
} else {
errs = 1;
error(_("path '%s' is unmerged"), ce->name);
@@ -288,7 +301,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
commit_locked_index(lock_file))
die(_("unable to write new index file"));
- resolve_ref("HEAD", rev, 0, &flag);
+ read_ref_full("HEAD", rev, 0, &flag);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -409,9 +422,11 @@ static int merge_working_tree(struct checkout_opts *opts,
topts.gently = opts->merge && old->commit;
topts.verbose_update = !opts->quiet;
topts.fn = twoway_merge;
- topts.dir = xcalloc(1, sizeof(*topts.dir));
- topts.dir->flags |= DIR_SHOW_IGNORED;
- topts.dir->exclude_per_dir = ".gitignore";
+ if (opts->overwrite_ignore) {
+ topts.dir = xcalloc(1, sizeof(*topts.dir));
+ topts.dir->flags |= DIR_SHOW_IGNORED;
+ setup_standard_excludes(topts.dir);
+ }
tree = parse_tree_indirect(old->commit ?
old->commit->object.sha1 :
EMPTY_TREE_SHA1_BIN);
@@ -540,7 +555,9 @@ static void update_refs_for_switch(struct checkout_opts *opts,
else
create_branch(old->name, opts->new_branch, new->name,
opts->new_branch_force ? 1 : 0,
- opts->new_branch_log, opts->track);
+ opts->new_branch_log,
+ opts->new_branch_force ? 1 : 0,
+ opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
}
@@ -565,8 +582,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
create_symref("HEAD", new->path, msg.buf);
if (!opts->quiet) {
if (old->path && !strcmp(new->path, old->path)) {
- fprintf(stderr, _("Already on '%s'\n"),
- new->name);
+ if (opts->new_branch_force)
+ fprintf(stderr, _("Reset branch '%s'\n"),
+ new->name);
+ else
+ fprintf(stderr, _("Already on '%s'\n"),
+ new->name);
} else if (opts->new_branch) {
if (opts->branch_exists)
fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
@@ -696,15 +717,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
{
int ret = 0;
struct branch_info old;
+ void *path_to_free;
unsigned char rev[20];
int flag;
memset(&old, 0, sizeof(old));
- old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
+ old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
old.commit = lookup_commit_reference_gently(rev, 1);
- if (!(flag & REF_ISSYMREF)) {
- free((char *)old.path);
+ if (!(flag & REF_ISSYMREF))
old.path = NULL;
- }
if (old.path && !prefixcmp(old.path, "refs/heads/"))
old.name = old.path + strlen("refs/heads/");
@@ -718,8 +738,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
}
ret = merge_working_tree(opts, &old, new);
- if (ret)
+ if (ret) {
+ free(path_to_free);
return ret;
+ }
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
orphaned_commit_warning(old.commit);
@@ -727,7 +749,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
update_refs_for_switch(opts, &old, new);
ret = post_checkout_hook(old.commit, new->commit, 1);
- free((char *)old.path);
+ free(path_to_free);
return ret || opts->writeout_error;
}
@@ -866,7 +888,7 @@ static int parse_branchname_arg(int argc, const char **argv,
setup_branch_path(new);
if (!check_refname_format(new->path, 0) &&
- resolve_ref(new->path, branch_rev, 1, NULL))
+ !read_ref(new->path, branch_rev))
hashcpy(rev, branch_rev);
else
new->path = NULL; /* not an existing branch */
@@ -926,6 +948,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
3),
OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
+ OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, "update ignored files (default)"),
OPT_STRING(0, "conflict", &conflict_style, "style",
"conflict style (merge or diff3)"),
OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
@@ -937,6 +960,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
memset(&opts, 0, sizeof(opts));
memset(&new, 0, sizeof(new));
+ opts.overwrite_ignore = 1;
gitmodules_config();
git_config(git_checkout_config, &opts);
@@ -1057,7 +1081,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
- !!opts.new_branch_force, 0);
+ !!opts.new_branch_force,
+ !!opts.new_branch_force);
strbuf_release(&buf);
}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index d083795e26..b9c331225f 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -9,7 +9,7 @@
#include "builtin.h"
#include "utf8.h"
-static const char commit_tree_usage[] = "git commit-tree <sha1> [(-p <sha1>)...] < changelog";
+static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-m <message>] [-F <file>] <sha1> <changelog";
static void new_parent(struct commit *parent, struct commit_list **parents_p)
{
@@ -27,7 +27,7 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
- int i;
+ int i, got_tree = 0;
struct commit_list *parents = NULL;
unsigned char tree_sha1[20];
unsigned char commit_sha1[20];
@@ -37,24 +37,66 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (argc < 2 || !strcmp(argv[1], "-h"))
usage(commit_tree_usage);
- if (get_sha1(argv[1], tree_sha1))
- die("Not a valid object name %s", argv[1]);
- for (i = 2; i < argc; i += 2) {
- unsigned char sha1[20];
- const char *a, *b;
- a = argv[i]; b = argv[i+1];
- if (!b || strcmp(a, "-p"))
- usage(commit_tree_usage);
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "-p")) {
+ unsigned char sha1[20];
+ if (argc <= ++i)
+ usage(commit_tree_usage);
+ if (get_sha1(argv[i], sha1))
+ die("Not a valid object name %s", argv[i]);
+ assert_sha1_type(sha1, OBJ_COMMIT);
+ new_parent(lookup_commit(sha1), &parents);
+ continue;
+ }
+
+ if (!strcmp(arg, "-m")) {
+ if (argc <= ++i)
+ usage(commit_tree_usage);
+ if (buffer.len)
+ strbuf_addch(&buffer, '\n');
+ strbuf_addstr(&buffer, argv[i]);
+ strbuf_complete_line(&buffer);
+ continue;
+ }
+
+ if (!strcmp(arg, "-F")) {
+ int fd;
- if (get_sha1(b, sha1))
- die("Not a valid object name %s", b);
- assert_sha1_type(sha1, OBJ_COMMIT);
- new_parent(lookup_commit(sha1), &parents);
+ if (argc <= ++i)
+ usage(commit_tree_usage);
+ if (buffer.len)
+ strbuf_addch(&buffer, '\n');
+ if (!strcmp(argv[i], "-"))
+ fd = 0;
+ else {
+ fd = open(argv[i], O_RDONLY);
+ if (fd < 0)
+ die_errno("git commit-tree: failed to open '%s'",
+ argv[i]);
+ }
+ if (strbuf_read(&buffer, fd, 0) < 0)
+ die_errno("git commit-tree: failed to read '%s'",
+ argv[i]);
+ if (fd && close(fd))
+ die_errno("git commit-tree: failed to close '%s'",
+ argv[i]);
+ strbuf_complete_line(&buffer);
+ continue;
+ }
+
+ if (get_sha1(arg, tree_sha1))
+ die("Not a valid object name %s", arg);
+ if (got_tree)
+ die("Cannot give more than one trees");
+ got_tree = 1;
}
- if (strbuf_read(&buffer, 0, 0) < 0)
- die_errno("git commit-tree: failed to read");
+ if (!buffer.len) {
+ if (strbuf_read(&buffer, 0, 0) < 0)
+ die_errno("git commit-tree: failed to read");
+ }
if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
strbuf_release(&buffer);
diff --git a/builtin/commit.c b/builtin/commit.c
index c46f2d18e1..d0f27f931a 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -81,7 +81,8 @@ static const char *template_file;
static const char *author_message, *author_message_buffer;
static char *edit_message, *use_message;
static char *fixup_message, *squash_message;
-static int all, edit_flag, also, interactive, patch_interactive, only, amend, signoff;
+static int all, also, interactive, patch_interactive, only, amend, signoff;
+static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
@@ -141,7 +142,7 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_FILENAME('t', "template", &template_file, "use specified template file"),
- OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+ OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
/* end commit message options */
@@ -394,6 +395,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
fd = hold_locked_index(&index_lock, 1);
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
refresh_cache_or_die(refresh_flags);
+ update_main_cache_tree(1);
if (write_cache(fd, active_cache, active_nr) ||
close_lock_file(&index_lock))
die(_("unable to write new_index file"));
@@ -414,6 +416,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
fd = hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
if (active_cache_changed) {
+ update_main_cache_tree(1);
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(&index_lock))
die(_("unable to write new_index file"));
@@ -862,10 +865,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
*/
discard_cache();
read_cache_from(index_file);
- if (!active_cache_tree)
- active_cache_tree = cache_tree();
- if (cache_tree_update(active_cache_tree,
- active_cache, active_nr, 0, 0) < 0) {
+ if (update_main_cache_tree(0)) {
error(_("Error building trees"));
return 0;
}
@@ -1020,8 +1020,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (logfile || message.len || use_message || fixup_message)
use_editor = 0;
- if (edit_flag)
- use_editor = 1;
+ if (0 <= edit_flag)
+ use_editor = edit_flag;
if (!use_editor)
setenv("GIT_EDITOR", ":", 1);
@@ -1259,7 +1259,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
struct commit *commit;
struct strbuf format = STRBUF_INIT;
unsigned char junk_sha1[20];
- const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
+ const char *head;
struct pretty_print_context pctx = {0};
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
@@ -1304,6 +1304,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
+ head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
printf("[%s%s ",
!prefixcmp(head, "refs/heads/") ?
head + 11 :
@@ -1382,6 +1383,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
int allow_fast_forward = 1;
struct wt_status s;
struct commit *current_head = NULL;
+ struct commit_extra_header *extra = NULL;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_commit_usage, builtin_commit_options);
@@ -1425,7 +1427,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
pptr = &commit_list_insert(c->item, pptr)->next;
} else if (whence == FROM_MERGE) {
struct strbuf m = STRBUF_INIT;
- struct commit *commit;
FILE *fp;
if (!reflog_msg)
@@ -1436,11 +1437,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die_errno(_("could not open '%s' for reading"),
git_path("MERGE_HEAD"));
while (strbuf_getline(&m, fp, '\n') != EOF) {
- unsigned char sha1[20];
- if (get_sha1_hex(m.buf, sha1) < 0)
+ struct commit *parent;
+
+ parent = get_merge_parent(m.buf);
+ if (!parent)
die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
- commit = lookup_commit_or_die(sha1, "MERGE_HEAD");
- pptr = &commit_list_insert(commit, pptr)->next;
+ pptr = &commit_list_insert(parent, pptr)->next;
}
fclose(fp);
strbuf_release(&m);
@@ -1483,12 +1485,16 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
exit(1);
}
- if (commit_tree(sb.buf, active_cache_tree->sha1, parents, sha1,
- author_ident.buf)) {
+ if (amend)
+ extra = read_commit_extra_headers(current_head);
+
+ if (commit_tree_extended(sb.buf, active_cache_tree->sha1, parents, sha1,
+ author_ident.buf, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
}
strbuf_release(&author_ident);
+ free_commit_extra_headers(extra);
ref_lock = lock_any_ref_for_update("HEAD",
!current_head
@@ -1514,6 +1520,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
unlink(git_path("CHERRY_PICK_HEAD"));
+ unlink(git_path("REVERT_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
diff --git a/builtin/diff.c b/builtin/diff.c
index 1118689fb2..0fe638fc45 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -182,7 +182,7 @@ static int builtin_diff_combined(struct rev_info *revs,
hashcpy((unsigned char *)(parent + i), ent[i].item->sha1);
diff_tree_combined(parent[0], parent + 1, ents - 1,
revs->dense_combined_merges, revs);
- free(parent);
+ free((void *)parent);
return 0;
}
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index c6bc8eb0aa..6207ecd298 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -556,11 +556,16 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
continue;
}
else {
- int order = path_match(ref->name, nr_match, match);
- if (order) {
- return_refs[order-1] = ref;
- continue; /* we will link it later */
+ int i;
+ for (i = 0; i < nr_match; i++) {
+ if (!strcmp(ref->name, match[i])) {
+ match[i][0] = '\0';
+ return_refs[i] = ref;
+ break;
+ }
}
+ if (i < nr_match)
+ continue; /* we will link it later */
}
free(ref);
}
@@ -976,7 +981,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.verbose ? CONNECT_VERBOSE : 0);
}
- get_remote_heads(fd[0], &ref, 0, NULL, 0, NULL);
+ get_remote_heads(fd[0], &ref, 0, NULL);
ref = fetch_pack(&args, fd, conn, ref, dest,
nr_heads, heads, pack_lockfile_ptr);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 91731b909a..33ad3aad2c 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -240,23 +240,23 @@ static int s_update_ref(const char *action,
static int update_local_ref(struct ref *ref,
const char *remote,
- char *display)
+ struct strbuf *display)
{
struct commit *current = NULL, *updated;
enum object_type type;
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
- *display = 0;
type = sha1_object_info(ref->new_sha1, NULL);
if (type < 0)
die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
if (verbosity > 0)
- sprintf(display, "= %-*s %-*s -> %s", TRANSPORT_SUMMARY_WIDTH,
- _("[up to date]"), REFCOL_WIDTH, remote,
- pretty_ref);
+ strbuf_addf(display, "= %-*s %-*s -> %s",
+ TRANSPORT_SUMMARY_WIDTH,
+ _("[up to date]"), REFCOL_WIDTH,
+ remote, pretty_ref);
return 0;
}
@@ -268,9 +268,10 @@ static int update_local_ref(struct ref *ref,
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
- sprintf(display, _("! %-*s %-*s -> %s (can't fetch in current branch)"),
- TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
- pretty_ref);
+ strbuf_addf(display,
+ _("! %-*s %-*s -> %s (can't fetch in current branch)"),
+ TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+ REFCOL_WIDTH, remote, pretty_ref);
return 1;
}
@@ -278,9 +279,11 @@ static int update_local_ref(struct ref *ref,
!prefixcmp(ref->name, "refs/tags/")) {
int r;
r = s_update_ref("updating tag", ref, 0);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
- TRANSPORT_SUMMARY_WIDTH, _("[tag update]"), REFCOL_WIDTH, remote,
- pretty_ref, r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : '-',
+ TRANSPORT_SUMMARY_WIDTH, _("[tag update]"),
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
}
@@ -303,9 +306,11 @@ static int update_local_ref(struct ref *ref,
}
r = s_update_ref(msg, ref, 0);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
- TRANSPORT_SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : '*',
+ TRANSPORT_SUMMARY_WIDTH, what,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
}
@@ -319,9 +324,11 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("fast-forward", ref, 1);
- sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
- TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
- pretty_ref, r ? _(" (unable to update local ref)") : "");
+ strbuf_addf(display, "%c %-*s %-*s -> %s%s",
+ r ? '!' : ' ',
+ TRANSPORT_SUMMARY_WIDTH, quickref,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _(" (unable to update local ref)") : "");
return r;
} else if (force || ref->force) {
char quickref[84];
@@ -333,15 +340,17 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("forced-update", ref, 1);
- sprintf(display, "%c %-*s %-*s -> %s (%s)", r ? '!' : '+',
- TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
- pretty_ref,
- r ? _("unable to update local ref") : _("forced update"));
+ strbuf_addf(display, "%c %-*s %-*s -> %s (%s)",
+ r ? '!' : '+',
+ TRANSPORT_SUMMARY_WIDTH, quickref,
+ REFCOL_WIDTH, remote, pretty_ref,
+ r ? _("unable to update local ref") : _("forced update"));
return r;
} else {
- sprintf(display, "! %-*s %-*s -> %s %s",
- TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
- pretty_ref, _("(non-fast-forward)"));
+ strbuf_addf(display, "! %-*s %-*s -> %s %s",
+ TRANSPORT_SUMMARY_WIDTH, _("[rejected]"),
+ REFCOL_WIDTH, remote, pretty_ref,
+ _("(non-fast-forward)"));
return 1;
}
}
@@ -363,8 +372,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
{
FILE *fp;
struct commit *commit;
- int url_len, i, note_len, shown_url = 0, rc = 0;
- char note[1024];
+ int url_len, i, shown_url = 0, rc = 0;
+ struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
@@ -427,19 +436,16 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
if (4 < i && !strncmp(".git", url + i - 3, 4))
url_len = i - 3;
- note_len = 0;
+ strbuf_reset(&note);
if (*what) {
if (*kind)
- note_len += sprintf(note + note_len, "%s ",
- kind);
- note_len += sprintf(note + note_len, "'%s' of ", what);
+ strbuf_addf(&note, "%s ", kind);
+ strbuf_addf(&note, "'%s' of ", what);
}
- note[note_len] = '\0';
fprintf(fp, "%s\t%s\t%s",
- sha1_to_hex(commit ? commit->object.sha1 :
- rm->old_sha1),
+ sha1_to_hex(rm->old_sha1),
rm->merge ? "" : "not-for-merge",
- note);
+ note.buf);
for (i = 0; i < url_len; ++i)
if ('\n' == url[i])
fputs("\\n", fp);
@@ -447,21 +453,24 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
fputc(url[i], fp);
fputc('\n', fp);
+ strbuf_reset(&note);
if (ref) {
- rc |= update_local_ref(ref, what, note);
+ rc |= update_local_ref(ref, what, &note);
free(ref);
} else
- sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
- TRANSPORT_SUMMARY_WIDTH, *kind ? kind : "branch",
- REFCOL_WIDTH, *what ? what : "HEAD");
- if (*note) {
+ strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
+ TRANSPORT_SUMMARY_WIDTH,
+ *kind ? kind : "branch",
+ REFCOL_WIDTH,
+ *what ? what : "HEAD");
+ if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
url_len, url);
shown_url = 1;
}
if (verbosity >= 0)
- fprintf(stderr, " %s\n", note);
+ fprintf(stderr, " %s\n", note.buf);
}
}
@@ -471,6 +480,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
"branches"), remote_name);
abort:
+ strbuf_release(&note);
free(url);
fclose(fp);
return rc;
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 7e2f22589d..c81a7fef26 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -5,32 +5,43 @@
#include "revision.h"
#include "tag.h"
#include "string-list.h"
+#include "branch.h"
+#include "fmt-merge-msg.h"
+#include "gpg-interface.h"
static const char * const fmt_merge_msg_usage[] = {
"git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]",
NULL
};
-static int shortlog_len;
+static int use_branch_desc;
-static int fmt_merge_msg_config(const char *key, const char *value, void *cb)
+int fmt_merge_msg_config(const char *key, const char *value, void *cb)
{
if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
int is_bool;
- shortlog_len = git_config_bool_or_int(key, value, &is_bool);
- if (!is_bool && shortlog_len < 0)
+ merge_log_config = git_config_bool_or_int(key, value, &is_bool);
+ if (!is_bool && merge_log_config < 0)
return error("%s: negative length %s", key, value);
- if (is_bool && shortlog_len)
- shortlog_len = DEFAULT_MERGE_LOG_LEN;
+ if (is_bool && merge_log_config)
+ merge_log_config = DEFAULT_MERGE_LOG_LEN;
+ } else if (!strcmp(key, "merge.branchdesc")) {
+ use_branch_desc = git_config_bool(key, value);
}
return 0;
}
+/* merge data per repository where the merged tips came from */
struct src_data {
struct string_list branch, tag, r_branch, generic;
int head_status;
};
+struct origin_data {
+ unsigned char sha1[20];
+ unsigned is_local_branch:1;
+};
+
static void init_src_data(struct src_data *data)
{
data->branch.strdup_strings = 1;
@@ -45,7 +56,7 @@ static struct string_list origins = STRING_LIST_INIT_DUP;
static int handle_line(char *line)
{
int i, len = strlen(line);
- unsigned char *sha1;
+ struct origin_data *origin_data;
char *src, *origin;
struct src_data *src_data;
struct string_list_item *item;
@@ -61,16 +72,23 @@ static int handle_line(char *line)
return 2;
line[40] = 0;
- sha1 = xmalloc(20);
- i = get_sha1(line, sha1);
+ origin_data = xcalloc(1, sizeof(struct origin_data));
+ i = get_sha1(line, origin_data->sha1);
line[40] = '\t';
- if (i)
+ if (i) {
+ free(origin_data);
return 3;
+ }
if (line[len - 1] == '\n')
line[len - 1] = 0;
line += 42;
+ /*
+ * At this point, line points at the beginning of comment e.g.
+ * "branch 'frotz' of git://that/repository.git".
+ * Find the repository name and point it with src.
+ */
src = strstr(line, " of ");
if (src) {
*src = 0;
@@ -93,6 +111,7 @@ static int handle_line(char *line)
origin = src;
src_data->head_status |= 1;
} else if (!prefixcmp(line, "branch ")) {
+ origin_data->is_local_branch = 1;
origin = line + 7;
string_list_append(&src_data->branch, origin);
src_data->head_status |= 2;
@@ -119,7 +138,9 @@ static int handle_line(char *line)
sprintf(new_origin, "%s of %s", origin, src);
origin = new_origin;
}
- string_list_append(&origins, origin)->util = sha1;
+ if (strcmp(".", src))
+ origin_data->is_local_branch = 0;
+ string_list_append(&origins, origin)->util = origin_data;
return 0;
}
@@ -140,9 +161,30 @@ static void print_joined(const char *singular, const char *plural,
}
}
-static void shortlog(const char *name, unsigned char *sha1,
- struct commit *head, struct rev_info *rev, int limit,
- struct strbuf *out)
+static void add_branch_desc(struct strbuf *out, const char *name)
+{
+ struct strbuf desc = STRBUF_INIT;
+
+ if (!read_branch_desc(&desc, name)) {
+ const char *bp = desc.buf;
+ while (*bp) {
+ const char *ep = strchrnul(bp, '\n');
+ if (*ep)
+ ep++;
+ strbuf_addf(out, " : %.*s", (int)(ep - bp), bp);
+ bp = ep;
+ }
+ if (out->buf[out->len - 1] != '\n')
+ strbuf_addch(out, '\n');
+ }
+ strbuf_release(&desc);
+}
+
+static void shortlog(const char *name,
+ struct origin_data *origin_data,
+ struct commit *head,
+ struct rev_info *rev, int limit,
+ struct strbuf *out)
{
int i, count = 0;
struct commit *commit;
@@ -150,6 +192,7 @@ static void shortlog(const char *name, unsigned char *sha1,
struct string_list subjects = STRING_LIST_INIT_DUP;
int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
struct strbuf sb = STRBUF_INIT;
+ const unsigned char *sha1 = origin_data->sha1;
branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
if (!branch || branch->type != OBJ_COMMIT)
@@ -188,6 +231,9 @@ static void shortlog(const char *name, unsigned char *sha1,
else
strbuf_addf(out, "\n* %s:\n", name);
+ if (origin_data->is_local_branch && use_branch_desc)
+ add_branch_desc(out, name);
+
for (i = 0; i < subjects.nr; i++)
if (i >= limit)
strbuf_addf(out, " ...\n");
@@ -203,7 +249,7 @@ static void shortlog(const char *name, unsigned char *sha1,
string_list_clear(&subjects, 0);
}
-static void do_fmt_merge_msg_title(struct strbuf *out,
+static void fmt_merge_msg_title(struct strbuf *out,
const char *current_branch) {
int i = 0;
char *sep = "";
@@ -256,14 +302,81 @@ static void do_fmt_merge_msg_title(struct strbuf *out,
strbuf_addf(out, " into %s\n", current_branch);
}
-static int do_fmt_merge_msg(int merge_title, struct strbuf *in,
- struct strbuf *out, int shortlog_len) {
+static void fmt_tag_signature(struct strbuf *tagbuf,
+ struct strbuf *sig,
+ const char *buf,
+ unsigned long len)
+{
+ const char *tag_body = strstr(buf, "\n\n");
+ if (tag_body) {
+ tag_body += 2;
+ strbuf_add(tagbuf, tag_body, buf + len - tag_body);
+ }
+ strbuf_complete_line(tagbuf);
+ strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
+}
+
+static void fmt_merge_msg_sigs(struct strbuf *out)
+{
+ int i, tag_number = 0, first_tag = 0;
+ struct strbuf tagbuf = STRBUF_INIT;
+
+ for (i = 0; i < origins.nr; i++) {
+ unsigned char *sha1 = origins.items[i].util;
+ enum object_type type;
+ unsigned long size, len;
+ char *buf = read_sha1_file(sha1, &type, &size);
+ struct strbuf sig = STRBUF_INIT;
+
+ if (!buf || type != OBJ_TAG)
+ goto next;
+ len = parse_signature(buf, size);
+
+ if (size == len)
+ ; /* merely annotated */
+ else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig)) {
+ if (!sig.len)
+ strbuf_addstr(&sig, "gpg verification failed.\n");
+ }
+
+ if (!tag_number++) {
+ fmt_tag_signature(&tagbuf, &sig, buf, len);
+ first_tag = i;
+ } else {
+ if (tag_number == 2) {
+ struct strbuf tagline = STRBUF_INIT;
+ strbuf_addf(&tagline, "\n# %s\n",
+ origins.items[first_tag].string);
+ strbuf_insert(&tagbuf, 0, tagline.buf,
+ tagline.len);
+ strbuf_release(&tagline);
+ }
+ strbuf_addf(&tagbuf, "\n# %s\n",
+ origins.items[i].string);
+ fmt_tag_signature(&tagbuf, &sig, buf, len);
+ }
+ strbuf_release(&sig);
+ next:
+ free(buf);
+ }
+ if (tagbuf.len) {
+ strbuf_addch(out, '\n');
+ strbuf_addbuf(out, &tagbuf);
+ }
+ strbuf_release(&tagbuf);
+}
+
+int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+ struct fmt_merge_msg_opts *opts)
+{
int i = 0, pos = 0;
unsigned char head_sha1[20];
const char *current_branch;
+ void *current_branch_to_free;
/* get current branch */
- current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+ current_branch = current_branch_to_free =
+ resolve_refdup("HEAD", head_sha1, 1, NULL);
if (!current_branch)
die("No current branch");
if (!prefixcmp(current_branch, "refs/heads/"))
@@ -283,13 +396,13 @@ static int do_fmt_merge_msg(int merge_title, struct strbuf *in,
die ("Error in line %d: %.*s", i, len, p);
}
- if (!srcs.nr)
- return 0;
+ if (opts->add_title && srcs.nr)
+ fmt_merge_msg_title(out, current_branch);
- if (merge_title)
- do_fmt_merge_msg_title(out, current_branch);
+ if (origins.nr)
+ fmt_merge_msg_sigs(out);
- if (shortlog_len) {
+ if (opts->shortlog_len) {
struct commit *head;
struct rev_info rev;
@@ -303,21 +416,21 @@ static int do_fmt_merge_msg(int merge_title, struct strbuf *in,
strbuf_addch(out, '\n');
for (i = 0; i < origins.nr; i++)
- shortlog(origins.items[i].string, origins.items[i].util,
- head, &rev, shortlog_len, out);
+ shortlog(origins.items[i].string,
+ origins.items[i].util,
+ head, &rev, opts->shortlog_len, out);
}
- return 0;
-}
-int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
- int merge_title, int shortlog_len) {
- return do_fmt_merge_msg(merge_title, in, out, shortlog_len);
+ strbuf_complete_line(out);
+ free(current_branch_to_free);
+ return 0;
}
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
const char *inpath = NULL;
const char *message = NULL;
+ int shortlog_len = -1;
struct option options[] = {
{ OPTION_INTEGER, 0, "log", &shortlog_len, "n",
"populate log with at most <n> entries from shortlog",
@@ -335,20 +448,15 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
FILE *in = stdin;
struct strbuf input = STRBUF_INIT, output = STRBUF_INIT;
int ret;
+ struct fmt_merge_msg_opts opts;
git_config(fmt_merge_msg_config, NULL);
argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage,
0);
if (argc > 0)
usage_with_options(fmt_merge_msg_usage, options);
- if (message && !shortlog_len) {
- char nl = '\n';
- write_in_full(STDOUT_FILENO, message, strlen(message));
- write_in_full(STDOUT_FILENO, &nl, 1);
- return 0;
- }
if (shortlog_len < 0)
- die("Negative --log=%d", shortlog_len);
+ shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
if (inpath && strcmp(inpath, "-")) {
in = fopen(inpath, "r");
@@ -361,10 +469,12 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
if (message)
strbuf_addstr(&output, message);
- ret = fmt_merge_msg(&input, &output,
- message ? 0 : 1,
- shortlog_len);
+ memset(&opts, 0, sizeof(opts));
+ opts.add_title = !message;
+ opts.shortlog_len = shortlog_len;
+
+ ret = fmt_merge_msg(&input, &output, &opts);
if (ret)
return ret;
write_in_full(STDOUT_FILENO, output.buf, output.len);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index d90e5d2b29..b01d76a243 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -628,11 +628,8 @@ static void populate_value(struct refinfo *ref)
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
unsigned char unused1[20];
- const char *symref;
- symref = resolve_ref(ref->refname, unused1, 1, NULL);
- if (symref)
- ref->symref = xstrdup(symref);
- else
+ ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+ if (!ref->symref)
ref->symref = "";
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index df1a88b51a..8c479a791b 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -11,6 +11,7 @@
#include "fsck.h"
#include "parse-options.h"
#include "dir.h"
+#include "progress.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -27,8 +28,10 @@ static const char *head_points_at;
static int errors_found;
static int write_lost_and_found;
static int verbose;
+static int show_progress = -1;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
+#define ERROR_PACK 04
#ifdef NO_D_INO_IN_DIRENT
#define SORT_DIRENT 0
@@ -137,7 +140,11 @@ static int traverse_one_object(struct object *obj)
static int traverse_reachable(void)
{
+ struct progress *progress = NULL;
+ unsigned int nr = 0;
int result = 0;
+ if (show_progress)
+ progress = start_progress_delay("Checking connectivity", 0, 0, 2);
while (pending.nr) {
struct object_array_entry *entry;
struct object *obj;
@@ -145,7 +152,9 @@ static int traverse_reachable(void)
entry = pending.objects + --pending.nr;
obj = entry->item;
result |= traverse_one_object(obj);
+ display_progress(progress, ++nr);
}
+ stop_progress(&progress);
return !!result;
}
@@ -281,14 +290,8 @@ static void check_connectivity(void)
}
}
-static int fsck_sha1(const unsigned char *sha1)
+static int fsck_obj(struct object *obj)
{
- struct object *obj = parse_object(sha1);
- if (!obj) {
- errors_found |= ERROR_OBJECT;
- return error("%s: object corrupt or missing",
- sha1_to_hex(sha1));
- }
if (obj->flags & SEEN)
return 0;
obj->flags |= SEEN;
@@ -331,6 +334,29 @@ static int fsck_sha1(const unsigned char *sha1)
return 0;
}
+static int fsck_sha1(const unsigned char *sha1)
+{
+ struct object *obj = parse_object(sha1);
+ if (!obj) {
+ errors_found |= ERROR_OBJECT;
+ return error("%s: object corrupt or missing",
+ sha1_to_hex(sha1));
+ }
+ return fsck_obj(obj);
+}
+
+static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
+ unsigned long size, void *buffer, int *eaten)
+{
+ struct object *obj;
+ obj = parse_object_buffer(sha1, type, size, buffer, eaten);
+ if (!obj) {
+ errors_found |= ERROR_OBJECT;
+ return error("%s: object corrupt or missing", sha1_to_hex(sha1));
+ }
+ return fsck_obj(obj);
+}
+
/*
* This is the sorting chunk size: make it reasonably
* big so that we can sort well..
@@ -512,15 +538,20 @@ static void get_default_heads(void)
static void fsck_object_dir(const char *path)
{
int i;
+ struct progress *progress = NULL;
if (verbose)
fprintf(stderr, "Checking object directory\n");
+ if (show_progress)
+ progress = start_progress("Checking object directories", 256);
for (i = 0; i < 256; i++) {
static char dir[4096];
sprintf(dir, "%s/%02x", path, i);
fsck_dir(i, dir);
+ display_progress(progress, i+1);
}
+ stop_progress(&progress);
fsck_sha1_list();
}
@@ -532,7 +563,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
@@ -591,6 +622,7 @@ static struct option fsck_opts[] = {
OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"),
OPT_BOOLEAN(0, "lost-found", &write_lost_and_found,
"write dangling objects in .git/lost-found"),
+ OPT_BOOL(0, "progress", &show_progress, "show progress"),
OPT_END(),
};
@@ -603,6 +635,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
read_replace_refs = 0;
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
+
+ if (show_progress == -1)
+ show_progress = isatty(2);
+ if (verbose)
+ show_progress = 0;
+
if (write_lost_and_found) {
check_full = 1;
include_reflogs = 0;
@@ -622,20 +660,28 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
if (check_full) {
struct packed_git *p;
+ uint32_t total = 0, count = 0;
+ struct progress *progress = NULL;
prepare_packed_git();
- for (p = packed_git; p; p = p->next)
- /* verify gives error messages itself */
- verify_pack(p);
+ if (show_progress) {
+ for (p = packed_git; p; p = p->next) {
+ if (open_pack_index(p))
+ continue;
+ total += p->num_objects;
+ }
+
+ progress = start_progress("Checking objects", total);
+ }
for (p = packed_git; p; p = p->next) {
- uint32_t j, num;
- if (open_pack_index(p))
- continue;
- num = p->num_objects;
- for (j = 0; j < num; j++)
- fsck_sha1(nth_packed_object_sha1(p, j));
+ /* verify gives error messages itself */
+ if (verify_pack(p, fsck_obj_buffer,
+ progress, count))
+ errors_found |= ERROR_PACK;
+ count += p->num_objects;
}
+ stop_progress(&progress);
}
heads = 0;
diff --git a/builtin/gc.c b/builtin/gc.c
index 0498094711..271376d82b 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -32,7 +32,7 @@ static const char *prune_expire = "2.weeks.ago";
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[MAX_ADD] = {"repack", "-d", "-l", NULL};
-static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
+static const char *argv_prune[] = {"prune", "--expire", NULL, NULL, NULL};
static const char *argv_rerere[] = {"rerere", "gc", NULL};
static int gc_config(const char *var, const char *value, void *cb)
@@ -243,6 +243,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (prune_expire) {
argv_prune[2] = prune_expire;
+ if (quiet)
+ argv_prune[3] = "--no-progress";
if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
return error(FAILED_RUN, argv_prune[0]);
}
diff --git a/builtin/grep.c b/builtin/grep.c
index 3d7329d78c..988ea1d332 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -557,18 +557,19 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
struct tree_desc *tree, struct strbuf *base, int tn_len)
{
- int hit = 0, match = 0;
+ int hit = 0;
+ enum interesting match = entry_not_interesting;
struct name_entry entry;
int old_baselen = base->len;
while (tree_entry(tree, &entry)) {
- int te_len = tree_entry_len(entry.path, entry.sha1);
+ int te_len = tree_entry_len(&entry);
- if (match != 2) {
+ if (match != all_entries_interesting) {
match = tree_entry_interesting(&entry, base, tn_len, pathspec);
- if (match < 0)
+ if (match == all_entries_not_interesting)
break;
- if (match == 0)
+ if (match == entry_not_interesting)
continue;
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 0945adbb3b..98025da767 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1122,8 +1122,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (!index_name)
die("--verify with no packfile name given");
read_idx_option(&opts, index_name);
- opts.flags |= WRITE_IDX_VERIFY;
+ opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT;
}
+ if (strict)
+ opts.flags |= WRITE_IDX_STRICT;
curr_pack = open_pack_file(pack_name);
parse_pack_header();
diff --git a/builtin/log.c b/builtin/log.c
index f5d4930590..89d0cc0132 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -19,6 +19,7 @@
#include "remote.h"
#include "string-list.h"
#include "parse-options.h"
+#include "branch.h"
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@@ -746,10 +747,24 @@ static void print_signature(void)
printf("-- \n%s\n\n", signature);
}
+static void add_branch_description(struct strbuf *buf, const char *branch_name)
+{
+ struct strbuf desc = STRBUF_INIT;
+ if (!branch_name || !*branch_name)
+ return;
+ read_branch_desc(&desc, branch_name);
+ if (desc.len) {
+ strbuf_addch(buf, '\n');
+ strbuf_add(buf, desc.buf, desc.len);
+ strbuf_addch(buf, '\n');
+ }
+}
+
static void make_cover_letter(struct rev_info *rev, int use_stdout,
int numbered, int numbered_files,
struct commit *origin,
int nr, struct commit **list, struct commit *head,
+ const char *branch_name,
int quiet)
{
const char *committer;
@@ -807,6 +822,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
pp_user_info(&pp, NULL, &sb, committer, encoding);
pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
pp_remainder(&pp, &msg, &sb, 0);
+ add_branch_description(&sb, branch_name);
printf("%s\n", sb.buf);
strbuf_release(&sb);
@@ -1006,6 +1022,35 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+static char *find_branch_name(struct rev_info *rev)
+{
+ int i, positive = -1;
+ unsigned char branch_sha1[20];
+ struct strbuf buf = STRBUF_INIT;
+ const char *branch;
+
+ for (i = 0; i < rev->cmdline.nr; i++) {
+ if (rev->cmdline.rev[i].flags & UNINTERESTING)
+ continue;
+ if (positive < 0)
+ positive = i;
+ else
+ return NULL;
+ }
+ if (positive < 0)
+ return NULL;
+ strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name);
+ branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL);
+ if (!branch ||
+ prefixcmp(branch, "refs/heads/") ||
+ hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1))
+ branch = NULL;
+ strbuf_release(&buf);
+ if (branch)
+ return xstrdup(rev->cmdline.rev[positive].name);
+ return NULL;
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -1027,6 +1072,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
int use_patch_format = 0;
int quiet = 0;
+ char *branch_name = NULL;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
"use [PATCH n/m] even with a single patch",
@@ -1217,8 +1263,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* origin" that prepares what the origin side still
* does not have.
*/
+ unsigned char sha1[20];
+ const char *ref;
+
rev.pending.objects[0].item->flags |= UNINTERESTING;
add_head_to_pending(&rev);
+ ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ if (ref && !prefixcmp(ref, "refs/heads/"))
+ branch_name = xstrdup(ref + strlen("refs/heads/"));
+ else
+ branch_name = xstrdup(""); /* no branch */
}
/*
* Otherwise, it is "format-patch -22 HEAD", and/or
@@ -1234,16 +1288,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.show_root_diff = 1;
if (cover_letter) {
- /* remember the range */
+ /*
+ * NEEDSWORK:randomly pick one positive commit to show
+ * diffstat; this is often the tip and the command
+ * happens to do the right thing in most cases, but a
+ * complex command like "--cover-letter a b c ^bottom"
+ * picks "c" and shows diffstat between bottom..c
+ * which may not match what the series represents at
+ * all and totally broken.
+ */
int i;
for (i = 0; i < rev.pending.nr; i++) {
struct object *o = rev.pending.objects[i].item;
if (!(o->flags & UNINTERESTING))
head = (struct commit *)o;
}
- /* We can't generate a cover letter without any patches */
+ /* There is nothing to show; it is not an error, though. */
if (!head)
return 0;
+ if (!branch_name)
+ branch_name = find_branch_name(&rev);
}
if (ignore_if_in_upstream) {
@@ -1294,7 +1358,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout, numbered, numbered_files,
- origin, nr, list, head, quiet);
+ origin, nr, list, head, branch_name, quiet);
total++;
start_number--;
}
@@ -1366,6 +1430,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
fclose(stdout);
}
free(list);
+ free(branch_name);
string_list_clear(&extra_to, 0);
string_list_clear(&extra_cc, 0);
string_list_clear(&extra_hdr, 0);
diff --git a/builtin/merge.c b/builtin/merge.c
index dffd5ec124..24579409c0 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -26,6 +26,7 @@
#include "merge-recursive.h"
#include "resolve-undo.h"
#include "remote.h"
+#include "fmt-merge-msg.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -44,10 +45,11 @@ static const char * const builtin_merge_usage[] = {
NULL
};
-static int show_diffstat = 1, shortlog_len, squash;
+static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = 1, allow_fast_forward = 1;
static int fast_forward_only, option_edit;
static int allow_trivial = 1, have_message;
+static int overwrite_ignore = 1;
static struct strbuf merge_msg;
static struct commit_list *remoteheads;
static struct strategy **use_strategies;
@@ -207,6 +209,7 @@ static struct option builtin_merge_options[] = {
OPT_BOOLEAN(0, "abort", &abort_current_merge,
"abort the current in-progress merge"),
OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
+ OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
OPT_END()
};
@@ -316,13 +319,15 @@ static void squash_message(struct commit *commit)
struct rev_info rev;
struct strbuf out = STRBUF_INIT;
struct commit_list *j;
+ const char *filename;
int fd;
struct pretty_print_context ctx = {0};
printf(_("Squash commit -- not updating HEAD\n"));
- fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+ filename = git_path("SQUASH_MSG");
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not write to '%s'"), git_path("SQUASH_MSG"));
+ die_errno(_("Could not write to '%s'"), filename);
init_revisions(&rev, NULL);
rev.ignore_merges = 1;
@@ -406,21 +411,11 @@ static void finish(struct commit *head_commit,
strbuf_release(&reflog_message);
}
-static struct object *want_commit(const char *name)
-{
- struct object *obj;
- unsigned char sha1[20];
- if (get_sha1(name, sha1))
- return NULL;
- obj = parse_object(sha1);
- return peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
/* Get the name for the merge commit's message. */
static void merge_name(const char *remote, struct strbuf *msg)
{
- struct object *remote_head;
- unsigned char branch_head[20], buf_sha[20];
+ struct commit *remote_head;
+ unsigned char branch_head[20];
struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
const char *ptr;
@@ -431,7 +426,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
remote = bname.buf;
memset(branch_head, 0, sizeof(branch_head));
- remote_head = want_commit(remote);
+ remote_head = get_merge_parent(remote);
if (!remote_head)
die(_("'%s' does not point to a commit"), remote);
@@ -441,6 +436,11 @@ static void merge_name(const char *remote, struct strbuf *msg)
sha1_to_hex(branch_head), remote);
goto cleanup;
}
+ if (!prefixcmp(found_ref, "refs/tags/")) {
+ strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
+ sha1_to_hex(branch_head), remote);
+ goto cleanup;
+ }
if (!prefixcmp(found_ref, "refs/remotes/")) {
strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
sha1_to_hex(branch_head), remote);
@@ -479,10 +479,10 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_addstr(&truname, "refs/heads/");
strbuf_addstr(&truname, remote);
strbuf_setlen(&truname, truname.len - len);
- if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
+ if (ref_exists(truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
- sha1_to_hex(remote_head->sha1),
+ sha1_to_hex(remote_head->object.sha1),
truname.buf + 11,
(early ? " (early part)" : ""));
strbuf_release(&truname);
@@ -492,14 +492,16 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (!strcmp(remote, "FETCH_HEAD") &&
!access(git_path("FETCH_HEAD"), R_OK)) {
+ const char *filename;
FILE *fp;
struct strbuf line = STRBUF_INIT;
char *ptr;
- fp = fopen(git_path("FETCH_HEAD"), "r");
+ filename = git_path("FETCH_HEAD");
+ fp = fopen(filename, "r");
if (!fp)
die_errno(_("could not open '%s' for reading"),
- git_path("FETCH_HEAD"));
+ filename);
strbuf_getline(&line, fp, '\n');
fclose(fp);
ptr = strstr(line.buf, "\tnot-for-merge\t");
@@ -510,7 +512,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
goto cleanup;
}
strbuf_addf(msg, "%s\t\tcommit '%s'\n",
- sha1_to_hex(remote_head->sha1), remote);
+ sha1_to_hex(remote_head->object.sha1), remote);
cleanup:
strbuf_release(&buf);
strbuf_release(&bname);
@@ -538,6 +540,8 @@ static void parse_branch_merge_options(char *bmo)
static int git_merge_config(const char *k, const char *v, void *cb)
{
+ int status;
+
if (branch && !prefixcmp(k, "branch.") &&
!prefixcmp(k + 7, branch) &&
!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
@@ -554,15 +558,7 @@ static int git_merge_config(const char *k, const char *v, void *cb)
return git_config_string(&pull_octopus, k, v);
else if (!strcmp(k, "merge.renormalize"))
option_renormalize = git_config_bool(k, v);
- else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
- int is_bool;
- shortlog_len = git_config_bool_or_int(k, v, &is_bool);
- if (!is_bool && shortlog_len < 0)
- return error(_("%s: negative length %s"), k, v);
- if (is_bool && shortlog_len)
- shortlog_len = DEFAULT_MERGE_LOG_LEN;
- return 0;
- } else if (!strcmp(k, "merge.ff")) {
+ else if (!strcmp(k, "merge.ff")) {
int boolval = git_config_maybe_bool(k, v);
if (0 <= boolval) {
allow_fast_forward = boolval;
@@ -575,6 +571,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
default_to_upstream = git_config_bool(k, v);
return 0;
}
+ status = fmt_merge_msg_config(k, v, cb);
+ if (status)
+ return status;
return git_diff_ui_config(k, v, cb);
}
@@ -714,7 +713,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
o.branch1 = head_arg;
- o.branch2 = remoteheads->item->util;
+ o.branch2 = merge_remote_util(remoteheads->item)->name;
for (j = common; j; j = j->next)
commit_list_insert(j->item, &reversed);
@@ -769,10 +768,12 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
memset(&trees, 0, sizeof(trees));
memset(&opts, 0, sizeof(opts));
memset(&t, 0, sizeof(t));
- memset(&dir, 0, sizeof(dir));
- dir.flags |= DIR_SHOW_IGNORED;
- dir.exclude_per_dir = ".gitignore";
- opts.dir = &dir;
+ if (overwrite_ignore) {
+ memset(&dir, 0, sizeof(dir));
+ dir.flags |= DIR_SHOW_IGNORED;
+ setup_standard_excludes(&dir);
+ opts.dir = &dir;
+ }
opts.head_idx = 1;
opts.src_index = &the_index;
@@ -847,20 +848,22 @@ static void add_strategies(const char *string, unsigned attr)
static void write_merge_msg(struct strbuf *msg)
{
- int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+ const char *filename = git_path("MERGE_MSG");
+ int fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MSG"));
+ filename);
if (write_in_full(fd, msg->buf, msg->len) != msg->len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG"));
+ die_errno(_("Could not write to '%s'"), filename);
close(fd);
}
static void read_merge_msg(struct strbuf *msg)
{
+ const char *filename = git_path("MERGE_MSG");
strbuf_reset(msg);
- if (strbuf_read_file(msg, git_path("MERGE_MSG"), 0) < 0)
- die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG"));
+ if (strbuf_read_file(msg, filename, 0) < 0)
+ die_errno(_("Could not read from '%s'"), filename);
}
static void write_merge_state(void);
@@ -948,13 +951,14 @@ static int finish_automerge(struct commit *head,
static int suggest_conflicts(int renormalizing)
{
+ const char *filename;
FILE *fp;
int pos;
- fp = fopen(git_path("MERGE_MSG"), "a");
+ filename = git_path("MERGE_MSG");
+ fp = fopen(filename, "a");
if (!fp)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MSG"));
+ die_errno(_("Could not open '%s' for writing"), filename);
fprintf(fp, "\nConflicts:\n");
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
@@ -1046,31 +1050,40 @@ static int setup_with_upstream(const char ***argv)
static void write_merge_state(void)
{
+ const char *filename;
int fd;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
- for (j = remoteheads; j; j = j->next)
- strbuf_addf(&buf, "%s\n",
- sha1_to_hex(j->item->object.sha1));
- fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+ for (j = remoteheads; j; j = j->next) {
+ unsigned const char *sha1;
+ struct commit *c = j->item;
+ if (c->util && merge_remote_util(c)->obj) {
+ sha1 = merge_remote_util(c)->obj->sha1;
+ } else {
+ sha1 = c->object.sha1;
+ }
+ strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+ }
+ filename = git_path("MERGE_HEAD");
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_HEAD"));
+ die_errno(_("Could not open '%s' for writing"), filename);
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD"));
+ die_errno(_("Could not write to '%s'"), filename);
close(fd);
strbuf_addch(&merge_msg, '\n');
write_merge_msg(&merge_msg);
- fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ filename = git_path("MERGE_MODE");
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("MERGE_MODE"));
+ die_errno(_("Could not open '%s' for writing"), filename);
strbuf_reset(&buf);
if (!allow_fast_forward)
strbuf_addf(&buf, "no-ff");
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE"));
+ die_errno(_("Could not write to '%s'"), filename);
close(fd);
}
@@ -1082,11 +1095,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit *head_commit;
struct strbuf buf = STRBUF_INIT;
const char *head_arg;
- int flag, i;
+ int flag, i, ret = 0;
int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
struct commit_list *common = NULL;
const char *best_strategy = NULL, *wt_strategy = NULL;
struct commit_list **remotes = &remoteheads;
+ void *branch_to_free;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1095,7 +1109,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = resolve_ref("HEAD", head_sha1, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
if (branch && !prefixcmp(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
@@ -1109,6 +1123,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
parse_branch_merge_options(branch_mergeoptions);
argc = parse_options(argc, argv, prefix, builtin_merge_options,
builtin_merge_usage, 0);
+ if (shortlog_len < 0)
+ shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
if (verbosity < 0 && show_progress == -1)
show_progress = 0;
@@ -1121,7 +1137,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("There is no merge to abort (MERGE_HEAD missing)."));
/* Invoke 'git reset --merge' */
- return cmd_reset(nargc, nargv, prefix);
+ ret = cmd_reset(nargc, nargv, prefix);
+ goto done;
}
if (read_cache_unmerged())
@@ -1160,9 +1177,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("You cannot combine --no-ff with --ff-only."));
if (!abort_current_merge) {
- if (!argc && default_to_upstream)
- argc = setup_with_upstream(&argv);
- else if (argc == 1 && !strcmp(argv[0], "-"))
+ if (!argc) {
+ if (default_to_upstream)
+ argc = setup_with_upstream(&argv);
+ else
+ die(_("No commit specified and merge.defaultToUpstream not set."));
+ } else if (argc == 1 && !strcmp(argv[0], "-"))
argv[0] = "@{-1}";
}
if (!argc)
@@ -1185,7 +1205,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
argv += 2;
argc -= 2;
} else if (!head_commit) {
- struct object *remote_head;
+ struct commit *remote_head;
/*
* If the merged head is a valid one there is no reason
* to forbid "git merge" into a branch yet to be born.
@@ -1199,13 +1219,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!allow_fast_forward)
die(_("Non-fast-forward commit does not make sense into "
"an empty head"));
- remote_head = want_commit(argv[0]);
+ remote_head = get_merge_parent(argv[0]);
if (!remote_head)
die(_("%s - not something we can merge"), argv[0]);
- read_empty(remote_head->sha1, 0);
- update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
- DIE_ON_ERR);
- return 0;
+ read_empty(remote_head->object.sha1, 0);
+ update_ref("initial pull", "HEAD", remote_head->object.sha1,
+ NULL, 0, DIE_ON_ERR);
+ goto done;
} else {
struct strbuf merge_names = STRBUF_INIT;
@@ -1213,19 +1233,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
head_arg = "HEAD";
/*
- * All the rest are the commits being merged;
- * prepare the standard merge summary message to
- * be appended to the given message. If remote
- * is invalid we will die later in the common
- * codepath so we discard the error in this
- * loop.
+ * All the rest are the commits being merged; prepare
+ * the standard merge summary message to be appended
+ * to the given message.
*/
for (i = 0; i < argc; i++)
merge_name(argv[i], &merge_names);
if (!have_message || shortlog_len) {
- fmt_merge_msg(&merge_names, &merge_msg, !have_message,
- shortlog_len);
+ struct fmt_merge_msg_opts opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.add_title = !have_message;
+ opts.shortlog_len = shortlog_len;
+
+ fmt_merge_msg(&merge_names, &merge_msg, &opts);
if (merge_msg.len)
strbuf_setlen(&merge_msg, merge_msg.len - 1);
}
@@ -1242,19 +1263,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf);
for (i = 0; i < argc; i++) {
- struct object *o;
- struct commit *commit;
-
- o = want_commit(argv[i]);
- if (!o)
+ struct commit *commit = get_merge_parent(argv[i]);
+ if (!commit)
die(_("%s - not something we can merge"), argv[i]);
- commit = lookup_commit(o->sha1);
- commit->util = (void *)argv[i];
remotes = &commit_list_insert(commit, remotes)->next;
-
- strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+ strbuf_addf(&buf, "GITHEAD_%s",
+ sha1_to_hex(commit->object.sha1));
setenv(buf.buf, argv[i], 1);
strbuf_reset(&buf);
+ if (merge_remote_util(commit) &&
+ merge_remote_util(commit)->obj &&
+ merge_remote_util(commit)->obj->type == OBJ_TAG) {
+ option_edit = 1;
+ allow_fast_forward = 0;
+ }
}
if (!use_strategies) {
@@ -1292,13 +1314,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* but first the most common case of merging one remote.
*/
finish_up_to_date("Already up-to-date.");
- return 0;
+ goto done;
} else if (allow_fast_forward && !remoteheads->next &&
!common->next &&
!hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
/* Again the most common case of merging one remote. */
struct strbuf msg = STRBUF_INIT;
- struct object *o;
+ struct commit *commit;
char hex[41];
strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
@@ -1312,16 +1334,21 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (have_message)
strbuf_addstr(&msg,
" (no commit created; -m option ignored)");
- o = want_commit(sha1_to_hex(remoteheads->item->object.sha1));
- if (!o)
- return 1;
+ commit = remoteheads->item;
+ if (!commit) {
+ ret = 1;
+ goto done;
+ }
- if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1))
- return 1;
+ if (checkout_fast_forward(head_commit->object.sha1,
+ commit->object.sha1)) {
+ ret = 1;
+ goto done;
+ }
- finish(head_commit, o->sha1, msg.buf);
+ finish(head_commit, commit->object.sha1, msg.buf);
drop_save();
- return 0;
+ goto done;
} else if (!remoteheads->next && common->next)
;
/*
@@ -1339,8 +1366,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
git_committer_info(IDENT_ERROR_ON_NO_NAME);
printf(_("Trying really trivial in-index merge...\n"));
if (!read_tree_trivial(common->item->object.sha1,
- head_commit->object.sha1, remoteheads->item->object.sha1))
- return merge_trivial(head_commit);
+ head_commit->object.sha1,
+ remoteheads->item->object.sha1)) {
+ ret = merge_trivial(head_commit);
+ goto done;
+ }
printf(_("Nope.\n"));
}
} else {
@@ -1368,7 +1398,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (up_to_date) {
finish_up_to_date("Already up-to-date. Yeeah!");
- return 0;
+ goto done;
}
}
@@ -1450,9 +1480,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* If we have a resulting tree, that means the strategy module
* auto resolved the merge cleanly.
*/
- if (automerge_was_ok)
- return finish_automerge(head_commit, common, result_tree,
- wt_strategy);
+ if (automerge_was_ok) {
+ ret = finish_automerge(head_commit, common, result_tree,
+ wt_strategy);
+ goto done;
+ }
/*
* Pick the result from the best strategy and have the user fix
@@ -1466,7 +1498,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
fprintf(stderr, _("Merge with strategy %s failed.\n"),
use_strategies[0]->name);
- return 2;
+ ret = 2;
+ goto done;
} else if (best_strategy == wt_strategy)
; /* We already have its result in the working tree. */
else {
@@ -1482,10 +1515,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
write_merge_state();
- if (merge_was_ok) {
+ if (merge_was_ok)
fprintf(stderr, _("Automatic merge went well; "
"stopped before committing as requested\n"));
- return 0;
- } else
- return suggest_conflicts(option_renormalize);
+ else
+ ret = suggest_conflicts(option_renormalize);
+
+done:
+ free(branch_to_free);
+ return ret;
}
diff --git a/builtin/mv.c b/builtin/mv.c
index 5efe6c5760..2a144b011c 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -59,6 +59,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int i, newfd;
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
struct option builtin_mv_options[] = {
+ OPT__VERBOSE(&verbose, "be verbose"),
OPT__DRY_RUN(&show_only, "dry run"),
OPT__FORCE(&force, "force move/rename even if target exists"),
OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
@@ -93,7 +94,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
destination = copy_pathspec(dest_path[0], argv, argc, 1);
} else {
if (argc != 1)
- usage_with_options(builtin_mv_usage, builtin_mv_options);
+ die("destination '%s' is not a directory", dest_path[0]);
destination = dest_path;
}
@@ -176,7 +177,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
* check both source and destination
*/
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- warning(_("%s; will overwrite!"), bad);
+ if (verbose)
+ warning(_("overwriting '%s'"), dst);
bad = NULL;
} else
bad = _("Cannot overwrite");
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 7864056f1e..1b374583c2 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -291,7 +291,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
max = get_max_object_index();
for (i = 0; i < max; i++) {
struct object *obj = get_indexed_object(i);
- if (!obj)
+ if (!obj || obj->type != OBJ_COMMIT)
continue;
show_name(obj, NULL,
always, allow_undefined, data.name_only);
diff --git a/builtin/notes.c b/builtin/notes.c
index f8e437db01..667e20a1e1 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -804,6 +804,8 @@ static int merge_commit(struct notes_merge_options *o)
struct notes_tree *t;
struct commit *partial;
struct pretty_print_context pretty_ctx;
+ void *local_ref_to_free;
+ int ret;
/*
* Read partial merge result from .git/NOTES_MERGE_PARTIAL,
@@ -825,7 +827,8 @@ static int merge_commit(struct notes_merge_options *o)
t = xcalloc(1, sizeof(struct notes_tree));
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
- o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL);
+ o->local_ref = local_ref_to_free =
+ resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
if (!o->local_ref)
die("Failed to resolve NOTES_MERGE_REF");
@@ -843,7 +846,9 @@ static int merge_commit(struct notes_merge_options *o)
free_notes(t);
strbuf_release(&msg);
- return merge_abort(o);
+ ret = merge_abort(o);
+ free(local_ref_to_free);
+ return ret;
}
static int merge(int argc, const char **argv, const char *prefix)
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 824ecee20b..96c1680976 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -76,7 +76,7 @@ static struct pack_idx_option pack_idx_opts;
static const char *base_name;
static int progress = 1;
static int window = 10;
-static unsigned long pack_size_limit, pack_size_limit_cfg;
+static unsigned long pack_size_limit;
static int depth = 50;
static int delta_search_threads;
static int pack_to_stdout;
@@ -409,25 +409,56 @@ static unsigned long write_object(struct sha1file *f,
return hdrlen + datalen;
}
-static int write_one(struct sha1file *f,
- struct object_entry *e,
- off_t *offset)
+enum write_one_status {
+ WRITE_ONE_SKIP = -1, /* already written */
+ WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
+ WRITE_ONE_WRITTEN = 1, /* normal */
+ WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
+};
+
+static enum write_one_status write_one(struct sha1file *f,
+ struct object_entry *e,
+ off_t *offset)
{
unsigned long size;
+ int recursing;
- /* offset is non zero if object is written already. */
- if (e->idx.offset || e->preferred_base)
- return -1;
+ /*
+ * we set offset to 1 (which is an impossible value) to mark
+ * the fact that this object is involved in "write its base
+ * first before writing a deltified object" recursion.
+ */
+ recursing = (e->idx.offset == 1);
+ if (recursing) {
+ warning("recursive delta detected for object %s",
+ sha1_to_hex(e->idx.sha1));
+ return WRITE_ONE_RECURSIVE;
+ } else if (e->idx.offset || e->preferred_base) {
+ /* offset is non zero if object is written already. */
+ return WRITE_ONE_SKIP;
+ }
/* if we are deltified, write out base object first. */
- if (e->delta && !write_one(f, e->delta, offset))
- return 0;
+ if (e->delta) {
+ e->idx.offset = 1; /* now recurse */
+ switch (write_one(f, e->delta, offset)) {
+ case WRITE_ONE_RECURSIVE:
+ /* we cannot depend on this one */
+ e->delta = NULL;
+ break;
+ default:
+ break;
+ case WRITE_ONE_BREAK:
+ e->idx.offset = recursing;
+ return WRITE_ONE_BREAK;
+ }
+ }
e->idx.offset = *offset;
size = write_object(f, e, *offset);
if (!size) {
- e->idx.offset = 0;
- return 0;
+ e->idx.offset = recursing;
+ return WRITE_ONE_BREAK;
}
written_list[nr_written++] = &e->idx;
@@ -435,7 +466,7 @@ static int write_one(struct sha1file *f,
if (signed_add_overflows(*offset, size))
die("pack too large for current definition of off_t");
*offset += size;
- return 1;
+ return WRITE_ONE_WRITTEN;
}
static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
@@ -607,7 +638,6 @@ static void write_pack_file(void)
uint32_t i = 0, j;
struct sha1file *f;
off_t offset;
- struct pack_header hdr;
uint32_t nr_remaining = nr_result;
time_t last_mtime = 0;
struct object_entry **write_order;
@@ -621,26 +651,18 @@ static void write_pack_file(void)
unsigned char sha1[20];
char *pack_tmp_name = NULL;
- if (pack_to_stdout) {
+ if (pack_to_stdout)
f = sha1fd_throughput(1, "<stdout>", progress_state);
- } else {
- char tmpname[PATH_MAX];
- int fd;
- fd = odb_mkstemp(tmpname, sizeof(tmpname),
- "pack/tmp_pack_XXXXXX");
- pack_tmp_name = xstrdup(tmpname);
- f = sha1fd(fd, pack_tmp_name);
- }
-
- hdr.hdr_signature = htonl(PACK_SIGNATURE);
- hdr.hdr_version = htonl(PACK_VERSION);
- hdr.hdr_entries = htonl(nr_remaining);
- sha1write(f, &hdr, sizeof(hdr));
- offset = sizeof(hdr);
+ else
+ f = create_tmp_packfile(&pack_tmp_name);
+
+ offset = write_pack_header(f, nr_remaining);
+ if (!offset)
+ die_errno("unable to write pack header");
nr_written = 0;
for (; i < nr_objects; i++) {
struct object_entry *e = write_order[i];
- if (!write_one(f, e, &offset))
+ if (write_one(f, e, &offset) == WRITE_ONE_BREAK)
break;
display_progress(progress_state, written);
}
@@ -662,20 +684,8 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
struct stat st;
- const char *idx_tmp_name;
char tmpname[PATH_MAX];
- idx_tmp_name = write_idx_file(NULL, written_list, nr_written,
- &pack_idx_opts, sha1);
-
- snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
- base_name, sha1_to_hex(sha1));
- free_pack_by_name(tmpname);
- if (adjust_shared_perm(pack_tmp_name))
- die_errno("unable to make temporary pack file readable");
- if (rename(pack_tmp_name, tmpname))
- die_errno("unable to rename temporary pack file");
-
/*
* Packs are runtime accessed in their mtime
* order since newer packs are more likely to contain
@@ -683,28 +693,27 @@ static void write_pack_file(void)
* packs then we should modify the mtime of later ones
* to preserve this property.
*/
- if (stat(tmpname, &st) < 0) {
+ if (stat(pack_tmp_name, &st) < 0) {
warning("failed to stat %s: %s",
- tmpname, strerror(errno));
+ pack_tmp_name, strerror(errno));
} else if (!last_mtime) {
last_mtime = st.st_mtime;
} else {
struct utimbuf utb;
utb.actime = st.st_atime;
utb.modtime = --last_mtime;
- if (utime(tmpname, &utb) < 0)
+ if (utime(pack_tmp_name, &utb) < 0)
warning("failed utime() on %s: %s",
tmpname, strerror(errno));
}
- snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
- base_name, sha1_to_hex(sha1));
- if (adjust_shared_perm(idx_tmp_name))
- die_errno("unable to make temporary index file readable");
- if (rename(idx_tmp_name, tmpname))
- die_errno("unable to rename temporary index file");
-
- free((void *) idx_tmp_name);
+ /* Enough space for "-<sha-1>.pack"? */
+ if (sizeof(tmpname) <= strlen(base_name) + 50)
+ die("pack base name '%s' too long", base_name);
+ snprintf(tmpname, sizeof(tmpname), "%s-", base_name);
+ finish_tmp_packfile(tmpname, pack_tmp_name,
+ written_list, nr_written,
+ &pack_idx_opts, sha1);
free(pack_tmp_name);
puts(sha1_to_hex(sha1));
}
@@ -1015,7 +1024,7 @@ static void add_pbase_object(struct tree_desc *tree,
while (tree_entry(tree,&entry)) {
if (S_ISGITLINK(entry.mode))
continue;
- cmp = tree_entry_len(entry.path, entry.sha1) != cmplen ? 1 :
+ cmp = tree_entry_len(&entry) != cmplen ? 1 :
memcmp(name, entry.path, cmplen);
if (cmp > 0)
continue;
@@ -2067,10 +2076,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)
pack_idx_opts.version);
return 0;
}
- if (!strcmp(k, "pack.packsizelimit")) {
- pack_size_limit_cfg = git_config_ulong(k, v);
- return 0;
- }
return git_default_config(k, v, cb);
}
diff --git a/builtin/prune.c b/builtin/prune.c
index e65690ba37..58d7cb8324 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -5,6 +5,7 @@
#include "builtin.h"
#include "reachable.h"
#include "parse-options.h"
+#include "progress.h"
#include "dir.h"
static const char * const prune_usage[] = {
@@ -14,6 +15,7 @@ static const char * const prune_usage[] = {
static int show_only;
static int verbose;
static unsigned long expire;
+static int show_progress = -1;
static int prune_tmp_object(const char *path, const char *filename)
{
@@ -124,9 +126,11 @@ static void remove_temporary_files(const char *path)
int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
+ struct progress *progress = NULL;
const struct option options[] = {
OPT__DRY_RUN(&show_only, "do not remove, show only"),
OPT__VERBOSE(&verbose, "report pruned objects"),
+ OPT_BOOL(0, "progress", &show_progress, "show progress"),
OPT_DATE(0, "expire", &expire,
"expire objects older than <time>"),
OPT_END()
@@ -152,7 +156,14 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
else
die("unrecognized argument: %s", name);
}
- mark_reachable_objects(&revs, 1);
+
+ if (show_progress == -1)
+ show_progress = isatty(2);
+ if (show_progress)
+ progress = start_progress_delay("Checking connectivity", 0, 0, 2);
+
+ mark_reachable_objects(&revs, 1, progress);
+ stop_progress(&progress);
prune_object_dir(get_object_directory());
prune_packed_objects(show_only);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 7ec68a1e80..d2dcb7e4af 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -37,6 +37,7 @@ static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
static const char *head_name;
+static void *head_name_to_free;
static int sent_capabilities;
static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -571,7 +572,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+ dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
@@ -695,7 +696,8 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
check_aliased_updates(commands);
- head_name = resolve_ref("HEAD", sha1, 0, NULL);
+ free(head_name_to_free);
+ head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
for (cmd = commands; cmd; cmd = cmd->next)
if (!cmd->skip_update)
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 3a9c80f3db..062d7dad1b 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -647,7 +647,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
init_revisions(&cb.revs, prefix);
if (cb.verbose)
printf("Marking reachable objects...");
- mark_reachable_objects(&cb.revs, 0);
+ mark_reachable_objects(&cb.revs, 0, NULL);
if (cb.verbose)
putchar('\n');
}
diff --git a/builtin/remote.c b/builtin/remote.c
index c810643815..583eec90e0 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -343,8 +343,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
states->tracked.strdup_strings = 1;
states->stale.strdup_strings = 1;
for (ref = fetch_map; ref; ref = ref->next) {
- unsigned char sha1[20];
- if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
+ if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
string_list_append(&states->new, abbrev_branch(ref->name));
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
@@ -574,7 +573,7 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (!prefixcmp(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
- symref = resolve_ref(refname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
@@ -710,7 +709,7 @@ static int mv(int argc, const char **argv)
int flag = 0;
unsigned char sha1[20];
- resolve_ref(item->string, sha1, 1, &flag);
+ read_ref_full(item->string, sha1, 1, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
@@ -1220,10 +1219,9 @@ static int set_head(int argc, const char **argv)
usage_with_options(builtin_remote_sethead_usage, options);
if (head_name) {
- unsigned char sha1[20];
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
/* make sure it's valid */
- if (!resolve_ref(buf2.buf, sha1, 1, NULL))
+ if (!ref_exists(buf2.buf))
result |= error("Not a valid ref: %s", buf2.buf);
else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
result |= error("Could not setup %s", buf.buf);
diff --git a/builtin/replace.c b/builtin/replace.c
index 517fa1031a..4a8970e9c9 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -58,7 +58,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
had_error = 1;
continue;
}
- if (!resolve_ref(ref, sha1, 1, NULL)) {
+ if (read_ref(ref, sha1)) {
error("replace ref '%s' not found.", *p);
had_error = 1;
continue;
@@ -97,7 +97,7 @@ static int replace_object(const char *object_ref, const char *replace_ref,
if (check_refname_format(ref, 0))
die("'%s' is not a valid ref name.", ref);
- if (!resolve_ref(ref, prev, 1, NULL))
+ if (read_ref(ref, prev))
hashclr(prev);
else if (!force)
die("replace ref '%s' already exists", ref);
diff --git a/builtin/reset.c b/builtin/reset.c
index 811e8e252c..8c2c1d52a2 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -43,6 +43,7 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
int nr = 1;
int newfd;
struct tree_desc desc[2];
+ struct tree *tree;
struct unpack_trees_options opts;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
@@ -84,6 +85,12 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
return error(_("Failed to find tree of %s."), sha1_to_hex(sha1));
if (unpack_trees(nr, desc, &opts))
return -1;
+
+ if (reset_type == MIXED || reset_type == HARD) {
+ tree = parse_tree_indirect(sha1);
+ prime_cache_tree(&active_cache_tree, tree);
+ }
+
if (write_cache(newfd, active_cache, active_nr) ||
commit_locked_index(lock))
return error(_("Could not write new index file."));
diff --git a/builtin/revert.c b/builtin/revert.c
index 87df70edc3..fce3f92981 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -40,7 +40,12 @@ static const char * const cherry_pick_usage[] = {
};
enum replay_action { REVERT, CHERRY_PICK };
-enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
+enum replay_subcommand {
+ REPLAY_NONE,
+ REPLAY_REMOVE_STATE,
+ REPLAY_CONTINUE,
+ REPLAY_ROLLBACK
+};
struct replay_opts {
enum replay_action action;
@@ -55,13 +60,14 @@ struct replay_opts {
int allow_rerere_auto;
int mainline;
- int commit_argc;
- const char **commit_argv;
/* Merge strategy */
const char *strategy;
const char **xopts;
size_t xopts_nr, xopts_alloc;
+
+ /* Only used by REPLAY_NONE */
+ struct rev_info *revs;
};
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -133,11 +139,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
{
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
- int reset = 0;
+ int remove_state = 0;
int contin = 0;
+ int rollback = 0;
struct option options[] = {
- OPT_BOOLEAN(0, "reset", &reset, "forget the current operation"),
- OPT_BOOLEAN(0, "continue", &contin, "continue the current operation"),
+ OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
OPT_NOOP_NOARG('r', NULL),
@@ -162,31 +170,38 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
die(_("program error"));
}
- opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
- PARSE_OPT_KEEP_ARGV0 |
- PARSE_OPT_KEEP_UNKNOWN);
+ argc = parse_options(argc, argv, NULL, options, usage_str,
+ PARSE_OPT_KEEP_ARGV0 |
+ PARSE_OPT_KEEP_UNKNOWN);
/* Check for incompatible subcommands */
verify_opt_mutually_compatible(me,
- "--reset", reset,
+ "--quit", remove_state,
"--continue", contin,
+ "--abort", rollback,
NULL);
/* Set the subcommand */
- if (reset)
- opts->subcommand = REPLAY_RESET;
+ if (remove_state)
+ opts->subcommand = REPLAY_REMOVE_STATE;
else if (contin)
opts->subcommand = REPLAY_CONTINUE;
+ else if (rollback)
+ opts->subcommand = REPLAY_ROLLBACK;
else
opts->subcommand = REPLAY_NONE;
/* Check for incompatible command line arguments */
if (opts->subcommand != REPLAY_NONE) {
char *this_operation;
- if (opts->subcommand == REPLAY_RESET)
- this_operation = "--reset";
- else
+ if (opts->subcommand == REPLAY_REMOVE_STATE)
+ this_operation = "--quit";
+ else if (opts->subcommand == REPLAY_CONTINUE)
this_operation = "--continue";
+ else {
+ assert(opts->subcommand == REPLAY_ROLLBACK);
+ this_operation = "--abort";
+ }
verify_opt_compatible(me, this_operation,
"--no-commit", opts->no_commit,
@@ -199,9 +214,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
NULL);
}
- else if (opts->commit_argc < 2)
- usage_with_options(usage_str, options);
-
if (opts->allow_ff)
verify_opt_compatible(me, "--ff",
"--signoff", opts->signoff,
@@ -209,7 +221,20 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
"-x", opts->record_origin,
"--edit", opts->edit,
NULL);
- opts->commit_argv = argv;
+
+ if (opts->subcommand != REPLAY_NONE) {
+ opts->revs = NULL;
+ } else {
+ opts->revs = xmalloc(sizeof(*opts->revs));
+ init_revisions(opts->revs, NULL);
+ opts->revs->no_walk = 1;
+ if (argc < 2)
+ usage_with_options(usage_str, options);
+ argc = setup_revisions(argc, argv, opts->revs, NULL);
+ }
+
+ if (argc > 1)
+ usage_with_options(usage_str, options);
}
struct commit_message {
@@ -286,19 +311,20 @@ static char *get_encoding(const char *message)
return NULL;
}
-static void write_cherry_pick_head(struct commit *commit)
+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
{
+ const char *filename;
int fd;
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
- fd = open(git_path("CHERRY_PICK_HEAD"), O_WRONLY | O_CREAT, 0666);
+ filename = git_path("%s", pseudoref);
+ fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("CHERRY_PICK_HEAD"));
+ die_errno(_("Could not open '%s' for writing"), filename);
if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
- die_errno(_("Could not write to '%s'"), git_path("CHERRY_PICK_HEAD"));
+ die_errno(_("Could not write to '%s'"), filename);
strbuf_release(&buf);
}
@@ -331,7 +357,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
int msg_fd = hold_lock_file_for_update(&msg_file, filename,
LOCK_DIE_ON_ERROR);
if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
- die_errno(_("Could not write to %s."), filename);
+ die_errno(_("Could not write to %s"), filename);
strbuf_release(msgbuf);
if (commit_lock_file(&msg_file) < 0)
die(_("Error wrapping up %s"), filename);
@@ -593,7 +619,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
* write it at all.
*/
if (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))
- write_cherry_pick_head(commit);
+ write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+ if (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))
+ write_cherry_pick_head(commit, "REVERT_HEAD");
if (res) {
error(opts->action == REVERT
@@ -614,23 +642,15 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
return res;
}
-static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)
+static void prepare_revs(struct replay_opts *opts)
{
- int argc;
-
- init_revisions(revs, NULL);
- revs->no_walk = 1;
if (opts->action != REVERT)
- revs->reverse = 1;
+ opts->revs->reverse ^= 1;
- argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
- if (argc > 1)
- usage(*revert_or_cherry_pick_usage(opts));
-
- if (prepare_revision_walk(revs))
+ if (prepare_revision_walk(opts->revs))
die(_("revision walk setup failed"));
- if (!revs->commits)
+ if (!opts->revs->commits)
die(_("empty commit set passed"));
}
@@ -767,7 +787,7 @@ static void read_populate_todo(struct commit_list **todo_list,
fd = open(todo_file, O_RDONLY);
if (fd < 0)
- die_errno(_("Could not open %s."), todo_file);
+ die_errno(_("Could not open %s"), todo_file);
if (strbuf_read(&buf, fd, 0) < 0) {
close(fd);
strbuf_release(&buf);
@@ -827,14 +847,13 @@ static void read_populate_opts(struct replay_opts **opts_ptr)
static void walk_revs_populate_todo(struct commit_list **todo_list,
struct replay_opts *opts)
{
- struct rev_info revs;
struct commit *commit;
struct commit_list **next;
- prepare_revs(&revs, opts);
+ prepare_revs(opts);
next = todo_list;
- while ((commit = get_revision(&revs)))
+ while ((commit = get_revision(opts->revs)))
next = commit_list_append(commit, next);
}
@@ -842,10 +861,13 @@ static int create_seq_dir(void)
{
const char *seq_dir = git_path(SEQ_DIR);
- if (file_exists(seq_dir))
- return error(_("%s already exists."), seq_dir);
+ if (file_exists(seq_dir)) {
+ error(_("a cherry-pick or revert is already in progress"));
+ advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
+ return -1;
+ }
else if (mkdir(seq_dir, 0777) < 0)
- die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
+ die_errno(_("Could not create sequencer directory %s"), seq_dir);
return 0;
}
@@ -859,11 +881,77 @@ static void save_head(const char *head)
fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
strbuf_addf(&buf, "%s\n", head);
if (write_in_full(fd, buf.buf, buf.len) < 0)
- die_errno(_("Could not write to %s."), head_file);
+ die_errno(_("Could not write to %s"), head_file);
if (commit_lock_file(&head_lock) < 0)
die(_("Error wrapping up %s."), head_file);
}
+static int reset_for_rollback(const unsigned char *sha1)
+{
+ const char *argv[4]; /* reset --merge <arg> + NULL */
+ argv[0] = "reset";
+ argv[1] = "--merge";
+ argv[2] = sha1_to_hex(sha1);
+ argv[3] = NULL;
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int rollback_single_pick(void)
+{
+ unsigned char head_sha1[20];
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ if (read_ref_full("HEAD", head_sha1, 0, NULL))
+ return error(_("cannot resolve HEAD"));
+ if (is_null_sha1(head_sha1))
+ return error(_("cannot abort from a branch yet to be born"));
+ return reset_for_rollback(head_sha1);
+}
+
+static int sequencer_rollback(struct replay_opts *opts)
+{
+ const char *filename;
+ FILE *f;
+ unsigned char sha1[20];
+ struct strbuf buf = STRBUF_INIT;
+
+ filename = git_path(SEQ_HEAD_FILE);
+ f = fopen(filename, "r");
+ if (!f && errno == ENOENT) {
+ /*
+ * There is no multiple-cherry-pick in progress.
+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
+ * a single-cherry-pick in progress, abort that.
+ */
+ return rollback_single_pick();
+ }
+ if (!f)
+ return error(_("cannot open %s: %s"), filename,
+ strerror(errno));
+ if (strbuf_getline(&buf, f, '\n')) {
+ error(_("cannot read %s: %s"), filename, ferror(f) ?
+ strerror(errno) : _("unexpected end of file"));
+ fclose(f);
+ goto fail;
+ }
+ fclose(f);
+ if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
+ error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
+ filename);
+ goto fail;
+ }
+ if (reset_for_rollback(sha1))
+ goto fail;
+ remove_sequencer_state();
+ strbuf_release(&buf);
+ return 0;
+fail:
+ strbuf_release(&buf);
+ return -1;
+}
+
static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
{
const char *todo_file = git_path(SEQ_TODO_FILE);
@@ -876,7 +964,7 @@ static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
die(_("Could not format %s."), todo_file);
if (write_in_full(fd, buf.buf, buf.len) < 0) {
strbuf_release(&buf);
- die_errno(_("Could not write to %s."), todo_file);
+ die_errno(_("Could not write to %s"), todo_file);
}
if (commit_lock_file(&todo_lock) < 0) {
strbuf_release(&buf);
@@ -930,33 +1018,64 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
for (cur = todo_list; cur; cur = cur->next) {
save_todo(cur, opts);
res = do_pick_commit(cur->item, opts);
- if (res) {
- if (!cur->next)
- /*
- * An error was encountered while
- * picking the last commit; the
- * sequencer state is useless now --
- * the user simply needs to resolve
- * the conflict and commit
- */
- remove_sequencer_state(0);
+ if (res)
return res;
- }
}
/*
* Sequence of picks finished successfully; cleanup by
* removing the .git/sequencer directory
*/
- remove_sequencer_state(1);
+ remove_sequencer_state();
return 0;
}
+static int continue_single_pick(void)
+{
+ const char *argv[] = { "commit", NULL };
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int sequencer_continue(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+
+ if (!file_exists(git_path(SEQ_TODO_FILE)))
+ return continue_single_pick();
+ read_populate_opts(&opts);
+ read_populate_todo(&todo_list, opts);
+
+ /* Verify that the conflict has been resolved */
+ if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
+ file_exists(git_path("REVERT_HEAD"))) {
+ int ret = continue_single_pick();
+ if (ret)
+ return ret;
+ }
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ todo_list = todo_list->next;
+ return pick_commits(todo_list, opts);
+}
+
+static int single_pick(struct commit *cmit, struct replay_opts *opts)
+{
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ return do_pick_commit(cmit, opts);
+}
+
static int pick_revisions(struct replay_opts *opts)
{
struct commit_list *todo_list = NULL;
unsigned char sha1[20];
+ if (opts->subcommand == REPLAY_NONE)
+ assert(opts->revs);
+
read_and_refresh_cache(opts);
/*
@@ -964,43 +1083,52 @@ static int pick_revisions(struct replay_opts *opts)
* cherry-pick should be handled differently from an existing
* one that is being continued
*/
- if (opts->subcommand == REPLAY_RESET) {
- remove_sequencer_state(1);
+ if (opts->subcommand == REPLAY_REMOVE_STATE) {
+ remove_sequencer_state();
return 0;
- } else if (opts->subcommand == REPLAY_CONTINUE) {
- if (!file_exists(git_path(SEQ_TODO_FILE)))
- goto error;
- read_populate_opts(&opts);
- read_populate_todo(&todo_list, opts);
-
- /* Verify that the conflict has been resolved */
- if (!index_differs_from("HEAD", 0))
- todo_list = todo_list->next;
- } else {
- /*
- * Start a new cherry-pick/ revert sequence; but
- * first, make sure that an existing one isn't in
- * progress
- */
+ }
+ if (opts->subcommand == REPLAY_ROLLBACK)
+ return sequencer_rollback(opts);
+ if (opts->subcommand == REPLAY_CONTINUE)
+ return sequencer_continue(opts);
- walk_revs_populate_todo(&todo_list, opts);
- if (create_seq_dir() < 0) {
- error(_("A cherry-pick or revert is in progress."));
- advise(_("Use --continue to continue the operation"));
- advise(_("or --reset to forget about it"));
- return -1;
- }
- if (get_sha1("HEAD", sha1)) {
- if (opts->action == REVERT)
- return error(_("Can't revert as initial commit"));
- return error(_("Can't cherry-pick into empty head"));
- }
- save_head(sha1_to_hex(sha1));
- save_opts(opts);
+ /*
+ * If we were called as "git cherry-pick <commit>", just
+ * cherry-pick/revert it, set CHERRY_PICK_HEAD /
+ * REVERT_HEAD, and don't touch the sequencer state.
+ * This means it is possible to cherry-pick in the middle
+ * of a cherry-pick sequence.
+ */
+ if (opts->revs->cmdline.nr == 1 &&
+ opts->revs->cmdline.rev->whence == REV_CMD_REV &&
+ opts->revs->no_walk &&
+ !opts->revs->cmdline.rev->flags) {
+ struct commit *cmit;
+ if (prepare_revision_walk(opts->revs))
+ die(_("revision walk setup failed"));
+ cmit = get_revision(opts->revs);
+ if (!cmit || get_revision(opts->revs))
+ die("BUG: expected exactly one commit from walk");
+ return single_pick(cmit, opts);
+ }
+
+ /*
+ * Start a new cherry-pick/ revert sequence; but
+ * first, make sure that an existing one isn't in
+ * progress
+ */
+
+ walk_revs_populate_todo(&todo_list, opts);
+ if (create_seq_dir() < 0)
+ return -1;
+ if (get_sha1("HEAD", sha1)) {
+ if (opts->action == REVERT)
+ return error(_("Can't revert as initial commit"));
+ return error(_("Can't cherry-pick into empty head"));
}
+ save_head(sha1_to_hex(sha1));
+ save_opts(opts);
return pick_commits(todo_list, opts);
-error:
- return error(_("No %s in progress"), action_name(opts));
}
int cmd_revert(int argc, const char **argv, const char *prefix)
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index e0b8030f2b..cd1115ffc6 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -494,8 +494,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
memset(&extra_have, 0, sizeof(extra_have));
- get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL,
- &extra_have);
+ get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have);
transport_verify_remote_names(nr_refspecs, refspecs);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 4b480d7c7c..a59e088cf5 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -726,10 +726,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- const char *refname;
- refname = resolve_ref("HEAD", sha1, 1, NULL);
- fake_av[0] = xstrdup(refname);
+ fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -791,7 +789,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
}
- head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+ head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index fafb6dd500..3911661900 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -225,7 +225,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
unsigned char sha1[20];
if (!prefixcmp(*pattern, "refs/") &&
- resolve_ref(*pattern, sha1, 1, NULL)) {
+ !read_ref(*pattern, sha1)) {
if (!quiet)
show_one(*pattern, sha1);
}
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 4d3b93fedb..f16986c0ae 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -22,8 +22,6 @@ static size_t cleanup(char *line, size_t len)
* 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.
*
@@ -77,7 +75,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
!strcmp(argv[1], "--strip-comments")))
strip_comments = 1;
else if (argc > 1)
- usage("git stripspace [-s | --strip-comments] < <stream>");
+ usage("git stripspace [-s | --strip-comments] < input");
if (strbuf_read(&buf, 0, 1024) < 0)
die_errno("could not read the input");
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index dea849c3c5..2ef5962386 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -12,7 +12,7 @@ static void check_symref(const char *HEAD, int quiet)
{
unsigned char sha1[20];
int flag;
- const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+ const char *refs_heads_master = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
if (!refs_heads_master)
die("No such ref: %s", HEAD);
diff --git a/builtin/tag.c b/builtin/tag.c
index 9b6fd95494..31f02e80f6 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -14,6 +14,7 @@
#include "parse-options.h"
#include "diff.h"
#include "revision.h"
+#include "gpg-interface.h"
static const char * const git_tag_usage[] = {
"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -23,8 +24,6 @@ static const char * const git_tag_usage[] = {
NULL
};
-static char signingkey[1000];
-
struct tag_filter {
const char **patterns;
int lines;
@@ -174,7 +173,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
had_error = 1;
continue;
}
- if (!resolve_ref(ref, sha1, 1, NULL)) {
+ if (read_ref(ref, sha1)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
continue;
@@ -208,83 +207,29 @@ static int verify_tag(const char *name, const char *ref,
static int do_sign(struct strbuf *buffer)
{
- struct child_process gpg;
- const char *args[4];
- char *bracket;
- int len;
- int i, j;
-
- if (!*signingkey) {
- if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
- sizeof(signingkey)) > sizeof(signingkey) - 1)
- return error(_("committer info too long."));
- bracket = strchr(signingkey, '>');
- if (bracket)
- bracket[1] = '\0';
- }
-
- /* When the username signingkey is bad, program could be terminated
- * because gpg exits without reading and then write gets SIGPIPE. */
- signal(SIGPIPE, SIG_IGN);
-
- 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."));
-
- if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
- close(gpg.in);
- close(gpg.out);
- finish_command(&gpg);
- return error(_("gpg did not accept the tag data"));
- }
- close(gpg.in);
- len = strbuf_read(buffer, gpg.out, 1024);
- close(gpg.out);
-
- if (finish_command(&gpg) || !len || len < 0)
- return error(_("gpg failed to sign the tag"));
-
- /* Strip CR from the line endings, in case we are on Windows. */
- for (i = j = 0; i < buffer->len; i++)
- if (buffer->buf[i] != '\r') {
- if (i != j)
- buffer->buf[j] = buffer->buf[i];
- j++;
- }
- strbuf_setlen(buffer, j);
-
- return 0;
+ return sign_buffer(buffer, buffer, get_signing_key());
}
static const char tag_template[] =
N_("\n"
"#\n"
"# Write a tag message\n"
+ "# Lines starting with '#' will be ignored.\n"
"#\n");
-static void set_signingkey(const char *value)
-{
- if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
- die(_("signing key value too long (%.10s...)"), value);
-}
+static const char tag_template_nocleanup[] =
+ N_("\n"
+ "#\n"
+ "# Write a tag message\n"
+ "# Lines starting with '#' will be kept; you may remove them"
+ " yourself if you want to.\n"
+ "#\n");
static int git_tag_config(const char *var, const char *value, void *cb)
{
- if (!strcmp(var, "user.signingkey")) {
- if (!value)
- return config_error_nonbool(var);
- set_signingkey(value);
- return 0;
- }
-
+ int status = git_gpg_config(var, value, cb);
+ if (status)
+ return status;
return git_default_config(var, value, cb);
}
@@ -319,8 +264,18 @@ static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
return 0;
}
+struct create_tag_options {
+ unsigned int message_given:1;
+ unsigned int sign;
+ enum {
+ CLEANUP_NONE,
+ CLEANUP_SPACE,
+ CLEANUP_ALL
+ } cleanup_mode;
+};
+
static void create_tag(const unsigned char *object, const char *tag,
- struct strbuf *buf, int message, int sign,
+ struct strbuf *buf, struct create_tag_options *opt,
unsigned char *prev, unsigned char *result)
{
enum object_type type;
@@ -345,7 +300,7 @@ static void create_tag(const unsigned char *object, const char *tag,
if (header_len > sizeof(header_buf) - 1)
die(_("tag header too big."));
- if (!message) {
+ if (!opt->message_given) {
int fd;
/* write the template message before editing: */
@@ -356,8 +311,12 @@ static void create_tag(const unsigned char *object, const char *tag,
if (!is_null_sha1(prev))
write_tag_body(fd, prev);
+ else if (opt->cleanup_mode == CLEANUP_ALL)
+ write_or_die(fd, _(tag_template),
+ strlen(_(tag_template)));
else
- write_or_die(fd, _(tag_template), strlen(_(tag_template)));
+ write_or_die(fd, _(tag_template_nocleanup),
+ strlen(_(tag_template_nocleanup)));
close(fd);
if (launch_editor(path, buf, NULL)) {
@@ -367,14 +326,15 @@ static void create_tag(const unsigned char *object, const char *tag,
}
}
- stripspace(buf, 1);
+ if (opt->cleanup_mode != CLEANUP_NONE)
+ stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
- if (!message && !buf->len)
+ if (!opt->message_given && !buf->len)
die(_("no tag message?"));
strbuf_insert(buf, 0, header_buf, header_len);
- if (build_tag_object(buf, sign, result) < 0) {
+ if (build_tag_object(buf, opt->sign, result) < 0) {
if (path)
fprintf(stderr, _("The tag message has been left in %s\n"),
path);
@@ -422,9 +382,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
unsigned char object[20], prev[20];
const char *object_ref, *tag;
struct ref_lock *lock;
-
- int annotate = 0, sign = 0, force = 0, lines = -1,
- list = 0, delete = 0, verify = 0;
+ struct create_tag_options opt;
+ char *cleanup_arg = NULL;
+ int annotate = 0, force = 0, lines = -1, list = 0,
+ delete = 0, verify = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
@@ -442,7 +403,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_CALLBACK('m', "message", &msg, "message",
"tag message", parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, "read message from file"),
- OPT_BOOLEAN('s', "sign", &sign, "annotated and GPG-signed tag"),
+ OPT_BOOLEAN('s', "sign", &opt.sign, "annotated and GPG-signed tag"),
+ OPT_STRING(0, "cleanup", &cleanup_arg, "mode",
+ "how to strip spaces and #comments from message"),
OPT_STRING('u', "local-user", &keyid, "key-id",
"use another key to sign the tag"),
OPT__FORCE(&force, "replace the tag if exists"),
@@ -459,13 +422,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
git_config(git_tag_config, NULL);
+ memset(&opt, 0, sizeof(opt));
+
argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
if (keyid) {
- sign = 1;
- set_signingkey(keyid);
+ opt.sign = 1;
+ set_signing_key(keyid);
}
- if (sign)
+ if (opt.sign)
annotate = 1;
if (argc == 0 && !(delete || verify))
list = 1;
@@ -518,14 +483,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (!resolve_ref(ref.buf, prev, 1, NULL))
+ if (read_ref(ref.buf, prev))
hashclr(prev);
else if (!force)
die(_("tag '%s' already exists"), tag);
+ opt.message_given = msg.given || msgfile;
+
+ if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
+ opt.cleanup_mode = CLEANUP_ALL;
+ else if (!strcmp(cleanup_arg, "verbatim"))
+ opt.cleanup_mode = CLEANUP_NONE;
+ else if (!strcmp(cleanup_arg, "whitespace"))
+ opt.cleanup_mode = CLEANUP_SPACE;
+ else
+ die(_("Invalid cleanup mode %s"), cleanup_arg);
+
if (annotate)
- create_tag(object, tag, &buf, msg.given || msgfile,
- sign, prev, object);
+ create_tag(object, tag, &buf, &opt, prev, object);
lock = lock_any_ref_for_update(ref.buf, prev, 0);
if (!lock)
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index c57e8bd8c8..b928beb8ed 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -14,22 +14,30 @@ static const char upload_archive_usage[] =
static const char deadchild[] =
"git upload-archive: archiver died with error";
-static const char lostchild[] =
-"git upload-archive: archiver process was lost";
-
#define MAX_ARGS (64)
-static void prepare_argv(const char **sent_argv, const char **argv)
+int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
{
+ const char *sent_argv[MAX_ARGS];
const char *arg_cmd = "argument ";
char *p, buf[4096];
int sent_argc;
int len;
+ if (argc != 2)
+ usage(upload_archive_usage);
+
+ if (strlen(argv[1]) + 1 > sizeof(buf))
+ die("insanely long repository name");
+
+ strcpy(buf, argv[1]); /* enter-repo smudges its argument */
+
+ if (!enter_repo(buf, 0))
+ die("'%s' does not appear to be a git repository", buf);
+
/* put received options in sent_argv[] */
- sent_argc = 2;
- sent_argv[0] = "archive";
- sent_argv[1] = "--remote-request";
+ sent_argc = 1;
+ sent_argv[0] = "git-upload-archive";
for (p = buf;;) {
/* This will die if not enough free space in buf */
len = packet_read_line(0, p, (buf + sizeof buf) - p);
@@ -52,6 +60,9 @@ static void prepare_argv(const char **sent_argv, const char **argv)
*p++ = 0;
}
sent_argv[sent_argc] = NULL;
+
+ /* parse all options sent by the client */
+ return write_archive(sent_argc, sent_argv, prefix, 0, NULL, 1);
}
__attribute__((format (printf, 1, 2)))
@@ -83,35 +94,33 @@ static ssize_t process_input(int child_fd, int band)
int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
- const char *sent_argv[MAX_ARGS];
- struct child_process cld = { sent_argv };
- cld.out = cld.err = -1;
- cld.git_cmd = 1;
-
- if (argc != 2)
- usage(upload_archive_usage);
-
- if (!enter_repo(argv[1], 0))
- die("'%s' does not appear to be a git repository", argv[1]);
-
- prepare_argv(sent_argv, argv);
- if (start_command(&cld)) {
+ struct child_process writer = { argv };
+
+ /*
+ * Set up sideband subprocess.
+ *
+ * We (parent) monitor and read from child, sending its fd#1 and fd#2
+ * multiplexed out to our fd#1. If the child dies, we tell the other
+ * end over channel #3.
+ */
+ argv[0] = "upload-archive--writer";
+ writer.out = writer.err = -1;
+ writer.git_cmd = 1;
+ if (start_command(&writer)) {
int err = errno;
- packet_write(1, "NACK fork failed on the remote side\n");
+ packet_write(1, "NACK unable to spawn subprocess\n");
die("upload-archive: %s", strerror(err));
}
- /* parent - read from child, multiplex and send out to fd#1 */
packet_write(1, "ACK\n");
packet_flush(1);
while (1) {
struct pollfd pfd[2];
- int status;
- pfd[0].fd = cld.out;
+ pfd[0].fd = writer.out;
pfd[0].events = POLLIN;
- pfd[1].fd = cld.err;
+ pfd[1].fd = writer.err;
pfd[1].events = POLLIN;
if (poll(pfd, 2, -1) < 0) {
if (errno != EINTR) {
@@ -130,9 +139,7 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
if (process_input(pfd[0].fd, 1))
continue;
- if (waitpid(cld.pid, &status, 0) < 0)
- error_clnt("%s", lostchild);
- else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
+ if (finish_command(&writer))
error_clnt("%s", deadchild);
packet_flush(1);
break;
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index 3134766049..28c2174338 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -11,6 +11,7 @@
#include "run-command.h"
#include <signal.h>
#include "parse-options.h"
+#include "gpg-interface.h"
static const char * const verify_tag_usage[] = {
"git verify-tag [-v|--verbose] <tag>...",
@@ -19,42 +20,16 @@ static const char * const verify_tag_usage[] = {
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];
- size_t len;
- int fd, ret;
+ int len;
- 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 = parse_signature(buf, size);
if (verbose)
write_in_full(1, buf, len);
- memset(&gpg, 0, sizeof(gpg));
- gpg.argv = args_gpg;
- gpg.in = -1;
- args_gpg[2] = path;
- if (start_command(&gpg)) {
- unlink(path);
- return error("could not run gpg.");
- }
-
- write_in_full(gpg.in, buf, len);
- close(gpg.in);
- ret = finish_command(&gpg);
+ if (size == len)
+ return error("no signature found");
- unlink_or_warn(path);
-
- return ret;
+ return verify_signed_buffer(buf, len, buf + len, size - len, NULL);
}
static int verify_tag(const char *name, int verbose)
diff --git a/bulk-checkin.c b/bulk-checkin.c
new file mode 100644
index 0000000000..6b0b6d4904
--- /dev/null
+++ b/bulk-checkin.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#include "bulk-checkin.h"
+#include "csum-file.h"
+#include "pack.h"
+
+static int pack_compression_level = Z_DEFAULT_COMPRESSION;
+
+static struct bulk_checkin_state {
+ unsigned plugged:1;
+
+ char *pack_tmp_name;
+ struct sha1file *f;
+ off_t offset;
+ struct pack_idx_option pack_idx_opts;
+
+ struct pack_idx_entry **written;
+ uint32_t alloc_written;
+ uint32_t nr_written;
+} state;
+
+static void finish_bulk_checkin(struct bulk_checkin_state *state)
+{
+ unsigned char sha1[20];
+ char packname[PATH_MAX];
+ int i;
+
+ if (!state->f)
+ return;
+
+ if (state->nr_written == 0) {
+ close(state->f->fd);
+ unlink(state->pack_tmp_name);
+ goto clear_exit;
+ } else if (state->nr_written == 1) {
+ sha1close(state->f, sha1, CSUM_FSYNC);
+ } else {
+ int fd = sha1close(state->f, sha1, 0);
+ fixup_pack_header_footer(fd, sha1, state->pack_tmp_name,
+ state->nr_written, sha1,
+ state->offset);
+ close(fd);
+ }
+
+ sprintf(packname, "%s/pack/pack-", get_object_directory());
+ finish_tmp_packfile(packname, state->pack_tmp_name,
+ state->written, state->nr_written,
+ &state->pack_idx_opts, sha1);
+ for (i = 0; i < state->nr_written; i++)
+ free(state->written[i]);
+
+clear_exit:
+ free(state->written);
+ memset(state, 0, sizeof(*state));
+
+ /* Make objects we just wrote available to ourselves */
+ reprepare_packed_git();
+}
+
+static int already_written(struct bulk_checkin_state *state, unsigned char sha1[])
+{
+ int i;
+
+ /* The object may already exist in the repository */
+ if (has_sha1_file(sha1))
+ return 1;
+
+ /* Might want to keep the list sorted */
+ for (i = 0; i < state->nr_written; i++)
+ if (!hashcmp(state->written[i]->sha1, sha1))
+ return 1;
+
+ /* This is a new object we need to keep */
+ return 0;
+}
+
+/*
+ * Read the contents from fd for size bytes, streaming it to the
+ * packfile in state while updating the hash in ctx. Signal a failure
+ * by returning a negative value when the resulting pack would exceed
+ * the pack size limit and this is not the first object in the pack,
+ * so that the caller can discard what we wrote from the current pack
+ * by truncating it and opening a new one. The caller will then call
+ * us again after rewinding the input fd.
+ *
+ * The already_hashed_to pointer is kept untouched by the caller to
+ * make sure we do not hash the same byte when we are called
+ * again. This way, the caller does not have to checkpoint its hash
+ * status before calling us just in case we ask it to call us again
+ * with a new pack.
+ */
+static int stream_to_pack(struct bulk_checkin_state *state,
+ git_SHA_CTX *ctx, off_t *already_hashed_to,
+ int fd, size_t size, enum object_type type,
+ const char *path, unsigned flags)
+{
+ git_zstream s;
+ unsigned char obuf[16384];
+ unsigned hdrlen;
+ int status = Z_OK;
+ int write_object = (flags & HASH_WRITE_OBJECT);
+ off_t offset = 0;
+
+ memset(&s, 0, sizeof(s));
+ git_deflate_init(&s, pack_compression_level);
+
+ hdrlen = encode_in_pack_object_header(type, size, obuf);
+ s.next_out = obuf + hdrlen;
+ s.avail_out = sizeof(obuf) - hdrlen;
+
+ while (status != Z_STREAM_END) {
+ unsigned char ibuf[16384];
+
+ if (size && !s.avail_in) {
+ ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
+ if (xread(fd, ibuf, rsize) != rsize)
+ die("failed to read %d bytes from '%s'",
+ (int)rsize, path);
+ offset += rsize;
+ if (*already_hashed_to < offset) {
+ size_t hsize = offset - *already_hashed_to;
+ if (rsize < hsize)
+ hsize = rsize;
+ if (hsize)
+ git_SHA1_Update(ctx, ibuf, hsize);
+ *already_hashed_to = offset;
+ }
+ s.next_in = ibuf;
+ s.avail_in = rsize;
+ size -= rsize;
+ }
+
+ status = git_deflate(&s, size ? 0 : Z_FINISH);
+
+ if (!s.avail_out || status == Z_STREAM_END) {
+ if (write_object) {
+ size_t written = s.next_out - obuf;
+
+ /* would we bust the size limit? */
+ if (state->nr_written &&
+ pack_size_limit_cfg &&
+ pack_size_limit_cfg < state->offset + written) {
+ git_deflate_abort(&s);
+ return -1;
+ }
+
+ sha1write(state->f, obuf, written);
+ state->offset += written;
+ }
+ s.next_out = obuf;
+ s.avail_out = sizeof(obuf);
+ }
+
+ switch (status) {
+ case Z_OK:
+ case Z_BUF_ERROR:
+ case Z_STREAM_END:
+ continue;
+ default:
+ die("unexpected deflate failure: %d", status);
+ }
+ }
+ git_deflate_end(&s);
+ return 0;
+}
+
+/* Lazily create backing packfile for the state */
+static void prepare_to_stream(struct bulk_checkin_state *state,
+ unsigned flags)
+{
+ if (!(flags & HASH_WRITE_OBJECT) || state->f)
+ return;
+
+ state->f = create_tmp_packfile(&state->pack_tmp_name);
+ reset_pack_idx_option(&state->pack_idx_opts);
+
+ /* Pretend we are going to write only one object */
+ state->offset = write_pack_header(state->f, 1);
+ if (!state->offset)
+ die_errno("unable to write pack header");
+}
+
+static int deflate_to_pack(struct bulk_checkin_state *state,
+ unsigned char result_sha1[],
+ int fd, size_t size,
+ enum object_type type, const char *path,
+ unsigned flags)
+{
+ off_t seekback, already_hashed_to;
+ git_SHA_CTX ctx;
+ unsigned char obuf[16384];
+ unsigned header_len;
+ struct sha1file_checkpoint checkpoint;
+ struct pack_idx_entry *idx = NULL;
+
+ seekback = lseek(fd, 0, SEEK_CUR);
+ if (seekback == (off_t) -1)
+ return error("cannot find the current offset");
+
+ header_len = sprintf((char *)obuf, "%s %" PRIuMAX,
+ typename(type), (uintmax_t)size) + 1;
+ git_SHA1_Init(&ctx);
+ git_SHA1_Update(&ctx, obuf, header_len);
+
+ /* Note: idx is non-NULL when we are writing */
+ if ((flags & HASH_WRITE_OBJECT) != 0)
+ idx = xcalloc(1, sizeof(*idx));
+
+ already_hashed_to = 0;
+
+ while (1) {
+ prepare_to_stream(state, flags);
+ if (idx) {
+ sha1file_checkpoint(state->f, &checkpoint);
+ idx->offset = state->offset;
+ crc32_begin(state->f);
+ }
+ if (!stream_to_pack(state, &ctx, &already_hashed_to,
+ fd, size, type, path, flags))
+ break;
+ /*
+ * Writing this object to the current pack will make
+ * it too big; we need to truncate it, start a new
+ * pack, and write into it.
+ */
+ if (!idx)
+ die("BUG: should not happen");
+ sha1file_truncate(state->f, &checkpoint);
+ state->offset = checkpoint.offset;
+ finish_bulk_checkin(state);
+ if (lseek(fd, seekback, SEEK_SET) == (off_t) -1)
+ return error("cannot seek back");
+ }
+ git_SHA1_Final(result_sha1, &ctx);
+ if (!idx)
+ return 0;
+
+ idx->crc32 = crc32_end(state->f);
+ if (already_written(state, result_sha1)) {
+ sha1file_truncate(state->f, &checkpoint);
+ state->offset = checkpoint.offset;
+ free(idx);
+ } else {
+ hashcpy(idx->sha1, result_sha1);
+ ALLOC_GROW(state->written,
+ state->nr_written + 1,
+ state->alloc_written);
+ state->written[state->nr_written++] = idx;
+ }
+ return 0;
+}
+
+int index_bulk_checkin(unsigned char *sha1,
+ int fd, size_t size, enum object_type type,
+ const char *path, unsigned flags)
+{
+ int status = deflate_to_pack(&state, sha1, fd, size, type,
+ path, flags);
+ if (!state.plugged)
+ finish_bulk_checkin(&state);
+ return status;
+}
+
+void plug_bulk_checkin(void)
+{
+ state.plugged = 1;
+}
+
+void unplug_bulk_checkin(void)
+{
+ state.plugged = 0;
+ if (state.f)
+ finish_bulk_checkin(&state);
+}
diff --git a/bulk-checkin.h b/bulk-checkin.h
new file mode 100644
index 0000000000..4f599f8841
--- /dev/null
+++ b/bulk-checkin.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2011, Google Inc.
+ */
+#ifndef BULK_CHECKIN_H
+#define BULK_CHECKIN_H
+
+#include "cache.h"
+
+extern int index_bulk_checkin(unsigned char sha1[],
+ int fd, size_t size, enum object_type type,
+ const char *path, unsigned flags);
+
+extern void plug_bulk_checkin(void);
+extern void unplug_bulk_checkin(void);
+
+#endif
diff --git a/bundle.c b/bundle.c
index 08020bc3a2..4742f2734e 100644
--- a/bundle.c
+++ b/bundle.c
@@ -320,7 +320,7 @@ int create_bundle(struct bundle_header *header, const char *path,
continue;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
- if (!resolve_ref(e->name, sha1, 1, &flag))
+ if (read_ref_full(e->name, sha1, 1, &flag))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
diff --git a/cache-tree.c b/cache-tree.c
index f755590da8..8de39590d5 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -150,7 +150,7 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
}
static int verify_cache(struct cache_entry **cache,
- int entries)
+ int entries, int silent)
{
int i, funny;
@@ -159,6 +159,8 @@ static int verify_cache(struct cache_entry **cache,
for (i = 0; i < entries; i++) {
struct cache_entry *ce = cache[i];
if (ce_stage(ce) || (ce->ce_flags & CE_INTENT_TO_ADD)) {
+ if (silent)
+ return -1;
if (10 < ++funny) {
fprintf(stderr, "...\n");
break;
@@ -370,10 +372,11 @@ int cache_tree_update(struct cache_tree *it,
struct cache_entry **cache,
int entries,
int missing_ok,
- int dryrun)
+ int dryrun,
+ int silent)
{
int i;
- i = verify_cache(cache, entries);
+ i = verify_cache(cache, entries, silent);
if (i)
return i;
i = update_one(it, cache, entries, "", 0, missing_ok, dryrun);
@@ -573,7 +576,7 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
if (cache_tree_update(active_cache_tree,
active_cache, active_nr,
- missing_ok, 0) < 0)
+ missing_ok, 0, 0) < 0)
return WRITE_TREE_UNMERGED_INDEX;
if (0 <= newfd) {
if (!write_cache(newfd, active_cache, active_nr) &&
@@ -668,3 +671,11 @@ int cache_tree_matches_traversal(struct cache_tree *root,
return it->entry_count;
return 0;
}
+
+int update_main_cache_tree (int silent)
+{
+ if (!the_index.cache_tree)
+ the_index.cache_tree = cache_tree();
+ return cache_tree_update(the_index.cache_tree,
+ the_index.cache, the_index.cache_nr, 0, 0, silent);
+}
diff --git a/cache-tree.h b/cache-tree.h
index 3df641f593..0ec0b2a159 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -29,7 +29,9 @@ void cache_tree_write(struct strbuf *, struct cache_tree *root);
struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
int cache_tree_fully_valid(struct cache_tree *);
-int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int);
+int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int, int);
+
+int update_main_cache_tree(int);
/* bitmasks to write_cache_as_tree flags */
#define WRITE_TREE_MISSING_OK 1
diff --git a/cache.h b/cache.h
index 2e6ad3604e..7d93df61c3 100644
--- a/cache.h
+++ b/cache.h
@@ -35,6 +35,7 @@ int git_inflate(git_zstream *, int flush);
void git_deflate_init(git_zstream *, int level);
void git_deflate_init_gzip(git_zstream *, int level);
void git_deflate_end(git_zstream *);
+int git_deflate_abort(git_zstream *);
int git_deflate_end_gently(git_zstream *);
int git_deflate(git_zstream *, int flush);
unsigned long git_deflate_bound(git_zstream *, unsigned long);
@@ -306,7 +307,7 @@ static inline unsigned int canon_mode(unsigned int mode)
}
#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
-#define cache_entry_size(len) flexible_size(cache_entry,len)
+#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
@@ -316,7 +317,6 @@ struct index_state {
struct string_list *resolve_undo;
struct cache_tree *cache_tree;
struct cache_time timestamp;
- void *alloc;
unsigned name_hash_initialized : 1,
initialized : 1;
struct hash_table name_hash;
@@ -598,6 +598,7 @@ extern size_t packed_git_window_size;
extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
extern unsigned long big_file_threshold;
+extern unsigned long pack_size_limit_cfg;
extern int read_replace_refs;
extern int fsync_object_files;
extern int core_preload_index;
@@ -832,6 +833,8 @@ static inline int get_sha1_with_context(const char *str, unsigned char *sha1, st
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
+extern int read_ref_full(const char *filename, unsigned char *sha1,
+ int reading, int *flags);
extern int read_ref(const char *filename, unsigned char *sha1);
/*
@@ -864,7 +867,8 @@ extern int read_ref(const char *filename, unsigned char *sha1);
*
* errno is sometimes set on errors, but not always.
*/
-extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
+extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
@@ -873,7 +877,7 @@ extern int get_sha1_mb(const char *str, unsigned char *sha1);
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
extern const char *ref_rev_parse_rules[];
-extern const char *ref_fetch_rules[];
+#define ref_fetch_rules ref_rev_parse_rules
extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
extern int validate_headref(const char *ref);
@@ -1028,12 +1032,11 @@ extern char *git_getpass(const char *prompt);
extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
extern int finish_connect(struct child_process *conn);
extern int git_connection_is_socket(struct child_process *conn);
-extern int path_match(const char *path, int nr, char **match);
struct extra_have_objects {
int nr, alloc;
unsigned char (*array)[20];
};
-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
+extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
extern int server_supports(const char *feature);
extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
diff --git a/commit.c b/commit.c
index 73b7e00292..b78127403b 100644
--- a/commit.c
+++ b/commit.c
@@ -840,14 +840,160 @@ struct commit_list *reduce_heads(struct commit_list *heads)
return result;
}
+static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
+{
+ struct merge_remote_desc *desc;
+ struct commit_extra_header *mergetag;
+ char *buf;
+ unsigned long size, len;
+ enum object_type type;
+
+ desc = merge_remote_util(parent);
+ if (!desc || !desc->obj)
+ return;
+ buf = read_sha1_file(desc->obj->sha1, &type, &size);
+ if (!buf || type != OBJ_TAG)
+ goto free_return;
+ len = parse_signature(buf, size);
+ if (size == len)
+ goto free_return;
+ /*
+ * We could verify this signature and either omit the tag when
+ * it does not validate, but the integrator may not have the
+ * public key of the signer of the tag he is merging, while a
+ * later auditor may have it while auditing, so let's not run
+ * verify-signed-buffer here for now...
+ *
+ * if (verify_signed_buffer(buf, len, buf + len, size - len, ...))
+ * warn("warning: signed tag unverified.");
+ */
+ mergetag = xcalloc(1, sizeof(*mergetag));
+ mergetag->key = xstrdup("mergetag");
+ mergetag->value = buf;
+ mergetag->len = size;
+
+ **tail = mergetag;
+ *tail = &mergetag->next;
+ return;
+
+free_return:
+ free(buf);
+}
+
+void append_merge_tag_headers(struct commit_list *parents,
+ struct commit_extra_header ***tail)
+{
+ while (parents) {
+ struct commit *parent = parents->item;
+ handle_signed_tag(parent, tail);
+ parents = parents->next;
+ }
+}
+
+static void add_extra_header(struct strbuf *buffer,
+ struct commit_extra_header *extra)
+{
+ strbuf_addstr(buffer, extra->key);
+ if (extra->len)
+ strbuf_add_lines(buffer, " ", extra->value, extra->len);
+ else
+ strbuf_addch(buffer, '\n');
+}
+
+struct commit_extra_header *read_commit_extra_headers(struct commit *commit)
+{
+ struct commit_extra_header *extra = NULL;
+ unsigned long size;
+ enum object_type type;
+ char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
+ if (buffer && type == OBJ_COMMIT)
+ extra = read_commit_extra_header_lines(buffer, size);
+ free(buffer);
+ return extra;
+}
+
+static inline int standard_header_field(const char *field, size_t len)
+{
+ return ((len == 4 && !memcmp(field, "tree ", 5)) ||
+ (len == 6 && !memcmp(field, "parent ", 7)) ||
+ (len == 6 && !memcmp(field, "author ", 7)) ||
+ (len == 9 && !memcmp(field, "committer ", 10)) ||
+ (len == 8 && !memcmp(field, "encoding ", 9)));
+}
+
+struct commit_extra_header *read_commit_extra_header_lines(const char *buffer, size_t size)
+{
+ struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
+ const char *line, *next, *eof, *eob;
+ struct strbuf buf = STRBUF_INIT;
+
+ for (line = buffer, eob = line + size;
+ line < eob && *line != '\n';
+ line = next) {
+ next = memchr(line, '\n', eob - line);
+ next = next ? next + 1 : eob;
+ if (*line == ' ') {
+ /* continuation */
+ if (it)
+ strbuf_add(&buf, line + 1, next - (line + 1));
+ continue;
+ }
+ if (it)
+ it->value = strbuf_detach(&buf, &it->len);
+ strbuf_reset(&buf);
+ it = NULL;
+
+ eof = strchr(line, ' ');
+ if (next <= eof)
+ eof = next;
+
+ if (standard_header_field(line, eof - line))
+ continue;
+
+ it = xcalloc(1, sizeof(*it));
+ it->key = xmemdupz(line, eof-line);
+ *tail = it;
+ tail = &it->next;
+ if (eof + 1 < next)
+ strbuf_add(&buf, eof + 1, next - (eof + 1));
+ }
+ if (it)
+ it->value = strbuf_detach(&buf, &it->len);
+ return extra;
+}
+
+void free_commit_extra_headers(struct commit_extra_header *extra)
+{
+ while (extra) {
+ struct commit_extra_header *next = extra->next;
+ free(extra->key);
+ free(extra->value);
+ free(extra);
+ extra = next;
+ }
+}
+
+int commit_tree(const char *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author)
+{
+ struct commit_extra_header *extra = NULL, **tail = &extra;
+ int result;
+
+ append_merge_tag_headers(parents, &tail);
+ result = commit_tree_extended(msg, tree, parents, ret, author, extra);
+ free_commit_extra_headers(extra);
+ return result;
+}
+
static const char commit_utf8_warn[] =
"Warning: commit message does not conform to UTF-8.\n"
"You may want to amend it after fixing the message, or set the config\n"
"variable i18n.commitencoding to the encoding your project uses.\n";
-int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author)
+int commit_tree_extended(const char *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author, struct commit_extra_header *extra)
{
int result;
int encoding_is_utf8;
@@ -868,8 +1014,10 @@ int commit_tree(const char *msg, unsigned char *tree,
*/
while (parents) {
struct commit_list *next = parents->next;
+ struct commit *parent = parents->item;
+
strbuf_addf(&buffer, "parent %s\n",
- sha1_to_hex(parents->item->object.sha1));
+ sha1_to_hex(parent->object.sha1));
free(parents);
parents = next;
}
@@ -881,6 +1029,11 @@ int commit_tree(const char *msg, unsigned char *tree,
strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
if (!encoding_is_utf8)
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+
+ while (extra) {
+ add_extra_header(&buffer, extra);
+ extra = extra->next;
+ }
strbuf_addch(&buffer, '\n');
/* And add the comment */
@@ -894,3 +1047,22 @@ int commit_tree(const char *msg, unsigned char *tree,
strbuf_release(&buffer);
return result;
}
+
+struct commit *get_merge_parent(const char *name)
+{
+ struct object *obj;
+ struct commit *commit;
+ unsigned char sha1[20];
+ if (get_sha1(name, sha1))
+ return NULL;
+ obj = parse_object(sha1);
+ commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+ if (commit && !commit->util) {
+ struct merge_remote_desc *desc;
+ desc = xmalloc(sizeof(*desc));
+ desc->obj = obj;
+ desc->name = strdup(name);
+ commit->util = desc;
+ }
+ return commit;
+}
diff --git a/commit.h b/commit.h
index 009b113e5b..3745f12099 100644
--- a/commit.h
+++ b/commit.h
@@ -181,8 +181,41 @@ static inline int single_parent(struct commit *commit)
struct commit_list *reduce_heads(struct commit_list *heads);
+struct commit_extra_header {
+ struct commit_extra_header *next;
+ char *key;
+ char *value;
+ size_t len;
+};
+
+extern void append_merge_tag_headers(struct commit_list *parents,
+ struct commit_extra_header ***tail);
+
extern int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author);
+ struct commit_list *parents, unsigned char *ret,
+ const char *author);
+
+extern int commit_tree_extended(const char *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author,
+ struct commit_extra_header *);
+
+extern struct commit_extra_header *read_commit_extra_headers(struct commit *);
+extern struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len);
+
+extern void free_commit_extra_headers(struct commit_extra_header *extra);
+
+struct merge_remote_desc {
+ struct object *obj; /* the named object, could be a tag */
+ const char *name;
+};
+#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
+
+/*
+ * Given "name" from the command line to merge, find the commit object
+ * and return it, while storing merge_remote_desc in its ->util field,
+ * to allow callers to tell if we are told to merge a tag.
+ */
+struct commit *get_merge_parent(const char *name);
#endif /* COMMIT_H */
diff --git a/compat/mingw.c b/compat/mingw.c
index efdc703257..a0ac487c0c 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1712,7 +1712,7 @@ char *getpass(const char *prompt)
return strbuf_detach(&buf, NULL);
}
-pid_t waitpid(pid_t pid, int *status, unsigned options)
+pid_t waitpid(pid_t pid, int *status, int options)
{
HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
FALSE, pid);
diff --git a/compat/mingw.h b/compat/mingw.h
index dfb0e87263..0ff1e04812 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -85,6 +85,8 @@ static inline int symlink(const char *oldpath, const char *newpath)
{ errno = ENOSYS; return -1; }
static inline int fchmod(int fildes, mode_t mode)
{ errno = ENOSYS; return -1; }
+static inline pid_t fork(void)
+{ errno = ENOSYS; return -1; }
static inline unsigned int alarm(unsigned int seconds)
{ return 0; }
static inline int fsync(int fd)
@@ -118,7 +120,7 @@ static inline int mingw_mkdir(const char *path, int mode)
#define mkdir mingw_mkdir
#define WNOHANG 1
-pid_t waitpid(pid_t pid, int *status, unsigned options);
+pid_t waitpid(pid_t pid, int *status, int options);
#define kill mingw_kill
int mingw_kill(pid_t pid, int sig);
diff --git a/compat/msvc.h b/compat/msvc.h
index a33b01c032..aa4b56315a 100644
--- a/compat/msvc.h
+++ b/compat/msvc.h
@@ -4,6 +4,7 @@
#include <direct.h>
#include <process.h>
#include <malloc.h>
+#include <io.h>
/* porting function */
#define inline __inline
diff --git a/compat/snprintf.c b/compat/snprintf.c
index e1e0e7543d..42ea1ac110 100644
--- a/compat/snprintf.c
+++ b/compat/snprintf.c
@@ -19,11 +19,14 @@
#undef vsnprintf
int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
{
+ va_list cp;
char *s;
int ret = -1;
if (maxsize > 0) {
- ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
+ va_copy(cp, ap);
+ ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp);
+ va_end(cp);
if (ret == maxsize-1)
ret = -1;
/* Windows does not NUL-terminate if result fills buffer */
@@ -42,7 +45,9 @@ int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
if (! str)
break;
s = str;
- ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
+ va_copy(cp, ap);
+ ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp);
+ va_end(cp);
if (ret == maxsize-1)
ret = -1;
}
diff --git a/compat/strtoimax.c b/compat/strtoimax.c
new file mode 100644
index 0000000000..ac09ed89e7
--- /dev/null
+++ b/compat/strtoimax.c
@@ -0,0 +1,10 @@
+#include "../git-compat-util.h"
+
+intmax_t gitstrtoimax (const char *nptr, char **endptr, int base)
+{
+#if defined(NO_STRTOULL)
+ return strtol(nptr, endptr, base);
+#else
+ return strtoll(nptr, endptr, base);
+#endif
+}
diff --git a/compat/vcbuild/include/arpa/inet.h b/compat/vcbuild/include/arpa/inet.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/arpa/inet.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/grp.h b/compat/vcbuild/include/grp.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/grp.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/inttypes.h b/compat/vcbuild/include/inttypes.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/inttypes.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/netdb.h b/compat/vcbuild/include/netdb.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/netdb.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/netinet/in.h b/compat/vcbuild/include/netinet/in.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/netinet/in.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/netinet/tcp.h b/compat/vcbuild/include/netinet/tcp.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/netinet/tcp.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/pwd.h b/compat/vcbuild/include/pwd.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/pwd.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/ioctl.h b/compat/vcbuild/include/sys/ioctl.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/sys/ioctl.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/select.h b/compat/vcbuild/include/sys/select.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/sys/select.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/socket.h b/compat/vcbuild/include/sys/socket.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/sys/socket.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/sys/wait.h b/compat/vcbuild/include/sys/wait.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/sys/wait.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/compat/vcbuild/include/termios.h b/compat/vcbuild/include/termios.h
deleted file mode 100644
index 0d8552a2c6..0000000000
--- a/compat/vcbuild/include/termios.h
+++ /dev/null
@@ -1 +0,0 @@
-/* Intentionally empty file to support building git with MSVC */
diff --git a/config.c b/config.c
index edf9914df6..40f9c6d103 100644
--- a/config.c
+++ b/config.c
@@ -333,7 +333,7 @@ static int git_parse_file(config_fn_t fn, void *data)
die("bad config file line %d in %s", cf->linenr, cf->name);
}
-static int parse_unit_factor(const char *end, unsigned long *val)
+static int parse_unit_factor(const char *end, uintmax_t *val)
{
if (!*end)
return 1;
@@ -356,11 +356,23 @@ static int git_parse_long(const char *value, long *ret)
{
if (value && *value) {
char *end;
- long val = strtol(value, &end, 0);
- unsigned long factor = 1;
+ intmax_t val;
+ uintmax_t uval;
+ uintmax_t factor = 1;
+
+ errno = 0;
+ val = strtoimax(value, &end, 0);
+ if (errno == ERANGE)
+ return 0;
if (!parse_unit_factor(end, &factor))
return 0;
- *ret = val * factor;
+ uval = abs(val);
+ uval *= factor;
+ if ((uval > maximum_signed_value_of_type(long)) ||
+ (abs(val) > uval))
+ return 0;
+ val *= factor;
+ *ret = val;
return 1;
}
return 0;
@@ -370,9 +382,19 @@ int git_parse_ulong(const char *value, unsigned long *ret)
{
if (value && *value) {
char *end;
- unsigned long val = strtoul(value, &end, 0);
+ uintmax_t val;
+ uintmax_t oldval;
+
+ errno = 0;
+ val = strtoumax(value, &end, 0);
+ if (errno == ERANGE)
+ return 0;
+ oldval = val;
if (!parse_unit_factor(end, &val))
return 0;
+ if ((val > maximum_unsigned_value_of_type(long)) ||
+ (oldval > val))
+ return 0;
*ret = val;
return 1;
}
@@ -553,7 +575,7 @@ static int git_default_core_config(const char *var, const char *value)
if (!strcmp(var, "core.packedgitwindowsize")) {
int pgsz_x2 = getpagesize() * 2;
- packed_git_window_size = git_config_int(var, value);
+ packed_git_window_size = git_config_ulong(var, value);
/* This value must be multiple of (pagesize * 2) */
packed_git_window_size /= pgsz_x2;
@@ -564,18 +586,17 @@ static int git_default_core_config(const char *var, const char *value)
}
if (!strcmp(var, "core.bigfilethreshold")) {
- long n = git_config_int(var, value);
- big_file_threshold = 0 < n ? n : 0;
+ big_file_threshold = git_config_ulong(var, value);
return 0;
}
if (!strcmp(var, "core.packedgitlimit")) {
- packed_git_limit = git_config_int(var, value);
+ packed_git_limit = git_config_ulong(var, value);
return 0;
}
if (!strcmp(var, "core.deltabasecachelimit")) {
- delta_base_cache_limit = git_config_int(var, value);
+ delta_base_cache_limit = git_config_ulong(var, value);
return 0;
}
@@ -797,6 +818,10 @@ int git_default_config(const char *var, const char *value, void *dummy)
return 0;
}
+ if (!strcmp(var, "pack.packsizelimit")) {
+ pack_size_limit_cfg = git_config_ulong(var, value);
+ return 0;
+ }
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}
@@ -865,12 +890,12 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
home = getenv("HOME");
if (home) {
- char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
+ char buf[PATH_MAX];
+ char *user_config = mksnpath(buf, sizeof(buf), "%s/.gitconfig", home);
if (!access(user_config, R_OK)) {
ret += git_config_from_file(fn, user_config, data);
found += 1;
}
- free(user_config);
}
if (repo_config && !access(repo_config, R_OK)) {
diff --git a/connect.c b/connect.c
index 51990fa0cb..c8d0ea5d75 100644
--- a/connect.c
+++ b/connect.c
@@ -53,7 +53,6 @@ static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1
* Read all the refs from the other end
*/
struct ref **get_remote_heads(int in, struct ref **list,
- int nr_match, char **match,
unsigned int flags,
struct extra_have_objects *extra_have)
{
@@ -92,8 +91,6 @@ struct ref **get_remote_heads(int in, struct ref **list,
if (!check_ref(name, name_len, flags))
continue;
- if (nr_match && !path_match(name, nr_match, match))
- continue;
ref = alloc_ref(buffer + 41);
hashcpy(ref->old_sha1, old_sha1);
*list = ref;
@@ -108,27 +105,6 @@ int server_supports(const char *feature)
strstr(server_capabilities, feature) != NULL;
}
-int path_match(const char *path, int nr, char **match)
-{
- int i;
- int pathlen = strlen(path);
-
- for (i = 0; i < nr; i++) {
- char *s = match[i];
- int len = strlen(s);
-
- if (!len || len > pathlen)
- continue;
- if (memcmp(path + pathlen - len, s, len))
- continue;
- if (pathlen > len && path[pathlen - len - 1] != '/')
- continue;
- *s = 0;
- return (i + 1);
- }
- return 0;
-}
-
enum protocol {
PROTO_LOCAL = 1,
PROTO_SSH,
@@ -175,6 +151,15 @@ static void get_host_and_port(char **host, const char **port)
}
}
+static void enable_keepalive(int sockfd)
+{
+ int ka = 1;
+
+ if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)
+ fprintf(stderr, "unable to set SO_KEEPALIVE on socket: %s\n",
+ strerror(errno));
+}
+
#ifndef NO_IPV6
static const char *ai_name(const struct addrinfo *ai)
@@ -239,6 +224,8 @@ static int git_tcp_connect_sock(char *host, int flags)
if (sockfd < 0)
die("unable to connect to %s:\n%s", host, error_message.buf);
+ enable_keepalive(sockfd);
+
if (flags & CONNECT_VERBOSE)
fprintf(stderr, "done.\n");
@@ -312,6 +299,8 @@ static int git_tcp_connect_sock(char *host, int flags)
if (sockfd < 0)
die("unable to connect to %s:\n%s", host, error_message.buf);
+ enable_keepalive(sockfd);
+
if (flags & CONNECT_VERBOSE)
fprintf(stderr, "done.\n");
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index b7c1edf1cc..cc1bdf9609 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -486,8 +486,13 @@ _get_comp_words_by_ref ()
fi
fi
-# __gitcomp accepts 1, 2, 3, or 4 arguments
-# generates completion reply with compgen
+# Generates completion reply with compgen, appending a space to possible
+# completion words, if necessary.
+# It accepts 1 to 4 arguments:
+# 1: List of possible completion words.
+# 2: A prefix to be added to each possible completion word (optional).
+# 3: Generate possible completion matches for this word (optional).
+# 4: A suffix to be appended to each possible completion word (optional).
__gitcomp ()
{
local cur_="$cur"
@@ -508,42 +513,49 @@ __gitcomp ()
esac
}
-# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
+# Generates completion reply with compgen from newline-separated possible
+# completion words by appending a space to all of them.
+# It accepts 1 to 4 arguments:
+# 1: List of possible completion words, separated by a single newline.
+# 2: A prefix to be added to each possible completion word (optional).
+# 3: Generate possible completion matches for this word (optional).
+# 4: A suffix to be appended to each possible completion word instead of
+# the default space (optional). If specified but empty, nothing is
+# appended.
+__gitcomp_nl ()
+{
+ local s=$'\n' IFS=' '$'\t'$'\n'
+ local cur_="$cur" suffix=" "
+
+ if [ $# -gt 2 ]; then
+ cur_="$3"
+ if [ $# -gt 3 ]; then
+ suffix="$4"
+ fi
+ fi
+
+ IFS=$s
+ COMPREPLY=($(compgen -P "${2-}" -S "$suffix" -W "$1" -- "$cur_"))
+}
+
__git_heads ()
{
- local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+ local dir="$(__gitdir)"
if [ -d "$dir" ]; then
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
refs/heads
return
fi
- for i in $(git ls-remote "${1-}" 2>/dev/null); do
- case "$is_hash,$i" in
- y,*) is_hash=n ;;
- n,*^{}) is_hash=y ;;
- n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
- n,*) is_hash=y; echo "$i" ;;
- esac
- done
}
-# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
__git_tags ()
{
- local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+ local dir="$(__gitdir)"
if [ -d "$dir" ]; then
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
refs/tags
return
fi
- for i in $(git ls-remote "${1-}" 2>/dev/null); do
- case "$is_hash,$i" in
- y,*) is_hash=n ;;
- n,*^{}) is_hash=y ;;
- n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
- n,*) is_hash=y; echo "$i" ;;
- esac
- done
}
# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments
@@ -551,7 +563,7 @@ __git_tags ()
# by checkout for tracking branches
__git_refs ()
{
- local i is_hash=y dir="$(__gitdir "${1-}")" track="${2-}"
+ local i hash dir="$(__gitdir "${1-}")" track="${2-}"
local format refs
if [ -d "$dir" ]; then
case "$cur" in
@@ -587,16 +599,27 @@ __git_refs ()
fi
return
fi
- for i in $(git ls-remote "$dir" 2>/dev/null); do
- case "$is_hash,$i" in
- y,*) is_hash=n ;;
- n,*^{}) is_hash=y ;;
- n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
- n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
- n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
- n,*) is_hash=y; echo "$i" ;;
- esac
- done
+ case "$cur" in
+ refs|refs/*)
+ git ls-remote "$dir" "$cur*" 2>/dev/null | \
+ while read hash i; do
+ case "$i" in
+ *^{}) ;;
+ *) echo "$i" ;;
+ esac
+ done
+ ;;
+ *)
+ git ls-remote "$dir" HEAD ORIG_HEAD 'refs/tags/*' 'refs/heads/*' 'refs/remotes/*' 2>/dev/null | \
+ while read hash i; do
+ case "$i" in
+ *^{}) ;;
+ refs/*) echo "${i#refs/*/}" ;;
+ *) echo "$i" ;;
+ esac
+ done
+ ;;
+ esac
}
# __git_refs2 requires 1 argument (to pass to __git_refs)
@@ -611,18 +634,10 @@ __git_refs2 ()
# __git_refs_remotes requires 1 argument (to pass to ls-remote)
__git_refs_remotes ()
{
- local cmd i is_hash=y
- for i in $(git ls-remote "$1" 2>/dev/null); do
- case "$is_hash,$i" in
- n,refs/heads/*)
- is_hash=y
- echo "$i:refs/remotes/$1/${i#refs/heads/}"
- ;;
- y,*) is_hash=n ;;
- n,*^{}) is_hash=y ;;
- n,refs/tags/*) is_hash=y;;
- n,*) is_hash=y; ;;
- esac
+ local i hash
+ git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
+ while read hash i; do
+ echo "$i:refs/remotes/$1/${i#refs/heads/}"
done
}
@@ -712,15 +727,15 @@ __git_complete_revlist_file ()
*...*)
pfx="${cur_%...*}..."
cur_="${cur_#*...}"
- __gitcomp "$(__git_refs)" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
;;
*..*)
pfx="${cur_%..*}.."
cur_="${cur_#*..}"
- __gitcomp "$(__git_refs)" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
;;
*)
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
;;
esac
}
@@ -760,7 +775,7 @@ __git_complete_remote_or_refspec ()
c=$((++c))
done
if [ -z "$remote" ]; then
- __gitcomp "$(__git_remotes)"
+ __gitcomp_nl "$(__git_remotes)"
return
fi
if [ $no_complete_refspec = 1 ]; then
@@ -785,23 +800,23 @@ __git_complete_remote_or_refspec ()
case "$cmd" in
fetch)
if [ $lhs = 1 ]; then
- __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
else
- __gitcomp "$(__git_refs)" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
fi
;;
pull)
if [ $lhs = 1 ]; then
- __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
else
- __gitcomp "$(__git_refs)" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
fi
;;
push)
if [ $lhs = 1 ]; then
- __gitcomp "$(__git_refs)" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
else
- __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur_"
+ __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
fi
;;
esac
@@ -1080,7 +1095,7 @@ _git_archive ()
return
;;
--remote=*)
- __gitcomp "$(__git_remotes)" "" "${cur##--remote=}"
+ __gitcomp_nl "$(__git_remotes)" "" "${cur##--remote=}"
return
;;
--*)
@@ -1111,7 +1126,7 @@ _git_bisect ()
case "$subcommand" in
bad|good|reset|skip|start)
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
;;
*)
COMPREPLY=()
@@ -1142,9 +1157,9 @@ _git_branch ()
;;
*)
if [ $only_local_ref = "y" -a $has_r = "n" ]; then
- __gitcomp "$(__git_heads)"
+ __gitcomp_nl "$(__git_heads)"
else
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
fi
;;
esac
@@ -1191,7 +1206,7 @@ _git_checkout ()
if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
track=''
fi
- __gitcomp "$(__git_refs '' $track)"
+ __gitcomp_nl "$(__git_refs '' $track)"
;;
esac
}
@@ -1208,7 +1223,7 @@ _git_cherry_pick ()
__gitcomp "--edit --no-commit"
;;
*)
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
;;
esac
}
@@ -1262,7 +1277,7 @@ _git_commit ()
;;
--reuse-message=*|--reedit-message=*|\
--fixup=*|--squash=*)
- __gitcomp "$(__git_refs)" "" "${cur#*=}"
+ __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
return
;;
--untracked-files=*)
@@ -1293,7 +1308,7 @@ _git_describe ()
"
return
esac
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
__git_diff_common_options="--stat --numstat --shortstat --summary
@@ -1459,13 +1474,13 @@ _git_grep ()
case "$cword,$prev" in
2,*|*,-*)
if test -r tags; then
- __gitcomp "$(__git_match_ctag "$cur" tags)"
+ __gitcomp_nl "$(__git_match_ctag "$cur" tags)"
return
fi
;;
esac
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_help ()
@@ -1523,7 +1538,7 @@ _git_ls_files ()
_git_ls_remote ()
{
- __gitcomp "$(__git_remotes)"
+ __gitcomp_nl "$(__git_remotes)"
}
_git_ls_tree ()
@@ -1619,7 +1634,7 @@ _git_merge ()
__gitcomp "$__git_merge_options"
return
esac
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_mergetool ()
@@ -1639,7 +1654,7 @@ _git_mergetool ()
_git_merge_base ()
{
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_mv ()
@@ -1670,7 +1685,7 @@ _git_notes ()
,*)
case "${words[cword-1]}" in
--ref)
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
;;
*)
__gitcomp "$subcommands --ref"
@@ -1679,7 +1694,7 @@ _git_notes ()
;;
add,--reuse-message=*|append,--reuse-message=*|\
add,--reedit-message=*|append,--reedit-message=*)
- __gitcomp "$(__git_refs)" "" "${cur#*=}"
+ __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
;;
add,--*|append,--*)
__gitcomp '--file= --message= --reedit-message=
@@ -1698,7 +1713,7 @@ _git_notes ()
-m|-F)
;;
*)
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
;;
esac
;;
@@ -1726,12 +1741,12 @@ _git_push ()
{
case "$prev" in
--repo)
- __gitcomp "$(__git_remotes)"
+ __gitcomp_nl "$(__git_remotes)"
return
esac
case "$cur" in
--repo=*)
- __gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+ __gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
return
;;
--*)
@@ -1769,7 +1784,7 @@ _git_rebase ()
return
esac
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_reflog ()
@@ -1780,7 +1795,7 @@ _git_reflog ()
if [ -z "$subcommand" ]; then
__gitcomp "$subcommands"
else
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
fi
}
@@ -1862,23 +1877,27 @@ _git_config ()
{
case "$prev" in
branch.*.remote)
- __gitcomp "$(__git_remotes)"
+ __gitcomp_nl "$(__git_remotes)"
return
;;
branch.*.merge)
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
return
;;
remote.*.fetch)
local remote="${prev#remote.}"
remote="${remote%.fetch}"
- __gitcomp "$(__git_refs_remotes "$remote")"
+ if [ -z "$cur" ]; then
+ COMPREPLY=("refs/heads/")
+ return
+ fi
+ __gitcomp_nl "$(__git_refs_remotes "$remote")"
return
;;
remote.*.push)
local remote="${prev#remote.}"
remote="${remote%.push}"
- __gitcomp "$(git --git-dir="$(__gitdir)" \
+ __gitcomp_nl "$(git --git-dir="$(__gitdir)" \
for-each-ref --format='%(refname):%(refname)' \
refs/heads)"
return
@@ -1925,7 +1944,7 @@ _git_config ()
return
;;
--get|--get-all|--unset|--unset-all)
- __gitcomp "$(__git_config_get_set_variables)"
+ __gitcomp_nl "$(__git_config_get_set_variables)"
return
;;
*.*)
@@ -1951,7 +1970,7 @@ _git_config ()
;;
branch.*)
local pfx="${cur%.*}." cur_="${cur#*.}"
- __gitcomp "$(__git_heads)" "$pfx" "$cur_" "."
+ __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
return
;;
guitool.*.*)
@@ -1980,7 +1999,7 @@ _git_config ()
pager.*)
local pfx="${cur%.*}." cur_="${cur#*.}"
__git_compute_all_commands
- __gitcomp "$__git_all_commands" "$pfx" "$cur_"
+ __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_"
return
;;
remote.*.*)
@@ -1993,7 +2012,7 @@ _git_config ()
;;
remote.*)
local pfx="${cur%.*}." cur_="${cur#*.}"
- __gitcomp "$(__git_remotes)" "$pfx" "$cur_" "."
+ __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
return
;;
url.*.*)
@@ -2294,7 +2313,7 @@ _git_remote ()
case "$subcommand" in
rename|rm|show|prune)
- __gitcomp "$(__git_remotes)"
+ __gitcomp_nl "$(__git_remotes)"
;;
update)
local i c='' IFS=$'\n'
@@ -2312,7 +2331,7 @@ _git_remote ()
_git_replace ()
{
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_reset ()
@@ -2325,7 +2344,7 @@ _git_reset ()
return
;;
esac
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_revert ()
@@ -2336,7 +2355,7 @@ _git_revert ()
return
;;
esac
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
}
_git_rm ()
@@ -2435,7 +2454,7 @@ _git_stash ()
COMPREPLY=()
;;
show,*|apply,*|drop,*|pop,*|branch,*)
- __gitcomp "$(git --git-dir="$(__gitdir)" stash list \
+ __gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
| sed -n -e 's/:.*//p')"
;;
*)
@@ -2569,7 +2588,7 @@ _git_tag ()
i="${words[c]}"
case "$i" in
-d|-v)
- __gitcomp "$(__git_tags)"
+ __gitcomp_nl "$(__git_tags)"
return
;;
-f)
@@ -2585,13 +2604,13 @@ _git_tag ()
;;
-*|tag)
if [ $f = 1 ]; then
- __gitcomp "$(__git_tags)"
+ __gitcomp_nl "$(__git_tags)"
else
COMPREPLY=()
fi
;;
*)
- __gitcomp "$(__git_refs)"
+ __gitcomp_nl "$(__git_refs)"
;;
esac
}
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index b975d67fca..594980302b 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -53,9 +53,10 @@ def p4_build_cmd(cmd):
def chdir(dir):
# P4 uses the PWD environment variable rather than getcwd(). Since we're
- # not using the shell, we have to set it ourselves.
- os.environ['PWD']=dir
+ # not using the shell, we have to set it ourselves. This path could
+ # be relative, so go there first, then figure out where we ended up.
os.chdir(dir)
+ os.environ['PWD'] = os.getcwd()
def die(msg):
if verbose:
@@ -847,6 +848,41 @@ class P4Submit(Command, P4UserMap):
return template
+ def edit_template(self, template_file):
+ """Invoke the editor to let the user change the submission
+ message. Return true if okay to continue with the submit."""
+
+ # if configured to skip the editing part, just submit
+ if gitConfig("git-p4.skipSubmitEdit") == "true":
+ return True
+
+ # look at the modification time, to check later if the user saved
+ # the file
+ mtime = os.stat(template_file).st_mtime
+
+ # invoke the editor
+ if os.environ.has_key("P4EDITOR"):
+ editor = os.environ.get("P4EDITOR")
+ else:
+ editor = read_pipe("git var GIT_EDITOR").strip()
+ system(editor + " " + template_file)
+
+ # If the file was not saved, prompt to see if this patch should
+ # be skipped. But skip this verification step if configured so.
+ if gitConfig("git-p4.skipSubmitEditCheck") == "true":
+ return True
+
+ # modification time updated means user saved the file
+ if os.stat(template_file).st_mtime > mtime:
+ return True
+
+ while True:
+ response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
+ if response == 'y':
+ return True
+ if response == 'n':
+ return False
+
def applyCommit(self, id):
print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
@@ -1001,7 +1037,7 @@ class P4Submit(Command, P4UserMap):
separatorLine = "######## everything below this line is just the diff #######\n"
- [handle, fileName] = tempfile.mkstemp()
+ (handle, fileName) = tempfile.mkstemp()
tmpFile = os.fdopen(handle, "w+")
if self.isWindows:
submitTemplate = submitTemplate.replace("\n", "\r\n")
@@ -1009,25 +1045,9 @@ class P4Submit(Command, P4UserMap):
newdiff = newdiff.replace("\n", "\r\n")
tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
tmpFile.close()
- mtime = os.stat(fileName).st_mtime
- if os.environ.has_key("P4EDITOR"):
- editor = os.environ.get("P4EDITOR")
- else:
- editor = read_pipe("git var GIT_EDITOR").strip()
- system(editor + " " + fileName)
- if gitConfig("git-p4.skipSubmitEditCheck") == "true":
- checkModTime = False
- else:
- checkModTime = True
-
- response = "y"
- if checkModTime and (os.stat(fileName).st_mtime <= mtime):
- response = "x"
- while response != "y" and response != "n":
- response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
-
- if response == "y":
+ if self.edit_template(fileName):
+ # read the edited message and submit
tmpFile = open(fileName, "rb")
message = tmpFile.read()
tmpFile.close()
@@ -1039,11 +1059,13 @@ class P4Submit(Command, P4UserMap):
if self.preserveUser:
if p4User:
# Get last changelist number. Cannot easily get it from
- # the submit command output as the output is unmarshalled.
+ # the submit command output as the output is
+ # unmarshalled.
changelist = self.lastP4Changelist()
self.modifyChangelistUser(changelist, p4User)
-
else:
+ # skip this patch
+ print "Submission cancelled, undoing p4 changes."
for f in editedFiles:
p4_revert(f)
for f in filesToAdd:
@@ -1099,6 +1121,10 @@ class P4Submit(Command, P4UserMap):
print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
self.oldWorkingDirectory = os.getcwd()
+ # ensure the clientPath exists
+ if not os.path.exists(self.clientPath):
+ os.makedirs(self.clientPath)
+
chdir(self.clientPath)
print "Synchronizing p4 checkout..."
p4_sync("...")
diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt
index 52003ae904..5044a121e0 100644
--- a/contrib/fast-import/git-p4.txt
+++ b/contrib/fast-import/git-p4.txt
@@ -202,11 +202,24 @@ able to find the relevant client. This client spec will be used to
both filter the files cloned by git and set the directory layout as
specified in the client (this implies --keep-path style semantics).
-git-p4.skipSubmitModTimeCheck
+git-p4.skipSubmitEdit
- git config [--global] git-p4.skipSubmitModTimeCheck false
+ git config [--global] git-p4.skipSubmitEdit false
-If true, submit will not check if the p4 change template has been modified.
+Normally, git-p4 invokes an editor after each commit is applied so
+that you can make changes to the submit message. Setting this
+variable to true will skip the editing step, submitting the change as is.
+
+git-p4.skipSubmitEditCheck
+
+ git config [--global] git-p4.skipSubmitEditCheck false
+
+After the editor is invoked, git-p4 normally makes sure you saved the
+change description, as an indication that you did indeed read it over
+and edit it. You can quit without saving to abort the submit (or skip
+this change and continue). Setting this variable to true will cause
+git-p4 not to check if you saved the change description. This variable
+only matters if git-p4.skipSubmitEdit has not been set to true.
git-p4.preserveUser
diff --git a/convert.c b/convert.c
index 3bb5a4dd57..346f9d45e0 100644
--- a/convert.c
+++ b/convert.c
@@ -641,7 +641,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
return 1;
}
-static int git_path_check_crlf(const char *path, struct git_attr_check *check)
+static enum crlf_action git_path_check_crlf(const char *path, struct git_attr_check *check)
{
const char *value = check->value;
@@ -658,7 +658,7 @@ static int git_path_check_crlf(const char *path, struct git_attr_check *check)
return CRLF_GUESS;
}
-static int git_path_check_eol(const char *path, struct git_attr_check *check)
+static enum eol git_path_check_eol(const char *path, struct git_attr_check *check)
{
const char *value = check->value;
@@ -811,7 +811,7 @@ int renormalize_buffer(const char *path, const char *src, size_t len, struct str
src = dst->buf;
len = dst->len;
}
- return ret | convert_to_git(path, src, len, dst, 0);
+ return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_FALSE);
}
/*****************************************************************
@@ -876,24 +876,39 @@ int is_null_stream_filter(struct stream_filter *filter)
/*
* LF-to-CRLF filter
*/
+
+struct lf_to_crlf_filter {
+ struct stream_filter filter;
+ int want_lf;
+};
+
static int lf_to_crlf_filter_fn(struct stream_filter *filter,
const char *input, size_t *isize_p,
char *output, size_t *osize_p)
{
- size_t count;
+ size_t count, o = 0;
+ struct lf_to_crlf_filter *lf_to_crlf = (struct lf_to_crlf_filter *)filter;
+
+ /* Output a pending LF if we need to */
+ if (lf_to_crlf->want_lf) {
+ output[o++] = '\n';
+ lf_to_crlf->want_lf = 0;
+ }
if (!input)
- return 0; /* we do not keep any states */
+ return 0; /* We've already dealt with the state */
+
count = *isize_p;
if (count) {
- size_t i, o;
- for (i = o = 0; o < *osize_p && i < count; i++) {
+ size_t i;
+ for (i = 0; o < *osize_p && i < count; i++) {
char ch = input[i];
if (ch == '\n') {
- if (o + 1 < *osize_p)
- output[o++] = '\r';
- else
- break;
+ output[o++] = '\r';
+ if (o >= *osize_p) {
+ lf_to_crlf->want_lf = 1;
+ continue; /* We need to increase i */
+ }
}
output[o++] = ch;
}
@@ -904,15 +919,24 @@ static int lf_to_crlf_filter_fn(struct stream_filter *filter,
return 0;
}
+static void lf_to_crlf_free_fn(struct stream_filter *filter)
+{
+ free(filter);
+}
+
static struct stream_filter_vtbl lf_to_crlf_vtbl = {
lf_to_crlf_filter_fn,
- null_free_fn,
+ lf_to_crlf_free_fn,
};
-static struct stream_filter lf_to_crlf_filter_singleton = {
- &lf_to_crlf_vtbl,
-};
+static struct stream_filter *lf_to_crlf_filter(void)
+{
+ struct lf_to_crlf_filter *lf_to_crlf = xmalloc(sizeof(*lf_to_crlf));
+ lf_to_crlf->filter.vtbl = &lf_to_crlf_vtbl;
+ lf_to_crlf->want_lf = 0;
+ return (struct stream_filter *)lf_to_crlf;
+}
/*
* Cascade filter
@@ -1194,7 +1218,7 @@ struct stream_filter *get_stream_filter(const char *path, const unsigned char *s
else if (output_eol(crlf_action) == EOL_CRLF &&
!(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS))
- filter = cascade_filter(filter, &lf_to_crlf_filter_singleton);
+ filter = cascade_filter(filter, lf_to_crlf_filter());
return filter;
}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
new file mode 100644
index 0000000000..390f194252
--- /dev/null
+++ b/credential-cache--daemon.c
@@ -0,0 +1,269 @@
+#include "cache.h"
+#include "credential.h"
+#include "unix-socket.h"
+#include "sigchain.h"
+
+static const char *socket_path;
+
+static void cleanup_socket(void)
+{
+ if (socket_path)
+ unlink(socket_path);
+}
+
+static void cleanup_socket_on_signal(int sig)
+{
+ cleanup_socket();
+ sigchain_pop(sig);
+ raise(sig);
+}
+
+struct credential_cache_entry {
+ struct credential item;
+ unsigned long expiration;
+};
+static struct credential_cache_entry *entries;
+static int entries_nr;
+static int entries_alloc;
+
+static void cache_credential(struct credential *c, int timeout)
+{
+ struct credential_cache_entry *e;
+
+ ALLOC_GROW(entries, entries_nr + 1, entries_alloc);
+ e = &entries[entries_nr++];
+
+ /* take ownership of pointers */
+ memcpy(&e->item, c, sizeof(*c));
+ memset(c, 0, sizeof(*c));
+ e->expiration = time(NULL) + timeout;
+}
+
+static struct credential_cache_entry *lookup_credential(const struct credential *c)
+{
+ int i;
+ for (i = 0; i < entries_nr; i++) {
+ struct credential *e = &entries[i].item;
+ if (credential_match(c, e))
+ return &entries[i];
+ }
+ return NULL;
+}
+
+static void remove_credential(const struct credential *c)
+{
+ struct credential_cache_entry *e;
+
+ e = lookup_credential(c);
+ if (e)
+ e->expiration = 0;
+}
+
+static int check_expirations(void)
+{
+ static unsigned long wait_for_entry_until;
+ int i = 0;
+ unsigned long now = time(NULL);
+ unsigned long next = (unsigned long)-1;
+
+ /*
+ * Initially give the client 30 seconds to actually contact us
+ * and store a credential before we decide there's no point in
+ * keeping the daemon around.
+ */
+ if (!wait_for_entry_until)
+ wait_for_entry_until = now + 30;
+
+ while (i < entries_nr) {
+ if (entries[i].expiration <= now) {
+ entries_nr--;
+ credential_clear(&entries[i].item);
+ if (i != entries_nr)
+ memcpy(&entries[i], &entries[entries_nr], sizeof(*entries));
+ /*
+ * Stick around 30 seconds in case a new credential
+ * shows up (e.g., because we just removed a failed
+ * one, and we will soon get the correct one).
+ */
+ wait_for_entry_until = now + 30;
+ }
+ else {
+ if (entries[i].expiration < next)
+ next = entries[i].expiration;
+ i++;
+ }
+ }
+
+ if (!entries_nr) {
+ if (wait_for_entry_until <= now)
+ return 0;
+ next = wait_for_entry_until;
+ }
+
+ return next - now;
+}
+
+static int read_request(FILE *fh, struct credential *c,
+ struct strbuf *action, int *timeout) {
+ static struct strbuf item = STRBUF_INIT;
+ const char *p;
+
+ strbuf_getline(&item, fh, '\n');
+ p = skip_prefix(item.buf, "action=");
+ if (!p)
+ return error("client sent bogus action line: %s", item.buf);
+ strbuf_addstr(action, p);
+
+ strbuf_getline(&item, fh, '\n');
+ p = skip_prefix(item.buf, "timeout=");
+ if (!p)
+ return error("client sent bogus timeout line: %s", item.buf);
+ *timeout = atoi(p);
+
+ if (credential_read(c, fh) < 0)
+ return -1;
+ return 0;
+}
+
+static void serve_one_client(FILE *in, FILE *out)
+{
+ struct credential c = CREDENTIAL_INIT;
+ struct strbuf action = STRBUF_INIT;
+ int timeout = -1;
+
+ if (read_request(in, &c, &action, &timeout) < 0)
+ /* ignore error */ ;
+ else if (!strcmp(action.buf, "get")) {
+ struct credential_cache_entry *e = lookup_credential(&c);
+ if (e) {
+ fprintf(out, "username=%s\n", e->item.username);
+ fprintf(out, "password=%s\n", e->item.password);
+ }
+ }
+ else if (!strcmp(action.buf, "exit"))
+ exit(0);
+ else if (!strcmp(action.buf, "erase"))
+ remove_credential(&c);
+ else if (!strcmp(action.buf, "store")) {
+ if (timeout < 0)
+ warning("cache client didn't specify a timeout");
+ else if (!c.username || !c.password)
+ warning("cache client gave us a partial credential");
+ else {
+ remove_credential(&c);
+ cache_credential(&c, timeout);
+ }
+ }
+ else
+ warning("cache client sent unknown action: %s", action.buf);
+
+ credential_clear(&c);
+ strbuf_release(&action);
+}
+
+static int serve_cache_loop(int fd)
+{
+ struct pollfd pfd;
+ unsigned long wakeup;
+
+ wakeup = check_expirations();
+ if (!wakeup)
+ return 0;
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ if (poll(&pfd, 1, 1000 * wakeup) < 0) {
+ if (errno != EINTR)
+ die_errno("poll failed");
+ return 1;
+ }
+
+ if (pfd.revents & POLLIN) {
+ int client, client2;
+ FILE *in, *out;
+
+ client = accept(fd, NULL, NULL);
+ if (client < 0) {
+ warning("accept failed: %s", strerror(errno));
+ return 1;
+ }
+ client2 = dup(client);
+ if (client2 < 0) {
+ warning("dup failed: %s", strerror(errno));
+ close(client);
+ return 1;
+ }
+
+ in = xfdopen(client, "r");
+ out = xfdopen(client2, "w");
+ serve_one_client(in, out);
+ fclose(in);
+ fclose(out);
+ }
+ return 1;
+}
+
+static void serve_cache(const char *socket_path)
+{
+ int fd;
+
+ fd = unix_stream_listen(socket_path);
+ if (fd < 0)
+ die_errno("unable to bind to '%s'", socket_path);
+
+ printf("ok\n");
+ fclose(stdout);
+
+ while (serve_cache_loop(fd))
+ ; /* nothing */
+
+ close(fd);
+ unlink(socket_path);
+}
+
+static const char permissions_advice[] =
+"The permissions on your socket directory are too loose; other\n"
+"users may be able to read your cached credentials. Consider running:\n"
+"\n"
+" chmod 0700 %s";
+static void check_socket_directory(const char *path)
+{
+ struct stat st;
+ char *path_copy = xstrdup(path);
+ char *dir = dirname(path_copy);
+
+ if (!stat(dir, &st)) {
+ if (st.st_mode & 077)
+ die(permissions_advice, dir);
+ free(path_copy);
+ return;
+ }
+
+ /*
+ * We must be sure to create the directory with the correct mode,
+ * not just chmod it after the fact; otherwise, there is a race
+ * condition in which somebody can chdir to it, sleep, then try to open
+ * our protected socket.
+ */
+ if (safe_create_leading_directories_const(dir) < 0)
+ die_errno("unable to create directories for '%s'", dir);
+ if (mkdir(dir, 0700) < 0)
+ die_errno("unable to mkdir '%s'", dir);
+ free(path_copy);
+}
+
+int main(int argc, const char **argv)
+{
+ socket_path = argv[1];
+
+ if (!socket_path)
+ die("usage: git-credential-cache--daemon <socket_path>");
+ check_socket_directory(socket_path);
+
+ atexit(cleanup_socket);
+ sigchain_push_common(cleanup_socket_on_signal);
+
+ serve_cache(socket_path);
+
+ return 0;
+}
diff --git a/credential-cache.c b/credential-cache.c
new file mode 100644
index 0000000000..dc98372e59
--- /dev/null
+++ b/credential-cache.c
@@ -0,0 +1,120 @@
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+#include "parse-options.h"
+#include "unix-socket.h"
+#include "run-command.h"
+
+#define FLAG_SPAWN 0x1
+#define FLAG_RELAY 0x2
+
+static int send_request(const char *socket, const struct strbuf *out)
+{
+ int got_data = 0;
+ int fd = unix_stream_connect(socket);
+
+ if (fd < 0)
+ return -1;
+
+ if (write_in_full(fd, out->buf, out->len) < 0)
+ die_errno("unable to write to cache daemon");
+ shutdown(fd, SHUT_WR);
+
+ while (1) {
+ char in[1024];
+ int r;
+
+ r = read_in_full(fd, in, sizeof(in));
+ if (r == 0)
+ break;
+ if (r < 0)
+ die_errno("read error from cache daemon");
+ write_or_die(1, in, r);
+ got_data = 1;
+ }
+ return got_data;
+}
+
+static void spawn_daemon(const char *socket)
+{
+ struct child_process daemon;
+ const char *argv[] = { NULL, NULL, NULL };
+ char buf[128];
+ int r;
+
+ memset(&daemon, 0, sizeof(daemon));
+ argv[0] = "git-credential-cache--daemon";
+ argv[1] = socket;
+ daemon.argv = argv;
+ daemon.no_stdin = 1;
+ daemon.out = -1;
+
+ if (start_command(&daemon))
+ die_errno("unable to start cache daemon");
+ r = read_in_full(daemon.out, buf, sizeof(buf));
+ if (r < 0)
+ die_errno("unable to read result code from cache daemon");
+ if (r != 3 || memcmp(buf, "ok\n", 3))
+ die("cache daemon did not start: %.*s", r, buf);
+ close(daemon.out);
+}
+
+static void do_cache(const char *socket, const char *action, int timeout,
+ int flags)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_addf(&buf, "action=%s\n", action);
+ strbuf_addf(&buf, "timeout=%d\n", timeout);
+ if (flags & FLAG_RELAY) {
+ if (strbuf_read(&buf, 0, 0) < 0)
+ die_errno("unable to relay credential");
+ }
+
+ if (!send_request(socket, &buf))
+ return;
+ if (flags & FLAG_SPAWN) {
+ spawn_daemon(socket);
+ send_request(socket, &buf);
+ }
+ strbuf_release(&buf);
+}
+
+int main(int argc, const char **argv)
+{
+ char *socket_path = NULL;
+ int timeout = 900;
+ const char *op;
+ const char * const usage[] = {
+ "git credential-cache [options] <action>",
+ NULL
+ };
+ struct option options[] = {
+ OPT_INTEGER(0, "timeout", &timeout,
+ "number of seconds to cache credentials"),
+ OPT_STRING(0, "socket", &socket_path, "path",
+ "path of cache-daemon socket"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options, usage, 0);
+ if (!argc)
+ usage_with_options(usage, options);
+ op = argv[0];
+
+ if (!socket_path)
+ socket_path = expand_user_path("~/.git-credential-cache/socket");
+ if (!socket_path)
+ die("unable to find a suitable socket path; use --socket");
+
+ if (!strcmp(op, "exit"))
+ do_cache(socket_path, op, timeout, 0);
+ else if (!strcmp(op, "get") || !strcmp(op, "erase"))
+ do_cache(socket_path, op, timeout, FLAG_RELAY);
+ else if (!strcmp(op, "store"))
+ do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN);
+ else
+ ; /* ignore unknown operation */
+
+ return 0;
+}
diff --git a/credential-store.c b/credential-store.c
new file mode 100644
index 0000000000..26f7589a60
--- /dev/null
+++ b/credential-store.c
@@ -0,0 +1,157 @@
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+#include "parse-options.h"
+
+static struct lock_file credential_lock;
+
+static void parse_credential_file(const char *fn,
+ struct credential *c,
+ void (*match_cb)(struct credential *),
+ void (*other_cb)(struct strbuf *))
+{
+ FILE *fh;
+ struct strbuf line = STRBUF_INIT;
+ struct credential entry = CREDENTIAL_INIT;
+
+ fh = fopen(fn, "r");
+ if (!fh) {
+ if (errno != ENOENT)
+ die_errno("unable to open %s", fn);
+ return;
+ }
+
+ while (strbuf_getline(&line, fh, '\n') != EOF) {
+ credential_from_url(&entry, line.buf);
+ if (entry.username && entry.password &&
+ credential_match(c, &entry)) {
+ if (match_cb) {
+ match_cb(&entry);
+ break;
+ }
+ }
+ else if (other_cb)
+ other_cb(&line);
+ }
+
+ credential_clear(&entry);
+ strbuf_release(&line);
+ fclose(fh);
+}
+
+static void print_entry(struct credential *c)
+{
+ printf("username=%s\n", c->username);
+ printf("password=%s\n", c->password);
+}
+
+static void print_line(struct strbuf *buf)
+{
+ strbuf_addch(buf, '\n');
+ write_or_die(credential_lock.fd, buf->buf, buf->len);
+}
+
+static void rewrite_credential_file(const char *fn, struct credential *c,
+ struct strbuf *extra)
+{
+ if (hold_lock_file_for_update(&credential_lock, fn, 0) < 0)
+ die_errno("unable to get credential storage lock");
+ if (extra)
+ print_line(extra);
+ parse_credential_file(fn, c, NULL, print_line);
+ if (commit_lock_file(&credential_lock) < 0)
+ die_errno("unable to commit credential store");
+}
+
+static void store_credential(const char *fn, struct credential *c)
+{
+ struct strbuf buf = STRBUF_INIT;
+
+ /*
+ * Sanity check that what we are storing is actually sensible.
+ * In particular, we can't make a URL without a protocol field.
+ * Without either a host or pathname (depending on the scheme),
+ * we have no primary key. And without a username and password,
+ * we are not actually storing a credential.
+ */
+ if (!c->protocol || !(c->host || c->path) ||
+ !c->username || !c->password)
+ return;
+
+ strbuf_addf(&buf, "%s://", c->protocol);
+ strbuf_addstr_urlencode(&buf, c->username, 1);
+ strbuf_addch(&buf, ':');
+ strbuf_addstr_urlencode(&buf, c->password, 1);
+ strbuf_addch(&buf, '@');
+ if (c->host)
+ strbuf_addstr_urlencode(&buf, c->host, 1);
+ if (c->path) {
+ strbuf_addch(&buf, '/');
+ strbuf_addstr_urlencode(&buf, c->path, 0);
+ }
+
+ rewrite_credential_file(fn, c, &buf);
+ strbuf_release(&buf);
+}
+
+static void remove_credential(const char *fn, struct credential *c)
+{
+ /*
+ * Sanity check that we actually have something to match
+ * against. The input we get is a restrictive pattern,
+ * so technically a blank credential means "erase everything".
+ * But it is too easy to accidentally send this, since it is equivalent
+ * to empty input. So explicitly disallow it, and require that the
+ * pattern have some actual content to match.
+ */
+ if (c->protocol || c->host || c->path || c->username)
+ rewrite_credential_file(fn, c, NULL);
+}
+
+static int lookup_credential(const char *fn, struct credential *c)
+{
+ parse_credential_file(fn, c, print_entry, NULL);
+ return c->username && c->password;
+}
+
+int main(int argc, const char **argv)
+{
+ const char * const usage[] = {
+ "git credential-store [options] <action>",
+ NULL
+ };
+ const char *op;
+ struct credential c = CREDENTIAL_INIT;
+ char *file = NULL;
+ struct option options[] = {
+ OPT_STRING(0, "file", &file, "path",
+ "fetch and store credentials in <path>"),
+ OPT_END()
+ };
+
+ umask(077);
+
+ argc = parse_options(argc, argv, NULL, options, usage, 0);
+ if (argc != 1)
+ usage_with_options(usage, options);
+ op = argv[0];
+
+ if (!file)
+ file = expand_user_path("~/.git-credentials");
+ if (!file)
+ die("unable to set up default path; use --file");
+
+ if (credential_read(&c, stdin) < 0)
+ die("unable to read credential");
+
+ if (!strcmp(op, "get"))
+ lookup_credential(file, &c);
+ else if (!strcmp(op, "erase"))
+ remove_credential(file, &c);
+ else if (!strcmp(op, "store"))
+ store_credential(file, &c);
+ else
+ ; /* Ignore unknown operation. */
+
+ return 0;
+}
diff --git a/credential.c b/credential.c
new file mode 100644
index 0000000000..a17eafea58
--- /dev/null
+++ b/credential.c
@@ -0,0 +1,365 @@
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+#include "run-command.h"
+#include "url.h"
+
+void credential_init(struct credential *c)
+{
+ memset(c, 0, sizeof(*c));
+ c->helpers.strdup_strings = 1;
+}
+
+void credential_clear(struct credential *c)
+{
+ free(c->protocol);
+ free(c->host);
+ free(c->path);
+ free(c->username);
+ free(c->password);
+ string_list_clear(&c->helpers, 0);
+
+ credential_init(c);
+}
+
+int credential_match(const struct credential *want,
+ const struct credential *have)
+{
+#define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
+ return CHECK(protocol) &&
+ CHECK(host) &&
+ CHECK(path) &&
+ CHECK(username);
+#undef CHECK
+}
+
+static int credential_config_callback(const char *var, const char *value,
+ void *data)
+{
+ struct credential *c = data;
+ const char *key, *dot;
+
+ key = skip_prefix(var, "credential.");
+ if (!key)
+ return 0;
+
+ if (!value)
+ return config_error_nonbool(var);
+
+ dot = strrchr(key, '.');
+ if (dot) {
+ struct credential want = CREDENTIAL_INIT;
+ char *url = xmemdupz(key, dot - key);
+ int matched;
+
+ credential_from_url(&want, url);
+ matched = credential_match(&want, c);
+
+ credential_clear(&want);
+ free(url);
+
+ if (!matched)
+ return 0;
+ key = dot + 1;
+ }
+
+ if (!strcmp(key, "helper"))
+ string_list_append(&c->helpers, value);
+ else if (!strcmp(key, "username")) {
+ if (!c->username)
+ c->username = xstrdup(value);
+ }
+ else if (!strcmp(key, "usehttppath"))
+ c->use_http_path = git_config_bool(var, value);
+
+ return 0;
+}
+
+static int proto_is_http(const char *s)
+{
+ if (!s)
+ return 0;
+ return !strcmp(s, "https") || !strcmp(s, "http");
+}
+
+static void credential_apply_config(struct credential *c)
+{
+ if (c->configured)
+ return;
+ git_config(credential_config_callback, c);
+ c->configured = 1;
+
+ if (!c->use_http_path && proto_is_http(c->protocol)) {
+ free(c->path);
+ c->path = NULL;
+ }
+}
+
+static void credential_describe(struct credential *c, struct strbuf *out)
+{
+ if (!c->protocol)
+ return;
+ strbuf_addf(out, "%s://", c->protocol);
+ if (c->username && *c->username)
+ strbuf_addf(out, "%s@", c->username);
+ if (c->host)
+ strbuf_addstr(out, c->host);
+ if (c->path)
+ strbuf_addf(out, "/%s", c->path);
+}
+
+static char *credential_ask_one(const char *what, struct credential *c)
+{
+ struct strbuf desc = STRBUF_INIT;
+ struct strbuf prompt = STRBUF_INIT;
+ char *r;
+
+ credential_describe(c, &desc);
+ if (desc.len)
+ strbuf_addf(&prompt, "%s for '%s': ", what, desc.buf);
+ else
+ strbuf_addf(&prompt, "%s: ", what);
+
+ /* FIXME: for usernames, we should do something less magical that
+ * actually echoes the characters. However, we need to read from
+ * /dev/tty and not stdio, which is not portable (but getpass will do
+ * it for us). http.c uses the same workaround. */
+ r = git_getpass(prompt.buf);
+
+ strbuf_release(&desc);
+ strbuf_release(&prompt);
+ return xstrdup(r);
+}
+
+static void credential_getpass(struct credential *c)
+{
+ if (!c->username)
+ c->username = credential_ask_one("Username", c);
+ if (!c->password)
+ c->password = credential_ask_one("Password", c);
+}
+
+int credential_read(struct credential *c, FILE *fp)
+{
+ struct strbuf line = STRBUF_INIT;
+
+ while (strbuf_getline(&line, fp, '\n') != EOF) {
+ char *key = line.buf;
+ char *value = strchr(key, '=');
+
+ if (!line.len)
+ break;
+
+ if (!value) {
+ warning("invalid credential line: %s", key);
+ strbuf_release(&line);
+ return -1;
+ }
+ *value++ = '\0';
+
+ if (!strcmp(key, "username")) {
+ free(c->username);
+ c->username = xstrdup(value);
+ } else if (!strcmp(key, "password")) {
+ free(c->password);
+ c->password = xstrdup(value);
+ } else if (!strcmp(key, "protocol")) {
+ free(c->protocol);
+ c->protocol = xstrdup(value);
+ } else if (!strcmp(key, "host")) {
+ free(c->host);
+ c->host = xstrdup(value);
+ } else if (!strcmp(key, "path")) {
+ free(c->path);
+ c->path = xstrdup(value);
+ }
+ /*
+ * Ignore other lines; we don't know what they mean, but
+ * this future-proofs us when later versions of git do
+ * learn new lines, and the helpers are updated to match.
+ */
+ }
+
+ strbuf_release(&line);
+ return 0;
+}
+
+static void credential_write_item(FILE *fp, const char *key, const char *value)
+{
+ if (!value)
+ return;
+ fprintf(fp, "%s=%s\n", key, value);
+}
+
+static void credential_write(const struct credential *c, FILE *fp)
+{
+ credential_write_item(fp, "protocol", c->protocol);
+ credential_write_item(fp, "host", c->host);
+ credential_write_item(fp, "path", c->path);
+ credential_write_item(fp, "username", c->username);
+ credential_write_item(fp, "password", c->password);
+}
+
+static int run_credential_helper(struct credential *c,
+ const char *cmd,
+ int want_output)
+{
+ struct child_process helper;
+ const char *argv[] = { NULL, NULL };
+ FILE *fp;
+
+ memset(&helper, 0, sizeof(helper));
+ argv[0] = cmd;
+ helper.argv = argv;
+ helper.use_shell = 1;
+ helper.in = -1;
+ if (want_output)
+ helper.out = -1;
+ else
+ helper.no_stdout = 1;
+
+ if (start_command(&helper) < 0)
+ return -1;
+
+ fp = xfdopen(helper.in, "w");
+ credential_write(c, fp);
+ fclose(fp);
+
+ if (want_output) {
+ int r;
+ fp = xfdopen(helper.out, "r");
+ r = credential_read(c, fp);
+ fclose(fp);
+ if (r < 0) {
+ finish_command(&helper);
+ return -1;
+ }
+ }
+
+ if (finish_command(&helper))
+ return -1;
+ return 0;
+}
+
+static int credential_do(struct credential *c, const char *helper,
+ const char *operation)
+{
+ struct strbuf cmd = STRBUF_INIT;
+ int r;
+
+ if (helper[0] == '!')
+ strbuf_addstr(&cmd, helper + 1);
+ else if (is_absolute_path(helper))
+ strbuf_addstr(&cmd, helper);
+ else
+ strbuf_addf(&cmd, "git credential-%s", helper);
+
+ strbuf_addf(&cmd, " %s", operation);
+ r = run_credential_helper(c, cmd.buf, !strcmp(operation, "get"));
+
+ strbuf_release(&cmd);
+ return r;
+}
+
+void credential_fill(struct credential *c)
+{
+ int i;
+
+ if (c->username && c->password)
+ return;
+
+ credential_apply_config(c);
+
+ for (i = 0; i < c->helpers.nr; i++) {
+ credential_do(c, c->helpers.items[i].string, "get");
+ if (c->username && c->password)
+ return;
+ }
+
+ credential_getpass(c);
+ if (!c->username && !c->password)
+ die("unable to get password from user");
+}
+
+void credential_approve(struct credential *c)
+{
+ int i;
+
+ if (c->approved)
+ return;
+ if (!c->username || !c->password)
+ return;
+
+ credential_apply_config(c);
+
+ for (i = 0; i < c->helpers.nr; i++)
+ credential_do(c, c->helpers.items[i].string, "store");
+ c->approved = 1;
+}
+
+void credential_reject(struct credential *c)
+{
+ int i;
+
+ credential_apply_config(c);
+
+ for (i = 0; i < c->helpers.nr; i++)
+ credential_do(c, c->helpers.items[i].string, "erase");
+
+ free(c->username);
+ c->username = NULL;
+ free(c->password);
+ c->password = NULL;
+ c->approved = 0;
+}
+
+void credential_from_url(struct credential *c, const char *url)
+{
+ const char *at, *colon, *cp, *slash, *host, *proto_end;
+
+ credential_clear(c);
+
+ /*
+ * Match one of:
+ * (1) proto://<host>/...
+ * (2) proto://<user>@<host>/...
+ * (3) proto://<user>:<pass>@<host>/...
+ */
+ proto_end = strstr(url, "://");
+ if (!proto_end)
+ return;
+ cp = proto_end + 3;
+ at = strchr(cp, '@');
+ colon = strchr(cp, ':');
+ slash = strchrnul(cp, '/');
+
+ if (!at || slash <= at) {
+ /* Case (1) */
+ host = cp;
+ }
+ else if (!colon || at <= colon) {
+ /* Case (2) */
+ c->username = url_decode_mem(cp, at - cp);
+ host = at + 1;
+ } else {
+ /* Case (3) */
+ c->username = url_decode_mem(cp, colon - cp);
+ c->password = url_decode_mem(colon + 1, at - (colon + 1));
+ host = at + 1;
+ }
+
+ if (proto_end - url > 0)
+ c->protocol = xmemdupz(url, proto_end - url);
+ if (slash - host > 0)
+ c->host = url_decode_mem(host, slash - host);
+ /* Trim leading and trailing slashes from path */
+ while (*slash == '/')
+ slash++;
+ if (*slash) {
+ char *p;
+ c->path = url_decode(slash);
+ p = c->path + strlen(c->path) - 1;
+ while (p > c->path && *p == '/')
+ *p-- = '\0';
+ }
+}
diff --git a/credential.h b/credential.h
new file mode 100644
index 0000000000..96ea41bd1c
--- /dev/null
+++ b/credential.h
@@ -0,0 +1,33 @@
+#ifndef CREDENTIAL_H
+#define CREDENTIAL_H
+
+#include "string-list.h"
+
+struct credential {
+ struct string_list helpers;
+ unsigned approved:1,
+ configured:1,
+ use_http_path:1;
+
+ char *username;
+ char *password;
+ char *protocol;
+ char *host;
+ char *path;
+};
+
+#define CREDENTIAL_INIT { STRING_LIST_INIT_DUP }
+
+void credential_init(struct credential *);
+void credential_clear(struct credential *);
+
+void credential_fill(struct credential *);
+void credential_approve(struct credential *);
+void credential_reject(struct credential *);
+
+int credential_read(struct credential *, FILE *);
+void credential_from_url(struct credential *, const char *url);
+int credential_match(const struct credential *have,
+ const struct credential *want);
+
+#endif /* CREDENTIAL_H */
diff --git a/csum-file.c b/csum-file.c
index fc97d6e045..53f5375b6c 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -158,6 +158,26 @@ struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp
return f;
}
+void sha1file_checkpoint(struct sha1file *f, struct sha1file_checkpoint *checkpoint)
+{
+ sha1flush(f);
+ checkpoint->offset = f->total;
+ checkpoint->ctx = f->ctx;
+}
+
+int sha1file_truncate(struct sha1file *f, struct sha1file_checkpoint *checkpoint)
+{
+ off_t offset = checkpoint->offset;
+
+ if (ftruncate(f->fd, offset) ||
+ lseek(f->fd, offset, SEEK_SET) != offset)
+ return -1;
+ f->total = offset;
+ f->ctx = checkpoint->ctx;
+ f->offset = 0; /* sha1flush() was called in checkpoint */
+ return 0;
+}
+
void crc32_begin(struct sha1file *f)
{
f->crc32 = crc32(0, NULL, 0);
diff --git a/csum-file.h b/csum-file.h
index 6a7967c6bf..3b540bdc21 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -17,6 +17,15 @@ struct sha1file {
unsigned char buffer[8192];
};
+/* Checkpoint */
+struct sha1file_checkpoint {
+ off_t offset;
+ git_SHA_CTX ctx;
+};
+
+extern void sha1file_checkpoint(struct sha1file *, struct sha1file_checkpoint *);
+extern int sha1file_truncate(struct sha1file *, struct sha1file_checkpoint *);
+
/* sha1close flags */
#define CSUM_CLOSE 1
#define CSUM_FSYNC 2
diff --git a/dir.c b/dir.c
index 6c0d782579..0a78d00b54 100644
--- a/dir.c
+++ b/dir.c
@@ -968,34 +968,34 @@ static int read_directory_recursive(struct dir_struct *dir,
{
DIR *fdir = opendir(*base ? base : ".");
int contents = 0;
+ struct dirent *de;
+ char path[PATH_MAX + 1];
- if (fdir) {
- struct dirent *de;
- char path[PATH_MAX + 1];
- memcpy(path, base, baselen);
-
- while ((de = readdir(fdir)) != NULL) {
- int len;
- switch (treat_path(dir, de, path, sizeof(path),
- baselen, simplify, &len)) {
- case path_recurse:
- contents += read_directory_recursive
- (dir, path, len, 0, simplify);
- continue;
- case path_ignored:
- continue;
- case path_handled:
- break;
- }
- contents++;
- if (check_only)
- goto exit_early;
- else
- dir_add_name(dir, path, len);
+ if (!fdir)
+ return 0;
+
+ memcpy(path, base, baselen);
+
+ while ((de = readdir(fdir)) != NULL) {
+ int len;
+ switch (treat_path(dir, de, path, sizeof(path),
+ baselen, simplify, &len)) {
+ case path_recurse:
+ contents += read_directory_recursive(dir, path, len, 0, simplify);
+ continue;
+ case path_ignored:
+ continue;
+ case path_handled:
+ break;
}
-exit_early:
- closedir(fdir);
+ contents++;
+ if (check_only)
+ goto exit_early;
+ else
+ dir_add_name(dir, path, len);
}
+exit_early:
+ closedir(fdir);
return contents;
}
diff --git a/environment.c b/environment.c
index 0bee6a7a88..c93b8f44df 100644
--- a/environment.c
+++ b/environment.c
@@ -9,6 +9,7 @@
*/
#include "cache.h"
#include "refs.h"
+#include "fmt-merge-msg.h"
char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
@@ -59,7 +60,9 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
char *notes_ref_name;
int grafts_replace_parents = 1;
int core_apply_sparse_checkout;
+int merge_log_config = -1;
struct startup_info *startup_info;
+unsigned long pack_size_limit_cfg;
/* Parallel index stat data preload? */
int core_preload_index = 0;
diff --git a/fast-import.c b/fast-import.c
index b59e7db6a6..4b9c4b73a0 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1143,17 +1143,11 @@ static int store_object(
return 0;
}
-static void truncate_pack(off_t to, git_SHA_CTX *ctx)
+static void truncate_pack(struct sha1file_checkpoint *checkpoint)
{
- if (ftruncate(pack_data->pack_fd, to)
- || lseek(pack_data->pack_fd, to, SEEK_SET) != to)
+ if (sha1file_truncate(pack_file, checkpoint))
die_errno("cannot truncate pack to skip duplicate");
- pack_size = to;
-
- /* yes this is a layering violation */
- pack_file->total = to;
- pack_file->offset = 0;
- pack_file->ctx = *ctx;
+ pack_size = checkpoint->offset;
}
static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
@@ -1166,8 +1160,8 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
unsigned long hdrlen;
off_t offset;
git_SHA_CTX c;
- git_SHA_CTX pack_file_ctx;
git_zstream s;
+ struct sha1file_checkpoint checkpoint;
int status = Z_OK;
/* Determine if we should auto-checkpoint. */
@@ -1175,11 +1169,8 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
|| (pack_size + 60 + len) < pack_size)
cycle_packfile();
- offset = pack_size;
-
- /* preserve the pack_file SHA1 ctx in case we have to truncate later */
- sha1flush(pack_file);
- pack_file_ctx = pack_file->ctx;
+ sha1file_checkpoint(pack_file, &checkpoint);
+ offset = checkpoint.offset;
hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
if (out_sz <= hdrlen)
@@ -1245,14 +1236,14 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
if (e->idx.offset) {
duplicate_count_by_type[OBJ_BLOB]++;
- truncate_pack(offset, &pack_file_ctx);
+ truncate_pack(&checkpoint);
} else if (find_sha1_pack(sha1, packed_git)) {
e->type = OBJ_BLOB;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
duplicate_count_by_type[OBJ_BLOB]++;
- truncate_pack(offset, &pack_file_ctx);
+ truncate_pack(&checkpoint);
} else {
e->depth = 0;
@@ -2173,6 +2164,11 @@ static uintmax_t do_change_note_fanout(
if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) {
/* This is a note entry */
+ if (fanout == 0xff) {
+ /* Counting mode, no rename */
+ num_notes++;
+ continue;
+ }
construct_path_with_fanout(hex_sha1, fanout, realpath);
if (!strcmp(fullpath, realpath)) {
/* Note entry is in correct location */
@@ -2379,7 +2375,7 @@ static void file_change_cr(struct branch *b, int rename)
leaf.tree);
}
-static void note_change_n(struct branch *b, unsigned char old_fanout)
+static void note_change_n(struct branch *b, unsigned char *old_fanout)
{
const char *p = command_buf.buf + 2;
static struct strbuf uq = STRBUF_INIT;
@@ -2390,6 +2386,23 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
uint16_t inline_data = 0;
unsigned char new_fanout;
+ /*
+ * When loading a branch, we don't traverse its tree to count the real
+ * number of notes (too expensive to do this for all non-note refs).
+ * This means that recently loaded notes refs might incorrectly have
+ * b->num_notes == 0, and consequently, old_fanout might be wrong.
+ *
+ * Fix this by traversing the tree and counting the number of notes
+ * when b->num_notes == 0. If the notes tree is truly empty, the
+ * calculation should not take long.
+ */
+ if (b->num_notes == 0 && *old_fanout == 0) {
+ /* Invoke change_note_fanout() in "counting mode". */
+ b->num_notes = change_note_fanout(&b->branch_tree, 0xff);
+ *old_fanout = convert_num_notes_to_fanout(b->num_notes);
+ }
+
+ /* Now parse the notemodify command. */
/* <dataref> or 'inline' */
if (*p == ':') {
char *x;
@@ -2450,7 +2463,7 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
typename(type), command_buf.buf);
}
- construct_path_with_fanout(sha1_to_hex(commit_sha1), old_fanout, path);
+ construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path);
if (tree_content_remove(&b->branch_tree, path, NULL))
b->num_notes--;
@@ -2637,7 +2650,7 @@ static void parse_new_commit(void)
else if (!prefixcmp(command_buf.buf, "C "))
file_change_cr(b, 0);
else if (!prefixcmp(command_buf.buf, "N "))
- note_change_n(b, prev_fanout);
+ note_change_n(b, &prev_fanout);
else if (!strcmp("deleteall", command_buf.buf))
file_change_deleteall(b);
else if (!prefixcmp(command_buf.buf, "ls "))
diff --git a/fmt-merge-msg.h b/fmt-merge-msg.h
new file mode 100644
index 0000000000..b28d3a6113
--- /dev/null
+++ b/fmt-merge-msg.h
@@ -0,0 +1,7 @@
+#ifndef FMT_MERGE_MSG_H
+#define FMT_MERGE_MSG_H
+
+extern int merge_log_config;
+extern int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+
+#endif /* FMT_MERGE_MSG_H */
diff --git a/git-am.sh b/git-am.sh
index 9042432e23..1c13b13991 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -530,7 +530,6 @@ else
echo "$sign" >"$dotest/sign"
echo "$utf8" >"$dotest/utf8"
echo "$keep" >"$dotest/keep"
- echo "$keepcr" >"$dotest/keepcr"
echo "$scissors" >"$dotest/scissors"
echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
echo "$GIT_QUIET" >"$dotest/quiet"
@@ -576,12 +575,6 @@ if test "$(cat "$dotest/keep")" = t
then
keep=-k
fi
-case "$(cat "$dotest/keepcr")" in
-t)
- keepcr=--keep-cr ;;
-f)
- keepcr=--no-keep-cr ;;
-esac
case "$(cat "$dotest/scissors")" in
t)
scissors=--scissors ;;
diff --git a/git-compat-util.h b/git-compat-util.h
index 5ef8ff76f6..8f3972cd32 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -116,7 +116,12 @@
#else
#include <poll.h>
#endif
-#ifndef __MINGW32__
+#if defined(__MINGW32__)
+/* pull in Windows compatibility stuff */
+#include "compat/mingw.h"
+#elif defined(_MSC_VER)
+#include "compat/msvc.h"
+#else
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/socket.h>
@@ -130,6 +135,7 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
+#include <sys/un.h>
#ifndef NO_INTTYPES_H
#include <inttypes.h>
#else
@@ -145,12 +151,6 @@
#include <grp.h>
#define _ALL_SOURCE 1
#endif
-#else /* __MINGW32__ */
-/* pull in Windows compatibility stuff */
-#include "compat/mingw.h"
-#endif /* __MINGW32__ */
-#ifdef _MSC_VER
-#include "compat/msvc.h"
#endif
#ifndef NO_LIBGEN_H
@@ -219,7 +219,7 @@ extern char *gitbasename(char *);
#define find_last_dir_sep(path) strrchr(path, '/')
#endif
-#if __HP_cc >= 61000
+#if defined(__HP_cc) && (__HP_cc >= 61000)
#define NORETURN __attribute__((noreturn))
#define NORETURN_PTR
#elif defined(__GNUC__) && !defined(NO_NORETURN)
@@ -351,6 +351,8 @@ extern size_t gitstrlcpy(char *, const char *, size_t);
#ifdef NO_STRTOUMAX
#define strtoumax gitstrtoumax
extern uintmax_t gitstrtoumax(const char *, char **, int);
+#define strtoimax gitstrtoimax
+extern intmax_t gitstrtoimax(const char *, char **, int);
#endif
#ifdef NO_STRTOK_R
diff --git a/git-gui/.gitattributes b/git-gui/.gitattributes
index f96112d47f..33d07c06bd 100644
--- a/git-gui/.gitattributes
+++ b/git-gui/.gitattributes
@@ -1,3 +1,4 @@
+* whitespace=indent-with-non-tab,trailing-space,space-before-tab,tabwidth=4
* encoding=US-ASCII
git-gui.sh encoding=UTF-8
/po/*.po encoding=UTF-8
diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN
index 1fb4d9b4b7..65709437ff 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.13.GITGUI
+DEF_VER=0.16.GITGUI
LF='
'
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index f8971603f7..ba4e5c1330 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -299,7 +299,9 @@ proc is_config_true {name} {
global repo_config
if {[catch {set v $repo_config($name)}]} {
return 0
- } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
+ }
+ set v [string tolower $v]
+ if {$v eq {} || $v eq {true} || $v eq {1} || $v eq {yes} || $v eq {on}} {
return 1
} else {
return 0
@@ -310,7 +312,9 @@ proc is_config_false {name} {
global repo_config
if {[catch {set v $repo_config($name)}]} {
return 0
- } elseif {$v eq {false} || $v eq {0} || $v eq {no}} {
+ }
+ set v [string tolower $v]
+ if {$v eq {false} || $v eq {0} || $v eq {no} || $v eq {off}} {
return 1
} else {
return 0
@@ -460,6 +464,35 @@ proc _which {what args} {
return {}
}
+# Test a file for a hashbang to identify executable scripts on Windows.
+proc is_shellscript {filename} {
+ if {![file exists $filename]} {return 0}
+ set f [open $filename r]
+ fconfigure $f -encoding binary
+ set magic [read $f 2]
+ close $f
+ return [expr {$magic eq "#!"}]
+}
+
+# Run a command connected via pipes on stdout.
+# This is for use with textconv filters and uses sh -c "..." to allow it to
+# contain a command with arguments. On windows we must check for shell
+# scripts specifically otherwise just call the filter command.
+proc open_cmd_pipe {cmd path} {
+ global env
+ if {![file executable [shellpath]]} {
+ set exe [auto_execok [lindex $cmd 0]]
+ if {[is_shellscript [lindex $exe 0]]} {
+ set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path]
+ } else {
+ set run [concat $exe [lrange $cmd 1 end] $path]
+ }
+ } else {
+ set run [list [shellpath] -c "$cmd \"\$0\"" $path]
+ }
+ return [open |$run r]
+}
+
proc _lappend_nice {cmd_var} {
global _nice
upvar $cmd_var cmd
@@ -725,7 +758,10 @@ if {[is_Windows]} {
gitlogo put gray26 -to 5 15 11 16
gitlogo redither
- wm iconphoto . -default gitlogo
+ image create photo gitlogo32 -width 32 -height 32
+ gitlogo32 copy gitlogo -zoom 2 2
+
+ wm iconphoto . -default gitlogo gitlogo32
}
}
@@ -846,6 +882,7 @@ set default_config(gui.fastcopyblame) false
set default_config(gui.copyblamethreshold) 40
set default_config(gui.blamehistoryctx) 7
set default_config(gui.diffcontext) 5
+set default_config(gui.diffopts) {}
set default_config(gui.commitmsgwidth) 75
set default_config(gui.newbranchtemplate) {}
set default_config(gui.spellingdictionary) {}
@@ -859,6 +896,7 @@ set font_descs {
{fontui font_ui {mc "Main Font"}}
{fontdiff font_diff {mc "Diff/Console Font"}}
}
+set default_config(gui.stageuntracked) ask
######################################################################
##
@@ -1060,6 +1098,10 @@ git-version proc _parse_config {arr_name args} {
} else {
set arr($name) $value
}
+ } elseif {[regexp {^([^\n]+)$} $line line name]} {
+ # no value given, but interpreting them as
+ # boolean will be handled as true
+ set arr($name) {}
}
}
}
@@ -1075,6 +1117,10 @@ git-version proc _parse_config {arr_name args} {
} else {
set arr($name) $value
}
+ } elseif {[regexp {^([^=]+)$} $line line name]} {
+ # no value given, but interpreting them as
+ # boolean will be handled as true
+ set arr($name) {}
}
}
close $fd_rc
@@ -2474,6 +2520,7 @@ proc toggle_or_diff {w x y} {
[concat $after [list ui_ready]]
}
} else {
+ set selected_paths($path) 1
show_diff $path $w $lno
}
}
@@ -3362,6 +3409,7 @@ foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 gr
$ui_diff tag configure clri3$n -background $c
}
$ui_diff tag configure clr1 -font font_diffbold
+$ui_diff tag configure clr4 -underline 1
$ui_diff tag conf d_info -foreground blue -font font_diffbold
@@ -3878,7 +3926,7 @@ after 1 {
$ui_comm configure -state disabled -background gray
}
}
-if {[is_enabled multicommit]} {
+if {[is_enabled multicommit] && ![is_config_false gui.gcwarning]} {
after 1000 hint_gc
}
if {[is_enabled retcode]} {
diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl
index 691941e959..324f7744c4 100644
--- a/git-gui/lib/blame.tcl
+++ b/git-gui/lib/blame.tcl
@@ -219,7 +219,8 @@ constructor new {i_commit i_path i_jump} {
eval grid $w_columns $w.file_pane.out.sby -sticky nsew
grid conf \
$w.file_pane.out.sbx \
- -column [expr {[llength $w_columns] - 1}] \
+ -column 0 \
+ -columnspan [expr {[llength $w_columns] + 1}] \
-sticky we
grid columnconfigure \
$w.file_pane.out \
@@ -229,12 +230,14 @@ constructor new {i_commit i_path i_jump} {
set finder [::searchbar::new \
$w.file_pane.out.ff $w_file \
- -column [expr {[llength $w_columns] - 1}] \
+ -column 0 \
+ -columnspan [expr {[llength $w_columns] + 1}] \
]
set gotoline [::linebar::new \
$w.file_pane.out.lf $w_file \
- -column [expr {[llength $w_columns] - 1}] \
+ -column 0 \
+ -columnspan [expr {[llength $w_columns] + 1}] \
]
set w_cviewer $w.file_pane.cm.t
@@ -473,14 +476,7 @@ method _load {jump} {
}
if {$commit eq {}} {
if {$do_textconv ne 0} {
- # Run textconv with sh -c "..." to allow it to
- # contain command + arguments. On windows, just
- # call the filter command.
- if {![file executable [shellpath]]} {
- set fd [open |[linsert $textconv end $path] r]
- } else {
- set fd [open |[list [shellpath] -c "$textconv \"\$0\"" $path] r]
- }
+ set fd [open_cmd_pipe $textconv $path]
} else {
set fd [open $path r]
}
@@ -572,7 +568,11 @@ method _read_file {fd jump} {
foreach i $w_columns {$i conf -state disabled}
if {[eof $fd]} {
- close $fd
+ fconfigure $fd -blocking 1; # enable error reporting on close
+ if {[catch {close $fd} err]} {
+ tk_messageBox -icon error -title [mc Error] \
+ -message $err
+ }
# If we don't force Tk to update the widgets *right now*
# none of our jump commands will cause a change in the UI.
@@ -1062,7 +1062,7 @@ method _gitkcommit {} {
set radius [get_config gui.blamehistoryctx]
set cmdline [list --select-commit=$cmit]
- if {$radius > 0} {
+ if {$radius > 0} {
set author_time {}
set committer_time {}
@@ -1170,7 +1170,7 @@ method _read_diff_load_commit {fd cparent new_path tline} {
}
if {[eof $fd]} {
- close $fd;
+ close $fd
set current_fd {}
_load_new_commit $this \
@@ -1201,6 +1201,7 @@ method _open_tooltip {cur_w} {
_hide_tooltip $this
set tooltip_wm [toplevel $cur_w.tooltip -borderwidth 1]
+ catch {wm attributes $tooltip_wm -type tooltip}
wm overrideredirect $tooltip_wm 1
wm transient $tooltip_wm [winfo toplevel $cur_w]
set tooltip_t $tooltip_wm.label
diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl
index a8c6223511..0328338fda 100644
--- a/git-gui/lib/browser.tcl
+++ b/git-gui/lib/browser.tcl
@@ -26,8 +26,14 @@ constructor new {commit {path {}}} {
wm withdraw $top
wm title $top [append "[appname] ([reponame]): " [mc "File Browser"]]
+ if {$path ne {}} {
+ if {[string index $path end] ne {/}} {
+ append path /
+ }
+ }
+
set browser_commit $commit
- set browser_path $browser_commit:$path
+ set browser_path "$browser_commit:[escape_path $path]"
${NS}::label $w.path \
-textvariable @browser_path \
diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl
index 54c7957a66..6dae7937d5 100644
--- a/git-gui/lib/choose_rev.tcl
+++ b/git-gui/lib/choose_rev.tcl
@@ -497,6 +497,7 @@ method _open_tooltip {} {
if {$tooltip_wm eq {}} {
set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1]
+ catch {wm attributes $tooltip_wm -type tooltip}
wm overrideredirect $tooltip_wm 1
wm transient $tooltip_wm [winfo toplevel $w_list]
set tooltip_t $tooltip_wm.label
diff --git a/git-gui/lib/class.tcl b/git-gui/lib/class.tcl
index c27b71476a..f08506f383 100644
--- a/git-gui/lib/class.tcl
+++ b/git-gui/lib/class.tcl
@@ -138,6 +138,7 @@ proc make_dialog {t w args} {
upvar $t top $w pfx this this
global use_ttk
uplevel [linsert $args 0 make_toplevel $t $w]
+ catch {wm attributes $top -type dialog}
pave_toplevel $pfx
}
diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl
index 372bed9948..0d81432af5 100644
--- a/git-gui/lib/commit.tcl
+++ b/git-gui/lib/commit.tcl
@@ -263,7 +263,9 @@ proc commit_commitmsg {curHEAD msg_p} {
global is_detached repo_config
global pch_error
- if {$is_detached && $repo_config(gui.warndetachedcommit)} {
+ if {$is_detached
+ && ![file exists [gitdir rebase-merge head-name]]
+ && [is_config_true gui.warndetachedcommit]} {
set msg [mc "You are about to commit on a detached head.\
This is a potentially dangerous thing to do because if you switch\
to another branch you will loose your changes and it can be difficult\
diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl
index cf8a95ec34..ec4405567a 100644
--- a/git-gui/lib/diff.tcl
+++ b/git-gui/lib/diff.tcl
@@ -309,6 +309,7 @@ proc start_show_diff {cont_info {add_opts {}}} {
lappend cmd -p
lappend cmd --color
+ set cmd [concat $cmd $repo_config(gui.diffopts)]
if {$repo_config(gui.diffcontext) >= 1} {
lappend cmd "-U$repo_config(gui.diffcontext)"
}
@@ -502,9 +503,9 @@ proc read_diff {fd conflict_size cont_info} {
foreach {posbegin colbegin posend colend} $markup {
set prefix clr
- foreach style [split $colbegin ";"] {
+ foreach style [lsort -integer [split $colbegin ";"]] {
if {$style eq "7"} {append prefix i; continue}
- if {$style < 30 || $style > 47} {continue}
+ if {$style != 4 && ($style < 30 || $style > 47)} {continue}
set a "$mark linestart + $posbegin chars"
set b "$mark linestart + $posend chars"
catch {$ui_diff tag add $prefix$style $a $b}
diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl
index e38b647b71..8efbbdde21 100644
--- a/git-gui/lib/index.tcl
+++ b/git-gui/lib/index.tcl
@@ -356,21 +356,33 @@ proc do_add_all {} {
global file_states
set paths [list]
- set unknown_paths [list]
+ set untracked_paths [list]
foreach path [array names file_states] {
switch -glob -- [lindex $file_states($path) 0] {
U? {continue}
?M -
?T -
?D {lappend paths $path}
- ?O {lappend unknown_paths $path}
+ ?O {lappend untracked_paths $path}
}
}
- if {[llength $unknown_paths]} {
- set reply [ask_popup [mc "There are unknown files do you also want
-to stage those?"]]
+ if {[llength $untracked_paths]} {
+ set reply 0
+ switch -- [get_config gui.stageuntracked] {
+ no {
+ set reply 0
+ }
+ yes {
+ set reply 1
+ }
+ ask -
+ default {
+ set reply [ask_popup [mc "Stage %d untracked files?" \
+ [llength $untracked_paths]]]
+ }
+ }
if {$reply} {
- set paths [concat $paths $unknown_paths]
+ set paths [concat $paths $untracked_paths]
}
}
add_helper {Adding all changed files} $paths
diff --git a/git-gui/lib/line.tcl b/git-gui/lib/line.tcl
index c160012de6..a026de954c 100644
--- a/git-gui/lib/line.tcl
+++ b/git-gui/lib/line.tcl
@@ -15,7 +15,7 @@ constructor new {i_w i_text args} {
${NS}::frame $w
${NS}::label $w.l -text [mc "Goto Line:"]
- entry $w.ent \
+ tentry $w.ent \
-textvariable ${__this}::linenum \
-background lightgreen \
-validate key \
diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl
index 3807c8d283..0cf1da1d7e 100644
--- a/git-gui/lib/option.tcl
+++ b/git-gui/lib/option.tcl
@@ -153,9 +153,12 @@ proc do_options {} {
{i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}}
{i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}}
{i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
+ {t gui.diffopts {mc "Additional Diff Parameters"}}
{i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}}
{t gui.newbranchtemplate {mc "New Branch Name Template"}}
{c gui.encoding {mc "Default File Contents Encoding"}}
+ {b gui.warndetachedcommit {mc "Warn before committing to a detached head"}}
+ {s gui.stageuntracked {mc "Staging of untracked files"} {list "yes" "no" "ask"}}
} {
set type [lindex $option 0]
set name [lindex $option 1]
@@ -208,6 +211,23 @@ proc do_options {} {
}
pack $w.$f.$optid -side top -anchor w -fill x
}
+ s {
+ set opts [eval [lindex $option 3]]
+ ${NS}::frame $w.$f.$optid
+ ${NS}::label $w.$f.$optid.l -text "$text:"
+ if {$use_ttk} {
+ ttk::combobox $w.$f.$optid.v \
+ -textvariable ${f}_config_new($name) \
+ -values $opts -state readonly
+ } else {
+ eval tk_optionMenu $w.$f.$optid.v \
+ ${f}_config_new($name) \
+ $opts
+ }
+ pack $w.$f.$optid.l -side left -anchor w -fill x
+ pack $w.$f.$optid.v -side right -anchor e -padx 5
+ pack $w.$f.$optid -side top -anchor w -fill x
+ }
}
}
}
diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl
index ef3486f083..ef1e55521d 100644
--- a/git-gui/lib/search.tcl
+++ b/git-gui/lib/search.tcl
@@ -7,9 +7,16 @@ field w
field ctext
field searchstring {}
-field casesensitive 1
+field regexpsearch
+field default_regexpsearch
+field casesensitive
+field default_casesensitive
+field smartcase
field searchdirn -forwards
+field history
+field history_index
+
field smarktop
field smarkbot
@@ -18,15 +25,37 @@ constructor new {i_w i_text args} {
set w $i_w
set ctext $i_text
+ set default_regexpsearch [is_config_true gui.search.regexp]
+ switch -- [get_config gui.search.case] {
+ no {
+ set default_casesensitive 0
+ set smartcase 0
+ }
+ smart {
+ set default_casesensitive 0
+ set smartcase 1
+ }
+ yes -
+ default {
+ set default_casesensitive 1
+ set smartcase 0
+ }
+ }
+
+ set history [list]
+
${NS}::frame $w
${NS}::label $w.l -text [mc Find:]
- entry $w.ent -textvariable ${__this}::searchstring -background lightgreen
+ tentry $w.ent -textvariable ${__this}::searchstring -background lightgreen
${NS}::button $w.bn -text [mc Next] -command [cb find_next]
${NS}::button $w.bp -text [mc Prev] -command [cb find_prev]
- ${NS}::checkbutton $w.cs -text [mc Case-Sensitive] \
+ ${NS}::checkbutton $w.re -text [mc RegExp] \
+ -variable ${__this}::regexpsearch -command [cb _incrsearch]
+ ${NS}::checkbutton $w.cs -text [mc Case] \
-variable ${__this}::casesensitive -command [cb _incrsearch]
pack $w.l -side left
pack $w.cs -side right
+ pack $w.re -side right
pack $w.bp -side right
pack $w.bn -side right
pack $w.ent -side left -expand 1 -fill x
@@ -37,6 +66,8 @@ constructor new {i_w i_text args} {
trace add variable searchstring write [cb _incrsearch_cb]
bind $w.ent <Return> [cb find_next]
bind $w.ent <Shift-Return> [cb find_prev]
+ bind $w.ent <Key-Up> [cb _prev_search]
+ bind $w.ent <Key-Down> [cb _next_search]
bind $w <Destroy> [list delete_this $this]
return $this
@@ -45,6 +76,10 @@ constructor new {i_w i_text args} {
method show {} {
if {![visible $this]} {
grid $w
+ $w.ent delete 0 end
+ set regexpsearch $default_regexpsearch
+ set casesensitive $default_casesensitive
+ set history_index [llength $history]
}
focus -force $w.ent
}
@@ -53,6 +88,7 @@ method hide {} {
if {[visible $this]} {
focus $ctext
grid remove $w
+ _save_search $this
}
}
@@ -98,6 +134,9 @@ method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
upvar $mlenvar mlen
lappend cmd -count mlen
}
+ if {$regexpsearch} {
+ lappend cmd -regexp
+ }
if {!$casesensitive} {
lappend cmd -nocase
}
@@ -105,14 +144,16 @@ method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
set dir $searchdirn
}
lappend cmd $dir -- $searchstring
- if {$endbound ne {}} {
- set here [eval $cmd [list $start] [list $endbound]]
- } else {
- set here [eval $cmd [list $start]]
- if {$here eq {}} {
- set here [eval $cmd [_get_wrap_anchor $this $dir]]
+ if {[catch {
+ if {$endbound ne {}} {
+ set here [eval $cmd [list $start] [list $endbound]]
+ } else {
+ set here [eval $cmd [list $start]]
+ if {$here eq {}} {
+ set here [eval $cmd [_get_wrap_anchor $this $dir]]
+ }
}
- }
+ } err]} { set here {} }
return $here
}
@@ -126,17 +167,74 @@ method _incrsearch {} {
$ctext mark set anchor [_get_new_anchor $this]
}
if {$searchstring ne {}} {
+ if {$smartcase && [regexp {[[:upper:]]} $searchstring]} {
+ set casesensitive 1
+ }
set here [_do_search $this anchor mlen]
if {$here ne {}} {
$ctext see $here
$ctext tag remove sel 1.0 end
$ctext tag add sel $here "$here + $mlen c"
- $w.ent configure -background lightgreen
+ #$w.ent configure -background lightgreen
+ $w.ent state !pressed
_set_marks $this 1
} else {
- $w.ent configure -background lightpink
+ #$w.ent configure -background lightpink
+ $w.ent state pressed
}
+ } elseif {$smartcase} {
+ # clearing the field resets the smart case detection
+ set casesensitive 0
+ }
+}
+
+method _save_search {} {
+ if {$searchstring eq {}} {
+ return
+ }
+ if {[llength $history] > 0} {
+ foreach {s_regexp s_case s_expr} [lindex $history end] break
+ } else {
+ set s_regexp $regexpsearch
+ set s_case $casesensitive
+ set s_expr ""
+ }
+ if {$searchstring eq $s_expr} {
+ # update modes
+ set history [lreplace $history end end \
+ [list $regexpsearch $casesensitive $searchstring]]
+ } else {
+ lappend history [list $regexpsearch $casesensitive $searchstring]
+ }
+ set history_index [llength $history]
+}
+
+method _prev_search {} {
+ if {$history_index > 0} {
+ incr history_index -1
+ foreach {s_regexp s_case s_expr} [lindex $history $history_index] break
+ $w.ent delete 0 end
+ $w.ent insert 0 $s_expr
+ set regexpsearch $s_regexp
+ set casesensitive $s_case
+ }
+}
+
+method _next_search {} {
+ if {$history_index < [llength $history]} {
+ incr history_index
+ }
+ if {$history_index < [llength $history]} {
+ foreach {s_regexp s_case s_expr} [lindex $history $history_index] break
+ } else {
+ set s_regexp $default_regexpsearch
+ set s_case $default_casesensitive
+ set s_expr ""
}
+ $w.ent delete 0 end
+ $w.ent insert 0 $s_expr
+ set regexpsearch $s_regexp
+ set casesensitive $s_case
}
method find_prev {} {
@@ -149,6 +247,7 @@ method find_next {{dir -forwards}} {
set searchdirn $dir
$ctext mark unset anchor
if {$searchstring ne {}} {
+ _save_search $this
set start [_get_new_anchor $this]
if {$dir eq "-forwards"} {
set start "$start + 1c"
diff --git a/git-gui/lib/sshkey.tcl b/git-gui/lib/sshkey.tcl
index 5f75bc96b3..aa6457bbb5 100644
--- a/git-gui/lib/sshkey.tcl
+++ b/git-gui/lib/sshkey.tcl
@@ -117,7 +117,7 @@ proc read_sshkey_output {fd w} {
} else {
set finfo [find_ssh_key]
if {$finfo eq {}} {
- set sshkey_title [mc "Generation succeded, but no keys found."]
+ set sshkey_title [mc "Generation succeeded, but no keys found."]
$w.contents insert end $sshkey_output
} else {
set sshkey_title [mc "Your key is in: %s" [lindex $finfo 0]]
diff --git a/git-gui/lib/themed.tcl b/git-gui/lib/themed.tcl
index 1da458673b..8b88d3678b 100644
--- a/git-gui/lib/themed.tcl
+++ b/git-gui/lib/themed.tcl
@@ -23,10 +23,59 @@ proc InitTheme {} {
ttk::style configure Gold.TFrame -background gold -relief flat
# listboxes should have a theme border so embed in ttk::frame
ttk::style layout SListbox.TFrame {
- SListbox.Frame.Entry.field -sticky news -border true -children {
- SListbox.Frame.padding -sticky news
- }
- }
+ SListbox.Frame.Entry.field -sticky news -border true -children {
+ SListbox.Frame.padding -sticky news
+ }
+ }
+
+ # Handle either current Tk or older versions of 8.5
+ if {[catch {set theme [ttk::style theme use]}]} {
+ set theme $::ttk::currentTheme
+ }
+
+ if {[lsearch -exact {default alt classic clam} $theme] != -1} {
+ # Simple override of standard ttk::entry to change the field
+ # packground according to a state flag. We should use 'user1'
+ # but not all versions of 8.5 support that so make use of 'pressed'
+ # which is not normally in use for entry widgets.
+ ttk::style layout Edged.Entry [ttk::style layout TEntry]
+ ttk::style map Edged.Entry {*}[ttk::style map TEntry]
+ ttk::style configure Edged.Entry {*}[ttk::style configure TEntry] \
+ -fieldbackground lightgreen
+ ttk::style map Edged.Entry -fieldbackground {
+ {pressed !disabled} lightpink
+ }
+ } else {
+ # For fancier themes, in particular the Windows ones, the field
+ # element may not support changing the background color. So instead
+ # override the fill using the default fill element. If we overrode
+ # the vista theme field element we would loose the themed border
+ # of the widget.
+ catch {
+ ttk::style element create color.fill from default
+ }
+
+ ttk::style layout Edged.Entry {
+ Edged.Entry.field -sticky nswe -border 0 -children {
+ Edged.Entry.border -sticky nswe -border 1 -children {
+ Edged.Entry.padding -sticky nswe -children {
+ Edged.Entry.color.fill -sticky nswe -children {
+ Edged.Entry.textarea -sticky nswe
+ }
+ }
+ }
+ }
+ }
+
+ ttk::style configure Edged.Entry {*}[ttk::style configure TEntry] \
+ -background lightgreen -padding 0 -borderwidth 0
+ ttk::style map Edged.Entry {*}[ttk::style map TEntry] \
+ -background {{pressed !disabled} lightpink}
+ }
+
+ if {[lsearch [bind . <<ThemeChanged>>] InitTheme] == -1} {
+ bind . <<ThemeChanged>> +[namespace code [list InitTheme]]
+ }
}
proc gold_frame {w args} {
@@ -74,6 +123,7 @@ proc paddedlabel {w args} {
# place a themed frame over the surface.
proc Dialog {w args} {
eval [linsert $args 0 toplevel $w -class Dialog]
+ catch {wm attributes $w -type dialog}
pave_toplevel $w
return $w
}
@@ -143,6 +193,47 @@ proc tspinbox {w args} {
}
}
+proc tentry {w args} {
+ global use_ttk
+ if {$use_ttk} {
+ InitTheme
+ ttk::entry $w -style Edged.Entry
+ } else {
+ entry $w
+ }
+
+ rename $w _$w
+ interp alias {} $w {} tentry_widgetproc $w
+ eval [linsert $args 0 tentry_widgetproc $w configure]
+ return $w
+}
+proc tentry_widgetproc {w cmd args} {
+ global use_ttk
+ switch -- $cmd {
+ state {
+ if {$use_ttk} {
+ return [uplevel 1 [list _$w $cmd] $args]
+ } else {
+ if {[lsearch -exact $args pressed] != -1} {
+ _$w configure -background lightpink
+ } else {
+ _$w configure -background lightgreen
+ }
+ }
+ }
+ configure {
+ if {$use_ttk} {
+ if {[set n [lsearch -exact $args -background]] != -1} {
+ set args [lreplace $args $n [incr n]]
+ if {[llength $args] == 0} {return}
+ }
+ }
+ return [uplevel 1 [list _$w $cmd] $args]
+ }
+ default { return [uplevel 1 [list _$w $cmd] $args] }
+ }
+}
+
# Tk 8.6 provides a standard font selection dialog. This uses the native
# dialogs on Windows and MacOSX or a standard Tk dialog on X11.
proc tchoosefont {w title familyvar sizevar} {
diff --git a/git-gui/lib/tools.tcl b/git-gui/lib/tools.tcl
index 95e6e5553e..6ec94113db 100644
--- a/git-gui/lib/tools.tcl
+++ b/git-gui/lib/tools.tcl
@@ -87,8 +87,14 @@ proc tools_exec {fullname} {
return
}
} elseif {[is_config_true "guitool.$fullname.confirm"]} {
- if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} {
- return
+ if {[is_config_true "guitool.$fullname.needsfile"]} {
+ if {[ask_popup [mc "Are you sure you want to run %1\$s on file \"%2\$s\"?" $fullname $current_diff_path]] ne {yes}} {
+ return
+ }
+ } else {
+ if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} {
+ return
+ }
}
}
diff --git a/git-gui/lib/transport.tcl b/git-gui/lib/transport.tcl
index 7fad9b7d91..e5d211edea 100644
--- a/git-gui/lib/transport.tcl
+++ b/git-gui/lib/transport.tcl
@@ -124,6 +124,7 @@ proc do_push_anywhere {} {
set w .push_setup
toplevel $w
+ catch {wm attributes $w -type dialog}
wm withdraw $w
wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
pave_toplevel $w
diff --git a/git-pull.sh b/git-pull.sh
index 9868a0bfb4..d8b64d7a67 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -44,6 +44,10 @@ merge_args=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short="${curr_branch#refs/heads/}"
rebase=$(git config --bool branch.$curr_branch_short.rebase)
+if test -z "$rebase"
+then
+ rebase=$(git config --bool pull.rebase)
+fi
dry_run=
while :
do
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 804001bb4e..5812222eb9 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -143,6 +143,21 @@ die_with_patch () {
die "$2"
}
+exit_with_patch () {
+ echo "$1" > "$state_dir"/stopped-sha
+ make_patch $1
+ git rev-parse --verify HEAD > "$amend"
+ warn "You can amend the commit now, with"
+ warn
+ warn " git commit --amend"
+ warn
+ warn "Once you are satisfied with your changes, run"
+ warn
+ warn " git rebase --continue"
+ warn
+ exit $2
+}
+
die_abort () {
rm -rf "$state_dir"
die "$1"
@@ -408,7 +423,13 @@ do_next () {
mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
- git commit --amend --no-post-rewrite
+ git commit --amend --no-post-rewrite || {
+ warn "Could not amend commit after successfully picking $sha1... $rest"
+ warn "This is most likely due to an empty commit message, or the pre-commit hook"
+ warn "failed. If the pre-commit hook failed, you may need to resolve the issue before"
+ warn "you are able to reword the commit."
+ exit_with_patch $sha1 1
+ }
record_in_rewritten $sha1
;;
edit|e)
@@ -417,19 +438,8 @@ do_next () {
mark_action_done
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
- echo "$sha1" > "$state_dir"/stopped-sha
- make_patch $sha1
- git rev-parse --verify HEAD > "$amend"
warn "Stopped at $sha1... $rest"
- warn "You can amend the commit now, with"
- warn
- warn " git commit --amend"
- warn
- warn "Once you are satisfied with your changes, run"
- warn
- warn " git rebase --continue"
- warn
- exit 0
+ exit_with_patch $sha1 0
;;
squash|s|fixup|f)
case "$command" in
diff --git a/git-request-pull.sh b/git-request-pull.sh
index fc080cc5e4..c6a5b7a6b3 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -35,44 +35,77 @@ do
shift
done
-base=$1
-url=$2
-head=${3-HEAD}
+base=$1 url=$2 head=${3-HEAD} status=0 branch_name=
-[ "$base" ] || usage
-[ "$url" ] || usage
+headref=$(git symbolic-ref -q "$head")
+if git show-ref -q --verify "$headref"
+then
+ branch_name=${headref#refs/heads/}
+ if test "z$branch_name" = "z$headref" ||
+ ! git config "branch.$branch_name.description" >/dev/null
+ then
+ branch_name=
+ fi
+fi
+
+tag_name=$(git describe --exact "$head^0" 2>/dev/null)
-baserev=`git rev-parse --verify "$base"^0` &&
-headrev=`git rev-parse --verify "$head"^0` || exit
+test -n "$base" && test -n "$url" || usage
+baserev=$(git rev-parse --verify "$base"^0) &&
+headrev=$(git rev-parse --verify "$head"^0) || exit
-merge_base=`git merge-base $baserev $headrev` ||
+merge_base=$(git merge-base $baserev $headrev) ||
die "fatal: No commits in common between $base and $head"
-branch=$(git ls-remote "$url" \
- | sed -n -e "/^$headrev refs.heads./{
- s/^.* refs.heads.//
- p
- q
- }")
+find_matching_branch="/^$headrev "'refs\/heads\//{
+ s/^.* refs\/heads\///
+ p
+ q
+}'
+branch=$(git ls-remote "$url" | sed -n -e "$find_matching_branch")
url=$(git ls-remote --get-url "$url")
-if [ -z "$branch" ]; then
- echo "warn: No branch of $url is at:" >&2
- git log --max-count=1 --pretty='tformat:warn: %h: %s' $headrev >&2
- echo "warn: Are you sure you pushed $head there?" >&2
- echo >&2
- echo >&2
- branch=..BRANCH.NOT.VERIFIED..
- status=1
-fi
git show -s --format='The following changes since commit %H:
%s (%ci)
-are available in the git repository at:' $baserev &&
-echo " $url $branch" &&
-echo &&
+are available in the git repository at:
+' $baserev &&
+echo " $url${branch+ $branch}" &&
+git show -s --format='
+for you to fetch changes up to %H:
+
+ %s (%ci)
+
+----------------------------------------------------------------' $headrev &&
+
+if test -n "$branch_name"
+then
+ echo "(from the branch description for $branch local branch)"
+ echo
+ git config "branch.$branch_name.description"
+fi &&
+
+if test -n "$tag_name"
+then
+ git cat-file tag "$tag_name" |
+ sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p
+ echo
+fi &&
+
+if test -n "$branch_name" || test -n "$tag_name"
+then
+ echo "----------------------------------------------------------------"
+fi &&
git shortlog ^$baserev $headrev &&
-git diff -M --stat --summary $patch $merge_base..$headrev || exit
+git diff -M --stat --summary $patch $merge_base..$headrev || status=1
+
+if test -z "$branch"
+then
+ echo "warn: No branch of $url is at:" >&2
+ git show -s --format='warn: %h: %s' $headrev >&2
+ echo "warn: Are you sure you pushed '$head' there?" >&2
+ status=1
+fi
exit $status
diff --git a/git-svn.perl b/git-svn.perl
index e30df22d89..eeb83d3759 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -5389,7 +5389,7 @@ sub apply_diff {
$self->{mergeinfo});
}
$self->rmdirs if $_rmdir;
- if (@$mods == 0) {
+ if (@$mods == 0 && !defined($self->{mergeinfo})) {
$self->abort_edit;
} else {
$self->close_edit;
diff --git a/git.c b/git.c
index fa918b5652..fb9029cbf1 100644
--- a/git.c
+++ b/git.c
@@ -434,6 +434,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "update-ref", cmd_update_ref, RUN_SETUP },
{ "update-server-info", cmd_update_server_info, RUN_SETUP },
{ "upload-archive", cmd_upload_archive },
+ { "upload-archive--writer", cmd_upload_archive_writer },
{ "var", cmd_var, RUN_SETUP_GENTLY },
{ "verify-pack", cmd_verify_pack },
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 4cde0c493b..64ef3c4013 100755
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -2,20 +2,16 @@
# Tcl ignores the next line -*- tcl -*- \
exec wish "$0" -- "$@"
-# Copyright © 2005-2009 Paul Mackerras. All rights reserved.
+# Copyright © 2005-2011 Paul Mackerras. All rights reserved.
# This program is free software; it may be used, copied, modified
# and distributed under the terms of the GNU General Public Licence,
# either version 2, or (at your option) any later version.
package require Tk
-proc gitdir {} {
- global env
- if {[info exists env(GIT_DIR)]} {
- return $env(GIT_DIR)
- } else {
- return [exec git rev-parse --git-dir]
- }
+proc hasworktree {} {
+ return [expr {[exec git rev-parse --is-bare-repository] == "false" &&
+ [exec git rev-parse --is-inside-git-dir] == "false"}]
}
# A simple scheduler for compute-intensive stuff.
@@ -468,11 +464,11 @@ proc updatecommits {} {
global viewactive viewcomplete tclencoding
global startmsecs showneartags showlocalchanges
global mainheadid viewmainheadid viewmainheadid_orig pending_select
- global isworktree
+ global hasworktree
global varcid vposids vnegids vflags vrevs
global show_notes
- set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+ set hasworktree [hasworktree]
rereadrefs
set view $curview
if {$mainheadid ne $viewmainheadid_orig($view)} {
@@ -659,7 +655,7 @@ proc newvarc {view id} {
if {![info exists commitinfo($id)]} {
parsecommit $id $commitdata($id) 1
}
- set cdate [lindex $commitinfo($id) 4]
+ set cdate [lindex [lindex $commitinfo($id) 4] 0]
if {![string is integer -strict $cdate]} {
set cdate 0
}
@@ -1621,7 +1617,7 @@ proc readcommit {id} {
}
proc parsecommit {id contents listed} {
- global commitinfo cdate
+ global commitinfo
set inhdr 1
set comment {}
@@ -1641,10 +1637,10 @@ proc parsecommit {id contents listed} {
set line [split $line " "]
set tag [lindex $line 0]
if {$tag == "author"} {
- set audate [lindex $line end-1]
+ set audate [lrange $line end-1 end]
set auname [join [lrange $line 1 end-2] " "]
} elseif {$tag == "committer"} {
- set comdate [lindex $line end-1]
+ set comdate [lrange $line end-1 end]
set comname [join [lrange $line 1 end-2] " "]
}
}
@@ -1671,11 +1667,9 @@ proc parsecommit {id contents listed} {
}
set comment $newcomment
}
- if {$comdate != {}} {
- set cdate($id) $comdate
- }
+ set hasnote [string first "\nNotes:\n" $contents]
set commitinfo($id) [list $headline $auname $audate \
- $comname $comdate $comment]
+ $comname $comdate $comment $hasnote]
}
proc getcommit {id} {
@@ -2437,9 +2431,9 @@ proc makewindow {} {
bindkey n "selnextline 1"
bindkey z "goback"
bindkey x "goforw"
- bindkey i "selnextline -1"
- bindkey k "selnextline 1"
- bindkey j "goback"
+ bindkey k "selnextline -1"
+ bindkey j "selnextline 1"
+ bindkey h "goback"
bindkey l "goforw"
bindkey b prevfile
bindkey d "$ctext yview scroll 18 units"
@@ -2815,7 +2809,7 @@ proc about {} {
message $w.m -text [mc "
Gitk - a commit viewer for git
-Copyright \u00a9 2005-2010 Paul Mackerras
+Copyright \u00a9 2005-2011 Paul Mackerras
Use and redistribute under the terms of the GNU General Public License"] \
-justify center -aspect 400 -border 2 -bg white -relief groove
@@ -2850,9 +2844,9 @@ proc keys {} {
[mc "<%s-W> Close window" $M1T]
[mc "<Home> Move to first commit"]
[mc "<End> Move to last commit"]
-[mc "<Up>, p, i Move up one commit"]
-[mc "<Down>, n, k Move down one commit"]
-[mc "<Left>, z, j Go back in history list"]
+[mc "<Up>, p, k Move up one commit"]
+[mc "<Down>, n, j Move down one commit"]
+[mc "<Left>, z, h Go back in history list"]
[mc "<Right>, x, l Go forward in history list"]
[mc "<PageUp> Move up one page in commit list"]
[mc "<PageDown> Move down one page in commit list"]
@@ -3333,8 +3327,7 @@ proc gitknewtmpdir {} {
global diffnum gitktmpdir gitdir
if {![info exists gitktmpdir]} {
- set gitktmpdir [file join [file dirname $gitdir] \
- [format ".gitk-tmp.%s" [pid]]]
+ set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]]
if {[catch {file mkdir $gitktmpdir} err]} {
error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
unset gitktmpdir
@@ -3366,10 +3359,10 @@ proc save_file_from_commit {filename output what} {
proc external_diff_get_one_file {diffid filename diffdir} {
global nullid nullid2 nullfile
- global gitdir
+ global worktree
if {$diffid == $nullid} {
- set difffile [file join [file dirname $gitdir] $filename]
+ set difffile [file join $worktree $filename]
if {[file exists $difffile]} {
return $difffile
}
@@ -3559,7 +3552,7 @@ proc make_relative {f} {
}
proc external_blame {parent_idx {line {}}} {
- global flist_menu_file gitdir
+ global flist_menu_file cdup
global nullid nullid2
global parentlist selectedline currentid
@@ -3578,7 +3571,7 @@ proc external_blame {parent_idx {line {}}} {
if {$line ne {} && $line > 1} {
lappend cmdline "--line=$line"
}
- set f [file join [file dirname $gitdir] $flist_menu_file]
+ set f [file join $cdup $flist_menu_file]
# Unfortunately it seems git gui blame doesn't like
# being given an absolute path...
set f [make_relative $f]
@@ -3591,7 +3584,7 @@ proc external_blame {parent_idx {line {}}} {
proc show_line_source {} {
global cmitmode currentid parents curview blamestuff blameinst
global diff_menu_line diff_menu_filebase flist_menu_file
- global nullid nullid2 gitdir
+ global nullid nullid2 gitdir cdup
set from_index {}
if {$cmitmode eq "tree"} {
@@ -3644,7 +3637,7 @@ proc show_line_source {} {
} else {
lappend blameargs $id
}
- lappend blameargs -- [file join [file dirname $gitdir] $flist_menu_file]
+ lappend blameargs -- [file join $cdup $flist_menu_file]
if {[catch {
set f [open $blameargs r]
} err]} {
@@ -4529,12 +4522,22 @@ proc makepatterns {l} {
proc do_file_hl {serial} {
global highlight_files filehighlight highlight_paths gdttype fhl_list
+ global cdup findtype
if {$gdttype eq [mc "touching paths:"]} {
+ # If "exact" match then convert backslashes to forward slashes.
+ # Most useful to support Windows-flavoured file paths.
+ if {$findtype eq [mc "Exact"]} {
+ set highlight_files [string map {"\\" "/"} $highlight_files]
+ }
if {[catch {set paths [shellsplit $highlight_files]}]} return
set highlight_paths [makepatterns $paths]
highlight_filelist
- set gdtargs [concat -- $paths]
+ set relative_paths {}
+ foreach path $paths {
+ lappend relative_paths [file join $cdup $path]
+ }
+ set gdtargs [concat -- $relative_paths]
} elseif {$gdttype eq [mc "adding/removing string:"]} {
set gdtargs [list "-S$highlight_files"]
} else {
@@ -5031,9 +5034,9 @@ proc dohidelocalchanges {} {
# spawn off a process to do git diff-index --cached HEAD
proc dodiffindex {} {
global lserial showlocalchanges vfilelimit curview
- global isworktree
+ global hasworktree
- if {!$showlocalchanges || !$isworktree} return
+ if {!$showlocalchanges || !$hasworktree} return
incr lserial
set cmd "|git diff-index --cached HEAD"
if {$vfilelimit($curview) ne {}} {
@@ -5899,6 +5902,9 @@ proc drawcmittext {id row col} {
|| [info exists idotherrefs($id)]} {
set xt [drawtags $id $x $xt $y]
}
+ if {[lindex $commitinfo($id) 6] > 0} {
+ set xt [drawnotesign $xt $y]
+ }
set headline [lindex $commitinfo($id) 0]
set name [lindex $commitinfo($id) 1]
set date [lindex $commitinfo($id) 2]
@@ -6345,6 +6351,17 @@ proc drawtags {id x xt y1} {
return $xt
}
+proc drawnotesign {xt y} {
+ global linespc canv fgcolor
+
+ set orad [expr {$linespc / 3}]
+ set t [$canv create rectangle [expr {$xt - $orad}] [expr {$y - $orad}] \
+ [expr {$xt + $orad - 1}] [expr {$y + $orad - 1}] \
+ -fill yellow -outline $fgcolor -width 1 -tags circle]
+ set xt [expr {$xt + $orad * 3}]
+ return $xt
+}
+
proc xcoord {i level ln} {
global canvx0 xspc1 xspc2
@@ -9043,6 +9060,7 @@ proc exec_citool {tool_args {baseid {}}} {
proc cherrypick {} {
global rowmenuid curview
global mainhead mainheadid
+ global gitdir
set oldhead [exec git rev-parse HEAD]
set dheads [descheads $rowmenuid]
@@ -9071,7 +9089,7 @@ proc cherrypick {} {
conflict.\nDo you wish to run git citool to\
resolve it?"]]} {
# Force citool to read MERGE_MSG
- file delete [file join [gitdir] "GITGUI_MSG"]
+ file delete [file join $gitdir "GITGUI_MSG"]
exec_citool {} $rowmenuid
}
} else {
@@ -9437,6 +9455,7 @@ proc refill_reflist {} {
proc getallcommits {} {
global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
global idheads idtags idotherrefs allparents tagobjid
+ global gitdir
if {![info exists allcommits]} {
set nextarc 0
@@ -9444,7 +9463,7 @@ proc getallcommits {} {
set seeds {}
set allcwait 0
set cachedarcs 0
- set allccache [file join [gitdir] "gitk.cache"]
+ set allccache [file join $gitdir "gitk.cache"]
if {![catch {
set f [open $allccache r]
set allcwait 1
@@ -11024,7 +11043,7 @@ proc prefsok {} {
proc formatdate {d} {
global datetimeformat
if {$d ne {}} {
- set d [clock format $d -format $datetimeformat]
+ set d [clock format [lindex $d 0] -format $datetimeformat]
}
return $d
}
@@ -11505,14 +11524,10 @@ setui $uicolor
setoptions
# check that we can find a .git directory somewhere...
-if {[catch {set gitdir [gitdir]}]} {
+if {[catch {set gitdir [exec git rev-parse --git-dir]}]} {
show_error {} . [mc "Cannot find a git repository here."]
exit 1
}
-if {![file isdirectory $gitdir]} {
- show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
- exit 1
-}
set selecthead {}
set selectheadid {}
@@ -11628,7 +11643,12 @@ set stopped 0
set stuffsaved 0
set patchnum 0
set lserial 0
-set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+set hasworktree [hasworktree]
+set cdup {}
+if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
+ set cdup [exec git rev-parse --show-cdup]
+}
+set worktree [exec git rev-parse --show-toplevel]
setcoords
makewindow
catch {
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 4f0c3bd90c..f80f2594cb 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -759,6 +759,7 @@ our @cgi_param_mapping = (
extra_options => "opt",
search_use_regexp => "sr",
ctag => "by_tag",
+ diff_style => "ds",
# this must be last entry (for manipulation from JavaScript)
javascript => "js"
);
@@ -2225,93 +2226,119 @@ sub format_diff_cc_simplified {
return $result;
}
-# format patch (diff) line (not to be used for diff headers)
-sub format_diff_line {
- my $line = shift;
- my ($from, $to) = @_;
- my $diff_class = "";
-
- chomp $line;
+sub diff_line_class {
+ my ($line, $from, $to) = @_;
+ # ordinary diff
+ my $num_sign = 1;
+ # combined diff
if ($from && $to && ref($from->{'href'}) eq "ARRAY") {
- # combined diff
- my $prefix = substr($line, 0, scalar @{$from->{'href'}});
- if ($line =~ m/^\@{3}/) {
- $diff_class = " chunk_header";
- } elsif ($line =~ m/^\\/) {
- $diff_class = " incomplete";
- } elsif ($prefix =~ tr/+/+/) {
- $diff_class = " add";
- } elsif ($prefix =~ tr/-/-/) {
- $diff_class = " rem";
- }
- } else {
- # assume ordinary diff
- my $char = substr($line, 0, 1);
- if ($char eq '+') {
- $diff_class = " add";
- } elsif ($char eq '-') {
- $diff_class = " rem";
- } elsif ($char eq '@') {
- $diff_class = " chunk_header";
- } elsif ($char eq "\\") {
- $diff_class = " incomplete";
- }
+ $num_sign = scalar @{$from->{'href'}};
+ }
+
+ my @diff_line_classifier = (
+ { regexp => qr/^\@\@{$num_sign} /, class => "chunk_header"},
+ { regexp => qr/^\\/, class => "incomplete" },
+ { regexp => qr/^ {$num_sign}/, class => "ctx" },
+ # classifier for context must come before classifier add/rem,
+ # or we would have to use more complicated regexp, for example
+ # qr/(?= {0,$m}\+)[+ ]{$num_sign}/, where $m = $num_sign - 1;
+ { regexp => qr/^[+ ]{$num_sign}/, class => "add" },
+ { regexp => qr/^[- ]{$num_sign}/, class => "rem" },
+ );
+ for my $clsfy (@diff_line_classifier) {
+ return $clsfy->{'class'}
+ if ($line =~ $clsfy->{'regexp'});
}
- $line = untabify($line);
- if ($from && $to && $line =~ m/^\@{2} /) {
- my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =
- $line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/;
- $from_lines = 0 unless defined $from_lines;
- $to_lines = 0 unless defined $to_lines;
+ # fallback
+ return "";
+}
- if ($from->{'href'}) {
- $from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start",
- -class=>"list"}, $from_text);
- }
- if ($to->{'href'}) {
- $to_text = $cgi->a({-href=>"$to->{'href'}#l$to_start",
- -class=>"list"}, $to_text);
- }
- $line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
- "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
- return "<div class=\"diff$diff_class\">$line</div>\n";
- } elsif ($from && $to && $line =~ m/^\@{3}/) {
- my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
- my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
+# assumes that $from and $to are defined and correctly filled,
+# and that $line holds a line of chunk header for unified diff
+sub format_unidiff_chunk_header {
+ my ($line, $from, $to) = @_;
- @from_text = split(' ', $ranges);
- for (my $i = 0; $i < @from_text; ++$i) {
- ($from_start[$i], $from_nlines[$i]) =
- (split(',', substr($from_text[$i], 1)), 0);
- }
+ my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =
+ $line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/;
- $to_text = pop @from_text;
- $to_start = pop @from_start;
- $to_nlines = pop @from_nlines;
+ $from_lines = 0 unless defined $from_lines;
+ $to_lines = 0 unless defined $to_lines;
- $line = "<span class=\"chunk_info\">$prefix ";
- for (my $i = 0; $i < @from_text; ++$i) {
- if ($from->{'href'}[$i]) {
- $line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
- -class=>"list"}, $from_text[$i]);
- } else {
- $line .= $from_text[$i];
- }
- $line .= " ";
- }
- if ($to->{'href'}) {
- $line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
- -class=>"list"}, $to_text);
+ if ($from->{'href'}) {
+ $from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start",
+ -class=>"list"}, $from_text);
+ }
+ if ($to->{'href'}) {
+ $to_text = $cgi->a({-href=>"$to->{'href'}#l$to_start",
+ -class=>"list"}, $to_text);
+ }
+ $line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
+ "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
+ return $line;
+}
+
+# assumes that $from and $to are defined and correctly filled,
+# and that $line holds a line of chunk header for combined diff
+sub format_cc_diff_chunk_header {
+ my ($line, $from, $to) = @_;
+
+ my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
+ my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
+
+ @from_text = split(' ', $ranges);
+ for (my $i = 0; $i < @from_text; ++$i) {
+ ($from_start[$i], $from_nlines[$i]) =
+ (split(',', substr($from_text[$i], 1)), 0);
+ }
+
+ $to_text = pop @from_text;
+ $to_start = pop @from_start;
+ $to_nlines = pop @from_nlines;
+
+ $line = "<span class=\"chunk_info\">$prefix ";
+ for (my $i = 0; $i < @from_text; ++$i) {
+ if ($from->{'href'}[$i]) {
+ $line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
+ -class=>"list"}, $from_text[$i]);
} else {
- $line .= $to_text;
+ $line .= $from_text[$i];
}
- $line .= " $prefix</span>" .
- "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
- return "<div class=\"diff$diff_class\">$line</div>\n";
+ $line .= " ";
}
- return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
+ if ($to->{'href'}) {
+ $line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
+ -class=>"list"}, $to_text);
+ } else {
+ $line .= $to_text;
+ }
+ $line .= " $prefix</span>" .
+ "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
+ return $line;
+}
+
+# process patch (diff) line (not to be used for diff headers),
+# returning class and HTML-formatted (but not wrapped) line
+sub process_diff_line {
+ my $line = shift;
+ my ($from, $to) = @_;
+
+ my $diff_class = diff_line_class($line, $from, $to);
+
+ chomp $line;
+ $line = untabify($line);
+
+ if ($from && $to && $line =~ m/^\@{2} /) {
+ $line = format_unidiff_chunk_header($line, $from, $to);
+ return $diff_class, $line;
+
+ } elsif ($from && $to && $line =~ m/^\@{3}/) {
+ $line = format_cc_diff_chunk_header($line, $from, $to);
+ return $diff_class, $line;
+
+ }
+ return $diff_class, esc_html($line, -nbsp=>1);
}
# Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
@@ -4833,8 +4860,97 @@ sub git_difftree_body {
print "</table>\n";
}
+sub print_sidebyside_diff_chunk {
+ my @chunk = @_;
+ my (@ctx, @rem, @add);
+
+ return unless @chunk;
+
+ # incomplete last line might be among removed or added lines,
+ # or both, or among context lines: find which
+ for (my $i = 1; $i < @chunk; $i++) {
+ if ($chunk[$i][0] eq 'incomplete') {
+ $chunk[$i][0] = $chunk[$i-1][0];
+ }
+ }
+
+ # guardian
+ push @chunk, ["", ""];
+
+ foreach my $line_info (@chunk) {
+ my ($class, $line) = @$line_info;
+
+ # print chunk headers
+ if ($class && $class eq 'chunk_header') {
+ print $line;
+ next;
+ }
+
+ ## print from accumulator when type of class of lines change
+ # empty contents block on start rem/add block, or end of chunk
+ if (@ctx && (!$class || $class eq 'rem' || $class eq 'add')) {
+ print join '',
+ '<div class="chunk_block ctx">',
+ '<div class="old">',
+ @ctx,
+ '</div>',
+ '<div class="new">',
+ @ctx,
+ '</div>',
+ '</div>';
+ @ctx = ();
+ }
+ # empty add/rem block on start context block, or end of chunk
+ if ((@rem || @add) && (!$class || $class eq 'ctx')) {
+ if (!@add) {
+ # pure removal
+ print join '',
+ '<div class="chunk_block rem">',
+ '<div class="old">',
+ @rem,
+ '</div>',
+ '</div>';
+ } elsif (!@rem) {
+ # pure addition
+ print join '',
+ '<div class="chunk_block add">',
+ '<div class="new">',
+ @add,
+ '</div>',
+ '</div>';
+ } else {
+ # assume that it is change
+ print join '',
+ '<div class="chunk_block chg">',
+ '<div class="old">',
+ @rem,
+ '</div>',
+ '<div class="new">',
+ @add,
+ '</div>',
+ '</div>';
+ }
+ @rem = @add = ();
+ }
+
+ ## adding lines to accumulator
+ # guardian value
+ last unless $line;
+ # rem, add or change
+ if ($class eq 'rem') {
+ push @rem, $line;
+ } elsif ($class eq 'add') {
+ push @add, $line;
+ }
+ # context line
+ if ($class eq 'ctx') {
+ push @ctx, $line;
+ }
+ }
+}
+
sub git_patchset_body {
- my ($fd, $difftree, $hash, @hash_parents) = @_;
+ my ($fd, $diff_style, $difftree, $hash, @hash_parents) = @_;
my ($hash_parent) = $hash_parents[0];
my $is_combined = (@hash_parents > 1);
@@ -4844,6 +4960,7 @@ sub git_patchset_body {
my $diffinfo;
my $to_name;
my (%from, %to);
+ my @chunk; # for side-by-side diff
print "<div class=\"patchset\">\n";
@@ -4950,10 +5067,29 @@ sub git_patchset_body {
next PATCH if ($patch_line =~ m/^diff /);
- print format_diff_line($patch_line, \%from, \%to);
+ my ($class, $line) = process_diff_line($patch_line, \%from, \%to);
+ my $diff_classes = "diff";
+ $diff_classes .= " $class" if ($class);
+ $line = "<div class=\"$diff_classes\">$line</div>\n";
+
+ if ($diff_style eq 'sidebyside' && !$is_combined) {
+ if ($class eq 'chunk_header') {
+ print_sidebyside_diff_chunk(@chunk);
+ @chunk = ( [ $class, $line ] );
+ } else {
+ push @chunk, [ $class, $line ];
+ }
+ } else {
+ # default 'inline' style and unknown styles
+ print $line;
+ }
}
} continue {
+ if (@chunk) {
+ print_sidebyside_diff_chunk(@chunk);
+ @chunk = ();
+ }
print "</div>\n"; # class="patch"
}
@@ -6949,6 +7085,7 @@ sub git_object {
sub git_blobdiff {
my $format = shift || 'html';
+ my $diff_style = $input_params{'diff_style'} || 'inline';
my $fd;
my @difftree;
@@ -7027,6 +7164,7 @@ sub git_blobdiff {
my $formats_nav =
$cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)},
"raw");
+ $formats_nav .= diff_style_nav($diff_style);
git_header_html(undef, $expires);
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
@@ -7058,7 +7196,8 @@ sub git_blobdiff {
if ($format eq 'html') {
print "<div class=\"page_body\">\n";
- git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
+ git_patchset_body($fd, $diff_style,
+ [ \%diffinfo ], $hash_base, $hash_parent_base);
close $fd;
print "</div>\n"; # class="page_body"
@@ -7083,9 +7222,31 @@ sub git_blobdiff_plain {
git_blobdiff('plain');
}
+# assumes that it is added as later part of already existing navigation,
+# so it returns "| foo | bar" rather than just "foo | bar"
+sub diff_style_nav {
+ my ($diff_style, $is_combined) = @_;
+ $diff_style ||= 'inline';
+
+ return "" if ($is_combined);
+
+ my @styles = (inline => 'inline', 'sidebyside' => 'side by side');
+ my %styles = @styles;
+ @styles =
+ @styles[ map { $_ * 2 } 0..$#styles/2 ];
+
+ return join '',
+ map { " | ".$_ }
+ map {
+ $_ eq $diff_style ? $styles{$_} :
+ $cgi->a({-href => href(-replay=>1, diff_style => $_)}, $styles{$_})
+ } @styles;
+}
+
sub git_commitdiff {
my %params = @_;
my $format = $params{-format} || 'html';
+ my $diff_style = $input_params{'diff_style'} || 'inline';
my ($patch_max) = gitweb_get_feature('patches');
if ($format eq 'patch') {
@@ -7111,6 +7272,7 @@ sub git_commitdiff {
$cgi->a({-href => href(action=>"patch", -replay=>1)},
"patch");
}
+ $formats_nav .= diff_style_nav($diff_style, @{$co{'parents'}} > 1);
if (defined $hash_parent &&
$hash_parent ne '-c' && $hash_parent ne '--cc') {
@@ -7128,8 +7290,8 @@ sub git_commitdiff {
}
}
$formats_nav .= ': ' .
- $cgi->a({-href => href(action=>"commitdiff",
- hash=>$hash_parent)},
+ $cgi->a({-href => href(-replay=>1,
+ hash=>$hash_parent, hash_base=>undef)},
esc_html($hash_parent_short)) .
')';
} elsif (!$co{'parent'}) {
@@ -7139,28 +7301,28 @@ sub git_commitdiff {
# single parent commit
$formats_nav .=
' (parent: ' .
- $cgi->a({-href => href(action=>"commitdiff",
- hash=>$co{'parent'})},
+ $cgi->a({-href => href(-replay=>1,
+ hash=>$co{'parent'}, hash_base=>undef)},
esc_html(substr($co{'parent'}, 0, 7))) .
')';
} else {
# merge commit
if ($hash_parent eq '--cc') {
$formats_nav .= ' | ' .
- $cgi->a({-href => href(action=>"commitdiff",
+ $cgi->a({-href => href(-replay=>1,
hash=>$hash, hash_parent=>'-c')},
'combined');
} else { # $hash_parent eq '-c'
$formats_nav .= ' | ' .
- $cgi->a({-href => href(action=>"commitdiff",
+ $cgi->a({-href => href(-replay=>1,
hash=>$hash, hash_parent=>'--cc')},
'compact');
}
$formats_nav .=
' (merge: ' .
join(' ', map {
- $cgi->a({-href => href(action=>"commitdiff",
- hash=>$_)},
+ $cgi->a({-href => href(-replay=>1,
+ hash=>$_, hash_base=>undef)},
esc_html(substr($_, 0, 7)));
} @{$co{'parents'}} ) .
')';
@@ -7289,7 +7451,8 @@ sub git_commitdiff {
$use_parents ? @{$co{'parents'}} : $hash_parent);
print "<br/>\n";
- git_patchset_body($fd, \@difftree, $hash,
+ git_patchset_body($fd, $diff_style,
+ \@difftree, $hash,
$use_parents ? @{$co{'parents'}} : $hash_parent);
close $fd;
print "</div>\n"; # class="page_body"
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
index 7d88509208..c7827e8f1d 100644
--- a/gitweb/static/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -475,6 +475,36 @@ div.diff.nodifferences {
color: #600000;
}
+/* side-by-side diff */
+div.chunk_block {
+ overflow: hidden;
+}
+
+div.chunk_block div.old {
+ float: left;
+ width: 50%;
+ overflow: hidden;
+}
+
+div.chunk_block div.new {
+ margin-left: 50%;
+ width: 50%;
+}
+
+div.chunk_block.rem div.old div.diff.rem {
+ background-color: #fff5f5;
+}
+div.chunk_block.add div.new div.diff.add {
+ background-color: #f8fff8;
+}
+div.chunk_block.chg div div.diff {
+ background-color: #fffff0;
+}
+div.chunk_block.ctx div div.diff.ctx {
+ color: #404040;
+}
+
+
div.index_include {
border: solid #d9d8d1;
border-width: 0px 0px 1px;
diff --git a/gpg-interface.c b/gpg-interface.c
new file mode 100644
index 0000000000..ff232c8c5d
--- /dev/null
+++ b/gpg-interface.c
@@ -0,0 +1,138 @@
+#include "cache.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "gpg-interface.h"
+#include "sigchain.h"
+
+static char *configured_signing_key;
+
+void set_signing_key(const char *key)
+{
+ free(configured_signing_key);
+ configured_signing_key = xstrdup(key);
+}
+
+int git_gpg_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "user.signingkey")) {
+ if (!value)
+ return config_error_nonbool(var);
+ set_signing_key(value);
+ }
+ return 0;
+}
+
+const char *get_signing_key(void)
+{
+ if (configured_signing_key)
+ return configured_signing_key;
+ return git_committer_info(IDENT_ERROR_ON_NO_NAME|IDENT_NO_DATE);
+}
+
+/*
+ * Create a detached signature for the contents of "buffer" and append
+ * it after "signature"; "buffer" and "signature" can be the same
+ * strbuf instance, which would cause the detached signature appended
+ * at the end.
+ */
+int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
+{
+ struct child_process gpg;
+ const char *args[4];
+ ssize_t len;
+ size_t i, j, bottom;
+
+ memset(&gpg, 0, sizeof(gpg));
+ gpg.argv = args;
+ gpg.in = -1;
+ gpg.out = -1;
+ args[0] = "gpg";
+ args[1] = "-bsau";
+ args[2] = signing_key;
+ args[3] = NULL;
+
+ if (start_command(&gpg))
+ return error(_("could not run gpg."));
+
+ /*
+ * When the username signingkey is bad, program could be terminated
+ * because gpg exits without reading and then write gets SIGPIPE.
+ */
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
+ close(gpg.in);
+ close(gpg.out);
+ finish_command(&gpg);
+ return error(_("gpg did not accept the data"));
+ }
+ close(gpg.in);
+
+ bottom = signature->len;
+ len = strbuf_read(signature, gpg.out, 1024);
+ close(gpg.out);
+
+ sigchain_pop(SIGPIPE);
+
+ if (finish_command(&gpg) || !len || len < 0)
+ return error(_("gpg failed to sign the data"));
+
+ /* Strip CR from the line endings, in case we are on Windows. */
+ for (i = j = bottom; i < signature->len; i++)
+ if (signature->buf[i] != '\r') {
+ if (i != j)
+ signature->buf[j] = signature->buf[i];
+ j++;
+ }
+ strbuf_setlen(signature, j);
+
+ return 0;
+}
+
+/*
+ * Run "gpg" to see if the payload matches the detached signature.
+ * gpg_output_to tells where the output from "gpg" should go:
+ * < 0: /dev/null
+ * = 0: standard error of the calling process
+ * > 0: the specified file descriptor
+ */
+int verify_signed_buffer(const char *payload, size_t payload_size,
+ const char *signature, size_t signature_size,
+ struct strbuf *gpg_output)
+{
+ struct child_process gpg;
+ const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
+ char path[PATH_MAX];
+ 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, signature, signature_size) < 0)
+ return error("failed writing detached signature to '%s': %s",
+ path, strerror(errno));
+ close(fd);
+
+ memset(&gpg, 0, sizeof(gpg));
+ gpg.argv = args_gpg;
+ gpg.in = -1;
+ if (gpg_output)
+ gpg.err = -1;
+ args_gpg[2] = path;
+ if (start_command(&gpg)) {
+ unlink(path);
+ return error("could not run gpg.");
+ }
+
+ write_in_full(gpg.in, payload, payload_size);
+ close(gpg.in);
+
+ if (gpg_output)
+ strbuf_read(gpg_output, gpg.err, 0);
+ ret = finish_command(&gpg);
+
+ unlink_or_warn(path);
+
+ return ret;
+}
diff --git a/gpg-interface.h b/gpg-interface.h
new file mode 100644
index 0000000000..b9c36088ce
--- /dev/null
+++ b/gpg-interface.h
@@ -0,0 +1,10 @@
+#ifndef GPG_INTERFACE_H
+#define GPG_INTERFACE_H
+
+extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
+extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output);
+extern int git_gpg_config(const char *, const char *, void *);
+extern void set_signing_key(const char *);
+extern const char *get_signing_key(void);
+
+#endif
diff --git a/http-fetch.c b/http-fetch.c
index 9719d58699..ba3ea10670 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -69,7 +69,7 @@ int main(int argc, const char **argv)
git_config(git_default_config, NULL);
- http_init(NULL, url);
+ http_init(NULL, url, 0);
walker = get_http_walker(url);
walker->get_tree = get_tree;
walker->get_history = get_history;
diff --git a/http-push.c b/http-push.c
index f856299254..f22f7e43ca 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1822,7 +1822,7 @@ int main(int argc, char **argv)
memset(remote_dir_exists, -1, 256);
- http_init(NULL, repo->url);
+ http_init(NULL, repo->url, 1);
#ifdef USE_CURL_MULTI
is_running_queue = 0;
diff --git a/http.c b/http.c
index 008ad72ae5..0ffd79cd81 100644
--- a/http.c
+++ b/http.c
@@ -3,8 +3,8 @@
#include "sideband.h"
#include "run-command.h"
#include "url.h"
+#include "credential.h"
-int data_received;
int active_requests;
int http_is_verbose;
size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
@@ -42,7 +42,8 @@ static long curl_low_speed_time = -1;
static int curl_ftp_no_epsv;
static const char *curl_http_proxy;
static const char *curl_cookie_file;
-static char *user_name, *user_pass, *description;
+static struct credential http_auth = CREDENTIAL_INIT;
+static int http_proactive_auth;
static const char *user_agent;
#if LIBCURL_VERSION_NUM >= 0x071700
@@ -53,7 +54,7 @@ static const char *user_agent;
#define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
#endif
-static char *ssl_cert_password;
+static struct credential cert_auth = CREDENTIAL_INIT;
static int ssl_cert_password_required;
static struct curl_slist *pragma_header;
@@ -99,13 +100,11 @@ size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
struct strbuf *buffer = buffer_;
strbuf_add(buffer, ptr, size);
- data_received++;
return size;
}
size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
{
- data_received++;
return eltsize * nmemb;
}
@@ -139,27 +138,6 @@ static void process_curl_messages(void)
}
#endif
-static char *git_getpass_with_description(const char *what, const char *desc)
-{
- struct strbuf prompt = STRBUF_INIT;
- char *r;
-
- if (desc)
- strbuf_addf(&prompt, "%s for '%s': ", what, desc);
- else
- strbuf_addf(&prompt, "%s: ", what);
- /*
- * NEEDSWORK: for usernames, we should do something less magical that
- * actually echoes the characters. However, we need to read from
- * /dev/tty and not stdio, which is not portable (but getpass will do
- * it for us). http.c uses the same workaround.
- */
- r = git_getpass(prompt.buf);
-
- strbuf_release(&prompt);
- return xstrdup(r);
-}
-
static int http_options(const char *var, const char *value, void *cb)
{
if (!strcmp("http.sslverify", var)) {
@@ -232,11 +210,11 @@ static int http_options(const char *var, const char *value, void *cb)
static void init_curl_http_auth(CURL *result)
{
- if (user_name) {
+ if (http_auth.username) {
struct strbuf up = STRBUF_INIT;
- if (!user_pass)
- user_pass = xstrdup(git_getpass_with_description("Password", description));
- strbuf_addf(&up, "%s:%s", user_name, user_pass);
+ credential_fill(&http_auth);
+ strbuf_addf(&up, "%s:%s",
+ http_auth.username, http_auth.password);
curl_easy_setopt(result, CURLOPT_USERPWD,
strbuf_detach(&up, NULL));
}
@@ -244,18 +222,14 @@ static void init_curl_http_auth(CURL *result)
static int has_cert_password(void)
{
- if (ssl_cert_password != NULL)
- return 1;
if (ssl_cert == NULL || ssl_cert_password_required != 1)
return 0;
- /* Only prompt the user once. */
- ssl_cert_password_required = -1;
- ssl_cert_password = git_getpass_with_description("Certificate Password", description);
- if (ssl_cert_password != NULL) {
- ssl_cert_password = xstrdup(ssl_cert_password);
- return 1;
- } else
- return 0;
+ if (!cert_auth.password) {
+ cert_auth.protocol = xstrdup("cert");
+ cert_auth.path = xstrdup(ssl_cert);
+ credential_fill(&cert_auth);
+ }
+ return 1;
}
static CURL *get_curl_handle(void)
@@ -279,10 +253,13 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
#endif
+ if (http_proactive_auth)
+ init_curl_http_auth(result);
+
if (ssl_cert != NULL)
curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
if (has_cert_password())
- curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
+ curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);
#if LIBCURL_VERSION_NUM >= 0x070903
if (ssl_key != NULL)
curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
@@ -324,42 +301,6 @@ static CURL *get_curl_handle(void)
return result;
}
-static void http_auth_init(const char *url)
-{
- const char *at, *colon, *cp, *slash, *host;
-
- cp = strstr(url, "://");
- if (!cp)
- return;
-
- /*
- * Ok, the URL looks like "proto://something". Which one?
- * "proto://<user>:<pass>@<host>/...",
- * "proto://<user>@<host>/...", or just
- * "proto://<host>/..."?
- */
- cp += 3;
- at = strchr(cp, '@');
- colon = strchr(cp, ':');
- slash = strchrnul(cp, '/');
- if (!at || slash <= at) {
- /* No credentials, but we may have to ask for some later */
- host = cp;
- }
- else if (!colon || at <= colon) {
- /* Only username */
- user_name = url_decode_mem(cp, at - cp);
- user_pass = NULL;
- host = at + 1;
- } else {
- user_name = url_decode_mem(cp, colon - cp);
- user_pass = url_decode_mem(colon + 1, at - (colon + 1));
- host = at + 1;
- }
-
- description = url_decode_mem(host, slash - host);
-}
-
static void set_from_env(const char **var, const char *envname)
{
const char *val = getenv(envname);
@@ -367,7 +308,7 @@ static void set_from_env(const char **var, const char *envname)
*var = val;
}
-void http_init(struct remote *remote, const char *url)
+void http_init(struct remote *remote, const char *url, int proactive_auth)
{
char *low_speed_limit;
char *low_speed_time;
@@ -378,6 +319,8 @@ void http_init(struct remote *remote, const char *url)
curl_global_init(CURL_GLOBAL_ALL);
+ http_proactive_auth = proactive_auth;
+
if (remote && remote->http_proxy)
curl_http_proxy = xstrdup(remote->http_proxy);
@@ -432,7 +375,7 @@ void http_init(struct remote *remote, const char *url)
curl_ftp_no_epsv = 1;
if (url) {
- http_auth_init(url);
+ credential_from_url(&http_auth, url);
if (!ssl_cert_password_required &&
getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
!prefixcmp(url, "https://"))
@@ -481,10 +424,10 @@ void http_cleanup(void)
curl_http_proxy = NULL;
}
- if (ssl_cert_password != NULL) {
- memset(ssl_cert_password, 0, strlen(ssl_cert_password));
- free(ssl_cert_password);
- ssl_cert_password = NULL;
+ if (cert_auth.password != NULL) {
+ memset(cert_auth.password, 0, strlen(cert_auth.password));
+ free(cert_auth.password);
+ cert_auth.password = NULL;
}
ssl_cert_password_required = 0;
}
@@ -536,7 +479,6 @@ struct active_request_slot *get_active_slot(void)
active_requests++;
slot->in_use = 1;
- slot->local = NULL;
slot->results = NULL;
slot->finished = NULL;
slot->callback_data = NULL;
@@ -640,8 +582,6 @@ void step_active_slots(void)
void run_active_slot(struct active_request_slot *slot)
{
#ifdef USE_CURL_MULTI
- long last_pos = 0;
- long current_pos;
fd_set readfds;
fd_set writefds;
fd_set excfds;
@@ -651,25 +591,33 @@ void run_active_slot(struct active_request_slot *slot)
slot->finished = &finished;
while (!finished) {
- data_received = 0;
step_active_slots();
- if (!data_received && slot->local != NULL) {
- current_pos = ftell(slot->local);
- if (current_pos > last_pos)
- data_received++;
- last_pos = current_pos;
- }
+ if (slot->in_use) {
+#if LIBCURL_VERSION_NUM >= 0x070f04
+ long curl_timeout;
+ curl_multi_timeout(curlm, &curl_timeout);
+ if (curl_timeout == 0) {
+ continue;
+ } else if (curl_timeout == -1) {
+ select_timeout.tv_sec = 0;
+ select_timeout.tv_usec = 50000;
+ } else {
+ select_timeout.tv_sec = curl_timeout / 1000;
+ select_timeout.tv_usec = (curl_timeout % 1000) * 1000;
+ }
+#else
+ select_timeout.tv_sec = 0;
+ select_timeout.tv_usec = 50000;
+#endif
- if (slot->in_use && !data_received) {
- max_fd = 0;
+ max_fd = -1;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&excfds);
- select_timeout.tv_sec = 0;
- select_timeout.tv_usec = 50000;
- select(max_fd, &readfds, &writefds,
- &excfds, &select_timeout);
+ curl_multi_fdset(curlm, &readfds, &writefds, &excfds, &max_fd);
+
+ select(max_fd+1, &readfds, &writefds, &excfds, &select_timeout);
}
}
#else
@@ -747,14 +695,6 @@ static inline int needs_quote(int ch)
return 1;
}
-static inline int hex(int v)
-{
- if (v < 10)
- return '0' + v;
- else
- return 'A' + v - 10;
-}
-
static char *quote_ref_url(const char *base, const char *ref)
{
struct strbuf buf = STRBUF_INIT;
@@ -822,7 +762,6 @@ static int http_request(const char *url, void *result, int target, int options)
headers = curl_slist_append(headers, buf.buf);
strbuf_reset(&buf);
}
- slot->local = result;
} else
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
fwrite_buffer);
@@ -844,17 +783,11 @@ static int http_request(const char *url, void *result, int target, int options)
else if (missing_target(&results))
ret = HTTP_MISSING_TARGET;
else if (results.http_code == 401) {
- if (user_name && user_pass) {
+ if (http_auth.username && http_auth.password) {
+ credential_reject(&http_auth);
ret = HTTP_NOAUTH;
} else {
- /*
- * git_getpass is needed here because its very likely stdin/stdout are
- * pipes to our parent process. So we instead need to use /dev/tty,
- * but that is non-portable. Using git_getpass() can at least be stubbed
- * on other platforms with a different implementation if/when necessary.
- */
- if (!user_name)
- user_name = xstrdup(git_getpass_with_description("Username", description));
+ credential_fill(&http_auth);
init_curl_http_auth(slot->curl);
ret = HTTP_REAUTH;
}
@@ -870,10 +803,12 @@ static int http_request(const char *url, void *result, int target, int options)
ret = HTTP_START_FAILED;
}
- slot->local = NULL;
curl_slist_free_all(headers);
strbuf_release(&buf);
+ if (ret == HTTP_OK)
+ credential_approve(&http_auth);
+
return ret;
}
@@ -1065,7 +1000,6 @@ void release_http_pack_request(struct http_pack_request *preq)
if (preq->packfile != NULL) {
fclose(preq->packfile);
preq->packfile = NULL;
- preq->slot->local = NULL;
}
if (preq->range_header != NULL) {
curl_slist_free_all(preq->range_header);
@@ -1087,7 +1021,6 @@ int finish_http_pack_request(struct http_pack_request *preq)
fclose(preq->packfile);
preq->packfile = NULL;
- preq->slot->local = NULL;
lst = preq->lst;
while (*lst != p)
@@ -1156,7 +1089,6 @@ struct http_pack_request *new_http_pack_request(
}
preq->slot = get_active_slot();
- preq->slot->local = preq->packfile;
curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
@@ -1213,7 +1145,6 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
git_SHA1_Update(&freq->c, expn,
sizeof(expn) - freq->stream.avail_out);
} while (freq->stream.avail_in && freq->zret == Z_OK);
- data_received++;
return size;
}
diff --git a/http.h b/http.h
index 3c332a98e9..0b61653894 100644
--- a/http.h
+++ b/http.h
@@ -49,7 +49,6 @@ struct slot_results {
struct active_request_slot {
CURL *curl;
- FILE *local;
int in_use;
CURLcode curl_result;
long http_code;
@@ -86,10 +85,10 @@ extern void add_fill_function(void *data, int (*fill)(void *));
extern void step_active_slots(void);
#endif
-extern void http_init(struct remote *remote, const char *url);
+extern void http_init(struct remote *remote, const char *url,
+ int proactive_auth);
extern void http_cleanup(void);
-extern int data_received;
extern int active_requests;
extern int http_is_verbose;
extern size_t http_post_buffer;
diff --git a/imap-send.c b/imap-send.c
index 9fba422f50..91763d3018 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -161,7 +161,6 @@ static struct imap_server_conf server = {
struct imap_store_conf {
struct store_conf gen;
struct imap_server_conf *server;
- unsigned use_namespace:1;
};
#define NIL (void *)0x1
diff --git a/list-objects.c b/list-objects.c
index 39d80c0175..3dd4a96019 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -71,7 +71,8 @@ static void process_tree(struct rev_info *revs,
struct tree_desc desc;
struct name_entry entry;
struct name_path me;
- int match = revs->diffopt.pathspec.nr == 0 ? 2 : 0;
+ enum interesting match = revs->diffopt.pathspec.nr == 0 ?
+ all_entries_interesting: entry_not_interesting;
int baselen = base->len;
if (!revs->tree_objects)
@@ -97,12 +98,12 @@ static void process_tree(struct rev_info *revs,
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
- if (match != 2) {
+ if (match != all_entries_interesting) {
match = tree_entry_interesting(&entry, base, 0,
&revs->diffopt.pathspec);
- if (match < 0)
+ if (match == all_entries_not_interesting)
break;
- if (match == 0)
+ if (match == entry_not_interesting)
continue;
}
diff --git a/mailmap.c b/mailmap.c
index 02fcfde0b0..8c3196c7d7 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -70,8 +70,7 @@ static void add_mapping(struct string_list *map,
} else {
/* create mailmap entry */
struct string_list_item *item = string_list_insert_at_index(map, index, old_email);
- item->util = xmalloc(sizeof(struct mailmap_entry));
- memset(item->util, 0, sizeof(struct mailmap_entry));
+ item->util = xcalloc(1, sizeof(struct mailmap_entry));
((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
}
me = (struct mailmap_entry *)map->items[index].util;
@@ -88,7 +87,7 @@ static void add_mapping(struct string_list *map,
me->email = xstrdup(new_email);
}
} else {
- struct mailmap_info *mi = xmalloc(sizeof(struct mailmap_info));
+ struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
if (new_name)
mi->name = xstrdup(new_name);
diff --git a/merge-recursive.c b/merge-recursive.c
index cc664c39b6..d83cd6c662 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -38,16 +38,15 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
return lookup_tree(shifted);
}
-/*
- * A virtual commit has (const char *)commit->util set to the name.
- */
-
static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
{
struct commit *commit = xcalloc(1, sizeof(struct commit));
+ struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
+
+ desc->name = comment;
+ desc->obj = (struct object *)commit;
commit->tree = tree;
- commit->util = (void*)comment;
- /* avoid warnings */
+ commit->util = desc;
commit->object.parsed = 1;
return commit;
}
@@ -184,7 +183,7 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
for (i = o->call_depth; i--;)
fputs(" ", stdout);
if (commit->util)
- printf("virtual %s\n", (char *)commit->util);
+ printf("virtual %s\n", merge_remote_util(commit)->name);
else {
printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
if (parse_commit(commit) != 0)
@@ -265,7 +264,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
if (!cache_tree_fully_valid(active_cache_tree) &&
cache_tree_update(active_cache_tree,
- active_cache, active_nr, 0, 0) < 0)
+ active_cache, active_nr, 0, 0, 0) < 0)
die("error building trees");
result = lookup_tree(active_cache_tree->sha1);
diff --git a/notes-merge.c b/notes-merge.c
index e9e4199311..2de27af751 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -21,14 +21,6 @@ void init_notes_merge_options(struct notes_merge_options *o)
o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
}
-#define OUTPUT(o, v, ...) \
- do { \
- if ((o)->verbosity >= (v)) { \
- printf(__VA_ARGS__); \
- puts(""); \
- } \
- } while (0)
-
static int path_to_sha1(const char *path, unsigned char *sha1)
{
char hex_sha1[40];
@@ -392,21 +384,26 @@ static int merge_one_change_manual(struct notes_merge_options *o,
strbuf_addf(&(o->commit_msg), "\t%s\n", sha1_to_hex(p->obj));
- OUTPUT(o, 2, "Auto-merging notes for %s", sha1_to_hex(p->obj));
+ if (o->verbosity >= 2)
+ printf("Auto-merging notes for %s\n", sha1_to_hex(p->obj));
check_notes_merge_worktree(o);
if (is_null_sha1(p->local)) {
/* D/F conflict, checkout p->remote */
assert(!is_null_sha1(p->remote));
- OUTPUT(o, 1, "CONFLICT (delete/modify): Notes for object %s "
- "deleted in %s and modified in %s. Version from %s "
- "left in tree.", sha1_to_hex(p->obj), lref, rref, rref);
+ if (o->verbosity >= 1)
+ printf("CONFLICT (delete/modify): Notes for object %s "
+ "deleted in %s and modified in %s. Version from %s "
+ "left in tree.\n",
+ sha1_to_hex(p->obj), lref, rref, rref);
write_note_to_worktree(p->obj, p->remote);
} else if (is_null_sha1(p->remote)) {
/* D/F conflict, checkout p->local */
assert(!is_null_sha1(p->local));
- OUTPUT(o, 1, "CONFLICT (delete/modify): Notes for object %s "
- "deleted in %s and modified in %s. Version from %s "
- "left in tree.", sha1_to_hex(p->obj), rref, lref, lref);
+ if (o->verbosity >= 1)
+ printf("CONFLICT (delete/modify): Notes for object %s "
+ "deleted in %s and modified in %s. Version from %s "
+ "left in tree.\n",
+ sha1_to_hex(p->obj), rref, lref, lref);
write_note_to_worktree(p->obj, p->local);
} else {
/* "regular" conflict, checkout result of ll_merge() */
@@ -415,8 +412,9 @@ static int merge_one_change_manual(struct notes_merge_options *o,
reason = "add/add";
assert(!is_null_sha1(p->local));
assert(!is_null_sha1(p->remote));
- OUTPUT(o, 1, "CONFLICT (%s): Merge conflict in notes for "
- "object %s", reason, sha1_to_hex(p->obj));
+ if (o->verbosity >= 1)
+ printf("CONFLICT (%s): Merge conflict in notes for "
+ "object %s\n", reason, sha1_to_hex(p->obj));
ll_merge_in_worktree(o, p);
}
@@ -438,24 +436,30 @@ static int merge_one_change(struct notes_merge_options *o,
case NOTES_MERGE_RESOLVE_MANUAL:
return merge_one_change_manual(o, p, t);
case NOTES_MERGE_RESOLVE_OURS:
- OUTPUT(o, 2, "Using local notes for %s", sha1_to_hex(p->obj));
+ if (o->verbosity >= 2)
+ printf("Using local notes for %s\n",
+ sha1_to_hex(p->obj));
/* nothing to do */
return 0;
case NOTES_MERGE_RESOLVE_THEIRS:
- OUTPUT(o, 2, "Using remote notes for %s", sha1_to_hex(p->obj));
+ if (o->verbosity >= 2)
+ printf("Using remote notes for %s\n",
+ sha1_to_hex(p->obj));
if (add_note(t, p->obj, p->remote, combine_notes_overwrite))
die("BUG: combine_notes_overwrite failed");
return 0;
case NOTES_MERGE_RESOLVE_UNION:
- OUTPUT(o, 2, "Concatenating local and remote notes for %s",
- sha1_to_hex(p->obj));
+ if (o->verbosity >= 2)
+ printf("Concatenating local and remote notes for %s\n",
+ sha1_to_hex(p->obj));
if (add_note(t, p->obj, p->remote, combine_notes_concatenate))
die("failed to concatenate notes "
"(combine_notes_concatenate)");
return 0;
case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
- OUTPUT(o, 2, "Concatenating unique lines in local and remote "
- "notes for %s", sha1_to_hex(p->obj));
+ if (o->verbosity >= 2)
+ printf("Concatenating unique lines in local and remote "
+ "notes for %s\n", sha1_to_hex(p->obj));
if (add_note(t, p->obj, p->remote, combine_notes_cat_sort_uniq))
die("failed to concatenate notes "
"(combine_notes_cat_sort_uniq)");
@@ -518,8 +522,9 @@ static int merge_from_diffs(struct notes_merge_options *o,
conflicts = merge_changes(o, changes, &num_changes, t);
free(changes);
- OUTPUT(o, 4, "Merge result: %i unmerged notes and a %s notes tree",
- conflicts, t->dirty ? "dirty" : "clean");
+ if (o->verbosity >= 4)
+ printf("Merge result: %i unmerged notes and a %s notes tree\n",
+ conflicts, t->dirty ? "dirty" : "clean");
return conflicts ? -1 : 1;
}
@@ -568,7 +573,7 @@ int notes_merge(struct notes_merge_options *o,
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (!resolve_ref(o->local_ref, local_sha1, 0, NULL))
+ if (read_ref_full(o->local_ref, local_sha1, 0, NULL))
die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_refname_format(o->local_ref, 0) &&
is_null_sha1(local_sha1))
@@ -617,33 +622,40 @@ int notes_merge(struct notes_merge_options *o,
if (!bases) {
base_sha1 = null_sha1;
base_tree_sha1 = EMPTY_TREE_SHA1_BIN;
- OUTPUT(o, 4, "No merge base found; doing history-less merge");
+ if (o->verbosity >= 4)
+ printf("No merge base found; doing history-less merge\n");
} else if (!bases->next) {
base_sha1 = bases->item->object.sha1;
base_tree_sha1 = bases->item->tree->object.sha1;
- OUTPUT(o, 4, "One merge base found (%.7s)",
- sha1_to_hex(base_sha1));
+ if (o->verbosity >= 4)
+ printf("One merge base found (%.7s)\n",
+ sha1_to_hex(base_sha1));
} else {
/* TODO: How to handle multiple merge-bases? */
base_sha1 = bases->item->object.sha1;
base_tree_sha1 = bases->item->tree->object.sha1;
- OUTPUT(o, 3, "Multiple merge bases found. Using the first "
- "(%.7s)", sha1_to_hex(base_sha1));
+ if (o->verbosity >= 3)
+ printf("Multiple merge bases found. Using the first "
+ "(%.7s)\n", sha1_to_hex(base_sha1));
}
- OUTPUT(o, 4, "Merging remote commit %.7s into local commit %.7s with "
- "merge-base %.7s", sha1_to_hex(remote->object.sha1),
- sha1_to_hex(local->object.sha1), sha1_to_hex(base_sha1));
+ if (o->verbosity >= 4)
+ printf("Merging remote commit %.7s into local commit %.7s with "
+ "merge-base %.7s\n", sha1_to_hex(remote->object.sha1),
+ sha1_to_hex(local->object.sha1),
+ sha1_to_hex(base_sha1));
if (!hashcmp(remote->object.sha1, base_sha1)) {
/* Already merged; result == local commit */
- OUTPUT(o, 2, "Already up-to-date!");
+ if (o->verbosity >= 2)
+ printf("Already up-to-date!\n");
hashcpy(result_sha1, local->object.sha1);
goto found_result;
}
if (!hashcmp(local->object.sha1, base_sha1)) {
/* Fast-forward; result == remote commit */
- OUTPUT(o, 2, "Fast-forward");
+ if (o->verbosity >= 2)
+ printf("Fast-forward\n");
hashcpy(result_sha1, remote->object.sha1);
goto found_result;
}
@@ -685,8 +697,9 @@ int notes_merge_commit(struct notes_merge_options *o,
int path_len = strlen(path), i;
const char *msg = strstr(partial_commit->buffer, "\n\n");
- OUTPUT(o, 3, "Committing notes in notes merge worktree at %.*s",
- path_len - 1, path);
+ if (o->verbosity >= 3)
+ printf("Committing notes in notes merge worktree at %.*s\n",
+ path_len - 1, path);
if (!msg || msg[2] == '\0')
die("partial notes commit has empty message");
@@ -701,7 +714,9 @@ int notes_merge_commit(struct notes_merge_options *o,
unsigned char obj_sha1[20], blob_sha1[20];
if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) {
- OUTPUT(o, 3, "Skipping non-SHA1 entry '%s'", ent->name);
+ if (o->verbosity >= 3)
+ printf("Skipping non-SHA1 entry '%s'\n",
+ ent->name);
continue;
}
@@ -713,14 +728,16 @@ int notes_merge_commit(struct notes_merge_options *o,
if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
die("Failed to add resolved note '%s' to notes tree",
ent->name);
- OUTPUT(o, 4, "Added resolved note for object %s: %s",
- sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
+ if (o->verbosity >= 4)
+ printf("Added resolved note for object %s: %s\n",
+ sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
}
create_notes_commit(partial_tree, partial_commit->parents, msg,
result_sha1);
- OUTPUT(o, 4, "Finalized notes merge commit: %s",
- sha1_to_hex(result_sha1));
+ if (o->verbosity >= 4)
+ printf("Finalized notes merge commit: %s\n",
+ sha1_to_hex(result_sha1));
free(path);
return 0;
}
@@ -732,7 +749,8 @@ int notes_merge_abort(struct notes_merge_options *o)
int ret;
strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
- OUTPUT(o, 3, "Removing notes merge worktree at %s", buf.buf);
+ if (o->verbosity >= 3)
+ printf("Removing notes merge worktree at %s\n", buf.buf);
ret = remove_dir_recursively(&buf, 0);
strbuf_release(&buf);
return ret;
diff --git a/object.c b/object.c
index 31976b5d70..d8d09f92aa 100644
--- a/object.c
+++ b/object.c
@@ -149,6 +149,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
struct tree *tree = lookup_tree(sha1);
if (tree) {
obj = &tree->object;
+ if (!tree->buffer)
+ tree->object.parsed = 0;
if (!tree->object.parsed) {
if (parse_tree_buffer(tree, buffer, size))
return NULL;
diff --git a/pack-check.c b/pack-check.c
index 0c19b6e5a5..63a595c45c 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "pack.h"
#include "pack-revindex.h"
+#include "progress.h"
struct idx_entry {
off_t offset;
@@ -42,7 +43,10 @@ int check_pack_crc(struct packed_git *p, struct pack_window **w_curs,
}
static int verify_packfile(struct packed_git *p,
- struct pack_window **w_curs)
+ struct pack_window **w_curs,
+ verify_fn fn,
+ struct progress *progress, uint32_t base_count)
+
{
off_t index_size = p->index_size;
const unsigned char *index_base = p->index_data;
@@ -113,20 +117,25 @@ static int verify_packfile(struct packed_git *p,
p->pack_name, (uintmax_t)offset);
}
data = unpack_entry(p, entries[i].offset, &type, &size);
- if (!data) {
+ if (!data)
err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
sha1_to_hex(entries[i].sha1), p->pack_name,
(uintmax_t)entries[i].offset);
- break;
- }
- if (check_sha1_signature(entries[i].sha1, data, size, typename(type))) {
+ else if (check_sha1_signature(entries[i].sha1, data, size, typename(type)))
err = error("packed %s from %s is corrupt",
sha1_to_hex(entries[i].sha1), p->pack_name);
- free(data);
- break;
+ else if (fn) {
+ int eaten = 0;
+ fn(entries[i].sha1, type, size, data, &eaten);
+ if (eaten)
+ data = NULL;
}
+ if (((base_count + i) & 1023) == 0)
+ display_progress(progress, base_count + i);
free(data);
+
}
+ display_progress(progress, base_count + i);
free(entries);
return err;
@@ -155,7 +164,8 @@ int verify_pack_index(struct packed_git *p)
return err;
}
-int verify_pack(struct packed_git *p)
+int verify_pack(struct packed_git *p, verify_fn fn,
+ struct progress *progress, uint32_t base_count)
{
int err = 0;
struct pack_window *w_curs = NULL;
@@ -164,7 +174,7 @@ int verify_pack(struct packed_git *p)
if (!p->index_data)
return -1;
- err |= verify_packfile(p, &w_curs);
+ err |= verify_packfile(p, &w_curs, fn, progress, base_count);
unuse_pack(&w_curs);
return err;
diff --git a/pack-write.c b/pack-write.c
index 9cd3bfbb4b..de2bd01414 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -129,6 +129,10 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
}
sha1write(f, obj->sha1, 20);
git_SHA1_Update(&ctx, obj->sha1, 20);
+ if ((opts->flags & WRITE_IDX_STRICT) &&
+ (i && !hashcmp(list[-2]->sha1, obj->sha1)))
+ die("The same object %s appears twice in the pack",
+ sha1_to_hex(obj->sha1));
}
if (index_version >= 2) {
@@ -178,6 +182,18 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
return index_name;
}
+off_t write_pack_header(struct sha1file *f, uint32_t nr_entries)
+{
+ struct pack_header hdr;
+
+ hdr.hdr_signature = htonl(PACK_SIGNATURE);
+ hdr.hdr_version = htonl(PACK_VERSION);
+ hdr.hdr_entries = htonl(nr_entries);
+ if (sha1write(f, &hdr, sizeof(hdr)))
+ return 0;
+ return sizeof(hdr);
+}
+
/*
* Update pack header with object_count and compute new SHA1 for pack data
* associated to pack_fd, and write that SHA1 at the end. That new SHA1
@@ -316,3 +332,44 @@ int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned
*hdr = c;
return n;
}
+
+struct sha1file *create_tmp_packfile(char **pack_tmp_name)
+{
+ char tmpname[PATH_MAX];
+ int fd;
+
+ fd = odb_mkstemp(tmpname, sizeof(tmpname), "pack/tmp_pack_XXXXXX");
+ *pack_tmp_name = xstrdup(tmpname);
+ return sha1fd(fd, *pack_tmp_name);
+}
+
+void finish_tmp_packfile(char *name_buffer,
+ const char *pack_tmp_name,
+ struct pack_idx_entry **written_list,
+ uint32_t nr_written,
+ struct pack_idx_option *pack_idx_opts,
+ unsigned char sha1[])
+{
+ const char *idx_tmp_name;
+ char *end_of_name_prefix = strrchr(name_buffer, 0);
+
+ if (adjust_shared_perm(pack_tmp_name))
+ die_errno("unable to make temporary pack file readable");
+
+ idx_tmp_name = write_idx_file(NULL, written_list, nr_written,
+ pack_idx_opts, sha1);
+ if (adjust_shared_perm(idx_tmp_name))
+ die_errno("unable to make temporary index file readable");
+
+ sprintf(end_of_name_prefix, "%s.pack", sha1_to_hex(sha1));
+ free_pack_by_name(name_buffer);
+
+ if (rename(pack_tmp_name, name_buffer))
+ die_errno("unable to rename temporary pack file");
+
+ sprintf(end_of_name_prefix, "%s.idx", sha1_to_hex(sha1));
+ if (rename(idx_tmp_name, name_buffer))
+ die_errno("unable to rename temporary index file");
+
+ free((void *)idx_tmp_name);
+}
diff --git a/pack.h b/pack.h
index 722a54e00a..aa6ee7d606 100644
--- a/pack.h
+++ b/pack.h
@@ -2,6 +2,7 @@
#define PACK_H
#include "object.h"
+#include "csum-file.h"
/*
* Packed object header
@@ -37,7 +38,8 @@ struct pack_header {
struct pack_idx_option {
unsigned flags;
/* flag bits */
-#define WRITE_IDX_VERIFY 01
+#define WRITE_IDX_VERIFY 01 /* verify only, do not write the idx file */
+#define WRITE_IDX_STRICT 02
uint32_t version;
uint32_t off32_limit;
@@ -70,10 +72,15 @@ struct pack_idx_entry {
off_t offset;
};
+
+struct progress;
+typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*);
+
extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, unsigned char *sha1);
extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
extern int verify_pack_index(struct packed_git *);
-extern int verify_pack(struct packed_git *);
+extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+extern off_t write_pack_header(struct sha1file *f, uint32_t);
extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
extern char *index_pack_lockfile(int fd);
extern int encode_in_pack_object_header(enum object_type, uintmax_t, unsigned char *);
@@ -82,4 +89,8 @@ extern int encode_in_pack_object_header(enum object_type, uintmax_t, unsigned ch
#define PH_ERROR_PACK_SIGNATURE (-2)
#define PH_ERROR_PROTOCOL (-3)
extern int read_pack_header(int fd, struct pack_header *);
+
+extern struct sha1file *create_tmp_packfile(char **pack_tmp_name);
+extern void finish_tmp_packfile(char *name_buffer, const char *pack_tmp_name, struct pack_idx_entry **written_list, uint32_t nr_written, struct pack_idx_option *pack_idx_opts, unsigned char sha1[]);
+
#endif
diff --git a/perl/.gitignore b/perl/.gitignore
index 98b24772c7..9235e73163 100644
--- a/perl/.gitignore
+++ b/perl/.gitignore
@@ -1,5 +1,6 @@
perl.mak
perl.mak.old
+MYMETA.yml
blib
blibdirs
pm_to_blib
diff --git a/reachable.c b/reachable.c
index 3fc6b1d320..bf7970661f 100644
--- a/reachable.c
+++ b/reachable.c
@@ -7,11 +7,25 @@
#include "revision.h"
#include "reachable.h"
#include "cache-tree.h"
+#include "progress.h"
+
+struct connectivity_progress {
+ struct progress *progress;
+ unsigned long count;
+};
+
+static void update_progress(struct connectivity_progress *cp)
+{
+ cp->count++;
+ if ((cp->count & 1023) == 0)
+ display_progress(cp->progress, cp->count);
+}
static void process_blob(struct blob *blob,
struct object_array *p,
struct name_path *path,
- const char *name)
+ const char *name,
+ struct connectivity_progress *cp)
{
struct object *obj = &blob->object;
@@ -20,6 +34,7 @@ static void process_blob(struct blob *blob,
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
+ update_progress(cp);
/* Nothing to do, really .. The blob lookup was the important part */
}
@@ -34,7 +49,8 @@ static void process_gitlink(const unsigned char *sha1,
static void process_tree(struct tree *tree,
struct object_array *p,
struct name_path *path,
- const char *name)
+ const char *name,
+ struct connectivity_progress *cp)
{
struct object *obj = &tree->object;
struct tree_desc desc;
@@ -46,6 +62,7 @@ static void process_tree(struct tree *tree,
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
+ update_progress(cp);
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
add_object(obj, p, path, name);
@@ -57,23 +74,25 @@ 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);
+ process_tree(lookup_tree(entry.sha1), p, &me, entry.path, cp);
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);
+ process_blob(lookup_blob(entry.sha1), p, &me, entry.path, cp);
}
free(tree->buffer);
tree->buffer = NULL;
}
-static void process_tag(struct tag *tag, struct object_array *p, const char *name)
+static void process_tag(struct tag *tag, struct object_array *p,
+ const char *name, struct connectivity_progress *cp)
{
struct object *obj = &tag->object;
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
+ update_progress(cp);
if (parse_tag(tag) < 0)
die("bad tag object %s", sha1_to_hex(obj->sha1));
@@ -81,15 +100,18 @@ static void process_tag(struct tag *tag, struct object_array *p, const char *nam
add_object(tag->tagged, p, NULL, name);
}
-static void walk_commit_list(struct rev_info *revs)
+static void walk_commit_list(struct rev_info *revs,
+ struct connectivity_progress *cp)
{
int i;
struct commit *commit;
struct object_array objects = OBJECT_ARRAY_INIT;
/* Walk all commits, process their trees */
- while ((commit = get_revision(revs)) != NULL)
- process_tree(commit->tree, &objects, NULL, "");
+ while ((commit = get_revision(revs)) != NULL) {
+ process_tree(commit->tree, &objects, NULL, "", cp);
+ update_progress(cp);
+ }
/* Then walk all the pending objects, recursively processing them too */
for (i = 0; i < revs->pending.nr; i++) {
@@ -97,15 +119,15 @@ static void walk_commit_list(struct rev_info *revs)
struct object *obj = pending->item;
const char *name = pending->name;
if (obj->type == OBJ_TAG) {
- process_tag((struct tag *) obj, &objects, name);
+ process_tag((struct tag *) obj, &objects, name, cp);
continue;
}
if (obj->type == OBJ_TREE) {
- process_tree((struct tree *)obj, &objects, NULL, name);
+ process_tree((struct tree *)obj, &objects, NULL, name, cp);
continue;
}
if (obj->type == OBJ_BLOB) {
- process_blob((struct blob *)obj, &objects, NULL, name);
+ process_blob((struct blob *)obj, &objects, NULL, name, cp);
continue;
}
die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
@@ -191,8 +213,11 @@ static void add_cache_refs(struct rev_info *revs)
add_cache_tree(active_cache_tree, revs);
}
-void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
+void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
+ struct progress *progress)
{
+ struct connectivity_progress cp;
+
/*
* Set up revision parsing, and mark us as being interested
* in all object types, not just commits.
@@ -211,11 +236,15 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
if (mark_reflog)
for_each_reflog(add_one_reflog, revs);
+ cp.progress = progress;
+ cp.count = 0;
+
/*
* Set up the revision walk - this will move all commits
* from the pending list to the commit walking list.
*/
if (prepare_revision_walk(revs))
die("revision walk setup failed");
- walk_commit_list(revs);
+ walk_commit_list(revs, &cp);
+ display_progress(cp.progress, cp.count);
}
diff --git a/reachable.h b/reachable.h
index 40751810b6..5d082adfec 100644
--- a/reachable.h
+++ b/reachable.h
@@ -1,6 +1,7 @@
#ifndef REACHEABLE_H
#define REACHEABLE_H
-extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog);
+struct progress;
+extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, struct progress *);
#endif
diff --git a/read-cache.c b/read-cache.c
index 5790a91044..a51bba1b95 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1001,7 +1001,8 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
*/
static struct cache_entry *refresh_cache_ent(struct index_state *istate,
struct cache_entry *ce,
- unsigned int options, int *err)
+ unsigned int options, int *err,
+ int *changed_ret)
{
struct stat st;
struct cache_entry *updated;
@@ -1033,6 +1034,8 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
}
changed = ie_match_stat(istate, ce, &st, options);
+ if (changed_ret)
+ *changed_ret = changed;
if (!changed) {
/*
* The path is unchanged. If we were told to ignore
@@ -1102,14 +1105,21 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
int first = 1;
int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
- const char *needs_update_fmt;
- const char *needs_merge_fmt;
-
- needs_update_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
- needs_merge_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
+ const char *modified_fmt;
+ const char *deleted_fmt;
+ const char *typechange_fmt;
+ const char *added_fmt;
+ const char *unmerged_fmt;
+
+ modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
+ deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n");
+ typechange_fmt = (in_porcelain ? "T\t%s\n" : "%s needs update\n");
+ added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n");
+ unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce, *new;
int cache_errno = 0;
+ int changed = 0;
ce = istate->cache[i];
if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
@@ -1122,7 +1132,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
i--;
if (allow_unmerged)
continue;
- show_file(needs_merge_fmt, ce->name, in_porcelain, &first, header_msg);
+ show_file(unmerged_fmt, ce->name, in_porcelain, &first, header_msg);
has_errors = 1;
continue;
}
@@ -1130,10 +1140,12 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
continue;
- new = refresh_cache_ent(istate, ce, options, &cache_errno);
+ new = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
if (new == ce)
continue;
if (!new) {
+ const char *fmt;
+
if (not_new && cache_errno == ENOENT)
continue;
if (really && cache_errno == EINVAL) {
@@ -1145,7 +1157,17 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
}
if (quiet)
continue;
- show_file(needs_update_fmt, ce->name, in_porcelain, &first, header_msg);
+
+ if (cache_errno == ENOENT)
+ fmt = deleted_fmt;
+ else if (ce->ce_flags & CE_INTENT_TO_ADD)
+ fmt = added_fmt; /* must be before other checks */
+ else if (changed & TYPE_CHANGED)
+ fmt = typechange_fmt;
+ else
+ fmt = modified_fmt;
+ show_file(fmt,
+ ce->name, in_porcelain, &first, header_msg);
has_errors = 1;
continue;
}
@@ -1157,7 +1179,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
{
- return refresh_cache_ent(&the_index, ce, really, NULL);
+ return refresh_cache_ent(&the_index, ce, really, NULL, NULL);
}
static int verify_hdr(struct cache_header *hdr, unsigned long size)
@@ -1202,29 +1224,18 @@ int read_index(struct index_state *istate)
return read_index_from(istate, get_index_file());
}
-static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
+static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk)
{
+ struct cache_entry *ce;
size_t len;
const char *name;
+ unsigned int flags;
- ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
- ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
- ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
- ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
- ce->ce_dev = ntohl(ondisk->dev);
- ce->ce_ino = ntohl(ondisk->ino);
- ce->ce_mode = ntohl(ondisk->mode);
- ce->ce_uid = ntohl(ondisk->uid);
- ce->ce_gid = ntohl(ondisk->gid);
- ce->ce_size = ntohl(ondisk->size);
/* On-disk flags are just 16 bits */
- ce->ce_flags = ntohs(ondisk->flags);
-
- hashcpy(ce->sha1, ondisk->sha1);
-
- len = ce->ce_flags & CE_NAMEMASK;
+ flags = ntohs(ondisk->flags);
+ len = flags & CE_NAMEMASK;
- if (ce->ce_flags & CE_EXTENDED) {
+ if (flags & CE_EXTENDED) {
struct ondisk_cache_entry_extended *ondisk2;
int extended_flags;
ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
@@ -1232,7 +1243,7 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
if (extended_flags & ~CE_EXTENDED_FLAGS)
die("Unknown index entry format %08x", extended_flags);
- ce->ce_flags |= extended_flags;
+ flags |= extended_flags;
name = ondisk2->name;
}
else
@@ -1240,25 +1251,26 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
if (len == CE_NAMEMASK)
len = strlen(name);
- /*
- * NEEDSWORK: If the original index is crafted, this copy could
- * go unchecked.
- */
- memcpy(ce->name, name, len + 1);
-}
-static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
-{
- size_t fix_size_mem = offsetof(struct cache_entry, name);
- size_t fix_size_dsk = offsetof(struct ondisk_cache_entry, name);
- long per_entry = (fix_size_mem - fix_size_dsk + 7) & ~7;
+ ce = xmalloc(cache_entry_size(len));
- /*
- * Alignment can cause differences. This should be "alignof", but
- * since that's a gcc'ism, just use the size of a pointer.
- */
- per_entry += sizeof(void *);
- return ondisk_size + entries*per_entry;
+ ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
+ ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
+ ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
+ ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
+ ce->ce_dev = ntohl(ondisk->dev);
+ ce->ce_ino = ntohl(ondisk->ino);
+ ce->ce_mode = ntohl(ondisk->mode);
+ ce->ce_uid = ntohl(ondisk->uid);
+ ce->ce_gid = ntohl(ondisk->gid);
+ ce->ce_size = ntohl(ondisk->size);
+ ce->ce_flags = flags;
+
+ hashcpy(ce->sha1, ondisk->sha1);
+
+ memcpy(ce->name, name, len);
+ ce->name[len] = '\0';
+ return ce;
}
/* remember to discard_cache() before reading a different cache! */
@@ -1266,7 +1278,7 @@ int read_index_from(struct index_state *istate, const char *path)
{
int fd, i;
struct stat st;
- unsigned long src_offset, dst_offset;
+ unsigned long src_offset;
struct cache_header *hdr;
void *mmap;
size_t mmap_size;
@@ -1305,29 +1317,18 @@ int read_index_from(struct index_state *istate, const char *path)
istate->cache_nr = ntohl(hdr->hdr_entries);
istate->cache_alloc = alloc_nr(istate->cache_nr);
istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
-
- /*
- * The disk format is actually larger than the in-memory format,
- * due to space for nsec etc, so even though the in-memory one
- * has room for a few more flags, we can allocate using the same
- * index size
- */
- istate->alloc = xmalloc(estimate_cache_size(mmap_size, istate->cache_nr));
istate->initialized = 1;
src_offset = sizeof(*hdr);
- dst_offset = 0;
for (i = 0; i < istate->cache_nr; i++) {
struct ondisk_cache_entry *disk_ce;
struct cache_entry *ce;
disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
- ce = (struct cache_entry *)((char *)istate->alloc + dst_offset);
- convert_from_disk(disk_ce, ce);
+ ce = create_from_disk(disk_ce);
set_index_entry(istate, i, ce);
src_offset += ondisk_ce_size(ce);
- dst_offset += ce_size(ce);
}
istate->timestamp.sec = st.st_mtime;
istate->timestamp.nsec = ST_MTIME_NSEC(st);
@@ -1361,11 +1362,15 @@ unmap:
int is_index_unborn(struct index_state *istate)
{
- return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec);
+ return (!istate->cache_nr && !istate->timestamp.sec);
}
int discard_index(struct index_state *istate)
{
+ int i;
+
+ for (i = 0; i < istate->cache_nr; i++)
+ free(istate->cache[i]);
resolve_undo_clear_index(istate);
istate->cache_nr = 0;
istate->cache_changed = 0;
@@ -1374,8 +1379,6 @@ int discard_index(struct index_state *istate)
istate->name_hash_initialized = 0;
free_hash(&istate->name_hash);
cache_tree_free(&(istate->cache_tree));
- free(istate->alloc);
- istate->alloc = NULL;
istate->initialized = 0;
/* no need to throw away allocated active_cache */
diff --git a/reflog-walk.c b/reflog-walk.c
index 5d81d39a52..64c677fc49 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -50,9 +50,13 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
for_each_reflog_ent(ref, read_one_reflog, reflogs);
if (reflogs->nr == 0) {
unsigned char sha1[20];
- const char *name = resolve_ref(ref, sha1, 1, NULL);
- if (name)
+ const char *name;
+ void *name_to_free;
+ name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
+ if (name) {
for_each_reflog_ent(name, read_one_reflog, reflogs);
+ free(name_to_free);
+ }
}
if (reflogs->nr == 0) {
int len = strlen(ref);
@@ -168,11 +172,11 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
else {
if (*branch == '\0') {
unsigned char sha1[20];
- const char *head = resolve_ref("HEAD", sha1, 0, NULL);
- if (!head)
- die ("No current branch");
free(branch);
- branch = xstrdup(head);
+ branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ if (!branch)
+ die ("No current branch");
+
}
reflogs = read_complete_reflog(branch);
if (!reflogs || reflogs->nr == 0) {
diff --git a/refs.c b/refs.c
index e69ba26b74..4bf45b1979 100644
--- a/refs.c
+++ b/refs.c
@@ -48,7 +48,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
}
static void add_ref(const char *name, const unsigned char *sha1,
- int flag, struct ref_array *refs,
+ int flag, int check_name, struct ref_array *refs,
struct ref_entry **new_entry)
{
int len;
@@ -59,7 +59,8 @@ static void add_ref(const char *name, const unsigned char *sha1,
entry = xmalloc(sizeof(struct ref_entry) + len);
hashcpy(entry->sha1, sha1);
hashclr(entry->peeled);
- if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+ if (check_name &&
+ check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
die("Reference has invalid format: '%s'", name);
memcpy(entry->name, name, len);
entry->flag = flag;
@@ -234,7 +235,7 @@ static void read_packed_refs(FILE *f, struct ref_array *array)
name = parse_ref_line(refline, sha1);
if (name) {
- add_ref(name, sha1, flag, array, &last);
+ add_ref(name, sha1, flag, 1, array, &last);
continue;
}
if (last &&
@@ -249,7 +250,7 @@ static void read_packed_refs(FILE *f, struct ref_array *array)
void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
{
- add_ref(name, sha1, flag, &extra_refs, NULL);
+ add_ref(name, sha1, flag, 0, &extra_refs, NULL);
}
void clear_extra_refs(void)
@@ -333,12 +334,11 @@ static void get_ref_dir(const char *submodule, const char *base,
hashclr(sha1);
flag |= REF_ISBROKEN;
}
- } else
- if (!resolve_ref(ref, sha1, 1, &flag)) {
- hashclr(sha1);
- flag |= REF_ISBROKEN;
- }
- add_ref(ref, sha1, flag, array, NULL);
+ } else if (read_ref_full(ref, sha1, 1, &flag)) {
+ hashclr(sha1);
+ flag |= REF_ISBROKEN;
+ }
+ add_ref(ref, sha1, flag, 1, array, NULL);
}
free(ref);
closedir(dir);
@@ -361,7 +361,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref(refname, junk, 0, NULL);
+ resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
if (!resolves_to || strcmp(resolves_to, d->refname))
return 0;
@@ -497,7 +497,7 @@ static int get_packed_ref(const char *ref, unsigned char *sha1)
return -1;
}
-const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
+const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH;
ssize_t len;
@@ -605,6 +605,12 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
return ref;
}
+char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+{
+ const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
+ return ret ? xstrdup(ret) : NULL;
+}
+
/* The argument to filter_refs */
struct ref_filter {
const char *pattern;
@@ -612,13 +618,18 @@ struct ref_filter {
void *cb_data;
};
-int read_ref(const char *ref, unsigned char *sha1)
+int read_ref_full(const char *ref, unsigned char *sha1, int reading, int *flags)
{
- if (resolve_ref(ref, sha1, 1, NULL))
+ if (resolve_ref_unsafe(ref, sha1, reading, flags))
return 0;
return -1;
}
+int read_ref(const char *ref, unsigned char *sha1)
+{
+ return read_ref_full(ref, sha1, 1, NULL);
+}
+
#define DO_FOR_EACH_INCLUDE_BROKEN 01
static int do_one_ref(const char *base, each_ref_fn fn, int trim,
int flags, void *cb_data, struct ref_entry *entry)
@@ -663,7 +674,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
goto fallback;
}
- if (!resolve_ref(ref, base, 1, &flag))
+ if (read_ref_full(ref, base, 1, &flag))
return -1;
if ((flag & REF_ISPACKED)) {
@@ -746,7 +757,7 @@ static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
return 0;
}
- if (resolve_ref("HEAD", sha1, 1, &flag))
+ if (!read_ref_full("HEAD", sha1, 1, &flag))
return fn("HEAD", sha1, flag, cb_data);
return 0;
@@ -826,7 +837,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (resolve_ref(buf.buf, sha1, 1, &flag))
+ if (!read_ref_full(buf.buf, sha1, 1, &flag))
ret = fn(buf.buf, sha1, flag, cb_data);
strbuf_release(&buf);
@@ -998,13 +1009,6 @@ const char *ref_rev_parse_rules[] = {
NULL
};
-const char *ref_fetch_rules[] = {
- "%.*s",
- "refs/%.*s",
- "refs/heads/%.*s",
- NULL
-};
-
int refname_match(const char *abbrev_name, const char *full_name, const char **rules)
{
const char **p;
@@ -1022,7 +1026,7 @@ int refname_match(const char *abbrev_name, const char *full_name, const char **r
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
- if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+ if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
return NULL;
@@ -1113,7 +1117,7 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
this_result = refs_found ? sha1_from_ref : sha1;
mksnpath(fullref, sizeof(fullref), *p, len, str);
- r = resolve_ref(fullref, this_result, 1, &flag);
+ r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -1143,7 +1147,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
const char *ref, *it;
mksnpath(path, sizeof(path), *p, len, str);
- ref = resolve_ref(path, hash, 1, NULL);
+ ref = resolve_ref_unsafe(path, hash, 1, NULL);
if (!ref)
continue;
if (!stat(git_path("logs/%s", path), &st) &&
@@ -1179,7 +1183,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- ref = resolve_ref(ref, lock->old_sha1, mustexist, &type);
+ ref = resolve_ref_unsafe(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;
@@ -1192,7 +1196,7 @@ 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, &type);
+ ref = resolve_ref_unsafe(orig_ref, lock->old_sha1, mustexist, &type);
}
if (type_p)
*type_p = type;
@@ -1355,7 +1359,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldref);
- symref = resolve_ref(oldref, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(oldref, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
return error("refname %s is a symbolic ref, renaming it is not supported",
oldref);
@@ -1377,7 +1381,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
goto rollback;
}
- if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
+ if (!read_ref_full(newref, sha1, 1, &flag) &&
+ delete_ref(newref, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newref))) {
error("Directory not empty: %s", newref);
@@ -1643,7 +1648,7 @@ int write_ref_sha1(struct ref_lock *lock,
unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
- head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+ head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -1929,7 +1934,7 @@ static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
retval = do_for_each_reflog(log, fn, cb_data);
} else {
unsigned char sha1[20];
- if (!resolve_ref(log, sha1, 0, NULL))
+ if (read_ref_full(log, sha1, 0, NULL))
retval = error("bad ref for %s", log);
else
retval = fn(log, sha1, 0, cb_data);
@@ -1980,7 +1985,7 @@ int update_ref(const char *action, const char *refname,
int ref_exists(const char *refname)
{
unsigned char sha1[20];
- return !!resolve_ref(refname, sha1, 1, NULL);
+ return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
}
struct ref *find_ref_by_name(const struct ref *list, const char *name)
@@ -2072,7 +2077,6 @@ char *shorten_unambiguous_ref(const char *ref, int strict)
*/
for (j = 0; j < rules_to_fail; j++) {
const char *rule = ref_rev_parse_rules[j];
- unsigned char short_objectname[20];
char refname[PATH_MAX];
/* skip matched rule */
@@ -2086,7 +2090,7 @@ char *shorten_unambiguous_ref(const char *ref, int strict)
*/
mksnpath(refname, sizeof(refname),
rule, short_name_len, short_name);
- if (!read_ref(refname, short_objectname))
+ if (ref_exists(refname))
break;
}
diff --git a/remote-curl.c b/remote-curl.c
index 0e720ee8bb..6a352de7be 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -200,7 +200,7 @@ static struct ref *parse_git_refs(struct discovery *heads)
if (start_async(&async))
die("cannot start thread to parse advertised refs");
- get_remote_heads(async.out, &list, 0, NULL, 0, NULL);
+ get_remote_heads(async.out, &list, 0, NULL);
close(async.out);
if (finish_async(&async))
die("ref parsing thread failed");
@@ -859,7 +859,7 @@ int main(int argc, const char **argv)
url = strbuf_detach(&buf, NULL);
- http_init(remote, url);
+ http_init(remote, url, 0);
do {
if (strbuf_getline(&buf, stdin, '\n') == EOF) {
diff --git a/remote.c b/remote.c
index e2ef991144..73a3809300 100644
--- a/remote.c
+++ b/remote.c
@@ -482,7 +482,7 @@ static void read_config(void)
return;
default_remote_name = xstrdup("origin");
current_branch = NULL;
- head_ref = resolve_ref("HEAD", sha1, 0, &flag);
+ head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
!prefixcmp(head_ref, "refs/heads/")) {
current_branch =
@@ -1007,7 +1007,7 @@ static char *guess_ref(const char *name, struct ref *peer)
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+ const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
if (!r)
return NULL;
@@ -1058,7 +1058,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
unsigned char sha1[20];
int flag;
- dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
+ dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
prefixcmp(dst_value, "refs/heads/")))
@@ -1507,13 +1507,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
* nothing to report.
*/
base = branch->merge[0]->dst;
- if (!resolve_ref(base, sha1, 1, NULL))
+ if (read_ref(base, sha1))
return 0;
theirs = lookup_commit_reference(sha1);
if (!theirs)
return 0;
- if (!resolve_ref(branch->refname, sha1, 1, NULL))
+ if (read_ref(branch->refname, sha1))
return 0;
ours = lookup_commit_reference(sha1);
if (!ours)
diff --git a/sequencer.c b/sequencer.c
index bc2c046aab..d1f28a6945 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3,17 +3,11 @@
#include "strbuf.h"
#include "dir.h"
-void remove_sequencer_state(int aggressive)
+void remove_sequencer_state(void)
{
struct strbuf seq_dir = STRBUF_INIT;
- struct strbuf seq_old_dir = STRBUF_INIT;
strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
- strbuf_addf(&seq_old_dir, "%s", git_path(SEQ_OLD_DIR));
- remove_dir_recursively(&seq_old_dir, 0);
- rename(git_path(SEQ_DIR), git_path(SEQ_OLD_DIR));
- if (aggressive)
- remove_dir_recursively(&seq_old_dir, 0);
+ remove_dir_recursively(&seq_dir, 0);
strbuf_release(&seq_dir);
- strbuf_release(&seq_old_dir);
}
diff --git a/sequencer.h b/sequencer.h
index 905d295012..2d4528f292 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -2,19 +2,11 @@
#define SEQUENCER_H
#define SEQ_DIR "sequencer"
-#define SEQ_OLD_DIR "sequencer-old"
#define SEQ_HEAD_FILE "sequencer/head"
#define SEQ_TODO_FILE "sequencer/todo"
#define SEQ_OPTS_FILE "sequencer/opts"
-/*
- * Removes SEQ_OLD_DIR and renames SEQ_DIR to SEQ_OLD_DIR, ignoring
- * any errors. Intended to be used by 'git reset'.
- *
- * With the aggressive flag, it additionally removes SEQ_OLD_DIR,
- * ignoring any errors. Inteded to be used by the sequencer's
- * '--reset' subcommand.
- */
-void remove_sequencer_state(int aggressive);
+/* Removes SEQ_DIR. */
+extern void remove_sequencer_state(void);
#endif
diff --git a/sha1_file.c b/sha1_file.c
index 27f3b9b278..f291f3f0f7 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -18,6 +18,7 @@
#include "refs.h"
#include "pack-revindex.h"
#include "sha1-lookup.h"
+#include "bulk-checkin.h"
#ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -1267,7 +1268,8 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
while (c & 0x80) {
if (len <= used || bitsizeof(long) <= shift) {
error("bad object header");
- return 0;
+ size = used = 0;
+ break;
}
c = buf[used++];
size += (c & 0x7f) << shift;
@@ -2616,7 +2618,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
if ((type == OBJ_BLOB) && path) {
struct strbuf nbuf = STRBUF_INIT;
if (convert_to_git(path, buf, size, &nbuf,
- write_object ? safe_crlf : 0)) {
+ write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
buf = strbuf_detach(&nbuf, &size);
re_allocated = 1;
}
@@ -2679,10 +2681,8 @@ static int index_core(unsigned char *sha1, int fd, size_t size,
}
/*
- * This creates one packfile per large blob, because the caller
- * immediately wants the result sha1, and fast-import can report the
- * object name via marks mechanism only by closing the created
- * packfile.
+ * This creates one packfile per large blob unless bulk-checkin
+ * machinery is "plugged".
*
* This also bypasses the usual "convert-to-git" dance, and that is on
* purpose. We could write a streaming version of the converting
@@ -2696,65 +2696,7 @@ static int index_stream(unsigned char *sha1, int fd, size_t size,
enum object_type type, const char *path,
unsigned flags)
{
- struct child_process fast_import;
- char export_marks[512];
- const char *argv[] = { "fast-import", "--quiet", export_marks, NULL };
- char tmpfile[512];
- char fast_import_cmd[512];
- char buf[512];
- int len, tmpfd;
-
- strcpy(tmpfile, git_path("hashstream_XXXXXX"));
- tmpfd = git_mkstemp_mode(tmpfile, 0600);
- if (tmpfd < 0)
- die_errno("cannot create tempfile: %s", tmpfile);
- if (close(tmpfd))
- die_errno("cannot close tempfile: %s", tmpfile);
- sprintf(export_marks, "--export-marks=%s", tmpfile);
-
- memset(&fast_import, 0, sizeof(fast_import));
- fast_import.in = -1;
- fast_import.argv = argv;
- fast_import.git_cmd = 1;
- if (start_command(&fast_import))
- die_errno("index-stream: git fast-import failed");
-
- len = sprintf(fast_import_cmd, "blob\nmark :1\ndata %lu\n",
- (unsigned long) size);
- write_or_whine(fast_import.in, fast_import_cmd, len,
- "index-stream: feeding fast-import");
- while (size) {
- char buf[10240];
- size_t sz = size < sizeof(buf) ? size : sizeof(buf);
- ssize_t actual;
-
- actual = read_in_full(fd, buf, sz);
- if (actual < 0)
- die_errno("index-stream: reading input");
- if (write_in_full(fast_import.in, buf, actual) != actual)
- die_errno("index-stream: feeding fast-import");
- size -= actual;
- }
- if (close(fast_import.in))
- die_errno("index-stream: closing fast-import");
- if (finish_command(&fast_import))
- die_errno("index-stream: finishing fast-import");
-
- tmpfd = open(tmpfile, O_RDONLY);
- if (tmpfd < 0)
- die_errno("index-stream: cannot open fast-import mark");
- len = read(tmpfd, buf, sizeof(buf));
- if (len < 0)
- die_errno("index-stream: reading fast-import mark");
- if (close(tmpfd) < 0)
- die_errno("index-stream: closing fast-import mark");
- if (unlink(tmpfile))
- die_errno("index-stream: unlinking fast-import mark");
- if (len != 44 ||
- memcmp(":1 ", buf, 3) ||
- get_sha1_hex(buf + 3, sha1))
- die_errno("index-stream: unexpected fast-import mark: <%s>", buf);
- return 0;
+ return index_bulk_checkin(sha1, fd, size, type, path, flags);
}
int index_fd(unsigned char *sha1, int fd, struct stat *st,
diff --git a/strbuf.c b/strbuf.c
index 3ad2cc0016..ff0b96b416 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -397,3 +397,54 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
return len;
}
+
+void strbuf_add_lines(struct strbuf *out, const char *prefix,
+ const char *buf, size_t size)
+{
+ while (size) {
+ const char *next = memchr(buf, '\n', size);
+ next = next ? (next + 1) : (buf + size);
+ strbuf_addstr(out, prefix);
+ strbuf_add(out, buf, next - buf);
+ size -= next - buf;
+ buf = next;
+ }
+ strbuf_complete_line(out);
+}
+
+static int is_rfc3986_reserved(char ch)
+{
+ switch (ch) {
+ case '!': case '*': case '\'': case '(': case ')': case ';':
+ case ':': case '@': case '&': case '=': case '+': case '$':
+ case ',': case '/': case '?': case '#': case '[': case ']':
+ return 1;
+ }
+ return 0;
+}
+
+static int is_rfc3986_unreserved(char ch)
+{
+ return isalnum(ch) ||
+ ch == '-' || ch == '_' || ch == '.' || ch == '~';
+}
+
+void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
+ int reserved)
+{
+ strbuf_grow(sb, len);
+ while (len--) {
+ char ch = *s++;
+ if (is_rfc3986_unreserved(ch) ||
+ (!reserved && is_rfc3986_reserved(ch)))
+ strbuf_addch(sb, ch);
+ else
+ strbuf_addf(sb, "%%%02x", ch);
+ }
+}
+
+void strbuf_addstr_urlencode(struct strbuf *sb, const char *s,
+ int reserved)
+{
+ strbuf_add_urlencode(sb, s, strlen(s), reserved);
+}
diff --git a/strbuf.h b/strbuf.h
index 46a33f8c46..fbf059f4d3 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -100,6 +100,14 @@ extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
__attribute__((format (printf,2,0)))
extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
+extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
+
+static inline void strbuf_complete_line(struct strbuf *sb)
+{
+ if (sb->len && sb->buf[sb->len - 1] != '\n')
+ strbuf_addch(sb, '\n');
+}
+
extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
/* XXX: if read fails, any partial read is undone */
extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
@@ -115,4 +123,9 @@ extern int launch_editor(const char *path, struct strbuf *buffer, const char *co
extern int strbuf_branchname(struct strbuf *sb, const char *name);
extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
+extern void strbuf_add_urlencode(struct strbuf *, const char *, size_t,
+ int reserved);
+extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
+ int reserved);
+
#endif /* STRBUF_H */
diff --git a/submodule.c b/submodule.c
index 0fd10a0fdb..68c1ba90b9 100644
--- a/submodule.c
+++ b/submodule.c
@@ -391,7 +391,7 @@ static void commit_need_pushing(struct commit *commit, struct commit_list *paren
rev.diffopt.format_callback_data = needs_pushing;
diff_tree_combined(commit->object.sha1, parents, n, 1, &rev);
- free(parents);
+ free((void *)parents);
}
int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
@@ -689,7 +689,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
cp.out = -1;
cp.dir = path;
if (start_command(&cp))
- die("Could not run git status --porcelain");
+ die("Could not run 'git status --porcelain' in submodule %s", path);
len = strbuf_read(&buf, cp.out, 1024);
line = buf.buf;
@@ -714,7 +714,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
close(cp.out);
if (finish_command(&cp))
- die("git status --porcelain failed");
+ die("'git status --porcelain' failed in submodule %s", path);
strbuf_release(&buf);
return dirty_submodule;
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
new file mode 100755
index 0000000000..4a37cd79e5
--- /dev/null
+++ b/t/lib-credential.sh
@@ -0,0 +1,254 @@
+#!/bin/sh
+
+# Try a set of credential helpers; the expected stdin,
+# stdout and stderr should be provided on stdin,
+# separated by "--".
+check() {
+ read_chunk >stdin &&
+ read_chunk >expect-stdout &&
+ read_chunk >expect-stderr &&
+ test-credential "$@" <stdin >stdout 2>stderr &&
+ test_cmp expect-stdout stdout &&
+ test_cmp expect-stderr stderr
+}
+
+read_chunk() {
+ while read line; do
+ case "$line" in
+ --) break ;;
+ *) echo "$line" ;;
+ esac
+ done
+}
+
+# Clear any residual data from previous tests. We only
+# need this when testing third-party helpers which read and
+# write outside of our trash-directory sandbox.
+#
+# Don't bother checking for success here, as it is
+# outside the scope of tests and represents a best effort to
+# clean up after ourselves.
+helper_test_clean() {
+ reject $1 https example.com store-user
+ reject $1 https example.com user1
+ reject $1 https example.com user2
+ reject $1 http path.tld user
+ reject $1 https timeout.tld user
+}
+
+reject() {
+ (
+ echo protocol=$2
+ echo host=$3
+ echo username=$4
+ ) | test-credential reject $1
+}
+
+helper_test() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) has no existing data" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com'\'':
+ askpass: Password for '\''https://askpass-username@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) stores password" '
+ check approve $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=store-user
+ password=store-pass
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can retrieve password" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ username=store-user
+ password=store-pass
+ --
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching protocol" '
+ check fill $HELPER <<-\EOF
+ protocol=http
+ host=example.com
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''http://example.com'\'':
+ askpass: Password for '\''http://askpass-username@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching host" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=other.tld
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://other.tld'\'':
+ askpass: Password for '\''https://askpass-username@other.tld'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching username" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=other
+ --
+ username=other
+ password=askpass-password
+ --
+ askpass: Password for '\''https://other@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) requires matching path" '
+ test_config credential.usehttppath true &&
+ check approve $HELPER <<-\EOF &&
+ protocol=http
+ host=path.tld
+ path=foo.git
+ username=user
+ password=pass
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=http
+ host=path.tld
+ path=bar.git
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''http://path.tld/bar.git'\'':
+ askpass: Password for '\''http://askpass-username@path.tld/bar.git'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can forget host" '
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://example.com'\'':
+ askpass: Password for '\''https://askpass-username@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can store multiple users" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user1
+ password=pass1
+ EOF
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user2
+ password=pass2
+ EOF
+ check fill $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user1
+ --
+ username=user1
+ password=pass1
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user2
+ --
+ username=user2
+ password=pass2
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) can forget user" '
+ check reject $HELPER <<-\EOF &&
+ protocol=https
+ host=example.com
+ username=user1
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user1
+ --
+ username=user1
+ password=askpass-password
+ --
+ askpass: Password for '\''https://user1@example.com'\'':
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) remembers other user" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user2
+ --
+ username=user2
+ password=pass2
+ EOF
+ '
+}
+
+helper_test_timeout() {
+ HELPER="$*"
+
+ test_expect_success "helper ($HELPER) times out" '
+ check approve "$HELPER" <<-\EOF &&
+ protocol=https
+ host=timeout.tld
+ username=user
+ password=pass
+ EOF
+ sleep 2 &&
+ check fill "$HELPER" <<-\EOF
+ protocol=https
+ host=timeout.tld
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://timeout.tld'\'':
+ askpass: Password for '\''https://askpass-username@timeout.tld'\'':
+ EOF
+ '
+}
+
+cat >askpass <<\EOF
+#!/bin/sh
+echo >&2 askpass: $*
+what=`echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z`
+echo "askpass-$what"
+EOF
+chmod +x askpass
+GIT_ASKPASS="$PWD/askpass"
+export GIT_ASKPASS
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 0a4cdfa93e..3c12b05d60 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -92,6 +92,9 @@ SSLEngine On
<Location /dumb/>
Dav on
</Location>
+ <Location /auth/dumb>
+ Dav on
+ </Location>
</IfDefine>
<IfDefine SVN>
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
new file mode 100755
index 0000000000..f97256292e
--- /dev/null
+++ b/t/t0090-cache-tree.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description="Test whether cache-tree is properly updated
+
+Tests whether various commands properly update and/or rewrite the
+cache-tree extension.
+"
+ . ./test-lib.sh
+
+cmp_cache_tree () {
+ test-dump-cache-tree >actual &&
+ sed "s/$_x40/SHA/" <actual >filtered &&
+ test_cmp "$1" filtered
+}
+
+# We don't bother with actually checking the SHA1:
+# test-dump-cache-tree already verifies that all existing data is
+# correct.
+test_shallow_cache_tree () {
+ echo "SHA " \
+ "($(git ls-files|wc -l) entries, 0 subtrees)" >expect &&
+ cmp_cache_tree expect
+}
+
+test_invalid_cache_tree () {
+ echo "invalid (0 subtrees)" >expect &&
+ echo "SHA #(ref) " \
+ "($(git ls-files|wc -l) entries, 0 subtrees)" >>expect &&
+ cmp_cache_tree expect
+}
+
+test_no_cache_tree () {
+ : >expect &&
+ cmp_cache_tree expect
+}
+
+test_expect_failure 'initial commit has cache-tree' '
+ test_commit foo &&
+ test_shallow_cache_tree
+'
+
+test_expect_success 'read-tree HEAD establishes cache-tree' '
+ git read-tree HEAD &&
+ test_shallow_cache_tree
+'
+
+test_expect_success 'git-add invalidates cache-tree' '
+ test_when_finished "git reset --hard; git read-tree HEAD" &&
+ echo "I changed this file" > foo &&
+ git add foo &&
+ test_invalid_cache_tree
+'
+
+test_expect_success 'update-index invalidates cache-tree' '
+ test_when_finished "git reset --hard; git read-tree HEAD" &&
+ echo "I changed this file" > foo &&
+ git update-index --add foo &&
+ test_invalid_cache_tree
+'
+
+test_expect_success 'write-tree establishes cache-tree' '
+ test-scrap-cache-tree &&
+ git write-tree &&
+ test_shallow_cache_tree
+'
+
+test_expect_success 'test-scrap-cache-tree works' '
+ git read-tree HEAD &&
+ test-scrap-cache-tree &&
+ test_no_cache_tree
+'
+
+test_expect_success 'second commit has cache-tree' '
+ test_commit bar &&
+ test_shallow_cache_tree
+'
+
+test_expect_success 'reset --hard gives cache-tree' '
+ test-scrap-cache-tree &&
+ git reset --hard &&
+ test_shallow_cache_tree
+'
+
+test_expect_success 'reset --hard without index gives cache-tree' '
+ rm -f .git/index &&
+ git reset --hard &&
+ test_shallow_cache_tree
+'
+
+test_expect_failure 'checkout gives cache-tree' '
+ git checkout HEAD^ &&
+ test_shallow_cache_tree
+'
+
+test_done
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
new file mode 100755
index 0000000000..885af8fb62
--- /dev/null
+++ b/t/t0300-credentials.sh
@@ -0,0 +1,279 @@
+#!/bin/sh
+
+test_description='basic credential helper tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+test_expect_success 'setup helper scripts' '
+ cat >dump <<-\EOF &&
+ whoami=`echo $0 | sed s/.*git-credential-//`
+ echo >&2 "$whoami: $*"
+ while IFS== read key value; do
+ echo >&2 "$whoami: $key=$value"
+ eval "$key=$value"
+ done
+ EOF
+
+ cat >git-credential-useless <<-\EOF &&
+ #!/bin/sh
+ . ./dump
+ exit 0
+ EOF
+ chmod +x git-credential-useless &&
+
+ cat >git-credential-verbatim <<-\EOF &&
+ #!/bin/sh
+ user=$1; shift
+ pass=$1; shift
+ . ./dump
+ test -z "$user" || echo username=$user
+ test -z "$pass" || echo password=$pass
+ EOF
+ chmod +x git-credential-verbatim &&
+
+ PATH="$PWD:$PATH"
+'
+
+test_expect_success 'credential_fill invokes helper' '
+ check fill "verbatim foo bar" <<-\EOF
+ --
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ EOF
+'
+
+test_expect_success 'credential_fill invokes multiple helpers' '
+ check fill useless "verbatim foo bar" <<-\EOF
+ --
+ username=foo
+ password=bar
+ --
+ useless: get
+ verbatim: get
+ EOF
+'
+
+test_expect_success 'credential_fill stops when we get a full response' '
+ check fill "verbatim one two" "verbatim three four" <<-\EOF
+ --
+ username=one
+ password=two
+ --
+ verbatim: get
+ EOF
+'
+
+test_expect_success 'credential_fill continues through partial response' '
+ check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
+ --
+ username=two
+ password=three
+ --
+ verbatim: get
+ verbatim: get
+ verbatim: username=one
+ EOF
+'
+
+test_expect_success 'credential_fill passes along metadata' '
+ check fill "verbatim one two" <<-\EOF
+ protocol=ftp
+ host=example.com
+ path=foo.git
+ --
+ username=one
+ password=two
+ --
+ verbatim: get
+ verbatim: protocol=ftp
+ verbatim: host=example.com
+ verbatim: path=foo.git
+ EOF
+'
+
+test_expect_success 'credential_approve calls all helpers' '
+ check approve useless "verbatim one two" <<-\EOF
+ username=foo
+ password=bar
+ --
+ --
+ useless: store
+ useless: username=foo
+ useless: password=bar
+ verbatim: store
+ verbatim: username=foo
+ verbatim: password=bar
+ EOF
+'
+
+test_expect_success 'do not bother storing password-less credential' '
+ check approve useless <<-\EOF
+ username=foo
+ --
+ --
+ EOF
+'
+
+
+test_expect_success 'credential_reject calls all helpers' '
+ check reject useless "verbatim one two" <<-\EOF
+ username=foo
+ password=bar
+ --
+ --
+ useless: erase
+ useless: username=foo
+ useless: password=bar
+ verbatim: erase
+ verbatim: username=foo
+ verbatim: password=bar
+ EOF
+'
+
+test_expect_success 'usernames can be preserved' '
+ check fill "verbatim \"\" three" <<-\EOF
+ username=one
+ --
+ username=one
+ password=three
+ --
+ verbatim: get
+ verbatim: username=one
+ EOF
+'
+
+test_expect_success 'usernames can be overridden' '
+ check fill "verbatim two three" <<-\EOF
+ username=one
+ --
+ username=two
+ password=three
+ --
+ verbatim: get
+ verbatim: username=one
+ EOF
+'
+
+test_expect_success 'do not bother completing already-full credential' '
+ check fill "verbatim three four" <<-\EOF
+ username=one
+ password=two
+ --
+ username=one
+ password=two
+ --
+ EOF
+'
+
+# We can't test the basic terminal password prompt here because
+# getpass() tries too hard to find the real terminal. But if our
+# askpass helper is run, we know the internal getpass is working.
+test_expect_success 'empty helper list falls back to internal getpass' '
+ check fill <<-\EOF
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username:
+ askpass: Password:
+ EOF
+'
+
+test_expect_success 'internal getpass does not ask for known username' '
+ check fill <<-\EOF
+ username=foo
+ --
+ username=foo
+ password=askpass-password
+ --
+ askpass: Password:
+ EOF
+'
+
+HELPER="!f() {
+ cat >/dev/null
+ echo username=foo
+ echo password=bar
+ }; f"
+test_expect_success 'respect configured credentials' '
+ test_config credential.helper "$HELPER" &&
+ check fill <<-\EOF
+ --
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'match configured credential' '
+ test_config credential.https://example.com.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ path=repo.git
+ --
+ username=foo
+ password=bar
+ --
+ EOF
+'
+
+test_expect_success 'do not match configured credential' '
+ test_config credential.https://foo.helper "$HELPER" &&
+ check fill <<-\EOF
+ protocol=https
+ host=bar
+ --
+ username=askpass-username
+ password=askpass-password
+ --
+ askpass: Username for '\''https://bar'\'':
+ askpass: Password for '\''https://askpass-username@bar'\'':
+ EOF
+'
+
+test_expect_success 'pull username from config' '
+ test_config credential.https://example.com.username foo &&
+ check fill <<-\EOF
+ protocol=https
+ host=example.com
+ --
+ username=foo
+ password=askpass-password
+ --
+ askpass: Password for '\''https://foo@example.com'\'':
+ EOF
+'
+
+test_expect_success 'http paths can be part of context' '
+ check fill "verbatim foo bar" <<-\EOF &&
+ protocol=https
+ host=example.com
+ path=foo.git
+ --
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ EOF
+ test_config credential.https://example.com.useHttpPath true &&
+ check fill "verbatim foo bar" <<-\EOF
+ protocol=https
+ host=example.com
+ path=foo.git
+ --
+ username=foo
+ password=bar
+ --
+ verbatim: get
+ verbatim: protocol=https
+ verbatim: host=example.com
+ verbatim: path=foo.git
+ EOF
+'
+
+test_done
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
new file mode 100755
index 0000000000..82c8411210
--- /dev/null
+++ b/t/t0301-credential-cache.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='credential-cache tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+test -z "$NO_UNIX_SOCKETS" || {
+ skip_all='skipping credential-cache tests, unix sockets not available'
+ test_done
+}
+
+# don't leave a stale daemon running
+trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
+
+helper_test cache
+helper_test_timeout cache --timeout=1
+
+# we can't rely on our "trap" above working after test_done,
+# as test_done will delete the trash directory containing
+# our socket, leaving us with no way to access the daemon.
+git credential-cache exit
+
+test_done
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
new file mode 100755
index 0000000000..f61b40c69b
--- /dev/null
+++ b/t/t0302-credential-store.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+test_description='credential-store tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+helper_test store
+
+test_done
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
new file mode 100755
index 0000000000..267f4c8ba3
--- /dev/null
+++ b/t/t0303-credential-external.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='external credential helper tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
+
+pre_test() {
+ test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
+ eval "$GIT_TEST_CREDENTIAL_HELPER_SETUP"
+
+ # clean before the test in case there is cruft left
+ # over from a previous run that would impact results
+ helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+}
+
+post_test() {
+ # clean afterwards so that we are good citizens
+ # and don't leave cruft in the helper's storage, which
+ # might be long-term system storage
+ helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+}
+
+if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
+ say "# skipping external helper tests (set GIT_TEST_CREDENTIAL_HELPER)"
+else
+ pre_test
+ helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+ post_test
+fi
+
+if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
+ say "# skipping external helper timeout tests"
+else
+ pre_test
+ helper_test_timeout "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"
+ post_test
+fi
+
+test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index 6d52b824b1..f83df8eb8b 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -189,7 +189,7 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do
done
test_expect_success 'corrupt tree' '
- echo abc >malformed-tree
+ echo abc >malformed-tree &&
test_must_fail git hash-object -t tree malformed-tree
'
diff --git a/t/t1013-loose-object-format.sh b/t/t1013-loose-object-format.sh
index 0a9cedd374..fbf5f2fc00 100755
--- a/t/t1013-loose-object-format.sh
+++ b/t/t1013-loose-object-format.sh
@@ -34,7 +34,7 @@ assert_blob_equals() {
}
test_expect_success setup '
- cp -R "$TEST_DIRECTORY/t1013/objects" .git/
+ cp -R "$TEST_DIRECTORY/t1013/objects" .git/ &&
git --version
'
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index deba111bd7..29d6024b7f 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -7,21 +7,97 @@ test_description='adding and checking out large blobs'
test_expect_success setup '
git config core.bigfilethreshold 200k &&
- echo X | dd of=large bs=1k seek=2000
+ echo X | dd of=large1 bs=1k seek=2000 &&
+ echo X | dd of=large2 bs=1k seek=2000 &&
+ echo X | dd of=large3 bs=1k seek=2000 &&
+ echo Y | dd of=huge bs=1k seek=2500
'
-test_expect_success 'add a large file' '
- git add large &&
- # make sure we got a packfile and no loose objects
- test -f .git/objects/pack/pack-*.pack &&
- test ! -f .git/objects/??/??????????????????????????????????????
+test_expect_success 'add a large file or two' '
+ git add large1 huge large2 &&
+ # make sure we got a single packfile and no loose objects
+ bad= count=0 idx= &&
+ for p in .git/objects/pack/pack-*.pack
+ do
+ count=$(( $count + 1 ))
+ if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+ then
+ continue
+ fi
+ bad=t
+ done &&
+ test -z "$bad" &&
+ test $count = 1 &&
+ cnt=$(git show-index <"$idx" | wc -l) &&
+ test $cnt = 2 &&
+ for l in .git/objects/??/??????????????????????????????????????
+ do
+ test -f "$l" || continue
+ bad=t
+ done &&
+ test -z "$bad" &&
+
+ # attempt to add another copy of the same
+ git add large3 &&
+ bad= count=0 &&
+ for p in .git/objects/pack/pack-*.pack
+ do
+ count=$(( $count + 1 ))
+ if test -f "$p" && idx=${p%.pack}.idx && test -f "$idx"
+ then
+ continue
+ fi
+ bad=t
+ done &&
+ test -z "$bad" &&
+ test $count = 1
'
test_expect_success 'checkout a large file' '
- large=$(git rev-parse :large) &&
- git update-index --add --cacheinfo 100644 $large another &&
+ large1=$(git rev-parse :large1) &&
+ git update-index --add --cacheinfo 100644 $large1 another &&
git checkout another &&
- cmp large another ;# this must not be test_cmp
+ cmp large1 another ;# this must not be test_cmp
+'
+
+test_expect_success 'packsize limit' '
+ test_create_repo mid &&
+ (
+ cd mid &&
+ git config core.bigfilethreshold 64k &&
+ git config pack.packsizelimit 256k &&
+
+ # mid1 and mid2 will fit within 256k limit but
+ # appending mid3 will bust the limit and will
+ # result in a separate packfile.
+ test-genrandom "a" $(( 66 * 1024 )) >mid1 &&
+ test-genrandom "b" $(( 80 * 1024 )) >mid2 &&
+ test-genrandom "c" $(( 128 * 1024 )) >mid3 &&
+ git add mid1 mid2 mid3 &&
+
+ count=0
+ for pi in .git/objects/pack/pack-*.idx
+ do
+ test -f "$pi" && count=$(( $count + 1 ))
+ done &&
+ test $count = 2 &&
+
+ (
+ git hash-object --stdin <mid1
+ git hash-object --stdin <mid2
+ git hash-object --stdin <mid3
+ ) |
+ sort >expect &&
+
+ for pi in .git/objects/pack/pack-*.idx
+ do
+ git show-index <"$pi"
+ done |
+ sed -e "s/^[0-9]* \([0-9a-f]*\) .*/\1/" |
+ sort >actual &&
+
+ test_cmp expect actual
+ )
'
test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 51caff047b..0690e0edf4 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -38,7 +38,7 @@ cat > expect << EOF
WhatEver = Second
EOF
test_expect_success 'similar section' '
- git config Cores.WhatEver Second
+ git config Cores.WhatEver Second &&
test_cmp expect .git/config
'
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 647d888507..3acd895afb 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -20,7 +20,7 @@ test_expect_success 'setup reflog with alternating commits' '
'
test_expect_success 'reflog shows all entries' '
- cat >expect <<-\EOF
+ cat >expect <<-\EOF &&
topic@{0} reset: moving to two
topic@{1} reset: moving to one
topic@{2} reset: moving to two
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index 63849836c8..e661147c57 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -48,7 +48,7 @@ test_expect_success 'setup: helper for testing rev-parse' '
'
test_expect_success 'setup: core.worktree = relative path' '
- unset GIT_WORK_TREE;
+ sane_unset GIT_WORK_TREE &&
GIT_DIR=repo.git &&
GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
export GIT_DIR GIT_CONFIG &&
@@ -89,7 +89,7 @@ test_expect_success 'subdir of work tree' '
'
test_expect_success 'setup: core.worktree = absolute path' '
- unset GIT_WORK_TREE;
+ sane_unset GIT_WORK_TREE &&
GIT_DIR=$(pwd)/repo.git &&
GIT_CONFIG=$GIT_DIR/config &&
export GIT_DIR GIT_CONFIG &&
@@ -334,7 +334,7 @@ test_expect_success 'absolute pathspec should fail gracefully' '
'
test_expect_success 'make_relative_path handles double slashes in GIT_DIR' '
- >dummy_file
+ >dummy_file &&
echo git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file &&
git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file
'
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
index ec50a9ad70..80aedfca8c 100755
--- a/t/t1510-repo-setup.sh
+++ b/t/t1510-repo-setup.sh
@@ -603,7 +603,7 @@ test_expect_success '#22a: core.worktree = GIT_DIR = .git dir' '
# like case #6.
setup_repo 22a "$here/22a/.git" "" unset &&
- setup_repo 22ab . "" unset
+ setup_repo 22ab . "" unset &&
mkdir -p 22a/.git/sub 22a/sub &&
mkdir -p 22ab/.git/sub 22ab/sub &&
try_case 22a/.git unset . \
@@ -742,7 +742,7 @@ test_expect_success '#28: core.worktree and core.bare conflict (gitfile case)' '
# Case #29: GIT_WORK_TREE(+core.worktree) overrides core.bare (gitfile case).
test_expect_success '#29: setup' '
setup_repo 29 non-existent gitfile true &&
- mkdir -p 29/sub/sub 29/wt/sub
+ mkdir -p 29/sub/sub 29/wt/sub &&
(
cd 29 &&
GIT_WORK_TREE="$here/29" &&
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
index e043cb7c64..eaefc777bd 100755
--- a/t/t1511-rev-parse-caret.sh
+++ b/t/t1511-rev-parse-caret.sh
@@ -6,7 +6,7 @@ test_description='tests for ref^{stuff}'
test_expect_success 'setup' '
echo blob >a-blob &&
- git tag -a -m blob blob-tag `git hash-object -w a-blob`
+ git tag -a -m blob blob-tag `git hash-object -w a-blob` &&
mkdir a-tree &&
echo moreblobs >a-tree/another-blob &&
git add . &&
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
index 75874e85df..2741262369 100755
--- a/t/t2018-checkout-branch.sh
+++ b/t/t2018-checkout-branch.sh
@@ -189,12 +189,13 @@ test_expect_success 'checkout -b <describe>' '
test_cmp expect actual
'
-test_expect_success 'checkout -B to the current branch fails before merging' '
+test_expect_success 'checkout -B to the current branch works' '
git checkout branch1 &&
+ git checkout -B branch1-scratch &&
+
setup_dirty_mergeable &&
- git commit -mfooble &&
- test_must_fail git checkout -B branch1 initial &&
- test_must_fail test_dirty_mergeable
+ git checkout -B branch1-scratch initial &&
+ test_dirty_mergeable
'
test_done
diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
new file mode 100755
index 0000000000..1a40ce02e5
--- /dev/null
+++ b/t/t2023-checkout-m.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+test_description='checkout -m -- <conflicted path>
+
+Ensures that checkout -m on a resolved file restores the conflicted file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ test_commit both.txt both.txt initial &&
+ git branch topic &&
+ test_commit modified_in_master both.txt in_master &&
+ test_commit added_in_master each.txt in_master &&
+ git checkout topic &&
+ test_commit modified_in_topic both.txt in_topic &&
+ test_commit added_in_topic each.txt in_topic
+'
+
+test_must_fail git merge master
+
+clean_branchnames () {
+ # Remove branch names after conflict lines
+ sed 's/^\([<>]\{5,\}\) .*$/\1/'
+}
+
+test_expect_success '-m restores 2-way conflicted+resolved file' '
+ cp each.txt each.txt.conflicted &&
+ echo resolved >each.txt &&
+ git add each.txt &&
+ git checkout -m -- each.txt &&
+ clean_branchnames <each.txt >each.txt.cleaned &&
+ clean_branchnames <each.txt.conflicted >each.txt.conflicted.cleaned &&
+ test_cmp each.txt.conflicted.cleaned each.txt.cleaned
+'
+
+test_expect_success '-m restores 3-way conflicted+resolved file' '
+ cp both.txt both.txt.conflicted &&
+ echo resolved >both.txt &&
+ git add both.txt &&
+ git checkout -m -- both.txt &&
+ clean_branchnames <both.txt >both.txt.cleaned &&
+ clean_branchnames <both.txt.conflicted >both.txt.conflicted.cleaned &&
+ test_cmp both.txt.conflicted.cleaned both.txt.cleaned
+'
+
+test_done
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index 55ef1895d7..a5e3da7e41 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -285,17 +285,7 @@ test_expect_success 'merge-recursive simple' '
rm -fr [abcd] &&
git checkout -f "$c2" &&
- git merge-recursive "$c0" -- "$c2" "$c1"
- status=$?
- case "$status" in
- 1)
- : happy
- ;;
- *)
- echo >&2 "why status $status!!!"
- false
- ;;
- esac
+ test_expect_code 1 git merge-recursive "$c0" -- "$c2" "$c1"
'
test_expect_success 'merge-recursive result' '
@@ -334,17 +324,7 @@ test_expect_success 'merge-recursive remove conflict' '
rm -fr [abcd] &&
git checkout -f "$c1" &&
- git merge-recursive "$c0" -- "$c1" "$c5"
- status=$?
- case "$status" in
- 1)
- : happy
- ;;
- *)
- echo >&2 "why status $status!!!"
- false
- ;;
- esac
+ test_expect_code 1 git merge-recursive "$c0" -- "$c1" "$c5"
'
test_expect_success 'merge-recursive remove conflict' '
@@ -388,17 +368,7 @@ test_expect_success 'merge-recursive d/f conflict' '
git reset --hard &&
git checkout -f "$c1" &&
- git merge-recursive "$c0" -- "$c1" "$c4"
- status=$?
- case "$status" in
- 1)
- : happy
- ;;
- *)
- echo >&2 "why status $status!!!"
- false
- ;;
- esac
+ test_expect_code 1 git merge-recursive "$c0" -- "$c1" "$c4"
'
test_expect_success 'merge-recursive d/f conflict result' '
@@ -422,17 +392,7 @@ test_expect_success 'merge-recursive d/f conflict the other way' '
git reset --hard &&
git checkout -f "$c4" &&
- git merge-recursive "$c0" -- "$c4" "$c1"
- status=$?
- case "$status" in
- 1)
- : happy
- ;;
- *)
- echo >&2 "why status $status!!!"
- false
- ;;
- esac
+ test_expect_code 1 git merge-recursive "$c0" -- "$c4" "$c1"
'
test_expect_success 'merge-recursive d/f conflict result the other way' '
@@ -456,17 +416,7 @@ test_expect_success 'merge-recursive d/f conflict' '
git reset --hard &&
git checkout -f "$c1" &&
- git merge-recursive "$c0" -- "$c1" "$c6"
- status=$?
- case "$status" in
- 1)
- : happy
- ;;
- *)
- echo >&2 "why status $status!!!"
- false
- ;;
- esac
+ test_expect_code 1 git merge-recursive "$c0" -- "$c1" "$c6"
'
test_expect_success 'merge-recursive d/f conflict result' '
@@ -490,17 +440,7 @@ test_expect_success 'merge-recursive d/f conflict' '
git reset --hard &&
git checkout -f "$c6" &&
- git merge-recursive "$c0" -- "$c6" "$c1"
- status=$?
- case "$status" in
- 1)
- : happy
- ;;
- *)
- echo >&2 "why status $status!!!"
- false
- ;;
- esac
+ test_expect_code 1 git merge-recursive "$c0" -- "$c6" "$c1"
'
test_expect_success 'merge-recursive d/f conflict result' '
diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh
index f6973e96a5..0a4ff6d824 100755
--- a/t/t3040-subprojects-basic.sh
+++ b/t/t3040-subprojects-basic.sh
@@ -3,81 +3,81 @@
test_description='Basic subproject functionality'
. ./test-lib.sh
-test_expect_success 'Super project creation' \
- ': >Makefile &&
- git add Makefile &&
- git commit -m "Superproject created"'
-
-
-cat >expected <<EOF
-:000000 160000 00000... A sub1
-:000000 160000 00000... A sub2
-EOF
-test_expect_success 'create subprojects' \
- 'mkdir sub1 &&
- ( cd sub1 && git init && : >Makefile && git add * &&
- git commit -q -m "subproject 1" ) &&
- mkdir sub2 &&
- ( cd sub2 && git init && : >Makefile && git add * &&
- git commit -q -m "subproject 2" ) &&
- git update-index --add sub1 &&
- git add sub2 &&
- git commit -q -m "subprojects added" &&
- git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
- test_cmp expected current'
-
-git branch save HEAD
-
-test_expect_success 'check if fsck ignores the subprojects' \
- 'git fsck --full'
-
-test_expect_success 'check if commit in a subproject detected' \
- '( cd sub1 &&
- echo "all:" >>Makefile &&
- echo " true" >>Makefile &&
- git commit -q -a -m "make all" ) && {
- git diff-files --exit-code
- test $? = 1
- }'
-
-test_expect_success 'check if a changed subproject HEAD can be committed' \
- 'git commit -q -a -m "sub1 changed" && {
- git diff-tree --exit-code HEAD^ HEAD
- test $? = 1
- }'
-
-test_expect_success 'check if diff-index works for subproject elements' \
- 'git diff-index --exit-code --cached save -- sub1
- test $? = 1'
-
-test_expect_success 'check if diff-tree works for subproject elements' \
- 'git diff-tree --exit-code HEAD^ HEAD -- sub1
- test $? = 1'
-
-test_expect_success 'check if git diff works for subproject elements' \
- 'git diff --exit-code HEAD^ HEAD
- test $? = 1'
-
-test_expect_success 'check if clone works' \
- 'git ls-files -s >expected &&
- git clone -l -s . cloned &&
- ( cd cloned && git ls-files -s ) >current &&
- test_cmp expected current'
-
-test_expect_success 'removing and adding subproject' \
- 'git update-index --force-remove -- sub2 &&
- mv sub2 sub3 &&
- git add sub3 &&
- git commit -q -m "renaming a subproject" && {
- git diff -M --name-status --exit-code HEAD^ HEAD
- test $? = 1
- }'
+test_expect_success 'setup: create superproject' '
+ : >Makefile &&
+ git add Makefile &&
+ git commit -m "Superproject created"
+'
+
+test_expect_success 'setup: create subprojects' '
+ mkdir sub1 &&
+ ( cd sub1 && git init && : >Makefile && git add * &&
+ git commit -q -m "subproject 1" ) &&
+ mkdir sub2 &&
+ ( cd sub2 && git init && : >Makefile && git add * &&
+ git commit -q -m "subproject 2" ) &&
+ git update-index --add sub1 &&
+ git add sub2 &&
+ git commit -q -m "subprojects added" &&
+ git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
+ git branch save HEAD &&
+ cat >expected <<-\EOF &&
+ :000000 160000 00000... A sub1
+ :000000 160000 00000... A sub2
+ EOF
+ test_cmp expected current
+'
+
+test_expect_success 'check if fsck ignores the subprojects' '
+ git fsck --full
+'
+
+test_expect_success 'check if commit in a subproject detected' '
+ ( cd sub1 &&
+ echo "all:" >>Makefile &&
+ echo " true" >>Makefile &&
+ git commit -q -a -m "make all" ) &&
+ test_expect_code 1 git diff-files --exit-code
+'
+
+test_expect_success 'check if a changed subproject HEAD can be committed' '
+ git commit -q -a -m "sub1 changed" &&
+ test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD
+'
+
+test_expect_success 'check if diff-index works for subproject elements' '
+ test_expect_code 1 git diff-index --exit-code --cached save -- sub1
+'
+
+test_expect_success 'check if diff-tree works for subproject elements' '
+ test_expect_code 1 git diff-tree --exit-code HEAD^ HEAD -- sub1
+'
+
+test_expect_success 'check if git diff works for subproject elements' '
+ test_expect_code 1 git diff --exit-code HEAD^ HEAD
+'
+
+test_expect_success 'check if clone works' '
+ git ls-files -s >expected &&
+ git clone -l -s . cloned &&
+ ( cd cloned && git ls-files -s ) >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'removing and adding subproject' '
+ git update-index --force-remove -- sub2 &&
+ mv sub2 sub3 &&
+ git add sub3 &&
+ git commit -q -m "renaming a subproject" &&
+ test_expect_code 1 git diff -M --name-status --exit-code HEAD^ HEAD
+'
# the index must contain the object name the HEAD of the
# subproject sub1 was at the point "save"
-test_expect_success 'checkout in superproject' \
- 'git checkout save &&
- git diff-index --exit-code --raw --cached save -- sub1'
+test_expect_success 'checkout in superproject' '
+ git checkout save &&
+ git diff-index --exit-code --raw --cached save -- sub1
+'
# just interesting what happened...
# git diff --name-status -M save master
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index bc73c2099b..ea82424e47 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -22,7 +22,7 @@ test_expect_success \
test_expect_success \
'git branch --help should not have created a bogus branch' '
- git branch --help </dev/null >/dev/null 2>/dev/null;
+ test_might_fail git branch --help </dev/null >/dev/null 2>/dev/null &&
test_path_is_missing .git/refs/heads/--help
'
@@ -88,7 +88,7 @@ test_expect_success \
test_expect_success \
'git branch -m n/n n should work' \
'git branch -l n/n &&
- git branch -m n/n n
+ git branch -m n/n n &&
test_path_is_file .git/logs/refs/heads/n'
test_expect_success 'git branch -m o/o o should fail when o/p exists' '
@@ -115,6 +115,22 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
git branch -M baz bam
'
+test_expect_success 'git branch -M master should work when master is checked out' '
+ git checkout master &&
+ git branch -M master
+'
+
+test_expect_success 'git branch -M master master should work when master is checked out' '
+ git checkout master &&
+ git branch -M master master
+'
+
+test_expect_success 'git branch -M master2 master2 should work when master is checked out' '
+ git checkout master &&
+ git branch master2 &&
+ git branch -M master2 master2
+'
+
test_expect_success 'git branch -v -d t should work' '
git branch t &&
test_path_is_file .git/refs/heads/t &&
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index 4ec4d11450..4367197953 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -389,7 +389,7 @@ test_expect_success 'abort notes merge' '
test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
test_cmp /dev/null output &&
# m has not moved (still == y)
- test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)"
+ test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)" &&
# Verify that other notes refs has not changed (w, x, y and z)
verify_notes w &&
verify_notes x &&
@@ -525,9 +525,9 @@ EOF
test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
test -f .git/NOTES_MERGE_WORKTREE/$commit_sha4 &&
# Refs are unchanged
- test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
- test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)"
- test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)"
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
+ test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
+ test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" &&
# Mention refs/notes/m, and its current and expected value in output
grep -q "refs/notes/m" output &&
grep -q "$(git rev-parse refs/notes/m)" output &&
@@ -545,7 +545,7 @@ test_expect_success 'resolve situation by aborting the notes merge' '
test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
test_cmp /dev/null output &&
# m has not moved (still == w)
- test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)"
+ test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
# Verify that other notes refs has not changed (w, x, y and z)
verify_notes w &&
verify_notes x &&
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 6eaecec906..c355533236 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -172,8 +172,8 @@ test_expect_success 'fail when upstream arg is missing and not configured' '
test_expect_success 'default to @{upstream} when upstream arg is missing' '
git checkout -b default topic &&
- git config branch.default.remote .
- git config branch.default.merge refs/heads/master
+ git config branch.default.remote . &&
+ git config branch.default.merge refs/heads/master &&
git rebase &&
test "$(git rev-parse default~1)" = "$(git rev-parse master)"
'
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
index aea6685984..7ba17974c5 100755
--- a/t/t3401-rebase-partial.sh
+++ b/t/t3401-rebase-partial.sh
@@ -11,51 +11,35 @@ local branch.
'
. ./test-lib.sh
-test_expect_success \
- 'prepare repository with topic branch' \
- 'echo First > 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 commit -m "Add B." &&
-
- echo AnotherSecond > C &&
- git update-index --add C &&
- git commit -m "Add C." &&
-
- git checkout -f master &&
-
- echo Third >> A &&
- git update-index A &&
- git commit -m "Modify A."
+test_expect_success 'prepare repository with topic branch' '
+ test_commit A &&
+ git checkout -b my-topic-branch &&
+ test_commit B &&
+ test_commit C &&
+ git checkout -f master &&
+ test_commit A2 A.t
'
-test_expect_success \
- 'pick top patch from topic branch into master' \
- '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
+test_expect_success 'pick top patch from topic branch into master' '
+ git cherry-pick C &&
+ git checkout -f my-topic-branch
'
-test_debug \
- 'git cherry master &&
- git format-patch -k --stdout --full-index master >/dev/null &&
- gitk --all & sleep 1
+test_debug '
+ git cherry master &&
+ git format-patch -k --stdout --full-index master >/dev/null &&
+ gitk --all & sleep 1
'
-test_expect_success \
- 'rebase topic branch against new master and check git am did not get halted' \
- 'git rebase master && test ! -d .git/rebase-apply'
+test_expect_success 'rebase topic branch against new master and check git am did not get halted' '
+ git rebase master &&
+ test_path_is_missing .git/rebase-apply
+'
-test_expect_success \
- 'rebase --merge topic branch that was partially merged upstream' \
- 'git checkout -f my-topic-branch-merge &&
- git rebase --merge master-merge &&
- test ! -d .git/rebase-merge'
+test_expect_success 'rebase --merge topic branch that was partially merged upstream' '
+ git reset --hard C &&
+ git rebase --merge master &&
+ test_path_is_missing .git/rebase-merge
+'
test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 1e855cdae5..2680375628 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -51,7 +51,7 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
test_commit "commit-new-file-F3-on-topic-branch" F3 32 &&
test_when_finished "rm -fr test-bin funny.was.run" &&
mkdir test-bin &&
- cat >test-bin/git-merge-funny <<-EOF
+ cat >test-bin/git-merge-funny <<-EOF &&
#!$SHELL_PATH
case "\$1" in --opt) ;; *) exit 2 ;; esac
shift &&
@@ -77,7 +77,7 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
test_expect_success 'rebase --continue remembers --rerere-autoupdate' '
rm -fr .git/rebase-* &&
git reset --hard commit-new-file-F3-on-topic-branch &&
- git checkout master
+ git checkout master &&
test_commit "commit-new-file-F3" F3 3 &&
git config rerere.enabled true &&
test_must_fail git rebase -m master topic &&
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
index bd8efaf005..e70ac10a0c 100755
--- a/t/t3419-rebase-patch-id.sh
+++ b/t/t3419-rebase-patch-id.sh
@@ -39,7 +39,7 @@ run()
}
test_expect_success 'setup' '
- git commit --allow-empty -m initial
+ git commit --allow-empty -m initial &&
git tag root
'
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index cb45574a7b..ee1659c178 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -253,6 +253,60 @@ test_expect_success 'revert also handles conflicts sanely' '
test_cmp expected actual
'
+test_expect_success 'failed revert sets REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'successful revert does not set REVERT_HEAD' '
+ pristine_detach base &&
+ git revert base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'revert --no-commit sets REVERT_HEAD' '
+ pristine_detach base &&
+ git revert --no-commit base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_cmp_rev base REVERT_HEAD
+'
+
+test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '
+ pristine_detach base &&
+ echo foo > foo &&
+ test_must_fail git revert base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'GIT_CHERRY_PICK_HELP does not suppress REVERT_HEAD' '
+ pristine_detach initial &&
+ (
+ GIT_CHERRY_PICK_HELP="and then do something else" &&
+ GIT_REVERT_HELP="and then do something else, again" &&
+ export GIT_CHERRY_PICK_HELP GIT_REVERT_HELP &&
+ test_must_fail git revert picked
+ ) &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'git reset clears REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ git reset &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'failed commit does not clear REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ test_must_fail git commit &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
test_expect_success 'revert conflict, diff3 -m style' '
pristine_detach initial &&
git config merge.conflictstyle diff3 &&
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 3bca2b3dd5..e80050e1fe 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -2,6 +2,8 @@
test_description='Test cherry-pick continuation features
+ + conflicting: rewrites unrelated to conflicting
+ + yetanotherpick: rewrites foo to e
+ anotherpick: rewrites foo to d
+ picked: rewrites foo to c
+ unrelatedpick: rewrites unrelated to reallyunrelated
@@ -13,13 +15,20 @@ test_description='Test cherry-pick continuation features
. ./test-lib.sh
pristine_detach () {
- git cherry-pick --reset &&
+ git cherry-pick --quit &&
git checkout -f "$1^0" &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x
}
+test_cmp_rev () {
+ git rev-parse --verify "$1" >expect.rev &&
+ git rev-parse --verify "$2" >actual.rev &&
+ test_cmp expect.rev actual.rev
+}
+
test_expect_success setup '
+ git config advice.detachedhead false
echo unrelated >unrelated &&
git add unrelated &&
test_commit initial foo a &&
@@ -27,8 +36,9 @@ test_expect_success setup '
test_commit unrelatedpick unrelated reallyunrelated &&
test_commit picked foo c &&
test_commit anotherpick foo d &&
- git config advice.detachedhead false
-
+ test_commit yetanotherpick foo e &&
+ pristine_detach initial &&
+ test_commit conflicting unrelated
'
test_expect_success 'cherry-pick persists data on failure' '
@@ -40,6 +50,18 @@ test_expect_success 'cherry-pick persists data on failure' '
test_path_is_file .git/sequencer/opts
'
+test_expect_success 'cherry-pick mid-cherry-pick-sequence' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ test_cmp_rev picked CHERRY_PICK_HEAD &&
+ # "oops, I forgot that these patches rely on the change from base"
+ git checkout HEAD foo &&
+ git cherry-pick base &&
+ git cherry-pick picked &&
+ git cherry-pick --continue &&
+ git diff --exit-code anotherpick
+'
+
test_expect_success 'cherry-pick persists opts correctly' '
pristine_detach initial &&
test_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick &&
@@ -70,22 +92,121 @@ test_expect_success 'cherry-pick cleans up sequencer state upon success' '
test_path_is_missing .git/sequencer
'
-test_expect_success '--reset does not complain when no cherry-pick is in progress' '
+test_expect_success '--quit does not complain when no cherry-pick is in progress' '
+ pristine_detach initial &&
+ git cherry-pick --quit
+'
+
+test_expect_success '--abort requires cherry-pick in progress' '
pristine_detach initial &&
- git cherry-pick --reset
+ test_must_fail git cherry-pick --abort
'
-test_expect_success '--reset cleans up sequencer state' '
+test_expect_success '--quit cleans up sequencer state' '
pristine_detach initial &&
test_must_fail git cherry-pick base..picked &&
- git cherry-pick --reset &&
+ git cherry-pick --quit &&
test_path_is_missing .git/sequencer
'
-test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '
+test_expect_success '--quit keeps HEAD and conflicted index intact' '
pristine_detach initial &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
test_must_fail git cherry-pick base..picked &&
+ git cherry-pick --quit &&
+ test_path_is_missing .git/sequencer &&
+ test_must_fail git update-index --refresh &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--abort to cancel multiple cherry-pick' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ git cherry-pick --abort &&
test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort to cancel single cherry-pick' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'cherry-pick --abort to cancel multiple revert' '
+ pristine_detach anotherpick &&
+ test_must_fail git revert base..picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'revert --abort works, too' '
+ pristine_detach anotherpick &&
+ test_must_fail git revert base..picked &&
+ git revert --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD
+'
+
+test_expect_success '--abort to cancel single revert' '
+ pristine_detach anotherpick &&
+ test_must_fail git revert picked &&
+ git revert --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort keeps unrelated change, easy case' '
+ pristine_detach unrelatedpick &&
+ echo changed >expect &&
+ test_must_fail git cherry-pick picked..yetanotherpick &&
+ echo changed >unrelated &&
+ git cherry-pick --abort &&
+ test_cmp expect unrelated
+'
+
+test_expect_success '--abort refuses to clobber unrelated change, harder case' '
+ pristine_detach initial &&
+ echo changed >expect &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ echo changed >unrelated &&
+ test_must_fail git cherry-pick --abort &&
+ test_cmp expect unrelated &&
+ git rev-list HEAD >log &&
+ test_line_count = 2 log &&
+ test_must_fail git update-index --refresh &&
+
+ git checkout unrelated &&
+ git cherry-pick --abort &&
+ test_cmp_rev initial HEAD
+'
+
+test_expect_success 'cherry-pick still writes sequencer state when one commit is left' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..picked &&
+ test_path_is_dir .git/sequencer &&
echo "resolved" >foo &&
git add foo &&
git commit &&
@@ -106,6 +227,16 @@ test_expect_success 'cherry-pick cleans up sequencer state when one commit is le
test_cmp expect actual
'
+test_expect_success '--abort after last commit in sequence' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
@@ -126,7 +257,66 @@ test_expect_success '--continue complains when there are unresolved conflicts' '
test_must_fail git cherry-pick --continue
'
-test_expect_success '--continue continues after conflicts are resolved' '
+test_expect_success '--continue of single cherry-pick' '
+ pristine_detach initial &&
+ echo c >expect &&
+ test_must_fail git cherry-pick picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ test_cmp expect foo &&
+ test_cmp_rev initial HEAD^ &&
+ git diff --exit-code HEAD &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success '--continue of single revert' '
+ pristine_detach initial &&
+ echo resolved >expect &&
+ echo "Revert \"picked\"" >expect.msg &&
+ test_must_fail git revert picked &&
+ echo resolved >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp expect foo &&
+ test_cmp_rev initial HEAD^ &&
+ git diff-tree -s --pretty=tformat:%s HEAD >msg &&
+ test_cmp expect.msg msg &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success '--continue after resolving conflicts' '
+ pristine_detach initial &&
+ echo d >expect &&
+ cat >expect.log <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_must_fail git cherry-pick base..anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual.log &&
+ test_cmp expect foo &&
+ test_cmp expect.log actual.log
+'
+
+test_expect_success '--continue after resolving conflicts and committing' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
echo "c" >foo &&
@@ -153,6 +343,29 @@ test_expect_success '--continue continues after conflicts are resolved' '
test_cmp expect actual
'
+test_expect_success '--continue asks for help after resolving patch to nil' '
+ pristine_detach conflicting &&
+ test_must_fail git cherry-pick initial..picked &&
+
+ test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
+ git checkout HEAD -- unrelated &&
+ test_must_fail git cherry-pick --continue 2>msg &&
+ test_i18ngrep "The previous cherry-pick is now empty" msg
+'
+
+test_expect_success 'follow advice and skip nil patch' '
+ pristine_detach conflicting &&
+ test_must_fail git cherry-pick initial..picked &&
+
+ git checkout HEAD -- unrelated &&
+ test_must_fail git cherry-pick --continue &&
+ git reset &&
+ git cherry-pick --continue &&
+
+ git rev-list initial..HEAD >commits &&
+ test_line_count = 3 commits
+'
+
test_expect_success '--continue respects opts' '
pristine_detach initial &&
test_must_fail git cherry-pick -x base..anotherpick &&
@@ -171,6 +384,29 @@ test_expect_success '--continue respects opts' '
grep "cherry picked from" anotherpick_msg
'
+test_expect_success '--continue of single-pick respects -x' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -x picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD >msg &&
+ grep "cherry picked from" msg
+'
+
+test_expect_success '--continue respects -x in first commit in multi-pick' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -x picked anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD^ >msg &&
+ picked=$(git rev-parse --verify picked) &&
+ grep "cherry picked from.*$picked" msg
+'
+
test_expect_success '--signoff is not automatically propagated to resolved conflict' '
pristine_detach initial &&
test_must_fail git cherry-pick --signoff base..anotherpick &&
@@ -189,6 +425,32 @@ test_expect_success '--signoff is not automatically propagated to resolved confl
grep "Signed-off-by:" anotherpick_msg
'
+test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp_rev initial HEAD^^ &&
+ git cat-file commit HEAD^ >msg &&
+ ! grep Signed-off-by: msg
+'
+
+test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp_rev initial HEAD^ &&
+ git cat-file commit HEAD >msg &&
+ ! grep Signed-off-by: msg
+'
+
test_expect_success 'malformed instruction sheet 1' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
@@ -211,4 +473,9 @@ test_expect_success 'malformed instruction sheet 2' '
test_must_fail git cherry-pick --continue
'
+test_expect_success 'empty commit set' '
+ pristine_detach initial &&
+ test_expect_code 128 git cherry-pick base..base
+'
+
test_done
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index b68c56b68c..4bd2a1c838 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -105,7 +105,7 @@ test_expect_funcname () {
grep "^@@.*@@ $1" diff
}
-for p in bibtex cpp csharp fortran html java objc pascal perl php python ruby tex
+for p in bibtex cpp csharp fortran html java matlab objc pascal perl php python ruby tex
do
test_expect_success "builtin $p pattern compiles" '
echo "*.java diff=$p" >.gitattributes &&
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index c374aa4c1c..6f1e5a2a15 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -299,6 +299,7 @@ test_language_driver csharp
test_language_driver fortran
test_language_driver html
test_language_driver java
+test_language_driver matlab
test_language_driver objc
test_language_driver pascal
test_language_driver perl
diff --git a/t/t4034/matlab/expect b/t/t4034/matlab/expect
new file mode 100644
index 0000000000..72cf3e93a2
--- /dev/null
+++ b/t/t4034/matlab/expect
@@ -0,0 +1,14 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index dc204db..70e05f0 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,9 +1,9 @@<RESET>
+(<RED>1<RESET><GREEN>0<RESET>) (<RED>-1e10<RESET><GREEN>-0e10<RESET>) '<RED>b<RESET><GREEN>y<RESET>';
+[<RED>a<RESET><GREEN>x<RESET>] {<RED>a<RESET><GREEN>x<RESET>} <RED>a<RESET><GREEN>x<RESET>.<RED>b<RESET><GREEN>y<RESET>;
+~<RED>a<RESET><GREEN>x<RESET>;
+<RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>.*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>./<RED>b a<RESET><GREEN>y x<RESET>^<RED>b a<RESET><GREEN>y x<RESET>.^<RED>b a<RESET><GREEN>y x<RESET>.\<RED>b a<RESET><GREEN>y x<RESET>.';
+<RED>a<RESET><GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>&<RED>b a<RESET><GREEN>y x<RESET>&&<RED>b a<RESET><GREEN>y x<RESET>|<RED>b a<RESET><GREEN>y x<RESET>||<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>~=<RED>b<RESET><GREEN>y<RESET>;
+<RED>a<RESET><GREEN>x<RESET>,<RED>b<RESET><GREEN>y<RESET>;
diff --git a/t/t4034/matlab/post b/t/t4034/matlab/post
new file mode 100644
index 0000000000..70e05f0753
--- /dev/null
+++ b/t/t4034/matlab/post
@@ -0,0 +1,9 @@
+(0) (-0e10) 'y';
+[x] {x} x.y;
+~x;
+x*y x.*y x/y x./y x^y x.^y x.\y x.';
+x+y x-y;
+x&y x&&y x|y x||y;
+x<y x<=y x>y x>=y;
+x==y x~=y;
+x,y;
diff --git a/t/t4034/matlab/pre b/t/t4034/matlab/pre
new file mode 100644
index 0000000000..dc204db486
--- /dev/null
+++ b/t/t4034/matlab/pre
@@ -0,0 +1,9 @@
+(1) (-1e10) 'b';
+[a] {a} a.b;
+~a;
+a*b a.*b a/b a./b a^b a.^b a.\b a.';
+a+b a-b;
+a&b a&&b a|b a||b;
+a<b a<=b a>b a>=b;
+a==b a~=b;
+a,b;
diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh
index 94373ca9a0..b1361ce546 100755
--- a/t/t4131-apply-fake-ancestor.sh
+++ b/t/t4131-apply-fake-ancestor.sh
@@ -11,7 +11,7 @@ test_expect_success 'setup' '
test_commit 1 &&
test_commit 2 &&
mkdir sub &&
- test_commit 3 sub/3 &&
+ test_commit 3 sub/3.t &&
test_commit 4
'
diff --git a/t/t4136-apply-check.sh b/t/t4136-apply-check.sh
new file mode 100755
index 0000000000..a321f7c245
--- /dev/null
+++ b/t/t4136-apply-check.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='git apply should exit non-zero with unrecognized input.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit 1
+'
+
+test_expect_success 'apply --check exits non-zero with unrecognized input' '
+ test_must_fail git apply --check - <<-\EOF
+ I am not a patch
+ I look nothing like a patch
+ git apply must fail
+ EOF
+'
+
+test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 983e34bec6..222f7559e9 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -346,11 +346,11 @@ test_expect_success 'set up more tangled history' '
'
cat > expect <<\EOF
-* Merge commit 'reach'
+* Merge tag 'reach'
|\
| \
| \
-*-. \ Merge commit 'octopus-a'; commit 'octopus-b'
+*-. \ Merge tags 'octopus-a' and 'octopus-b'
|\ \ \
* | | | seventh
| | * | octopus-b
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 889842e7fc..527c9e7548 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -242,6 +242,14 @@ test_expect_success \
'git archive --list outside of a git repo' \
'GIT_DIR=some/non-existing/directory git archive --list'
+test_expect_success 'clients cannot access unreachable commits' '
+ test_commit unreachable &&
+ sha1=`git rev-parse HEAD` &&
+ git reset --hard HEAD^ &&
+ git archive $sha1 >remote.tar &&
+ test_must_fail git archive --remote=. $sha1 >remote.tar
+'
+
test_expect_success 'git-archive --prefix=olde-' '
git archive --prefix=olde- >h.tar HEAD &&
(
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
index 9cc0a42ea9..ea6f692baf 100755
--- a/t/t5150-request-pull.sh
+++ b/t/t5150-request-pull.sh
@@ -86,6 +86,7 @@ test_expect_success 'setup: two scripts for reading pull requests' '
s/$downstream_url_for_sed/URL/g
s/for-upstream/BRANCH/g
s/mnemonic.txt/FILENAME/g
+ s/^version [0-9]/VERSION/
/^ FILENAME | *[0-9]* [-+]*\$/ b diffstat
/^AUTHOR ([0-9]*):\$/ b shortlog
p
@@ -193,8 +194,17 @@ test_expect_success 'pull request format' '
SUBJECT (DATE)
are available in the git repository at:
+
URL BRANCH
+ for you to fetch changes up to OBJECT_NAME:
+
+ SUBJECT (DATE)
+
+ ----------------------------------------------------------------
+ VERSION
+
+ ----------------------------------------------------------------
SHORTLOG
DIFFSTAT
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index bafcca765e..9bf69e9a0f 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -97,7 +97,7 @@ test_expect_success 'setup' '
git symbolic-ref HEAD refs/heads/B
'
-pull_to_client 1st "B A" $((11*3))
+pull_to_client 1st "refs/heads/B refs/heads/A" $((11*3))
test_expect_success 'post 1st pull setup' '
add A11 $A10 &&
@@ -110,9 +110,9 @@ test_expect_success 'post 1st pull setup' '
done
'
-pull_to_client 2nd "B" $((64*3))
+pull_to_client 2nd "refs/heads/B" $((64*3))
-pull_to_client 3rd "A" $((1*3))
+pull_to_client 3rd "refs/heads/A" $((1*3))
test_expect_success 'clone shallow' '
git clone --depth 2 "file://$(pwd)/." shallow
diff --git a/t/t5501-fetch-push-alternates.sh b/t/t5501-fetch-push-alternates.sh
index b5ced8483a..1bc57ac03f 100755
--- a/t/t5501-fetch-push-alternates.sh
+++ b/t/t5501-fetch-push-alternates.sh
@@ -28,7 +28,7 @@ test_expect_success setup '
done
) &&
(
- git clone --reference=original "file:///$(pwd)/original" one &&
+ git clone --reference=original "file://$(pwd)/original" one &&
cd one &&
echo Z >count &&
git add count &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index e0af4c4e62..e88dbd50f3 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -166,7 +166,7 @@ test_expect_success 'fetch must not resolve short tag name' '
'
-test_expect_success 'fetch must not resolve short remote name' '
+test_expect_success 'fetch can now resolve short remote name' '
cd "$D" &&
git update-ref refs/remotes/six/HEAD HEAD &&
@@ -175,8 +175,7 @@ test_expect_success 'fetch must not resolve short remote name' '
cd six &&
git init &&
- test_must_fail git fetch .. six:six
-
+ git fetch .. six:six
'
test_expect_success 'create bundle 1' '
diff --git a/t/t5515/fetch.br-branches-default b/t/t5515/fetch.br-branches-default
index 2e0414f6c3..a1bc3d53a6 100644
--- a/t/t5515/fetch.br-branches-default
+++ b/t/t5515/fetch.br-branches-default
@@ -1,8 +1,8 @@
# br-branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-merge b/t/t5515/fetch.br-branches-default-merge
index ca2cc1d1b4..e3a41ae811 100644
--- a/t/t5515/fetch.br-branches-default-merge
+++ b/t/t5515/fetch.br-branches-default-merge
@@ -1,9 +1,9 @@
# br-branches-default-merge
754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-merge_branches-default b/t/t5515/fetch.br-branches-default-merge_branches-default
index 7d947cd80f..1f60561cb1 100644
--- a/t/t5515/fetch.br-branches-default-merge_branches-default
+++ b/t/t5515/fetch.br-branches-default-merge_branches-default
@@ -1,9 +1,9 @@
# br-branches-default-merge branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-octopus b/t/t5515/fetch.br-branches-default-octopus
index ec39c54b7e..f31e1b3080 100644
--- a/t/t5515/fetch.br-branches-default-octopus
+++ b/t/t5515/fetch.br-branches-default-octopus
@@ -2,9 +2,9 @@
754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-octopus_branches-default b/t/t5515/fetch.br-branches-default-octopus_branches-default
index 6bf42e24b6..7060bd9ae5 100644
--- a/t/t5515/fetch.br-branches-default-octopus_branches-default
+++ b/t/t5515/fetch.br-branches-default-octopus_branches-default
@@ -2,9 +2,9 @@
754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default_branches-default b/t/t5515/fetch.br-branches-default_branches-default
index 4a2bf3c95c..8cbd718936 100644
--- a/t/t5515/fetch.br-branches-default_branches-default
+++ b/t/t5515/fetch.br-branches-default_branches-default
@@ -1,8 +1,8 @@
# br-branches-default branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one b/t/t5515/fetch.br-branches-one
index 12ac8d20fb..c98f670526 100644
--- a/t/t5515/fetch.br-branches-one
+++ b/t/t5515/fetch.br-branches-one
@@ -1,8 +1,8 @@
# br-branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-merge b/t/t5515/fetch.br-branches-one-merge
index b4b3b35ce0..aa1c8a937e 100644
--- a/t/t5515/fetch.br-branches-one-merge
+++ b/t/t5515/fetch.br-branches-one-merge
@@ -1,9 +1,9 @@
# br-branches-one-merge
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-merge_branches-one b/t/t5515/fetch.br-branches-one-merge_branches-one
index 2ecef384eb..c93310a730 100644
--- a/t/t5515/fetch.br-branches-one-merge_branches-one
+++ b/t/t5515/fetch.br-branches-one-merge_branches-one
@@ -1,9 +1,9 @@
# br-branches-one-merge branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-octopus b/t/t5515/fetch.br-branches-one-octopus
index 96e3029416..97c4b544b8 100644
--- a/t/t5515/fetch.br-branches-one-octopus
+++ b/t/t5515/fetch.br-branches-one-octopus
@@ -1,9 +1,9 @@
# br-branches-one-octopus
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-octopus_branches-one b/t/t5515/fetch.br-branches-one-octopus_branches-one
index 55e0bad621..df705f74c7 100644
--- a/t/t5515/fetch.br-branches-one-octopus_branches-one
+++ b/t/t5515/fetch.br-branches-one-octopus_branches-one
@@ -1,9 +1,9 @@
# br-branches-one-octopus branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one_branches-one b/t/t5515/fetch.br-branches-one_branches-one
index 281fa09d48..96890e5bd9 100644
--- a/t/t5515/fetch.br-branches-one_branches-one
+++ b/t/t5515/fetch.br-branches-one_branches-one
@@ -1,8 +1,8 @@
# br-branches-one branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit b/t/t5515/fetch.br-config-explicit
index e2fa9c8654..68fc927263 100644
--- a/t/t5515/fetch.br-config-explicit
+++ b/t/t5515/fetch.br-config-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-merge b/t/t5515/fetch.br-config-explicit-merge
index ec1a7231aa..f6475b717a 100644
--- a/t/t5515/fetch.br-config-explicit-merge
+++ b/t/t5515/fetch.br-config-explicit-merge
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-merge_config-explicit b/t/t5515/fetch.br-config-explicit-merge_config-explicit
index 54f689151f..018bdd752a 100644
--- a/t/t5515/fetch.br-config-explicit-merge_config-explicit
+++ b/t/t5515/fetch.br-config-explicit-merge_config-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-octopus b/t/t5515/fetch.br-config-explicit-octopus
index 7011dfc181..36d0270502 100644
--- a/t/t5515/fetch.br-config-explicit-octopus
+++ b/t/t5515/fetch.br-config-explicit-octopus
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-octopus_config-explicit b/t/t5515/fetch.br-config-explicit-octopus_config-explicit
index bdad51f871..6654ad0898 100644
--- a/t/t5515/fetch.br-config-explicit-octopus_config-explicit
+++ b/t/t5515/fetch.br-config-explicit-octopus_config-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit_config-explicit b/t/t5515/fetch.br-config-explicit_config-explicit
index 1b237dde6e..b19b0162e1 100644
--- a/t/t5515/fetch.br-config-explicit_config-explicit
+++ b/t/t5515/fetch.br-config-explicit_config-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob b/t/t5515/fetch.br-config-glob
index e75ec2f72b..946d70ca07 100644
--- a/t/t5515/fetch.br-config-glob
+++ b/t/t5515/fetch.br-config-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-merge b/t/t5515/fetch.br-config-glob-merge
index ce8f739a0d..8bb5e8bc4e 100644
--- a/t/t5515/fetch.br-config-glob-merge
+++ b/t/t5515/fetch.br-config-glob-merge
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-merge_config-glob b/t/t5515/fetch.br-config-glob-merge_config-glob
index 5817bed8f8..113c08dabe 100644
--- a/t/t5515/fetch.br-config-glob-merge_config-glob
+++ b/t/t5515/fetch.br-config-glob-merge_config-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-octopus b/t/t5515/fetch.br-config-glob-octopus
index 938e532db2..9bbd537579 100644
--- a/t/t5515/fetch.br-config-glob-octopus
+++ b/t/t5515/fetch.br-config-glob-octopus
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-octopus_config-glob b/t/t5515/fetch.br-config-glob-octopus_config-glob
index c9225bf6ff..4e510437d9 100644
--- a/t/t5515/fetch.br-config-glob-octopus_config-glob
+++ b/t/t5515/fetch.br-config-glob-octopus_config-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob_config-glob b/t/t5515/fetch.br-config-glob_config-glob
index a6c20f92ce..19daf0cb77 100644
--- a/t/t5515/fetch.br-config-glob_config-glob
+++ b/t/t5515/fetch.br-config-glob_config-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit b/t/t5515/fetch.br-remote-explicit
index 83534d2ec8..ab44bc5519 100644
--- a/t/t5515/fetch.br-remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-merge b/t/t5515/fetch.br-remote-explicit-merge
index a9064dd65a..7421b2cb86 100644
--- a/t/t5515/fetch.br-remote-explicit-merge
+++ b/t/t5515/fetch.br-remote-explicit-merge
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-merge_remote-explicit b/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
index 732a37e4d3..b6975d3890 100644
--- a/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-octopus b/t/t5515/fetch.br-remote-explicit-octopus
index ecf020d929..76812812ee 100644
--- a/t/t5515/fetch.br-remote-explicit-octopus
+++ b/t/t5515/fetch.br-remote-explicit-octopus
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit b/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
index af77531011..4c896cfc1a 100644
--- a/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit_remote-explicit b/t/t5515/fetch.br-remote-explicit_remote-explicit
index 51fae567c8..01e014e6a0 100644
--- a/t/t5515/fetch.br-remote-explicit_remote-explicit
+++ b/t/t5515/fetch.br-remote-explicit_remote-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob b/t/t5515/fetch.br-remote-glob
index 94e6ad31e3..09bfcee00f 100644
--- a/t/t5515/fetch.br-remote-glob
+++ b/t/t5515/fetch.br-remote-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-merge b/t/t5515/fetch.br-remote-glob-merge
index 09362e25af..4b62b01de4 100644
--- a/t/t5515/fetch.br-remote-glob-merge
+++ b/t/t5515/fetch.br-remote-glob-merge
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-merge_remote-glob b/t/t5515/fetch.br-remote-glob-merge_remote-glob
index e2eabec62e..7478f1f158 100644
--- a/t/t5515/fetch.br-remote-glob-merge_remote-glob
+++ b/t/t5515/fetch.br-remote-glob-merge_remote-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-octopus b/t/t5515/fetch.br-remote-glob-octopus
index b08e046195..2543420581 100644
--- a/t/t5515/fetch.br-remote-glob-octopus
+++ b/t/t5515/fetch.br-remote-glob-octopus
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-octopus_remote-glob b/t/t5515/fetch.br-remote-glob-octopus_remote-glob
index d4d547c847..5ffde9c03c 100644
--- a/t/t5515/fetch.br-remote-glob-octopus_remote-glob
+++ b/t/t5515/fetch.br-remote-glob-octopus_remote-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob_remote-glob b/t/t5515/fetch.br-remote-glob_remote-glob
index 646dbc8770..20ba5cb172 100644
--- a/t/t5515/fetch.br-remote-glob_remote-glob
+++ b/t/t5515/fetch.br-remote-glob_remote-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig b/t/t5515/fetch.br-unconfig
index 65ce6d99e2..887ccfc41f 100644
--- a/t/t5515/fetch.br-unconfig
+++ b/t/t5515/fetch.br-unconfig
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_--tags_.._.git b/t/t5515/fetch.br-unconfig_--tags_.._.git
index 8258c80868..1669cc4af0 100644
--- a/t/t5515/fetch.br-unconfig_--tags_.._.git
+++ b/t/t5515/fetch.br-unconfig_--tags_.._.git
@@ -1,7 +1,7 @@
# br-unconfig --tags ../.git
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
index f02bab2fb4..74115361ba 100644
--- a/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
+++ b/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -2,7 +2,7 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
index 85de41109e..7726983818 100644
--- a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
+++ b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -1,7 +1,7 @@
# br-unconfig ../.git tag tag-one-tree tag tag-three-file
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
index 0da2337f1b..7b3750ce5c 100644
--- a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
+++ b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
@@ -1,7 +1,7 @@
# br-unconfig ../.git tag tag-one tag tag-three
8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b tag 'tag-three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 tag 'tag-three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_branches-default b/t/t5515/fetch.br-unconfig_branches-default
index fc7041eefc..da30e3c62c 100644
--- a/t/t5515/fetch.br-unconfig_branches-default
+++ b/t/t5515/fetch.br-unconfig_branches-default
@@ -1,8 +1,8 @@
# br-unconfig branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_branches-one b/t/t5515/fetch.br-unconfig_branches-one
index e94cde745b..e4614314c5 100644
--- a/t/t5515/fetch.br-unconfig_branches-one
+++ b/t/t5515/fetch.br-unconfig_branches-one
@@ -1,8 +1,8 @@
# br-unconfig branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_config-explicit b/t/t5515/fetch.br-unconfig_config-explicit
index 01a283e70d..ed323c9871 100644
--- a/t/t5515/fetch.br-unconfig_config-explicit
+++ b/t/t5515/fetch.br-unconfig_config-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_config-glob b/t/t5515/fetch.br-unconfig_config-glob
index 3a556c5e96..2372ed03c5 100644
--- a/t/t5515/fetch.br-unconfig_config-glob
+++ b/t/t5515/fetch.br-unconfig_config-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_remote-explicit b/t/t5515/fetch.br-unconfig_remote-explicit
index db216dfa56..6318dd11b4 100644
--- a/t/t5515/fetch.br-unconfig_remote-explicit
+++ b/t/t5515/fetch.br-unconfig_remote-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_remote-glob b/t/t5515/fetch.br-unconfig_remote-glob
index aee65c204d..1d9afad7d8 100644
--- a/t/t5515/fetch.br-unconfig_remote-glob
+++ b/t/t5515/fetch.br-unconfig_remote-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master b/t/t5515/fetch.master
index 950fd078db..9b29d67200 100644
--- a/t/t5515/fetch.master
+++ b/t/t5515/fetch.master
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_--tags_.._.git b/t/t5515/fetch.master_--tags_.._.git
index 0e59950c7b..8a7493537b 100644
--- a/t/t5515/fetch.master_--tags_.._.git
+++ b/t/t5515/fetch.master_--tags_.._.git
@@ -1,7 +1,7 @@
# master --tags ../.git
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
index 82868524ca..0672d1292f 100644
--- a/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
+++ b/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -2,7 +2,7 @@
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
index 2e133eff29..0fd737cf81 100644
--- a/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
+++ b/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -1,7 +1,7 @@
# master ../.git tag tag-one-tree tag tag-three-file
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three b/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
index 92b18b40cc..e488986653 100644
--- a/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
+++ b/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
@@ -1,7 +1,7 @@
# master ../.git tag tag-one tag tag-three
8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b tag 'tag-three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 tag 'tag-three' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-default b/t/t5515/fetch.master_branches-default
index 603d6d2331..2eedd3bfa4 100644
--- a/t/t5515/fetch.master_branches-default
+++ b/t/t5515/fetch.master_branches-default
@@ -1,8 +1,8 @@
# master branches-default
754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-one b/t/t5515/fetch.master_branches-one
index fe9bb0b798..901ce21d33 100644
--- a/t/t5515/fetch.master_branches-one
+++ b/t/t5515/fetch.master_branches-one
@@ -1,8 +1,8 @@
# master branches-one
8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-explicit b/t/t5515/fetch.master_config-explicit
index 4be97c7575..251c826aa9 100644
--- a/t/t5515/fetch.master_config-explicit
+++ b/t/t5515/fetch.master_config-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-glob b/t/t5515/fetch.master_config-glob
index cb0726ff8d..27c158e332 100644
--- a/t/t5515/fetch.master_config-glob
+++ b/t/t5515/fetch.master_config-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-explicit b/t/t5515/fetch.master_remote-explicit
index 44a1ca8429..b3cfe6b98b 100644
--- a/t/t5515/fetch.master_remote-explicit
+++ b/t/t5515/fetch.master_remote-explicit
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-glob b/t/t5515/fetch.master_remote-glob
index 724e8db0a5..118befd1e4 100644
--- a/t/t5515/fetch.master_remote-glob
+++ b/t/t5515/fetch.master_remote-glob
@@ -3,9 +3,9 @@
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
-754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../
8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
-0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+c61a82b60967180544e3c19f819ddbd0c9f89899 not-for-merge tag 'tag-three' of ../
0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
-6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
+525b7fb068d59950d185a8779dc957c77eed73ba not-for-merge tag 'tag-two' of ../
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 0e5eb678ce..35304b41e9 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -94,16 +94,35 @@ test_expect_success '--rebase' '
test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
test new = $(git show HEAD:file2)
'
+test_expect_success 'pull.rebase' '
+ git reset --hard before-rebase &&
+ git config --bool pull.rebase true &&
+ test_when_finished "git config --unset pull.rebase" &&
+ git pull . copy &&
+ test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
+ test new = $(git show HEAD:file2)
+'
test_expect_success 'branch.to-rebase.rebase' '
git reset --hard before-rebase &&
- git config branch.to-rebase.rebase 1 &&
+ git config --bool branch.to-rebase.rebase true &&
+ test_when_finished "git config --unset branch.to-rebase.rebase" &&
git pull . copy &&
- git config branch.to-rebase.rebase 0 &&
test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
test new = $(git show HEAD:file2)
'
+test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
+ git reset --hard before-rebase &&
+ git config --bool pull.rebase true &&
+ test_when_finished "git config --unset pull.rebase" &&
+ git config --bool branch.to-rebase.rebase false &&
+ test_when_finished "git config --unset branch.to-rebase.rebase" &&
+ git pull . copy &&
+ test $(git rev-parse HEAD^) != $(git rev-parse copy) &&
+ test new = $(git show HEAD:file2)
+'
+
test_expect_success '--rebase with rebased upstream' '
git remote add -f me . &&
diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh
new file mode 100755
index 0000000000..edea9f957e
--- /dev/null
+++ b/t/t5527-fetch-odd-refs.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description='test fetching of oddly-named refs'
+. ./test-lib.sh
+
+# afterwards we will have:
+# HEAD - two
+# refs/for/refs/heads/master - one
+# refs/heads/master - three
+test_expect_success 'setup repo with odd suffix ref' '
+ echo content >file &&
+ git add . &&
+ git commit -m one &&
+ git update-ref refs/for/refs/heads/master HEAD &&
+ echo content >>file &&
+ git commit -a -m two &&
+ echo content >>file &&
+ git commit -a -m three &&
+ git checkout HEAD^
+'
+
+test_expect_success 'suffix ref is ignored during fetch' '
+ git clone --bare file://"$PWD" suffix &&
+ echo three >expect &&
+ git --git-dir=suffix log -1 --format=%s refs/heads/master >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index 64767d8705..1eea647656 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -40,6 +40,22 @@ test_expect_success 'setup remote repository' '
mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
'
+test_expect_success 'create password-protected repository' '
+ mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb" &&
+ cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+ "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
+'
+
+test_expect_success 'setup askpass helper' '
+ cat >askpass <<-\EOF &&
+ #!/bin/sh
+ echo user@host
+ EOF
+ chmod +x askpass &&
+ GIT_ASKPASS="$PWD/askpass" &&
+ export GIT_ASKPASS
+'
+
test_expect_success 'clone remote repository' '
cd "$ROOT_PATH" &&
git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
@@ -144,6 +160,24 @@ test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
"$ROOT_PATH"/test_repo_clone master
+test_expect_success 'push to password-protected repository (user in URL)' '
+ test_commit pw-user &&
+ git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
+ git rev-parse --verify HEAD >expect &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+ rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'push to password-protected repository (no user in URL)' '
+ test_commit pw-nouser &&
+ git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+ git rev-parse --verify HEAD >expect &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+ rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
stop_httpd
test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
index 311a33ca84..95a133d697 100755
--- a/t/t5550-http-fetch.sh
+++ b/t/t5550-http-fetch.sh
@@ -49,40 +49,84 @@ test_expect_success 'setup askpass helpers' '
EOF
chmod +x askpass &&
GIT_ASKPASS="$PWD/askpass" &&
- export GIT_ASKPASS &&
- >askpass-expect-none &&
- echo "askpass: Password for '\''$HTTPD_DEST'\'': " >askpass-expect-pass &&
- { echo "askpass: Username for '\''$HTTPD_DEST'\'': " &&
- cat askpass-expect-pass
- } >askpass-expect-both
-'
+ export GIT_ASKPASS
+'
+
+expect_askpass() {
+ dest=$HTTPD_DEST
+ {
+ case "$1" in
+ none)
+ ;;
+ pass)
+ echo "askpass: Password for 'http://$2@$dest': "
+ ;;
+ both)
+ echo "askpass: Username for 'http://$dest': "
+ echo "askpass: Password for 'http://$2@$dest': "
+ ;;
+ *)
+ false
+ ;;
+ esac
+ } >askpass-expect &&
+ test_cmp askpass-expect askpass-query
+}
test_expect_success 'cloning password-protected repository can fail' '
>askpass-query &&
echo wrong >askpass-response &&
test_must_fail git clone "$HTTPD_URL/auth/repo.git" clone-auth-fail &&
- test_cmp askpass-expect-both askpass-query
+ expect_askpass both wrong
'
test_expect_success 'http auth can use user/pass in URL' '
>askpass-query &&
- echo wrong >askpass-reponse &&
+ echo wrong >askpass-response &&
git clone "$HTTPD_URL_USER_PASS/auth/repo.git" clone-auth-none &&
- test_cmp askpass-expect-none askpass-query
+ expect_askpass none
'
test_expect_success 'http auth can use just user in URL' '
>askpass-query &&
echo user@host >askpass-response &&
git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-pass &&
- test_cmp askpass-expect-pass askpass-query
+ expect_askpass pass user@host
'
test_expect_success 'http auth can request both user and pass' '
>askpass-query &&
echo user@host >askpass-response &&
git clone "$HTTPD_URL/auth/repo.git" clone-auth-both &&
- test_cmp askpass-expect-both askpass-query
+ expect_askpass both user@host
+'
+
+test_expect_success 'http auth respects credential helper config' '
+ test_config_global credential.helper "!f() {
+ cat >/dev/null
+ echo username=user@host
+ echo password=user@host
+ }; f" &&
+ >askpass-query &&
+ echo wrong >askpass-response &&
+ git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper &&
+ expect_askpass none
+'
+
+test_expect_success 'http auth can get username from config' '
+ test_config_global "credential.$HTTPD_URL.username" user@host &&
+ >askpass-query &&
+ echo user@host >askpass-response &&
+ git clone "$HTTPD_URL/auth/repo.git" clone-auth-user &&
+ expect_askpass pass user@host
+'
+
+test_expect_success 'configured username does not override URL' '
+ test_config_global "credential.$HTTPD_URL.username" wrong &&
+ >askpass-query &&
+ echo user@host >askpass-response &&
+ git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-user2 &&
+ expect_askpass pass user@host
'
test_expect_success 'fetch changes via http' '
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index 895f5595ae..c4c375ac04 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -146,4 +146,11 @@ test_expect_success 'cloning with reference being subset of source (-l -s)' \
cd "$base_dir"
+test_expect_success 'clone with reference from a tagged repository' '
+ (
+ cd A && git tag -a -m 'tagged' HEAD
+ ) &&
+ git clone --reference=A A I
+'
+
test_done
diff --git a/t/t7106-reset-sequence.sh b/t/t7106-reset-sequence.sh
deleted file mode 100755
index 4956caaf82..0000000000
--- a/t/t7106-reset-sequence.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-test_description='Test interaction of reset --hard with sequencer
-
- + anotherpick: rewrites foo to d
- + picked: rewrites foo to c
- + unrelatedpick: rewrites unrelated to reallyunrelated
- + base: rewrites foo to b
- + initial: writes foo as a, unrelated as unrelated
-'
-
-. ./test-lib.sh
-
-pristine_detach () {
- git cherry-pick --reset &&
- git checkout -f "$1^0" &&
- git read-tree -u --reset HEAD &&
- git clean -d -f -f -q -x
-}
-
-test_expect_success setup '
- echo unrelated >unrelated &&
- git add unrelated &&
- test_commit initial foo a &&
- test_commit base foo b &&
- test_commit unrelatedpick unrelated reallyunrelated &&
- test_commit picked foo c &&
- test_commit anotherpick foo d &&
- git config advice.detachedhead false
-
-'
-
-test_expect_success 'reset --hard cleans up sequencer state, providing one-level undo' '
- pristine_detach initial &&
- test_must_fail git cherry-pick base..anotherpick &&
- test_path_is_dir .git/sequencer &&
- git reset --hard &&
- test_path_is_missing .git/sequencer &&
- test_path_is_dir .git/sequencer-old &&
- git reset --hard &&
- test_path_is_missing .git/sequencer-old
-'
-
-test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 3ad04363b5..8bb38337a9 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -8,39 +8,39 @@
test_description='git commit'
. ./test-lib.sh
+. "$TEST_DIRECTORY/diff-lib.sh"
-test_tick
+author='The Real Author <someguy@his.email.org>'
-test_expect_success \
- "initial status" \
- "echo 'bongo bongo' >file &&
- git add file"
+test_tick
-test_expect_success "Constructing initial commit" '
+test_expect_success 'initial status' '
+ echo bongo bongo >file &&
+ git add file &&
git status >actual &&
test_i18ngrep "Initial commit" actual
'
-test_expect_success \
- "fail initial amend" \
- "test_must_fail git commit --amend"
+test_expect_success 'fail initial amend' '
+ test_must_fail git commit --amend
+'
-test_expect_success \
- "initial commit" \
- "git commit -m initial"
+test_expect_success 'setup: initial commit' '
+ git commit -m initial
+'
-test_expect_success \
- "invalid options 1" \
- "test_must_fail git commit -m foo -m bar -F file"
+test_expect_success '-m and -F do not mix' '
+ test_must_fail git commit -m foo -m bar -F file
+'
-test_expect_success \
- "invalid options 2" \
- "test_must_fail git commit -C HEAD -m illegal"
+test_expect_success '-m and -C do not mix' '
+ test_must_fail git commit -C HEAD -m illegal
+'
-test_expect_success \
- "using paths with -a" \
- "echo King of the bongo >file &&
- test_must_fail git commit -m foo -a file"
+test_expect_success 'paths and -a do not mix' '
+ echo King of the bongo >file &&
+ test_must_fail git commit -m foo -a file
+'
test_expect_success PERL 'can use paths with --interactive' '
echo bong-o-bong >file &&
@@ -50,139 +50,163 @@ test_expect_success PERL 'can use paths with --interactive' '
git reset --hard HEAD^
'
-test_expect_success \
- "using invalid commit with -C" \
- "test_must_fail git commit -C bogus"
+test_expect_success 'using invalid commit with -C' '
+ test_must_fail git commit -C bogus
+'
-test_expect_success \
- "testing nothing to commit" \
- "test_must_fail git commit -m initial"
+test_expect_success 'nothing to commit' '
+ test_must_fail git commit -m initial
+'
-test_expect_success \
- "next commit" \
- "echo 'bongo bongo bongo' >file \
- git commit -m next -a"
+test_expect_success 'setup: non-initial commit' '
+ echo bongo bongo bongo >file &&
+ git commit -m next -a
+'
-test_expect_success \
- "commit message from non-existing file" \
- "echo 'more bongo: bongo bongo bongo bongo' >file && \
- test_must_fail git commit -F gah -a"
+test_expect_success 'commit message from non-existing file' '
+ echo more bongo: bongo bongo bongo bongo >file &&
+ test_must_fail git commit -F gah -a
+'
-# Empty except stray tabs and spaces on a few lines.
-sed -e 's/@$//' >msg <<EOF
- @
+test_expect_success 'empty commit message' '
+ # Empty except stray tabs and spaces on a few lines.
+ sed -e "s/@//g" >msg <<-\EOF &&
+ @ @
+ @@
+ @ @
+ @Signed-off-by: hula@
+ EOF
+ test_must_fail git commit -F msg -a
+'
- @
-Signed-off-by: hula
-EOF
-test_expect_success \
- "empty commit message" \
- "test_must_fail git commit -F msg -a"
+test_expect_success 'setup: commit message from file' '
+ echo this is the commit message, coming from a file >msg &&
+ 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"
+test_expect_success 'amend commit' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor &&
+ EDITOR=./editor git commit --amend
+'
-cat >editor <<\EOF
-#!/bin/sh
-sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
-mv "$1-" "$1"
-EOF
-chmod 755 editor
+test_expect_success 'set up editor' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e "s/unamended/amended/g" <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor
+'
-test_expect_success \
- "amend commit" \
- "EDITOR=./editor git commit --amend"
+test_expect_success 'amend without launching editor' '
+ echo unamended >expect &&
+ git commit --allow-empty -m "unamended" &&
+ echo needs more bongo >file &&
+ git add file &&
+ EDITOR=./editor git commit --no-edit --amend &&
+ git diff --exit-code HEAD -- file &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ test_cmp expect msg
+'
-test_expect_success \
- "passing -m and -F" \
- "echo 'enough with the bongos' >file && \
- test_must_fail git commit -F msg -m amending ."
+test_expect_success '--amend --edit' '
+ echo amended >expect &&
+ git commit --allow-empty -m "unamended" &&
+ echo bongo again >file &&
+ git add file &&
+ EDITOR=./editor git commit --edit --amend &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ test_cmp expect msg
+'
-test_expect_success \
- "using message from other commit" \
- "git commit -C HEAD^ ."
+test_expect_success '-m --edit' '
+ echo amended >expect &&
+ git commit --allow-empty -m buffer &&
+ echo bongo bongo >file &&
+ git add file &&
+ EDITOR=./editor git commit -m unamended --edit &&
+ git diff-tree -s --format=%s HEAD >msg &&
+ test_cmp expect msg
+'
-cat >editor <<\EOF
-#!/bin/sh
-sed -e "s/amend/older/g" < "$1" > "$1-"
-mv "$1-" "$1"
-EOF
-chmod 755 editor
-
-test_expect_success \
- "editing message from other commit" \
- "echo 'hula hula' >file && \
- EDITOR=./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 >output 2>&1"
-
-test_expect_success \
- "commit --author output mentions author" \
- "grep Rubber.Duck output"
-
-test_expect_success PERL \
- "interactive add" \
- "echo 7 | git commit --interactive | grep 'What now'"
-
-test_expect_success PERL \
- "commit --interactive doesn't change index if editor aborts" \
- "echo zoo >file &&
+test_expect_success '-m and -F do not mix' '
+ echo enough with the bongos >file &&
+ test_must_fail git commit -F msg -m amending .
+'
+
+test_expect_success 'using message from other commit' '
+ git commit -C HEAD^ .
+'
+
+test_expect_success 'editing message from other commit' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e "s/amend/older/g" < "$1" > "$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor &&
+ echo hula hula >file &&
+ EDITOR=./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 >output 2>&1 &&
+ grep Rubber.Duck output
+'
+
+test_expect_success PERL 'interactive add' '
+ echo 7 |
+ git commit --interactive |
+ grep "What now"
+'
+
+test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
+ echo zoo >file &&
test_must_fail git diff --exit-code >diff1 &&
- (echo u ; echo '*' ; echo q) |
- (EDITOR=: && export EDITOR &&
- test_must_fail git commit --interactive) &&
+ (echo u ; echo "*" ; echo q) |
+ (
+ EDITOR=: &&
+ export EDITOR &&
+ test_must_fail git commit --interactive
+ ) &&
git diff >diff2 &&
- test_cmp diff1 diff2"
-
-test_expect_success \
- "showing committed revisions" \
- "git rev-list HEAD >current"
+ compare_diff_patch diff1 diff2
+'
-cat >editor <<\EOF
-#!/bin/sh
-sed -e "s/good/bad/g" < "$1" > "$1-"
-mv "$1-" "$1"
-EOF
-chmod 755 editor
-
-cat >msg <<EOF
-A good commit message.
-EOF
-
-test_expect_success \
- 'editor not invoked if -F is given' '
- echo "moo" >file &&
- EDITOR=./editor git commit -a -F msg &&
- git show -s --pretty=format:"%s" | grep -q good &&
- echo "quack" >file &&
- echo "Another good message." | EDITOR=./editor git commit -a -F - &&
- git show -s --pretty=format:"%s" | grep -q good
- '
-# 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.' \
- 'test_cmp expected current'
+test_expect_success 'editor not invoked if -F is given' '
+ cat >editor <<-\EOF &&
+ #!/bin/sh
+ sed -e s/good/bad/g <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
+ chmod 755 editor &&
+
+ echo A good commit message. >msg &&
+ echo moo >file &&
+
+ EDITOR=./editor git commit -a -F msg &&
+ git show -s --pretty=format:%s >subject &&
+ grep -q good subject &&
+
+ echo quack >file &&
+ echo Another good message. |
+ EDITOR=./editor git commit -a -F - &&
+ git show -s --pretty=format:%s >subject &&
+ grep -q good subject
+'
test_expect_success 'partial commit that involves removal (1)' '
@@ -216,7 +240,6 @@ test_expect_success 'partial commit that involves removal (3)' '
'
-author="The Real Author <someguy@his.email.org>"
test_expect_success 'amend commit to fix author' '
oldtick=$GIT_AUTHOR_DATE &&
@@ -345,7 +368,6 @@ test_expect_success 'multiple -m' '
'
-author="The Real Author <someguy@his.email.org>"
test_expect_success 'amend commit to fix author' '
oldtick=$GIT_AUTHOR_DATE &&
@@ -372,15 +394,8 @@ test_expect_success 'git commit <file> with dirty index' '
test_expect_success 'same tree (single parent)' '
- git reset --hard
-
- if git commit -m empty
- then
- echo oops -- should have complained
- false
- else
- : happy
- fi
+ git reset --hard &&
+ test_must_fail git commit -m empty
'
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 3008e4e121..5d8c428543 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -38,8 +38,8 @@ printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
>empty
create_merge_msgs () {
- echo "Merge commit 'c2'" >msg.1-5 &&
- echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 &&
+ echo "Merge tag 'c2'" >msg.1-5 &&
+ echo "Merge tags 'c2' and 'c3'" >msg.1-5-9 &&
{
echo "Squashed commit of the following:" &&
echo &&
@@ -57,7 +57,7 @@ create_merge_msgs () {
} >squash.1-5-9 &&
echo >msg.nolog &&
{
- echo "* commit 'c3':" &&
+ echo "* tag 'c3':" &&
echo " commit 3" &&
echo
} >msg.log
@@ -96,7 +96,11 @@ verify_parents () {
verify_mergeheads () {
printf '%s\n' "$@" >mergehead.expected &&
- test_cmp mergehead.expected .git/MERGE_HEAD
+ while read sha1 rest
+ do
+ git rev-parse $sha1
+ done <.git/MERGE_HEAD >mergehead.actual &&
+ test_cmp mergehead.expected mergehead.actual
}
verify_no_mergehead () {
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
index 9114785ef7..89619cf446 100755
--- a/t/t7604-merge-custom-message.sh
+++ b/t/t7604-merge-custom-message.sh
@@ -11,7 +11,7 @@ create_merge_msgs() {
cp exp.subject exp.log &&
echo >>exp.log "" &&
- echo >>exp.log "* commit 'c2':" &&
+ echo >>exp.log "* tag 'c2':" &&
echo >>exp.log " c2"
}
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
index 9225fa6f02..8e7e0a5865 100755
--- a/t/t7608-merge-messages.sh
+++ b/t/t7608-merge-messages.sh
@@ -35,7 +35,7 @@ test_expect_success 'merge tag' '
git checkout master &&
test_commit master-3 &&
git merge tag-1 &&
- check_oneline "Merge commit Qtag-1Q"
+ check_oneline "Merge tag Qtag-1Q"
'
test_expect_success 'ambiguous tag' '
@@ -44,7 +44,7 @@ test_expect_success 'ambiguous tag' '
git checkout master &&
test_commit master-4 &&
git merge ambiguous &&
- check_oneline "Merge commit QambiguousQ"
+ check_oneline "Merge tag QambiguousQ"
'
test_expect_success 'remote-tracking branch' '
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index 463254c727..83acf68bc3 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -505,9 +505,63 @@ test_expect_success 'verify that non-notes are untouched by a fanout change' '
test_cmp expect_non-note3 actual
'
+
+# Change the notes for the three top commits
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+changing notes for the top three commits
+COMMIT
+from refs/notes/many_notes^0
+INPUT_END
+
+rm expect
+i=$num_commits
+j=0
+while test $j -lt 3
+do
+ cat >>input <<INPUT_END
+N inline refs/heads/many_commits~$j
+data <<EOF
+changed note for commit #$i
+EOF
+INPUT_END
+ cat >>expect <<EXPECT_END
+ commit #$i
+ changed note for commit #$i
+EXPECT_END
+ i=$(($i - 1))
+ j=$(($j + 1))
+done
+
+test_expect_success 'change a few existing notes' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits |
+ grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'verify that changing notes respect existing fanout' '
+
+ # None of the entries in the top-level notes tree should be a full SHA1
+ git ls-tree --name-only refs/notes/many_notes |
+ while read path
+ do
+ if test $(expr length "$path") -ge 40
+ then
+ return 1
+ fi
+ done
+
+'
+
remaining_notes=10
test_tick
-cat >>input <<INPUT_END
+cat >input <<INPUT_END
commit refs/notes/many_notes
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
@@ -516,12 +570,11 @@ COMMIT
from refs/notes/many_notes^0
INPUT_END
-i=$remaining_notes
-while test $i -lt $num_commits
+i=$(($num_commits - $remaining_notes))
+for sha1 in $(git rev-list -n $i refs/heads/many_commits)
do
- i=$(($i + 1))
cat >>input <<INPUT_END
-N 0000000000000000000000000000000000000000 :$i
+N 0000000000000000000000000000000000000000 $sha1
INPUT_END
done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 53297156a3..ab24917812 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -274,6 +274,53 @@ test_expect_success \
'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"'
# ----------------------------------------------------------------------
+# commitdiff testing (incomplete lines)
+
+test_expect_success 'setup incomplete lines' '
+ cat >file<<-\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.
+ CHANGE_ME
+ EOF
+ git commit -a -m "Preparing for incomplete lines" &&
+ echo "incomplete" | tr -d "\\012" >>file &&
+ git commit -a -m "Add incomplete line" &&
+ git tag incomplete_lines_add &&
+ sed -e s/CHANGE_ME/change_me/ <file >file+ &&
+ mv -f file+ file &&
+ git commit -a -m "Incomplete context line" &&
+ git tag incomplete_lines_ctx &&
+ echo "Dominus regit me," >file &&
+ echo "incomplete line" | tr -d "\\012" >>file &&
+ git commit -a -m "Change incomplete line" &&
+ git tag incomplete_lines_chg
+ echo "Dominus regit me," >file &&
+ git commit -a -m "Remove incomplete line" &&
+ git tag incomplete_lines_rem
+'
+
+test_expect_success 'commitdiff(1): addition of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_add"
+'
+
+test_expect_success 'commitdiff(1): incomplete line as context line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_ctx"
+'
+
+test_expect_success 'commitdiff(1): change incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_chg"
+'
+
+test_expect_success 'commitdiff(1): removal of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_rem"
+'
+
+# ----------------------------------------------------------------------
# commit, commitdiff: merge, large
test_expect_success \
'Create a merge' \
@@ -282,7 +329,8 @@ test_expect_success \
git add b &&
git commit -a -m "On branch" &&
git checkout master &&
- git pull . b'
+ git pull . b &&
+ git tag merge_commit'
test_expect_success \
'commit(0): merge commit' \
@@ -332,6 +380,29 @@ test_expect_success \
'gitweb_run "p=.git;a=commitdiff;h=b"'
# ----------------------------------------------------------------------
+# side-by-side diff
+
+test_expect_success 'side-by-side: addition of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_add;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: incomplete line as context line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_ctx;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: changed incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_chg;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: removal of incomplete line' '
+ gitweb_run "p=.git;a=commitdiff;h=incomplete_lines_rem;ds=sidebyside"
+'
+
+test_expect_success 'side-by-side: merge commit' '
+ gitweb_run "p=.git;a=commitdiff;h=merge_commit;ds=sidebyside"
+'
+
+# ----------------------------------------------------------------------
# tags testing
test_expect_success \
diff --git a/t/t9805-skip-submit-edit.sh b/t/t9805-skip-submit-edit.sh
new file mode 100755
index 0000000000..df929e0555
--- /dev/null
+++ b/t/t9805-skip-submit-edit.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+test_description='git-p4 skipSubmitEdit config variables'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "change 1"
+ )
+'
+
+# this works because EDITOR is set to :
+test_expect_success 'no config, unedited, say yes' '
+ "$GITP4" clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ echo line >>file1 &&
+ git commit -a -m "change 2" &&
+ echo y | "$GITP4" submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 2 wc
+ )
+'
+
+test_expect_success 'no config, unedited, say no' '
+ "$GITP4" clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ echo line >>file1 &&
+ git commit -a -m "change 3 (not really)" &&
+ printf "bad response\nn\n" | "$GITP4" submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 2 wc
+ )
+'
+
+test_expect_success 'skipSubmitEdit' '
+ "$GITP4" clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEdit true &&
+ # will fail if editor is even invoked
+ git config core.editor /bin/false &&
+ echo line >>file1 &&
+ git commit -a -m "change 3" &&
+ "$GITP4" submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 3 wc
+ )
+'
+
+test_expect_success 'skipSubmitEditCheck' '
+ "$GITP4" clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git config git-p4.skipSubmitEditCheck true &&
+ echo line >>file1 &&
+ git commit -a -m "change 4" &&
+ "$GITP4" submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 4 wc
+ )
+'
+
+# check the normal case, where the template really is edited
+test_expect_success 'no config, edited' '
+ "$GITP4" clone --dest="$git" //depot &&
+ test_when_finished cleanup_git &&
+ ed="$TRASH_DIRECTORY/ed.sh" &&
+ test_when_finished "rm \"$ed\"" &&
+ cat >"$ed" <<-EOF &&
+ #!$SHELL_PATH
+ sleep 1
+ touch "\$1"
+ exit 0
+ EOF
+ chmod 755 "$ed" &&
+ (
+ cd "$git" &&
+ echo line >>file1 &&
+ git commit -a -m "change 5" &&
+ EDITOR="\"$ed\"" "$GITP4" submit &&
+ p4 changes //depot/... >wc &&
+ test_line_count = 5 wc
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9807-submit.sh b/t/t9807-submit.sh
new file mode 100755
index 0000000000..2cb724e14c
--- /dev/null
+++ b/t/t9807-submit.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git-p4 submit'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "change 1"
+ )
+'
+
+test_expect_success 'submit with no client dir' '
+ test_when_finished cleanup_git &&
+ "$GITP4" clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ echo file2 >file2 &&
+ git add file2 &&
+ git commit -m "git commit 2" &&
+ rm -rf "$cli" &&
+ git config git-p4.skipSubmitEdit true &&
+ "$GITP4" submit
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9808-chdir.sh b/t/t9808-chdir.sh
new file mode 100755
index 0000000000..eb8cc9523e
--- /dev/null
+++ b/t/t9808-chdir.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='git-p4 relative chdir'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+ echo file1 >file1 &&
+ p4 add file1 &&
+ p4 submit -d "change 1"
+ )
+'
+
+# P4 reads from P4CONFIG file to find its server params, if the
+# environment variable is set
+test_expect_success 'P4CONFIG and absolute dir clone' '
+ printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config &&
+ test_when_finished "rm \"$TRASH_DIRECTORY/p4config\"" &&
+ test_when_finished cleanup_git &&
+ (
+ P4CONFIG=p4config && export P4CONFIG &&
+ unset P4PORT P4CLIENT &&
+ "$GITP4" clone --verbose --dest="$git" //depot
+ )
+'
+
+# same thing, but with relative directory name, note missing $ on --dest
+test_expect_success 'P4CONFIG and relative dir clone' '
+ printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config &&
+ test_when_finished "rm \"$TRASH_DIRECTORY/p4config\"" &&
+ test_when_finished cleanup_git &&
+ (
+ P4CONFIG=p4config && export P4CONFIG &&
+ unset P4PORT P4CLIENT &&
+ "$GITP4" clone --verbose --dest="git" //depot
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 9cfabe447b..0e932dd975 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -380,6 +380,11 @@ test_config () {
git config "$@"
}
+test_config_global () {
+ test_when_finished "test_unconfig --global '$1'" &&
+ git config --global "$@"
+}
+
# Use test_set_prereq to tell that a particular prerequisite is available.
# The prerequisite can later be checked for in two ways:
#
diff --git a/tag.c b/tag.c
index 7d38cc0f4d..3aa186df62 100644
--- a/tag.c
+++ b/tag.c
@@ -139,6 +139,11 @@ int parse_tag(struct tag *item)
return ret;
}
+/*
+ * Look at a signed tag object, and return the offset where
+ * the embedded detached signature begins, or the end of the
+ * data when there is no such signature.
+ */
size_t parse_signature(const char *buf, unsigned long size)
{
char *eol;
diff --git a/test-credential.c b/test-credential.c
new file mode 100644
index 0000000000..dee200e7f2
--- /dev/null
+++ b/test-credential.c
@@ -0,0 +1,38 @@
+#include "cache.h"
+#include "credential.h"
+#include "string-list.h"
+
+static const char usage_msg[] =
+"test-credential <fill|approve|reject> [helper...]";
+
+int main(int argc, const char **argv)
+{
+ const char *op;
+ struct credential c = CREDENTIAL_INIT;
+ int i;
+
+ op = argv[1];
+ if (!op)
+ usage(usage_msg);
+ for (i = 2; i < argc; i++)
+ string_list_append(&c.helpers, argv[i]);
+
+ if (credential_read(&c, stdin) < 0)
+ die("unable to read credential from stdin");
+
+ if (!strcmp(op, "fill")) {
+ credential_fill(&c);
+ if (c.username)
+ printf("username=%s\n", c.username);
+ if (c.password)
+ printf("password=%s\n", c.password);
+ }
+ else if (!strcmp(op, "approve"))
+ credential_approve(&c);
+ else if (!strcmp(op, "reject"))
+ credential_reject(&c);
+ else
+ usage(usage_msg);
+
+ return 0;
+}
diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c
index 1f73f1ea7d..e6c292385f 100644
--- a/test-dump-cache-tree.c
+++ b/test-dump-cache-tree.c
@@ -59,6 +59,6 @@ int main(int ac, char **av)
struct cache_tree *another = cache_tree();
if (read_cache() < 0)
die("unable to read index file");
- cache_tree_update(another, active_cache, active_nr, 0, 1);
+ cache_tree_update(another, active_cache, active_nr, 0, 1, 0);
return dump_cache_tree(active_cache_tree, another, "");
}
diff --git a/test-scrap-cache-tree.c b/test-scrap-cache-tree.c
new file mode 100644
index 0000000000..4728013910
--- /dev/null
+++ b/test-scrap-cache-tree.c
@@ -0,0 +1,17 @@
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+static struct lock_file index_lock;
+
+int main(int ac, char **av)
+{
+ int fd = hold_locked_index(&index_lock, 1);
+ if (read_cache() < 0)
+ die("unable to read index file");
+ active_cache_tree = NULL;
+ if (write_cache(fd, active_cache, active_nr)
+ || commit_lock_file(&index_lock))
+ die("unable to write index file");
+ return 0;
+}
diff --git a/transport.c b/transport.c
index 51814b5da3..63f38e3940 100644
--- a/transport.c
+++ b/transport.c
@@ -163,7 +163,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
/* Follow symbolic refs (mainly for HEAD). */
localname = ref->peer_ref->name;
remotename = ref->name;
- tmp = resolve_ref(localname, sha, 1, &flag);
+ tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
if (tmp && flag & REF_ISSYMREF &&
!prefixcmp(tmp, "refs/heads/"))
localname = tmp;
@@ -502,7 +502,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
struct ref *refs;
connect_setup(transport, for_push, 0);
- get_remote_heads(data->fd[0], &refs, 0, NULL,
+ get_remote_heads(data->fd[0], &refs,
for_push ? REF_NORMAL : 0, &data->extra_have);
data->got_remote_heads = 1;
@@ -537,7 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport,
if (!data->got_remote_heads) {
connect_setup(transport, 0, 0);
- get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
+ get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
data->got_remote_heads = 1;
}
@@ -772,8 +772,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
struct ref *tmp_refs;
connect_setup(transport, 1, 0);
- get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
- NULL);
+ get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL);
data->got_remote_heads = 1;
}
diff --git a/tree-diff.c b/tree-diff.c
index b3cc2e4753..7a51d091b5 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -21,8 +21,8 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
sha1 = tree_entry_extract(t1, &path1, &mode1);
sha2 = tree_entry_extract(t2, &path2, &mode2);
- pathlen1 = tree_entry_len(path1, sha1);
- pathlen2 = tree_entry_len(path2, sha2);
+ pathlen1 = tree_entry_len(&t1->entry);
+ pathlen2 = tree_entry_len(&t2->entry);
cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
if (cmp < 0) {
show_entry(opt, "-", t1, base);
@@ -64,14 +64,14 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
static void show_tree(struct diff_options *opt, const char *prefix,
struct tree_desc *desc, struct strbuf *base)
{
- int match = 0;
+ enum interesting match = entry_not_interesting;
for (; desc->size; update_tree_entry(desc)) {
- if (match != 2) {
+ if (match != all_entries_interesting) {
match = tree_entry_interesting(&desc->entry, base, 0,
&opt->pathspec);
- if (match < 0)
+ if (match == all_entries_not_interesting)
break;
- if (match == 0)
+ if (match == entry_not_interesting)
continue;
}
show_entry(opt, prefix, desc, base);
@@ -85,7 +85,7 @@ static void show_entry(struct diff_options *opt, const char *prefix,
unsigned mode;
const char *path;
const unsigned char *sha1 = tree_entry_extract(desc, &path, &mode);
- int pathlen = tree_entry_len(path, sha1);
+ int pathlen = tree_entry_len(&desc->entry);
int old_baselen = base->len;
strbuf_add(base, path, pathlen);
@@ -114,12 +114,13 @@ static void show_entry(struct diff_options *opt, const char *prefix,
}
static void skip_uninteresting(struct tree_desc *t, struct strbuf *base,
- struct diff_options *opt, int *match)
+ struct diff_options *opt,
+ enum interesting *match)
{
while (t->size) {
*match = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec);
if (*match) {
- if (*match < 0)
+ if (*match == all_entries_not_interesting)
t->size = 0;
break;
}
@@ -132,7 +133,8 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
{
struct strbuf base;
int baselen = strlen(base_str);
- int t1_match = 0, t2_match = 0;
+ enum interesting t1_match = entry_not_interesting;
+ enum interesting t2_match = entry_not_interesting;
/* Enable recursion indefinitely */
opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
diff --git a/tree-walk.c b/tree-walk.c
index 418107ec83..f82dba6a1f 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -116,7 +116,7 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
{
- int len = tree_entry_len(n->path, n->sha1);
+ int len = tree_entry_len(n);
int pathlen = info->pathlen;
path[pathlen + len] = 0;
@@ -126,7 +126,7 @@ char *make_traverse_path(char *path, const struct traverse_info *info, const str
break;
path[--pathlen] = '/';
n = &info->name;
- len = tree_entry_len(n->path, n->sha1);
+ len = tree_entry_len(n);
info = info->prev;
pathlen -= len;
}
@@ -253,7 +253,7 @@ static void extended_entry_extract(struct tree_desc_x *t,
* The caller wants "first" from this tree, or nothing.
*/
path = a->path;
- len = tree_entry_len(a->path, a->sha1);
+ len = tree_entry_len(a);
switch (check_entry_match(first, first_len, path, len)) {
case -1:
entry_clear(a);
@@ -271,7 +271,7 @@ static void extended_entry_extract(struct tree_desc_x *t,
while (probe.size) {
entry_extract(&probe, a);
path = a->path;
- len = tree_entry_len(a->path, a->sha1);
+ len = tree_entry_len(a);
switch (check_entry_match(first, first_len, path, len)) {
case -1:
entry_clear(a);
@@ -362,7 +362,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
e = entry + i;
if (!e->path)
continue;
- len = tree_entry_len(e->path, e->sha1);
+ len = tree_entry_len(e);
if (!first) {
first = e->path;
first_len = len;
@@ -381,7 +381,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
/* Cull the ones that are not the earliest */
if (!e->path)
continue;
- len = tree_entry_len(e->path, e->sha1);
+ len = tree_entry_len(e);
if (name_compare(e->path, len, first, first_len))
entry_clear(e);
}
@@ -434,8 +434,8 @@ static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char
int entrylen, cmp;
sha1 = tree_entry_extract(t, &entry, mode);
+ entrylen = tree_entry_len(&t->entry);
update_tree_entry(t);
- entrylen = tree_entry_len(entry, sha1);
if (entrylen > namelen)
continue;
cmp = memcmp(name, entry, entrylen);
@@ -465,7 +465,6 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
int retval;
void *tree;
unsigned long size;
- struct tree_desc t;
unsigned char root[20];
tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
@@ -478,8 +477,13 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
return 0;
}
- init_tree_desc(&t, tree, size);
- retval = find_tree_entry(&t, name, sha1, mode);
+ if (!size) {
+ retval = -1;
+ } else {
+ struct tree_desc t;
+ init_tree_desc(&t, tree, size);
+ retval = find_tree_entry(&t, name, sha1, mode);
+ }
free(tree);
return retval;
}
@@ -573,30 +577,26 @@ static int match_dir_prefix(const char *base,
*
* Pre-condition: either baselen == base_offset (i.e. empty path)
* or base[baselen-1] == '/' (i.e. with trailing slash).
- *
- * Return:
- * - 2 for "yes, and all subsequent entries will be"
- * - 1 for yes
- * - zero for no
- * - negative for "no, and no subsequent entries will be either"
*/
-int tree_entry_interesting(const struct name_entry *entry,
- struct strbuf *base, int base_offset,
- const struct pathspec *ps)
+enum interesting tree_entry_interesting(const struct name_entry *entry,
+ struct strbuf *base, int base_offset,
+ const struct pathspec *ps)
{
int i;
int pathlen, baselen = base->len - base_offset;
- int never_interesting = ps->has_wildcard ? 0 : -1;
+ int never_interesting = ps->has_wildcard ?
+ entry_not_interesting : all_entries_not_interesting;
if (!ps->nr) {
if (!ps->recursive || ps->max_depth == -1)
- return 2;
- return !!within_depth(base->buf + base_offset, baselen,
- !!S_ISDIR(entry->mode),
- ps->max_depth);
+ return all_entries_interesting;
+ return within_depth(base->buf + base_offset, baselen,
+ !!S_ISDIR(entry->mode),
+ ps->max_depth) ?
+ entry_interesting : entry_not_interesting;
}
- pathlen = tree_entry_len(entry->path, entry->sha1);
+ pathlen = tree_entry_len(entry);
for (i = ps->nr - 1; i >= 0; i--) {
const struct pathspec_item *item = ps->items+i;
@@ -610,12 +610,13 @@ int tree_entry_interesting(const struct name_entry *entry,
goto match_wildcards;
if (!ps->recursive || ps->max_depth == -1)
- return 2;
+ return all_entries_interesting;
- return !!within_depth(base_str + matchlen + 1,
- baselen - matchlen - 1,
- !!S_ISDIR(entry->mode),
- ps->max_depth);
+ return within_depth(base_str + matchlen + 1,
+ baselen - matchlen - 1,
+ !!S_ISDIR(entry->mode),
+ ps->max_depth) ?
+ entry_interesting : entry_not_interesting;
}
/* Either there must be no base, or the base must match. */
@@ -623,25 +624,25 @@ int tree_entry_interesting(const struct name_entry *entry,
if (match_entry(entry, pathlen,
match + baselen, matchlen - baselen,
&never_interesting))
- return 1;
+ return entry_interesting;
- if (ps->items[i].use_wildcard) {
+ if (item->use_wildcard) {
if (!fnmatch(match + baselen, entry->path, 0))
- return 1;
+ return entry_interesting;
/*
* Match all directories. We'll try to
* match files later on.
*/
if (ps->recursive && S_ISDIR(entry->mode))
- return 1;
+ return entry_interesting;
}
continue;
}
match_wildcards:
- if (!ps->items[i].use_wildcard)
+ if (!item->use_wildcard)
continue;
/*
@@ -653,7 +654,7 @@ match_wildcards:
if (!fnmatch(match, base->buf + base_offset, 0)) {
strbuf_setlen(base, base_offset + baselen);
- return 1;
+ return entry_interesting;
}
strbuf_setlen(base, base_offset + baselen);
@@ -662,7 +663,7 @@ match_wildcards:
* later on.
*/
if (ps->recursive && S_ISDIR(entry->mode))
- return 1;
+ return entry_interesting;
}
return never_interesting; /* No matches */
}
diff --git a/tree-walk.h b/tree-walk.h
index 0089581e1d..2bf0db9814 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -20,9 +20,9 @@ static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, co
return desc->entry.sha1;
}
-static inline int tree_entry_len(const char *name, const unsigned char *sha1)
+static inline int tree_entry_len(const struct name_entry *ne)
{
- return (const char *)sha1 - name - 1;
+ return (const char *)ne->sha1 - ne->path - 1;
}
void update_tree_entry(struct tree_desc *);
@@ -58,9 +58,19 @@ extern void setup_traverse_info(struct traverse_info *info, const char *base);
static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
{
- return info->pathlen + tree_entry_len(n->path, n->sha1);
+ return info->pathlen + tree_entry_len(n);
}
-extern int tree_entry_interesting(const struct name_entry *, struct strbuf *, int, const struct pathspec *ps);
+/* in general, positive means "kind of interesting" */
+enum interesting {
+ all_entries_not_interesting = -1, /* no, and no subsequent entries will be either */
+ entry_not_interesting = 0,
+ entry_interesting = 1,
+ all_entries_interesting = 2 /* yes, and all subsequent entries will be */
+};
+
+extern enum interesting tree_entry_interesting(const struct name_entry *,
+ struct strbuf *, int,
+ const struct pathspec *ps);
#endif
diff --git a/tree.c b/tree.c
index 698ecf7af1..676e9f710c 100644
--- a/tree.c
+++ b/tree.c
@@ -52,7 +52,8 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
struct tree_desc desc;
struct name_entry entry;
unsigned char sha1[20];
- int len, retval = 0, oldlen = base->len;
+ int len, oldlen = base->len;
+ enum interesting retval = entry_not_interesting;
if (parse_tree(tree))
return -1;
@@ -60,11 +61,11 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
- if (retval != 2) {
+ if (retval != all_entries_interesting) {
retval = tree_entry_interesting(&entry, base, 0, pathspec);
- if (retval < 0)
+ if (retval == all_entries_not_interesting)
break;
- if (retval == 0)
+ if (retval == entry_not_interesting)
continue;
}
@@ -99,7 +100,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
else
continue;
- len = tree_entry_len(entry.path, entry.sha1);
+ len = tree_entry_len(&entry);
strbuf_add(base, entry.path, len);
strbuf_addch(base, '/');
retval = read_tree_1(lookup_tree(sha1),
diff --git a/unix-socket.c b/unix-socket.c
new file mode 100644
index 0000000000..84b15099f2
--- /dev/null
+++ b/unix-socket.c
@@ -0,0 +1,56 @@
+#include "cache.h"
+#include "unix-socket.h"
+
+static int unix_stream_socket(void)
+{
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ die_errno("unable to create socket");
+ return fd;
+}
+
+static void unix_sockaddr_init(struct sockaddr_un *sa, const char *path)
+{
+ int size = strlen(path) + 1;
+ if (size > sizeof(sa->sun_path))
+ die("socket path is too long to fit in sockaddr");
+ memset(sa, 0, sizeof(*sa));
+ sa->sun_family = AF_UNIX;
+ memcpy(sa->sun_path, path, size);
+}
+
+int unix_stream_connect(const char *path)
+{
+ int fd;
+ struct sockaddr_un sa;
+
+ unix_sockaddr_init(&sa, path);
+ fd = unix_stream_socket();
+ if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+int unix_stream_listen(const char *path)
+{
+ int fd;
+ struct sockaddr_un sa;
+
+ unix_sockaddr_init(&sa, path);
+ fd = unix_stream_socket();
+
+ unlink(path);
+ if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 5) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/unix-socket.h b/unix-socket.h
new file mode 100644
index 0000000000..e271aeec5a
--- /dev/null
+++ b/unix-socket.h
@@ -0,0 +1,7 @@
+#ifndef UNIX_SOCKET_H
+#define UNIX_SOCKET_H
+
+int unix_stream_connect(const char *path);
+int unix_stream_listen(const char *path);
+
+#endif /* UNIX_SOCKET_H */
diff --git a/unpack-trees.c b/unpack-trees.c
index 8282f5e5f6..7c9ecf665d 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -446,7 +446,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
newinfo.prev = info;
newinfo.pathspec = info->pathspec;
newinfo.name = *p;
- newinfo.pathlen += tree_entry_len(p->path, p->sha1) + 1;
+ newinfo.pathlen += tree_entry_len(p) + 1;
newinfo.conflicts |= df_conflicts;
for (i = 0; i < n; i++, dirmask >>= 1) {
@@ -495,7 +495,7 @@ static int do_compare_entry(const struct cache_entry *ce, const struct traverse_
ce_len -= pathlen;
ce_name = ce->name + pathlen;
- len = tree_entry_len(n->path, n->sha1);
+ len = tree_entry_len(n);
return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
}
@@ -626,7 +626,7 @@ static int find_cache_pos(struct traverse_info *info,
struct unpack_trees_options *o = info->data;
struct index_state *index = o->src_index;
int pfxlen = info->pathlen;
- int p_len = tree_entry_len(p->path, p->sha1);
+ int p_len = tree_entry_len(p);
for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
struct cache_entry *ce = index->cache[pos];
diff --git a/userdiff.c b/userdiff.c
index bf553ad91b..76109da4bc 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -37,6 +37,9 @@ PATTERNS("java",
"|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
"|[-+*/<>%&^|=!]="
"|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
+PATTERNS("matlab",
+ "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$",
+ "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
PATTERNS("objc",
/* Negate C statements that can look like functions */
"!^[ \t]*(do|for|if|else|return|switch|while)\n"
@@ -115,7 +118,7 @@ PATTERNS("cpp",
/* Jump targets or access declarations */
"!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
/* C/++ functions/methods at top level */
- "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
+ "^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
/* compound type at top level */
"^((struct|class|enum)[^;]*)$",
/* -- */
diff --git a/wt-status.c b/wt-status.c
index 70fdb76ff2..9ffc535f1a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -111,7 +111,6 @@ void status_printf_more(struct wt_status *s, const char *color,
void wt_status_prepare(struct wt_status *s)
{
unsigned char sha1[20];
- const char *head;
memset(s, 0, sizeof(*s));
memcpy(s->color_palette, default_wt_status_colors,
@@ -119,8 +118,7 @@ void wt_status_prepare(struct wt_status *s)
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
s->use_color = -1;
s->relative_paths = 1;
- head = resolve_ref("HEAD", sha1, 0, NULL);
- s->branch = head ? xstrdup(head) : NULL;
+ s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
diff --git a/zlib.c b/zlib.c
index 3c63d480c7..2b2c0c780e 100644
--- a/zlib.c
+++ b/zlib.c
@@ -188,13 +188,20 @@ void git_deflate_init_gzip(git_zstream *strm, int level)
strm->z.msg ? strm->z.msg : "no message");
}
-void git_deflate_end(git_zstream *strm)
+int git_deflate_abort(git_zstream *strm)
{
int status;
zlib_pre_call(strm);
status = deflateEnd(&strm->z);
zlib_post_call(strm);
+ return status;
+}
+
+void git_deflate_end(git_zstream *strm)
+{
+ int status = git_deflate_abort(strm);
+
if (status == Z_OK)
return;
error("deflateEnd: %s (%s)", zerr_to_string(status),