summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.mailmap1
-rw-r--r--Documentation/Makefile32
-rw-r--r--Documentation/RelNotes/2.2.0.txt274
-rw-r--r--Documentation/config.txt62
-rw-r--r--Documentation/everyday.txto9
-rw-r--r--Documentation/git-credential-cache--daemon.txt6
-rw-r--r--Documentation/git-fast-export.txt61
-rw-r--r--Documentation/git-imap-send.txt14
-rw-r--r--Documentation/git-init.txt85
-rw-r--r--Documentation/git-interpret-trailers.txt314
-rw-r--r--Documentation/git-prune-packed.txt2
-rw-r--r--Documentation/git-push.txt11
-rw-r--r--Documentation/git-quiltimport.txt2
-rw-r--r--Documentation/git-rebase.txt31
-rw-r--r--Documentation/git-receive-pack.txt65
-rw-r--r--Documentation/git-rev-list.txt2
-rw-r--r--Documentation/git-rev-parse.txt1
-rw-r--r--Documentation/git-stage.txt2
-rw-r--r--Documentation/git-status.txt4
-rw-r--r--Documentation/git-svn.txt17
-rw-r--r--Documentation/git-update-index.txt2
-rw-r--r--Documentation/git.txt4
-rw-r--r--Documentation/gitattributes.txt4
-rw-r--r--Documentation/gitcore-tutorial.txt2
-rw-r--r--Documentation/gitcvs-migration.txt2
-rw-r--r--Documentation/giteveryday.txt (renamed from Documentation/everyday.txt)278
-rw-r--r--Documentation/gitglossary.txt2
-rw-r--r--Documentation/gittutorial-2.txt4
-rw-r--r--Documentation/gittutorial.txt4
-rw-r--r--Documentation/pretty-formats.txt16
-rw-r--r--Documentation/rev-list-options.txt22
-rw-r--r--Documentation/technical/api-allocation-growing.txt3
-rw-r--r--Documentation/technical/api-config.txt155
-rw-r--r--Documentation/technical/api-lockfile.txt254
-rw-r--r--Documentation/technical/api-run-command.txt13
-rw-r--r--Documentation/technical/api-strbuf.txt14
-rw-r--r--Documentation/technical/pack-protocol.txt49
-rw-r--r--Documentation/technical/protocol-capabilities.txt13
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile216
-rw-r--r--README2
l---------RelNotes2
-rw-r--r--abspath.c124
-rw-r--r--advice.c3
-rw-r--r--alias.c25
-rw-r--r--archive-tar.c3
-rw-r--r--archive.c109
-rw-r--r--attr.c3
-rw-r--r--bisect.c7
-rw-r--r--branch.c57
-rw-r--r--builtin.h1
-rw-r--r--builtin/add.c4
-rw-r--r--builtin/apply.c17
-rw-r--r--builtin/blame.c7
-rw-r--r--builtin/branch.c105
-rw-r--r--builtin/cat-file.c5
-rw-r--r--builtin/checkout-index.c2
-rw-r--r--builtin/checkout.c20
-rw-r--r--builtin/clean.c12
-rw-r--r--builtin/clone.c17
-rw-r--r--builtin/commit.c228
-rw-r--r--builtin/config.c40
-rw-r--r--builtin/count-objects.c101
-rw-r--r--builtin/describe.c1
-rw-r--r--builtin/diff.c1
-rw-r--r--builtin/fast-export.c300
-rw-r--r--builtin/fetch.c34
-rw-r--r--builtin/fmt-merge-msg.c2
-rw-r--r--builtin/for-each-ref.c28
-rw-r--r--builtin/fsck.c4
-rw-r--r--builtin/gc.c53
-rw-r--r--builtin/get-tar-commit-id.c5
-rw-r--r--builtin/grep.c8
-rw-r--r--builtin/hash-object.c103
-rw-r--r--builtin/help.c4
-rw-r--r--builtin/index-pack.c10
-rw-r--r--builtin/init-db.c32
-rw-r--r--builtin/interpret-trailers.c44
-rw-r--r--builtin/log.c22
-rw-r--r--builtin/ls-files.c2
-rw-r--r--builtin/mailsplit.c16
-rw-r--r--builtin/merge-tree.c18
-rw-r--r--builtin/merge.c27
-rw-r--r--builtin/mv.c176
-rw-r--r--builtin/notes.c7
-rw-r--r--builtin/pack-objects.c96
-rw-r--r--builtin/prune-packed.c68
-rw-r--r--builtin/prune.c89
-rw-r--r--builtin/push.c14
-rw-r--r--builtin/read-tree.c1
-rw-r--r--builtin/receive-pack.c465
-rw-r--r--builtin/reflog.c6
-rw-r--r--builtin/remote-ext.c13
-rw-r--r--builtin/remote.c20
-rw-r--r--builtin/repack.c27
-rw-r--r--builtin/replace.c19
-rw-r--r--builtin/reset.c1
-rw-r--r--builtin/rev-parse.c11
-rw-r--r--builtin/rm.c4
-rw-r--r--builtin/send-pack.c4
-rw-r--r--builtin/show-branch.c14
-rw-r--r--builtin/symbolic-ref.c2
-rw-r--r--builtin/tag.c16
-rw-r--r--builtin/unpack-objects.c14
-rw-r--r--builtin/update-index.c3
-rw-r--r--builtin/update-ref.c61
-rw-r--r--builtin/verify-pack.c3
-rw-r--r--bulk-checkin.c1
-rw-r--r--bulk-checkin.h2
-rw-r--r--bundle.c26
-rw-r--r--cache-tree.c20
-rw-r--r--cache-tree.h1
-rw-r--r--cache.h186
-rw-r--r--color.c13
-rw-r--r--color.h4
-rw-r--r--column.c8
-rw-r--r--command-list.txt1
-rw-r--r--commit-slab.h3
-rw-r--r--commit.c82
-rw-r--r--commit.h29
-rw-r--r--compat/bswap.h4
-rw-r--r--compat/mingw.h7
-rw-r--r--config.c442
-rw-r--r--config.mak.uname5
-rw-r--r--configure.ac14
-rw-r--r--connect.c24
-rw-r--r--connected.c3
-rw-r--r--contrib/completion/git-completion.bash29
-rw-r--r--contrib/contacts/.gitignore3
-rw-r--r--contrib/contacts/Makefile71
-rw-r--r--contrib/subtree/.gitignore3
-rw-r--r--contrib/subtree/Makefile14
-rw-r--r--contrib/svn-fe/Makefile60
-rw-r--r--convert.c58
-rw-r--r--convert.h10
-rw-r--r--copy.c26
-rw-r--r--credential-cache--daemon.c25
-rw-r--r--credential-cache.c3
-rw-r--r--credential-store.c1
-rw-r--r--credential.c3
-rw-r--r--daemon.c67
-rw-r--r--date.c27
-rw-r--r--diff.c58
-rw-r--r--diffcore-rename.c6
-rw-r--r--diffcore.h4
-rw-r--r--dir.c14
-rw-r--r--editor.c3
-rw-r--r--exec_cmd.c6
-rw-r--r--fast-import.c142
-rw-r--r--fetch-pack.c39
-rw-r--r--fsck.c133
-rw-r--r--fsck.h4
-rwxr-xr-xgit-bisect.sh4
-rw-r--r--git-compat-util.h60
-rwxr-xr-xgit-difftool.perl6
-rw-r--r--git-mergetool--lib.sh2
-rwxr-xr-xgit-mergetool.sh52
-rwxr-xr-xgit-pull.sh2
-rw-r--r--git-sh-setup.sh8
-rwxr-xr-xgit-stash.sh17
-rwxr-xr-xgit-svn.perl65
-rw-r--r--git.c41
-rwxr-xr-xgitweb/gitweb.perl4
-rw-r--r--gpg-interface.c63
-rw-r--r--gpg-interface.h17
-rw-r--r--graph.c34
-rw-r--r--grep.c2
-rw-r--r--help.c2
-rw-r--r--http-backend.c47
-rw-r--r--http-walker.c15
-rw-r--r--http.c16
-rw-r--r--ident.c28
-rw-r--r--imap-send.c160
-rw-r--r--khash.h12
-rw-r--r--line-log.c2
-rw-r--r--list-objects.c14
-rw-r--r--ll-merge.c23
-rw-r--r--lockfile.c337
-rw-r--r--lockfile.h87
-rw-r--r--log-tree.c49
-rw-r--r--log-tree.h10
-rw-r--r--merge-recursive.c35
-rw-r--r--merge.c1
-rw-r--r--mergetools/bc25
-rw-r--r--mergetools/bc326
-rw-r--r--mergetools/meld9
-rw-r--r--notes-merge.c2
-rw-r--r--object.c61
-rw-r--r--object.h14
-rw-r--r--pack-bitmap-write.c3
-rw-r--r--pack-bitmap.c6
-rw-r--r--pack-objects.c3
-rw-r--r--pack-write.c11
-rw-r--r--pager.c57
-rw-r--r--parse-options.c14
-rw-r--r--path.c10
-rw-r--r--pretty.c117
-rw-r--r--prompt.c3
-rw-r--r--reachable.c263
-rw-r--r--reachable.h5
-rw-r--r--read-cache.c54
-rw-r--r--reflog-walk.c7
-rw-r--r--reflog-walk.h2
-rw-r--r--refs.c819
-rw-r--r--refs.h122
-rw-r--r--remote-curl.c18
-rw-r--r--remote-testsvn.c3
-rw-r--r--remote.c100
-rw-r--r--remote.h3
-rw-r--r--rerere.c44
-rw-r--r--rerere.h2
-rw-r--r--revision.c117
-rw-r--r--revision.h7
-rw-r--r--run-command.c83
-rw-r--r--run-command.h4
-rw-r--r--send-pack.c204
-rw-r--r--send-pack.h2
-rw-r--r--sequencer.c31
-rw-r--r--server-info.c123
-rw-r--r--setup.c102
-rw-r--r--sh-i18n--envsubst.c5
-rw-r--r--sha1-lookup.c2
-rw-r--r--sha1_file.c276
-rw-r--r--sha1_name.c27
-rw-r--r--shallow.c17
-rw-r--r--sigchain.c2
-rw-r--r--strbuf.c53
-rw-r--r--strbuf.h4
-rw-r--r--string-list.c3
-rw-r--r--submodule.c21
-rw-r--r--t/README6
-rwxr-xr-xt/lib-credential.sh4
-rwxr-xr-xt/lib-gpg.sh1
-rw-r--r--t/lib-httpd/apache.conf1
-rwxr-xr-xt/t0005-signals.sh22
-rwxr-xr-xt/t0008-ignores.sh2
-rwxr-xr-xt/t0021-conversion.sh24
-rwxr-xr-xt/t0026-eol-config.sh20
-rwxr-xr-xt/t0027-auto-crlf.sh220
-rwxr-xr-xt/t0064-sha1-array.sh94
-rwxr-xr-xt/t0081-line-buffer.sh2
-rwxr-xr-xt/t0090-cache-tree.sh162
-rwxr-xr-xt/t1020-subdirectory.sh2
-rwxr-xr-xt/t1050-large.sh22
-rwxr-xr-xt/t1300-repo-config.sh6
-rwxr-xr-xt/t1304-default-acl.sh2
-rwxr-xr-xt/t1308-config-set.sh221
-rwxr-xr-xt/t1400-update-ref.sh62
-rwxr-xr-xt/t1402-check-ref-format.sh40
-rwxr-xr-xt/t1413-reflog-detach.sh70
-rwxr-xr-xt/t1430-bad-ref-name.sh207
-rwxr-xr-xt/t1450-fsck.sh38
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh4
-rwxr-xr-xt/t1503-rev-parse-verify.sh37
-rwxr-xr-xt/t3200-branch.sh9
-rwxr-xr-xt/t3201-branch-contains.sh29
-rwxr-xr-xt/t3210-pack-refs.sh31
-rwxr-xr-xt/t3302-notes-index-expensive.sh2
-rwxr-xr-xt/t3419-rebase-patch-id.sh2
-rwxr-xr-xt/t3901-i18n-patch.sh10
-rwxr-xr-xt/t3903-stash.sh42
-rwxr-xr-xt/t4055-diff-context.sh2
-rwxr-xr-xt/t4201-shortlog.sh6
-rwxr-xr-xt/t4205-log-pretty-formats.sh26
-rwxr-xr-xt/t4210-log-i18n.sh4
-rwxr-xr-xt/t5000-tar-tree.sh14
-rwxr-xr-xt/t5302-pack-index.sh19
-rwxr-xr-xt/t5304-prune.sh62
-rwxr-xr-xt/t5310-pack-bitmaps.sh9
-rwxr-xr-xt/t5401-update-hooks.sh13
-rwxr-xr-xt/t5516-fetch-push.sh13
-rwxr-xr-xt/t5534-push-signed.sh171
-rwxr-xr-xt/t5541-http-push-smart.sh41
-rwxr-xr-xt/t5601-clone.sh4
-rwxr-xr-xt/t6000-rev-list-misc.sh23
-rwxr-xr-xt/t6031-merge-recursive.sh1
-rwxr-xr-xt/t6038-merge-text-auto.sh54
-rwxr-xr-xt/t6501-freshen-objects.sh132
-rwxr-xr-xt/t7001-mv.sh15
-rwxr-xr-xt/t7004-tag.sh4
-rwxr-xr-xt/t7513-interpret-trailers.sh863
-rwxr-xr-xt/t7610-mergetool.sh933
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh13
-rwxr-xr-xt/t8005-blame-i18n.sh8
-rwxr-xr-xt/t9119-git-svn-info.sh30
-rwxr-xr-xt/t9300-fast-import.sh42
-rwxr-xr-xt/t9351-fast-export-anonymize.sh112
-rwxr-xr-xt/t9809-git-p4-client-view.sh4
-rwxr-xr-xt/t9812-git-p4-wildcards.sh14
-rwxr-xr-xt/t9815-git-p4-submit-fail.sh2
-rw-r--r--t/test-lib-functions.sh39
-rw-r--r--t/test-lib.sh50
-rw-r--r--tag.c20
-rw-r--r--tag.h1
-rwxr-xr-xtemplates/hooks--pre-push.sample2
-rw-r--r--test-config.c152
-rw-r--r--test-date.c10
-rw-r--r--test-dump-cache-tree.c4
-rw-r--r--test-regex.c2
-rw-r--r--test-run-command.c4
-rw-r--r--test-scrap-cache-tree.c1
-rw-r--r--test-sha1-array.c34
-rw-r--r--test-sigchain.c2
-rw-r--r--test-subprocess.c3
-rw-r--r--thread-utils.h4
-rw-r--r--trace.c11
-rw-r--r--trailer.c851
-rw-r--r--trailer.h6
-rw-r--r--transport-helper.c29
-rw-r--r--transport.c23
-rw-r--r--transport.h5
-rw-r--r--unix-socket.c14
-rw-r--r--upload-pack.c7
-rw-r--r--urlmatch.c8
-rw-r--r--utf8.c10
-rw-r--r--varint.c1
-rw-r--r--varint.h2
-rw-r--r--walker.c89
-rw-r--r--wrapper.c113
-rw-r--r--write_or_die.c15
-rw-r--r--wt-status.c11
322 files changed, 11752 insertions, 4383 deletions
diff --git a/.gitignore b/.gitignore
index 81e12c0621..a05241916c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,7 @@
/git-index-pack
/git-init
/git-init-db
+/git-interpret-trailers
/git-instaweb
/git-log
/git-ls-files
@@ -178,6 +179,7 @@
/gitweb/static/gitweb.min.*
/test-chmtime
/test-ctype
+/test-config
/test-date
/test-delta
/test-dump-cache-tree
@@ -198,6 +200,7 @@
/test-revision-walking
/test-run-command
/test-sha1
+/test-sha1-array
/test-sigchain
/test-string-list
/test-subprocess
diff --git a/.mailmap b/.mailmap
index 8aefb5a452..bb6f52ecd9 100644
--- a/.mailmap
+++ b/.mailmap
@@ -205,6 +205,7 @@ Shawn O. Pearce <spearce@spearce.org>
Simon Hausmann <hausmann@kde.org> <simon@lst.de>
Simon Hausmann <hausmann@kde.org> <shausman@trolltech.com>
Stefan Beller <stefanbeller@gmail.com> <stefanbeller@googlemail.com>
+Stefan Beller <stefanbeller@gmail.com> <sbeller@google.com>
Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@atlas-elektronik.com>
Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@googlemail.com>
Stefan Sperling <stsp@elego.de> <stsp@stsp.name>
diff --git a/Documentation/Makefile b/Documentation/Makefile
index cea0e7ae3d..2f6b6aabd7 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -5,6 +5,7 @@ MAN7_TXT =
TECH_DOCS =
ARTICLES =
SP_ARTICLES =
+OBSOLETE_HTML =
MAN1_TXT += $(filter-out \
$(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
@@ -26,6 +27,7 @@ MAN7_TXT += gitcore-tutorial.txt
MAN7_TXT += gitcredentials.txt
MAN7_TXT += gitcvs-migration.txt
MAN7_TXT += gitdiffcore.txt
+MAN7_TXT += giteveryday.txt
MAN7_TXT += gitglossary.txt
MAN7_TXT += gitnamespaces.txt
MAN7_TXT += gitrevisions.txt
@@ -37,11 +39,11 @@ MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT))
MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
-OBSOLETE_HTML = git-remote-helpers.html
+OBSOLETE_HTML += everyday.html
+OBSOLETE_HTML += git-remote-helpers.html
DOC_HTML = $(MAN_HTML) $(OBSOLETE_HTML)
ARTICLES += howto-index
-ARTICLES += everyday
ARTICLES += git-tools
ARTICLES += git-bisect-lk2009
# with their own formatting rules.
@@ -97,6 +99,13 @@ man7dir = $(mandir)/man7
ASCIIDOC = asciidoc
ASCIIDOC_EXTRA =
+ASCIIDOC_HTML = xhtml11
+ASCIIDOC_DOCBOOK = docbook
+ASCIIDOC_CONF = -f asciidoc.conf
+ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA) $(ASCIIDOC_CONF) \
+ -agit-version=$(GIT_VERSION)
+TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
+TXT_TO_XML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_DOCBOOK)
MANPAGE_XSL = manpage-normal.xsl
XMLTO = xmlto
XMLTO_EXTRA =
@@ -304,14 +313,12 @@ clean:
$(MAN_HTML): %.html : %.txt asciidoc.conf
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
- $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
- $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
+ $(TXT_TO_HTML) -d manpage -o $@+ $< && \
mv $@+ $@
$(OBSOLETE_HTML): %.html : %.txto asciidoc.conf
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
- $(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
- $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
+ $(TXT_TO_HTML) -o $@+ $< && \
mv $@+ $@
manpage-base-url.xsl: manpage-base-url.xsl.in
@@ -323,13 +330,12 @@ manpage-base-url.xsl: manpage-base-url.xsl.in
%.xml : %.txt asciidoc.conf
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
- $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
- $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \
+ $(TXT_TO_XML) -d manpage -o $@+ $< && \
mv $@+ $@
user-manual.xml: user-manual.txt user-manual.conf
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
- $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d article -o $@+ $< && \
+ $(TXT_TO_XML) -d article -o $@+ $< && \
mv $@+ $@
technical/api-index.txt: technical/api-index-skel.txt \
@@ -338,8 +344,7 @@ technical/api-index.txt: technical/api-index-skel.txt \
technical/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
$(patsubst %,%.html,$(API_DOCS) technical/api-index $(TECH_DOCS)): %.html : %.txt asciidoc.conf
- $(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \
- $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) $*.txt
+ $(QUIET_ASCIIDOC)$(TXT_TO_HTML) $*.txt
XSLT = docbook.xsl
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
@@ -386,14 +391,15 @@ howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
mv $@+ $@
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
- $(QUIET_ASCIIDOC)$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b xhtml11 $*.txt
+ $(QUIET_ASCIIDOC)$(TXT_TO_HTML) $*.txt
WEBDOC_DEST = /pub/software/scm/git/docs
howto/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
- sed -e '1,/^$$/d' $< | $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b xhtml11 - >$@+ && \
+ sed -e '1,/^$$/d' $< | \
+ $(TXT_TO_HTML) - >$@+ && \
mv $@+ $@
install-webdoc : html
diff --git a/Documentation/RelNotes/2.2.0.txt b/Documentation/RelNotes/2.2.0.txt
new file mode 100644
index 0000000000..b8017a6140
--- /dev/null
+++ b/Documentation/RelNotes/2.2.0.txt
@@ -0,0 +1,274 @@
+Git v2.2 Release Notes
+======================
+
+Updates since v2.1
+------------------
+
+Ports
+
+ * Building on older MacOS X systems automatically sets
+ the necessary NO_APPLE_COMMON_CRYPTO build-time option.
+
+ * The support to build with NO_PTHREADS has been resurrected.
+
+
+UI, Workflows & Features
+
+ * "git archive" learned to filter what gets archived with pathspec.
+
+ * "git config --edit --global" starts from a skeletal per-user
+ configuration file contents, instead of a total blank, when the
+ user does not already have any. This immediately reduces the
+ need for a later "Have you forgotten setting core.user?" and we
+ can add more to the template as we gain more experience.
+
+ * "git stash list -p" used to be almost always a no-op because each
+ stash entry is represented as a merge commit. It learned to show
+ the difference between the base commit version and the working tree
+ version, which is in line with what "git show" gives.
+
+ * Sometimes users want to report a bug they experience on their
+ repository, but they are not at liberty to share the contents of
+ the repository. "fast-export" was taught an "--anonymize" option
+ to replace blob contents, names of people and paths and log
+ messages with bland and simple strings to help them.
+
+ * "log --date=iso" uses a slight variant of ISO 8601 format that is
+ made more human readable. A new "--date=iso-strict" option gives
+ datetime output that is more strictly conformant.
+
+ * A broken reimplementation of Git could write an invalid index that
+ records both stage #0 and higher stage entries for the same path.
+ We now notice and reject such an index, as there is no sensible
+ fallback (we do not know if the broken tool wanted to resolve and
+ forgot to remove higher stage entries, or if it wanted to unresolve
+ and forgot to remove the stage#0 entry).
+
+ * The temporary files "git mergetool" uses are named to avoid too
+ many dots in them (e.g. a temporary file for "hello.c" used to be
+ named e.g. "hello.BASE.4321.c" but now uses underscore instead,
+ e.g. "hello_BASE_4321.c").
+
+ * The temporary files "git mergetools" uses can be placed in a newly
+ creted temporary directory, instead of the current directory, by
+ setting the mergetool.writeToTemp configuration variable.
+
+ * The "pre-receive" and "post-receive" hooks are no longer required
+ to consume their input fully (not following this requirement used
+ to result in intermittent errors in "git push").
+
+ * The pretty-format specifier "%d", which expanded to " (tagname)"
+ for a tagged commit, gained a cousin "%D" that just gives the
+ "tagname" without frills.
+
+ * "git push" learned "--signed" push, that allows a push (i.e.
+ request to update the refs on the other side to point at a new
+ history, together with the transmission of necessary objects) to be
+ signed, so that it can be verified and audited, using the GPG
+ signature of the person who pushed, that the tips of branches at a
+ public repository really point the commits the pusher wanted to,
+ without having to "trust" the server.
+
+ * "git interpret-trailers" is a new filter to programatically edit
+ the tail end of the commit log messages.
+
+ * "git help everyday" shows the "Everyday Git in 20 commands or so"
+ document, whose contents have been updated to more modern Git
+ practice.
+
+
+Performance, Internal Implementation, etc.
+
+ * The API to manipulate the "refs" has been restructured to make it
+ more transactional, with the eventual goal to allow all-or-none
+ atomic updates and migrating the storage to something other than
+ the traditional filesystem based one (e.g. databases).
+
+ * The lockfile API and its users have been cleaned up.
+
+ * We no longer attempt to keep track of individual dependencies to
+ the header files in the build procedure, relying on automated
+ dependency generation support from modern compilers.
+
+ * In tests, we have been using NOT_{MINGW,CYGWIN} test prerequisites
+ long before negated prerequisites e.g. !MINGW were invented.
+ The former has been converted to the latter to avoid confusion.
+
+ * Looking up remotes configuration in a repository with very many
+ remotes defined has been optimized.
+
+ * There are cases where you lock and open to write a file, close it
+ to show the updated contents to external processes, and then have
+ to update the file again while still holding the lock, but the
+ lockfile API lacked support for such an access pattern.
+
+ * The API to allocate the structure to keep track of commit
+ decoration has been updated to make it less cumbersome to use.
+
+ * An in-core caching layer to let us avoid reading the same
+ configuration files number of times has been added. A few commands
+ have been converted to use this subsystem.
+
+ * Various code paths have been cleaned up and simplified by using
+ "strbuf", "starts_with()", and "skip_prefix()" APIs more.
+
+ * A few codepaths that died when large blobs that would not fit in
+ core are involved in their operation have been taught to punt
+ instead, by e.g. marking too large a blob as not to be diffed.
+
+ * A few more code paths in "commit" and "checkout" have been taught
+ to repopulate the cache-tree in the index, to help speed up later
+ "write-tree" (used in "commit") and "diff-index --cached" (used in
+ "status").
+
+ * A common programming mistake to assign the same short option name
+ to two separate options is detected by parse_options() API to help
+ developers.
+
+ * The code path to write out the packed-refs file has been optimized,
+ which especially matters in a repository with a large number of
+ refs.
+
+ * The check to see if a ref $F can be created by making sure no
+ existing ref has $F/ as its prefix has been optimized, which
+ especially matters in a repository with a large number of existing
+ refs.
+
+ * "git fsck" was taught to check contents of tag objects a bit more.
+
+ * "git hash-object" was taught a "--literally" option to help
+ debugging.
+
+ * When running a required clean filter, we do not have to mmap the
+ original before feeding the filter. Instead, stream the file
+ contents directly to the filter and process its output.
+
+ * The scripts in the test suite can be run with "-x" option to show
+ a shell-trace of each command run in them.
+
+ * The "run-command" API learned to manage the argv and environment
+ array for child process, alleviating the need for the callers to
+ allocate and deallocate them.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.1
+----------------
+
+Unless otherwise noted, all the fixes since v2.1 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * "git log --pretty/format=" with an empty format string did not
+ mean the more obvious "No output whatsoever" but "Use default
+ format", which was counterintuitive.
+
+ * "git -c section.var command" and "git -c section.var= command"
+ should pass the configuration differently (the former should be a
+ boolean true, the latter should be an empty string).
+
+ * Applying a patch not generated by Git in a subdirectory used to
+ check the whitespace breakage using the attributes for incorrect
+ paths. Also whitespace checks were performed even for paths
+ excluded via "git apply --exclude=<path>" mechanism.
+
+ * "git bundle create" with date-range specification were meant to
+ exclude tags outside the range, but it didn't.
+
+ * "git add x" where x that used to be a directory has become a
+ symbolic link to a directory misbehaved.
+
+ * The prompt script checked $GIT_DIR/ref/stash file to see if there
+ is a stash, which was a no-no.
+
+ * Pack-protocol documentation had a minor typo.
+
+ * "git checkout -m" did not switch to another branch while carrying
+ the local changes forward when a path was deleted from the index.
+
+ * "git daemon" (with NO_IPV6 build configuration) used to incorrectly
+ use the hostname even when gethostbyname() reported that the given
+ hostname is not found.
+ (merge 107efbe rs/daemon-fixes later to maint).
+
+ * With sufficiently long refnames, "git fast-import" could have
+ overflown an on-stack buffer.
+
+ * After "pack-refs --prune" packed refs at the top-level, it failed
+ to prune them.
+
+ * Progress output from "git gc --auto" was visible in "git fetch -q".
+
+ * We used to pass -1000 to poll(2), expecting it to also mean "no
+ timeout", which should be spelled as -1.
+
+ * "git rebase" documentation was unclear that it is required to
+ specify on what <upstream> the rebase is to be done when telling it
+ to first check out <branch>.
+ (merge 95c6826 so/rebase-doc later to maint).
+
+ * "git push" over HTTP transport had an artificial limit on number of
+ refs that can be pushed imposed by the command line length.
+ (merge 26be19b jk/send-pack-many-refspecs later to maint).
+
+ * When receiving an invalid pack stream that records the same object
+ twice, multiple threads got confused due to a race.
+ (merge ab791dd jk/index-pack-threading-races later to maint).
+
+ * An attempt to remove the entire tree in the "git fast-import" input
+ stream caused it to misbehave.
+ (merge 2668d69 mb/fast-import-delete-root later to maint).
+
+ * Reachability check (used in "git prune" and friends) did not add a
+ detached HEAD as a starting point to traverse objects still in use.
+ (merge c40fdd0 mk/reachable-protect-detached-head later to maint).
+
+ * "git config --add section.var val" used to lose existing
+ section.var whose value was an empty string.
+ (merge c1063be ta/config-add-to-empty-or-true-fix later to maint).
+
+ * "git fsck" failed to report that it found corrupt objects via its
+ exit status in some cases.
+ (merge 30d1038 jk/fsck-exit-code-fix later to maint).
+
+ * Use of "--verbose" option used to break "git branch --merged".
+ (merge 12994dd jk/maint-branch-verbose-merged later to maint).
+
+ * Some MUAs mangled a line in a message that begins with "From " to
+ ">From " when writing to a mailbox file and feeding such an input
+ to "git am" used to lose such a line.
+ (merge 85de86a jk/mbox-from-line later to maint).
+
+ * "rev-parse --verify --quiet $name" is meant to quietly exit with a
+ non-zero status when $name is not a valid object name, but still
+ gave error messages in some cases.
+
+ * A handful of C source files have been updated to include
+ "git-compat-util.h" as the first thing, to conform better to our
+ coding guidelines.
+ (merge 1c4b660 da/include-compat-util-first-in-c later to maint).
+
+ * t7004 test, which tried to run Git with small stack space, has been
+ updated to give a bit larger stack to avoid false breakage on some
+ platforms.
+ (merge b9a1907 sk/tag-contains-wo-recursion later to maint).
+
+ * A few documentation pages had example sections marked up not quite
+ correctly, which passed AsciiDoc but failed with AsciiDoctor.
+ (merge c30c43c bc/asciidoc-pretty-formats-fix later to maint).
+ (merge f8a48af bc/asciidoc later to maint).
+
+ * "gitweb" used deprecated CGI::startfrom, which was removed from
+ CGI.pm as of 4.04; use CGI::start_from instead.
+ (merge 4750f4b rm/gitweb-start-form later to maint).
+
+ * Newer versions of 'meld' breaks the auto-detection we use to see if
+ they are new enough to support the `--output` option.
+ (merge b12d045 da/mergetool-meld later to maint).
+
+ * "git pack-objects" forgot to disable the codepath to generate
+ object recheability bitmap when it needs to split the resulting
+ pack.
+ (merge 2113471 jk/pack-objects-no-bitmap-when-splitting later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 99b5c6928b..8212897cf1 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -204,13 +204,26 @@ advice.*::
--
core.fileMode::
- If false, the executable bit differences between the index and
- the working tree are ignored; useful on broken filesystems like FAT.
- See linkgit:git-update-index[1].
+ Tells Git if the executable bit of files in the working tree
+ is to be honored.
+
-The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
-will probe and set core.fileMode false if appropriate when the
-repository is created.
+Some filesystems lose the executable bit when a file that is
+marked as executable is checked out, or checks out an
+non-executable file with executable bit on.
+linkgit:git-clone[1] or linkgit:git-init[1] probe the filesystem
+to see if it handles the executable bit correctly
+and this variable is automatically set as necessary.
++
+A repository, however, may be on a filesystem that handles
+the filemode correctly, and this variable is set to 'true'
+when created, but later may be made accessible from another
+environment that loses the filemode (e.g. exporting ext4 via
+CIFS mount, visiting a Cygwin created repository with
+Git for Windows or Eclipse).
+In such a case it may be necessary to set this variable to 'false'.
+See linkgit:git-update-index[1].
++
+The default is true (when core.filemode is not specified in the config file).
core.ignorecase::
If true, this option enables various workarounds to enable
@@ -499,7 +512,8 @@ core.bigFileThreshold::
Files larger than this size are stored deflated, without
attempting delta compression. Storing large files without
delta compression avoids excessive memory usage, at the
- slight expense of increased disk usage.
+ slight expense of increased disk usage. Additionally files
+ larger than this size are always treated as binary.
+
Default is 512 MiB on all platforms. This should be reasonable
for most projects as source code and other text files can still
@@ -1754,6 +1768,15 @@ mergetool.<tool>.trustExitCode::
if the file has been updated, otherwise the user is prompted to
indicate the success of the merge.
+mergetool.meld.hasOutput::
+ Older versions of `meld` do not support the `--output` option.
+ Git will attempt to detect whether `meld` supports `--output`
+ by inspecting the output of `meld --help`. Configuring
+ `mergetool.meld.hasOutput` will make Git skip these checks and
+ use the configured value instead. Setting `mergetool.meld.hasOutput`
+ to `true` tells Git to unconditionally use the `--output` option,
+ and `false` avoids using `--output`.
+
mergetool.keepBackup::
After performing a merge, the original file with conflict markers
can be saved as a file with a `.orig` extension. If this variable
@@ -1767,6 +1790,12 @@ mergetool.keepTemporaries::
preserved, otherwise they will be removed after the tool has
exited. Defaults to `false`.
+mergetool.writeToTemp::
+ Git writes temporary 'BASE', 'LOCAL', and 'REMOTE' versions of
+ conflicting files in the worktree by default. Git will attempt
+ to use a temporary directory for these files when set `true`.
+ Defaults to `false`.
+
mergetool.prompt::
Prompt before each invocation of the merge resolution program.
@@ -2044,6 +2073,25 @@ receive.autogc::
receiving data from git-push and updating refs. You can stop
it by setting this variable to false.
+receive.certnonceseed::
+ By setting this variable to a string, `git receive-pack`
+ will accept a `git push --signed` and verifies it by using
+ a "nonce" protected by HMAC using this string as a secret
+ key.
+
+receive.certnonceslop::
+ When a `git push --signed` sent a push certificate with a
+ "nonce" that was issued by a receive-pack serving the same
+ repository within this many seconds, export the "nonce"
+ found in the certificate to `GIT_PUSH_CERT_NONCE` to the
+ hooks (instead of what the receive-pack asked the sending
+ side to include). This may allow writing checks in
+ `pre-receive` and `post-receive` a bit easier. Instead of
+ checking `GIT_PUSH_CERT_NONCE_SLOP` environment variable
+ that records by how many seconds the nonce is stale to
+ decide if they want to accept the certificate, they only
+ can check `GIT_PUSH_CERT_NONCE_STATUS` is `OK`.
+
receive.fsckObjects::
If it is set to true, git-receive-pack will check all received
objects. It will abort in the case of a malformed object or a
diff --git a/Documentation/everyday.txto b/Documentation/everyday.txto
new file mode 100644
index 0000000000..c5047d8f9b
--- /dev/null
+++ b/Documentation/everyday.txto
@@ -0,0 +1,9 @@
+Everyday Git With 20 Commands Or So
+===================================
+
+This document has been moved to linkgit:giteveryday[1].
+
+Please let the owners of the referring site know so that they can update the
+link you clicked to get here.
+
+Thanks.
diff --git a/Documentation/git-credential-cache--daemon.txt b/Documentation/git-credential-cache--daemon.txt
index d15db42d43..7051c6bdf8 100644
--- a/Documentation/git-credential-cache--daemon.txt
+++ b/Documentation/git-credential-cache--daemon.txt
@@ -8,7 +8,7 @@ git-credential-cache--daemon - Temporarily store user credentials in memory
SYNOPSIS
--------
[verse]
-git credential-cache--daemon <socket>
+git credential-cache--daemon [--debug] <socket>
DESCRIPTION
-----------
@@ -21,6 +21,10 @@ 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.
+If the `--debug` option is specified, the daemon does not close its
+stderr stream, and may output extra diagnostics to it even after it has
+begun listening for clients.
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index 221506b04b..dbe9a46833 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -105,6 +105,11 @@ marks the same across runs.
in the commit (as opposed to just listing the files which are
different from the commit's first parent).
+--anonymize::
+ Anonymize the contents of the repository while still retaining
+ the shape of the history and stored tree. See the section on
+ `ANONYMIZING` below.
+
--refspec::
Apply the specified refspec to each ref exported. Multiple of them can
be specified.
@@ -141,6 +146,62 @@ referenced by that revision range contains the string
'refs/heads/master'.
+ANONYMIZING
+-----------
+
+If the `--anonymize` option is given, git will attempt to remove all
+identifying information from the repository while still retaining enough
+of the original tree and history patterns to reproduce some bugs. The
+goal is that a git bug which is found on a private repository will
+persist in the anonymized repository, and the latter can be shared with
+git developers to help solve the bug.
+
+With this option, git will replace all refnames, paths, blob contents,
+commit and tag messages, names, and email addresses in the output with
+anonymized data. Two instances of the same string will be replaced
+equivalently (e.g., two commits with the same author will have the same
+anonymized author in the output, but bear no resemblance to the original
+author string). The relationship between commits, branches, and tags is
+retained, as well as the commit timestamps (but the commit messages and
+refnames bear no resemblance to the originals). The relative makeup of
+the tree is retained (e.g., if you have a root tree with 10 files and 3
+trees, so will the output), but their names and the contents of the
+files will be replaced.
+
+If you think you have found a git bug, you can start by exporting an
+anonymized stream of the whole repository:
+
+---------------------------------------------------
+$ git fast-export --anonymize --all >anon-stream
+---------------------------------------------------
+
+Then confirm that the bug persists in a repository created from that
+stream (many bugs will not, as they really do depend on the exact
+repository contents):
+
+---------------------------------------------------
+$ git init anon-repo
+$ cd anon-repo
+$ git fast-import <../anon-stream
+$ ... test your bug ...
+---------------------------------------------------
+
+If the anonymized repository shows the bug, it may be worth sharing
+`anon-stream` along with a regular bug report. Note that the anonymized
+stream compresses very well, so gzipping it is encouraged. If you want
+to examine the stream to see that it does not contain any private data,
+you can peruse it directly before sending. You may also want to try:
+
+---------------------------------------------------
+$ perl -pe 's/\d+/X/g' <anon-stream | sort -u | less
+---------------------------------------------------
+
+which shows all of the unique lines (with numbers converted to "X", to
+collapse "User 0", "User 1", etc into "User X"). This produces a much
+smaller output, and it is usually easy to quickly confirm that there is
+no private data in the stream.
+
+
Limitations
-----------
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 875d2831a5..c7c0d21429 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -38,18 +38,17 @@ Variables
imap.folder::
The folder to drop the mails into, which is typically the Drafts
folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required to use imap-send.
+ "[Gmail]/Drafts". Required.
imap.tunnel::
Command used to setup a tunnel to the IMAP server through which
commands will be piped instead of using a direct network connection
- to the server. Required when imap.host is not set to use imap-send.
+ to the server. Required when imap.host is not set.
imap.host::
A URL identifying the server. Use a `imap://` prefix for non-secure
connections and a `imaps://` prefix for secure connections.
- Ignored when imap.tunnel is set, but required to use imap-send
- otherwise.
+ Ignored when imap.tunnel is set, but required otherwise.
imap.user::
The username to use when logging in to the server.
@@ -76,7 +75,8 @@ imap.preformattedHTML::
imap.authMethod::
Specify authenticate method for authentication with IMAP server.
- Current supported method is 'CRAM-MD5' only.
+ Current supported method is 'CRAM-MD5' only. If this is not set
+ then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
Examples
~~~~~~~~
@@ -97,7 +97,7 @@ Using direct mode:
host = imap://imap.example.com
user = bob
pass = p4ssw0rd
-..........................
+.........................
Using direct mode with SSL:
@@ -109,7 +109,7 @@ Using direct mode with SSL:
pass = p4ssw0rd
port = 123
sslverify = false
-..........................
+.........................
EXAMPLE
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index afd721e3a9..369f889bb4 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -43,7 +43,7 @@ OPTIONS
-q::
--quiet::
-Only print error and warning messages, all other output will be suppressed.
+Only print error and warning messages; all other output will be suppressed.
--bare::
@@ -57,12 +57,12 @@ DIRECTORY" section below.)
--separate-git-dir=<git dir>::
-Instead of initializing the repository where it is supposed to be,
-place a filesytem-agnostic Git symbolic link there, pointing to the
-specified path, and initialize a Git repository at the path. The
-result is Git repository can be separated from working tree. If this
-is reinitialization, the repository will be moved to the specified
-path.
+Instead of initializing the repository as a directory to either `$GIT_DIR` or
+`./.git/`, create a text file there containing the path to the actual
+repository. This file acts as filesystem-agnostic Git symbolic link to the
+repository.
++
+If this is reinitialization, the repository will be moved to the specified path.
--shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
@@ -72,60 +72,65 @@ repository. When specified, the config variable "core.sharedRepository" is
set so that files and directories under `$GIT_DIR` are created with the
requested permissions. When not specified, Git will use permissions reported
by umask(2).
-
++
The option can have the following values, defaulting to 'group' if no value
is given:
++
+--
+'umask' (or 'false')::
- - 'umask' (or 'false'): Use permissions reported by umask(2). The default,
- when `--shared` is not specified.
+Use permissions reported by umask(2). The default, when `--shared` is not
+specified.
- - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
- the git group may be not the primary group of all users).
- This is used to loosen the permissions of an otherwise safe umask(2) value.
- Note that the umask still applies to the other permission bits (e.g. if
- umask is '0022', using 'group' will not remove read privileges from other
- (non-group) users). See '0xxx' for how to exactly specify the repository
- permissions.
+'group' (or 'true')::
- - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
- readable by all users.
+Make the repository group-writable, (and g+sx, since the git group may be not
+the primary group of all users). This is used to loosen the permissions of an
+otherwise safe umask(2) value. Note that the umask still applies to the other
+permission bits (e.g. if umask is '0022', using 'group' will not remove read
+privileges from other (non-group) users). See '0xxx' for how to exactly specify
+the repository permissions.
- - '0xxx': '0xxx' is an octal number and each file will have mode '0xxx'.
- '0xxx' will override users' umask(2) value (and not only loosen permissions
- as 'group' and 'all' does). '0640' will create a repository which is
- group-readable, but not group-writable or accessible to others. '0660' will
- create a repo that is readable and writable to the current user and group,
- but inaccessible to others.
+'all' (or 'world' or 'everybody')::
-By default, the configuration flag receive.denyNonFastForwards is enabled
+Same as 'group', but make the repository readable by all users.
+
+'0xxx'::
+
+'0xxx' is an octal number and each file will have mode '0xxx'. '0xxx' will
+override users' umask(2) value (and not only loosen permissions as 'group' and
+'all' does). '0640' will create a repository which is group-readable, but not
+group-writable or accessible to others. '0660' will create a repo that is
+readable and writable to the current user and group, but inaccessible to others.
+--
+
+By default, the configuration flag `receive.denyNonFastForwards` is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.
-If you name a (possibly non-existent) directory at the end of the command
-line, the command is run inside the directory (possibly after creating it).
+If you provide a 'directory', the command is run inside it. If this directory
+does not exist, it will be created.
--
-
TEMPLATE DIRECTORY
------------------
The template directory contains files and directories that will be copied to
the `$GIT_DIR` after it is created.
-The template directory used will (in order):
+The template directory will be one of the following (in order):
- - The argument given with the `--template` option.
+ - the argument given with the `--template` option;
- - The contents of the `$GIT_TEMPLATE_DIR` environment variable.
+ - the contents of the `$GIT_TEMPLATE_DIR` environment variable;
- - The `init.templatedir` configuration variable.
+ - the `init.templatedir` configuration variable; or
- - The default template directory: `/usr/share/git-core/templates`.
+ - the default template directory: `/usr/share/git-core/templates`.
-The default template directory includes some directory structure, some
-suggested "exclude patterns", and copies of sample "hook" files.
-The suggested patterns and hook files are all modifiable and extensible.
+The default template directory includes some directory structure, suggested
+"exclude patterns" (see linkgit:gitignore[5]), and sample hook files (see linkgit:githooks[5]).
EXAMPLES
--------
@@ -136,10 +141,12 @@ Start a new Git repository for an existing code base::
$ cd /path/to/my/codebase
$ git init <1>
$ git add . <2>
+$ git commit <3>
----------------
+
-<1> prepare /path/to/my/codebase/.git directory
-<2> add all existing file to the index
+<1> Create a /path/to/my/codebase/.git directory.
+<2> Add all existing files to the index.
+<3> Record the pristine state as the first commit in the history.
GIT
---
diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
new file mode 100644
index 0000000000..81fac3d26d
--- /dev/null
+++ b/Documentation/git-interpret-trailers.txt
@@ -0,0 +1,314 @@
+git-interpret-trailers(1)
+=========================
+
+NAME
+----
+git-interpret-trailers - help add stuctured information into commit messages
+
+SYNOPSIS
+--------
+[verse]
+'git interpret-trailers' [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
+
+DESCRIPTION
+-----------
+Help adding 'trailers' lines, that look similar to RFC 822 e-mail
+headers, at the end of the otherwise free-form part of a commit
+message.
+
+This command reads some patches or commit messages from either the
+<file> arguments or the standard input if no <file> is specified. Then
+this command applies the arguments passed using the `--trailer`
+option, if any, to the commit message part of each input file. The
+result is emitted on the standard output.
+
+Some configuration variables control the way the `--trailer` arguments
+are applied to each commit message and the way any existing trailer in
+the commit message is changed. They also make it possible to
+automatically add some trailers.
+
+By default, a '<token>=<value>' or '<token>:<value>' argument given
+using `--trailer` will be appended after the existing trailers only if
+the last trailer has a different (<token>, <value>) pair (or if there
+is no existing trailer). The <token> and <value> parts will be trimmed
+to remove starting and trailing whitespace, and the resulting trimmed
+<token> and <value> will appear in the message like this:
+
+------------------------------------------------
+token: value
+------------------------------------------------
+
+This means that the trimmed <token> and <value> will be separated by
+`': '` (one colon followed by one space).
+
+By default the new trailer will appear at the end of all the existing
+trailers. If there is no existing trailer, the new trailer will appear
+after the commit message part of the ouput, and, if there is no line
+with only spaces at the end of the commit message part, one blank line
+will be added before the new trailer.
+
+Existing trailers are extracted from the input message by looking for
+a group of one or more lines that contain a colon (by default), where
+the group is preceded by one or more empty (or whitespace-only) lines.
+The group must either be at the end of the message or be the last
+non-whitespace lines before a line that starts with '---'. Such three
+minus signs start the patch part of the message.
+
+When reading trailers, there can be whitespaces before and after the
+token, the separator and the value. There can also be whitespaces
+indide the token and the value.
+
+Note that 'trailers' do not follow and are not intended to follow many
+rules for RFC 822 headers. For example they do not follow the line
+folding rules, the encoding rules and probably many other rules.
+
+OPTIONS
+-------
+--trim-empty::
+ If the <value> part of any trailer contains only whitespace,
+ the whole trailer will be removed from the resulting message.
+ This apply to existing trailers as well as new trailers.
+
+--trailer <token>[(=|:)<value>]::
+ Specify a (<token>, <value>) pair that should be applied as a
+ trailer to the input messages. See the description of this
+ command.
+
+CONFIGURATION VARIABLES
+-----------------------
+
+trailer.separators::
+ This option tells which characters are recognized as trailer
+ separators. By default only ':' is recognized as a trailer
+ separator, except that '=' is always accepted on the command
+ line for compatibility with other git commands.
++
+The first character given by this option will be the default character
+used when another separator is not specified in the config for this
+trailer.
++
+For example, if the value for this option is "%=$", then only lines
+using the format '<token><sep><value>' with <sep> containing '%', '='
+or '$' and then spaces will be considered trailers. And '%' will be
+the default separator used, so by default trailers will appear like:
+'<token>% <value>' (one percent sign and one space will appear between
+the token and the value).
+
+trailer.where::
+ This option tells where a new trailer will be added.
++
+This can be `end`, which is the default, `start`, `after` or `before`.
++
+If it is `end`, then each new trailer will appear at the end of the
+existing trailers.
++
+If it is `start`, then each new trailer will appear at the start,
+instead of the end, of the existing trailers.
++
+If it is `after`, then each new trailer will appear just after the
+last trailer with the same <token>.
++
+If it is `before`, then each new trailer will appear just before the
+first trailer with the same <token>.
+
+trailer.ifexists::
+ This option makes it possible to choose what action will be
+ performed when there is already at least one trailer with the
+ same <token> in the message.
++
+The valid values for this option are: `addIfDifferentNeighbor` (this
+is the default), `addIfDifferent`, `add`, `overwrite` or `doNothing`.
++
+With `addIfDifferentNeighbor`, a new trailer will be added only if no
+trailer with the same (<token>, <value>) pair is above or below the line
+where the new trailer will be added.
++
+With `addIfDifferent`, a new trailer will be added only if no trailer
+with the same (<token>, <value>) pair is already in the message.
++
+With `add`, a new trailer will be added, even if some trailers with
+the same (<token>, <value>) pair are already in the message.
++
+With `replace`, an existing trailer with the same <token> will be
+deleted and the new trailer will be added. The deleted trailer will be
+the closest one (with the same <token>) to the place where the new one
+will be added.
++
+With `doNothing`, nothing will be done; that is no new trailer will be
+added if there is already one with the same <token> in the message.
+
+trailer.ifmissing::
+ This option makes it possible to choose what action will be
+ performed when there is not yet any trailer with the same
+ <token> in the message.
++
+The valid values for this option are: `add` (this is the default) and
+`doNothing`.
++
+With `add`, a new trailer will be added.
++
+With `doNothing`, nothing will be done.
+
+trailer.<token>.key::
+ This `key` will be used instead of <token> in the trailer. At
+ the end of this key, a separator can appear and then some
+ space characters. By default the only valid separator is ':',
+ but this can be changed using the `trailer.separators` config
+ variable.
++
+If there is a separator, then the key will be used instead of both the
+<token> and the default separator when adding the trailer.
+
+trailer.<token>.where::
+ This option takes the same values as the 'trailer.where'
+ configuration variable and it overrides what is specified by
+ that option for trailers with the specified <token>.
+
+trailer.<token>.ifexist::
+ This option takes the same values as the 'trailer.ifexist'
+ configuration variable and it overrides what is specified by
+ that option for trailers with the specified <token>.
+
+trailer.<token>.ifmissing::
+ This option takes the same values as the 'trailer.ifmissing'
+ configuration variable and it overrides what is specified by
+ that option for trailers with the specified <token>.
+
+trailer.<token>.command::
+ This option can be used to specify a shell command that will
+ be called to automatically add or modify a trailer with the
+ specified <token>.
++
+When this option is specified, the behavior is as if a special
+'<token>=<value>' argument were added at the beginning of the command
+line, where <value> is taken to be the standard output of the
+specified command with any leading and trailing whitespace trimmed
+off.
++
+If the command contains the `$ARG` string, this string will be
+replaced with the <value> part of an existing trailer with the same
+<token>, if any, before the command is launched.
++
+If some '<token>=<value>' arguments are also passed on the command
+line, when a 'trailer.<token>.command' is configured, the command will
+also be executed for each of these arguments. And the <value> part of
+these arguments, if any, will be used to replace the `$ARG` string in
+the command.
+
+EXAMPLES
+--------
+
+* Configure a 'sign' trailer with a 'Signed-off-by' key, and then
+ add two of these trailers to a message:
++
+------------
+$ git config trailer.sign.key "Signed-off-by"
+$ cat msg.txt
+subject
+
+message
+$ cat msg.txt | git interpret-trailers --trailer 'sign: Alice <alice@example.com>' --trailer 'sign: Bob <bob@example.com>'
+subject
+
+message
+
+Signed-off-by: Alice <alice@example.com>
+Signed-off-by: Bob <bob@example.com>
+------------
+
+* Extract the last commit as a patch, and add a 'Cc' and a
+ 'Reviewed-by' trailer to it:
++
+------------
+$ git format-patch -1
+0001-foo.patch
+$ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Reviewed-by: Bob <bob@example.com>' 0001-foo.patch >0001-bar.patch
+------------
+
+* Configure a 'sign' trailer with a command to automatically add a
+ 'Signed-off-by: ' with the author information only if there is no
+ 'Signed-off-by: ' already, and show how it works:
++
+------------
+$ git config trailer.sign.key "Signed-off-by: "
+$ git config trailer.sign.ifmissing add
+$ git config trailer.sign.ifexists doNothing
+$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
+$ git interpret-trailers <<EOF
+> EOF
+
+Signed-off-by: Bob <bob@example.com>
+$ git interpret-trailers <<EOF
+> Signed-off-by: Alice <alice@example.com>
+> EOF
+
+Signed-off-by: Alice <alice@example.com>
+------------
+
+* Configure a 'fix' trailer with a key that contains a '#' and no
+ space after this character, and show how it works:
++
+------------
+$ git config trailer.separators ":#"
+$ git config trailer.fix.key "Fix #"
+$ echo "subject" | git interpret-trailers --trailer fix=42
+subject
+
+Fix #42
+------------
+
+* Configure a 'see' trailer with a command to show the subject of a
+ commit that is related, and show how it works:
++
+------------
+$ git config trailer.see.key "See-also: "
+$ git config trailer.see.ifExists "replace"
+$ git config trailer.see.ifMissing "doNothing"
+$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
+$ git interpret-trailers <<EOF
+> subject
+>
+> message
+>
+> see: HEAD~2
+> EOF
+subject
+
+message
+
+See-also: fe3187489d69c4 (subject of related commit)
+------------
+
+* Configure a commit template with some trailers with empty values
+ (using sed to show and keep the trailing spaces at the end of the
+ trailers), then configure a commit-msg hook that uses
+ 'git interpret-trailers' to remove trailers with empty values and
+ to add a 'git-version' trailer:
++
+------------
+$ sed -e 's/ Z$/ /' >commit_template.txt <<EOF
+> ***subject***
+>
+> ***message***
+>
+> Fixes: Z
+> Cc: Z
+> Reviewed-by: Z
+> Signed-off-by: Z
+> EOF
+$ git config commit.template commit_template.txt
+$ cat >.git/hooks/commit-msg <<EOF
+> #!/bin/sh
+> git interpret-trailers --trim-empty --trailer "git-version: \$(git describe)" "\$1" > "\$1.new"
+> mv "\$1.new" "\$1"
+> EOF
+$ chmod +x .git/hooks/commit-msg
+------------
+
+SEE ALSO
+--------
+linkgit:git-commit[1], linkgit:git-format-patch[1], linkgit:git-config[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index 6738055bd3..9fed59a317 100644
--- a/Documentation/git-prune-packed.txt
+++ b/Documentation/git-prune-packed.txt
@@ -1,5 +1,5 @@
git-prune-packed(1)
-=====================
+===================
NAME
----
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index c0d7403b9a..21b3f29c3b 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
- [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
+ [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
+ [-u | --set-upstream] [--signed]
[--force-with-lease[=<refname>[:<expect>]]]
[--no-verify] [<repository> [<refspec>...]]
@@ -33,7 +34,7 @@ When the command line does not specify what to push with `<refspec>...`
arguments or `--all`, `--mirror`, `--tags` options, the command finds
the default `<refspec>` by consulting `remote.*.push` configuration,
and if it is not found, honors `push.default` configuration to decide
-what to push (See linkgit:git-config[1] for the meaning of `push.default`).
+what to push (See gitlink:git-config[1] for the meaning of `push.default`).
OPTIONS[[OPTIONS]]
@@ -129,6 +130,12 @@ already exists on the remote side.
from the remote but are pointing at commit-ish that are
reachable from the refs being pushed.
+--signed::
+ GPG-sign the push request to update refs on the receiving
+ side, to allow it to be checked by the hooks and/or be
+ logged. See linkgit:git-receive-pack[1] for the details
+ on the receiving end.
+
--receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>::
Path to the 'git-receive-pack' program on the remote
diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt
index a356196586..d64388cb8e 100644
--- a/Documentation/git-quiltimport.txt
+++ b/Documentation/git-quiltimport.txt
@@ -1,5 +1,5 @@
git-quiltimport(1)
-================
+==================
NAME
----
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4138554912..924827dc2e 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -21,15 +21,17 @@ If <branch> is specified, 'git rebase' will perform an automatic
it remains on the current branch.
If <upstream> is not specified, the upstream configured in
-branch.<name>.remote and branch.<name>.merge options will be used; see
-linkgit:git-config[1] for details. If you are currently not on any
-branch or if the current branch does not have a configured upstream,
-the rebase will abort.
+branch.<name>.remote and branch.<name>.merge options will be used (see
+linkgit:git-config[1] for details) and the `--fork-point` option is
+assumed. If you are currently not on any branch or if the current
+branch does not have a configured upstream, the rebase will abort.
All changes made by commits in the current branch but that are not
in <upstream> are saved to a temporary area. This is the same set
-of commits that would be shown by `git log <upstream>..HEAD` (or
-`git log HEAD`, if --root is specified).
+of commits that would be shown by `git log <upstream>..HEAD`; or by
+`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the
+description on `--fork-point` below); or by `git log HEAD`, if the
+`--root` option is specified.
The current branch is reset to <upstream>, or <newbase> if the
--onto option was supplied. This has the exact same effect as
@@ -327,13 +329,18 @@ link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details)
--fork-point::
--no-fork-point::
- Use 'git merge-base --fork-point' to find a better common ancestor
- between `upstream` and `branch` when calculating which commits have
- have been introduced by `branch` (see linkgit:git-merge-base[1]).
+ Use reflog to find a better common ancestor between <upstream>
+ and <branch> when calculating which commits have been
+ introduced by <branch>.
+
-If no non-option arguments are given on the command line, then the default is
-`--fork-point @{u}` otherwise the `upstream` argument is interpreted literally
-unless the `--fork-point` option is specified.
+When --fork-point is active, 'fork_point' will be used instead of
+<upstream> to calculate the set of commits to rebase, where
+'fork_point' is the result of `git merge-base --fork-point <upstream>
+<branch>` command (see linkgit:git-merge-base[1]). If 'fork_point'
+ends up being empty, the <upstream> will be used as a fallback.
++
+If either <upstream> or --root is given on the command line, then the
+default is `--no-fork-point`, otherwise the default is `--fork-point`.
--ignore-whitespace::
--whitespace=<option>::
diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index b1f7dc643a..9016960e27 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -53,6 +53,56 @@ the update. Refs to be created will have sha1-old equal to 0\{40},
while refs to be deleted will have sha1-new equal to 0\{40}, otherwise
sha1-old and sha1-new should be valid objects in the repository.
+When accepting a signed push (see linkgit:git-push[1]), the signed
+push certificate is stored in a blob and an environment variable
+`GIT_PUSH_CERT` can be consulted for its object name. See the
+description of `post-receive` hook for an example. In addition, the
+certificate is verified using GPG and the result is exported with
+the following environment variables:
+
+`GIT_PUSH_CERT_SIGNER`::
+ The name and the e-mail address of the owner of the key that
+ signed the push certificate.
+
+`GIT_PUSH_CERT_KEY`::
+ The GPG key ID of the key that signed the push certificate.
+
+`GIT_PUSH_CERT_STATUS`::
+ The status of GPG verification of the push certificate,
+ using the same mnemonic as used in `%G?` format of `git log`
+ family of commands (see linkgit:git-log[1]).
+
+`GIT_PUSH_CERT_NONCE`::
+ The nonce string the process asked the signer to include
+ in the push certificate. If this does not match the value
+ recorded on the "nonce" header in the push certificate, it
+ may indicate that the certificate is a valid one that is
+ being replayed from a separate "git push" session.
+
+`GIT_PUSH_CERT_NONCE_STATUS`::
+`UNSOLICITED`;;
+ "git push --signed" sent a nonce when we did not ask it to
+ send one.
+`MISSING`;;
+ "git push --signed" did not send any nonce header.
+`BAD`;;
+ "git push --signed" sent a bogus nonce.
+`OK`;;
+ "git push --signed" sent the nonce we asked it to send.
+`SLOP`;;
+ "git push --signed" sent a nonce different from what we
+ asked it to send now, but in a previous session. See
+ `GIT_PUSH_CERT_NONCE_SLOP` environment variable.
+
+`GIT_PUSH_CERT_NONCE_SLOP`::
+ "git push --signed" sent a nonce different from what we
+ asked it to send now, but in a different session whose
+ starting time is different by this many seconds from the
+ current session. Only meaningful when
+ `GIT_PUSH_CERT_NONCE_STATUS` says `SLOP`.
+ Also read about `receive.certnonceslop` variable in
+ linkgit:git-config[1].
+
This hook is called before any refname is updated and before any
fast-forward checks are performed.
@@ -101,9 +151,14 @@ the update. Refs that were created will have sha1-old equal to
0\{40}, otherwise sha1-old and sha1-new should be valid objects in
the repository.
+The `GIT_PUSH_CERT*` environment variables can be inspected, just as
+in `pre-receive` hook, after accepting a signed push.
+
Using this hook, it is easy to generate mails describing the updates
to the repository. This example script sends one mail message per
-ref listing the commits pushed to the repository:
+ref listing the commits pushed to the repository, and logs the push
+certificates of signed pushes with good signatures to a logger
+service:
#!/bin/sh
# mail out commit update information.
@@ -119,6 +174,14 @@ ref listing the commits pushed to the repository:
fi |
mail -s "Changes to ref $ref" commit-list@mydomain
done
+ # log signed push certificate, if any
+ if test -n "${GIT_PUSH_CERT-}" && test ${GIT_PUSH_CERT_STATUS} = G
+ then
+ (
+ echo expected nonce is ${GIT_PUSH_NONCE}
+ git cat-file blob ${GIT_PUSH_CERT}
+ ) | mail -s "push certificate from $GIT_PUSH_CERT_SIGNER" push-log@mydomain
+ fi
exit 0
The exit code from this hook invocation is ignored, however a
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 7a1585def0..fd7f8b5bc1 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -45,7 +45,7 @@ SYNOPSIS
[ \--regexp-ignore-case | -i ]
[ \--extended-regexp | -E ]
[ \--fixed-strings | -F ]
- [ \--date=(local|relative|default|iso|rfc|short) ]
+ [ \--date=(local|relative|default|iso|iso-strict|rfc|short) ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
[ \--pretty | \--header ]
[ \--bisect ]
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 0b84769bd9..fa4a8c3afc 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -114,6 +114,7 @@ can be used.
Only meaningful in `--verify` mode. Do not output an error
message if the first argument is not a valid object name;
instead exit with non-zero status silently.
+ SHA-1s for valid object names are printed to stdout on success.
--sq::
Usually the output is made one line per flag and
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index ba3fe0d7f5..25bcda936d 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -1,5 +1,5 @@
git-stage(1)
-==============
+============
NAME
----
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index def635f578..4d8d530d35 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -116,7 +116,7 @@ In the short-format, the status of each path is shown as
where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is
shown only when `PATH1` corresponds to a different path in the
-index/worktree (i.e. the file is renamed). The 'XY' is a two-letter
+index/worktree (i.e. the file is renamed). The `XY` is a two-letter
status code.
The fields (including the `->`) are separated from each other by a
@@ -125,7 +125,7 @@ characters, that field will be quoted in the manner of a C string
literal: surrounded by ASCII double quote (34) characters, and with
interior special characters backslash-escaped.
-For paths with merge conflicts, `X` and 'Y' show the modification
+For paths with merge conflicts, `X` and `Y` show the modification
states of each side of the merge. For paths that do not have merge
conflicts, `X` shows the status of the index, and `Y` shows the status
of the work tree. For untracked paths, `XY` are `??`. Other status
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 44c970ce18..ef8ef1c545 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -386,11 +386,13 @@ Any other arguments are passed directly to 'git log'
tree-ish to specify which branch should be searched). When given a
tree-ish, returns the corresponding SVN revision number.
+
+-B;;
--before;;
Don't require an exact match if given an SVN revision, instead find
the commit corresponding to the state of the SVN repository (on the
current branch) at the specified revision.
+
+-A;;
--after;;
Don't require an exact match if given an SVN revision; if there is
not an exact match return the closest match searching forward in the
@@ -608,21 +610,6 @@ config key: svn.authorsfile
Make 'git svn' less verbose. Specify a second time to make it
even less verbose.
---repack[=<n>]::
---repack-flags=<flags>::
- These should help keep disk usage sane for large fetches with
- many revisions.
-+
---repack takes an optional argument for the number of revisions
-to fetch before repacking. This defaults to repacking every
-1000 commits fetched if no argument is specified.
-+
---repack-flags are passed directly to 'git repack'.
-+
-[verse]
-config key: svn.repack
-config key: svn.repackflags
-
-m::
--merge::
-s<strategy>::
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index dfc09d93d8..82eca6fdf6 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -202,7 +202,7 @@ merging.
To pretend you have a file with mode and sha1 at path, say:
----------------
-$ git update-index --cacheinfo mode sha1 path
+$ git update-index --cacheinfo <mode>,<sha1>,<path>
----------------
'--info-only' is used to register files without placing them in the object
diff --git a/Documentation/git.txt b/Documentation/git.txt
index c6175d45e4..9e0a42ce56 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -22,7 +22,7 @@ unusually rich command set that provides both high-level operations
and full access to internals.
See linkgit:gittutorial[7] to get started, then see
-link:everyday.html[Everyday Git] for a useful minimum set of
+linkgit:giteveryday[7] for a useful minimum set of
commands. The link:user-manual.html[Git User's Manual] has a more
in-depth introduction.
@@ -1098,7 +1098,7 @@ subscribed to the list to send a message there.
SEE ALSO
--------
linkgit:gittutorial[7], linkgit:gittutorial-2[7],
-link:everyday.html[Everyday Git], linkgit:gitcvs-migration[7],
+linkgit:giteveryday[7], linkgit:gitcvs-migration[7],
linkgit:gitglossary[7], linkgit:gitcore-tutorial[7],
linkgit:gitcli[7], link:user-manual.html[The Git User's Manual],
linkgit:gitworkflows[7]
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 643c1ba929..9b45bda748 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -440,8 +440,8 @@ Unspecified::
A path to which the `diff` attribute is unspecified
first gets its contents inspected, and if it looks like
- text, it is treated as text. Otherwise it would
- generate `Binary files differ`.
+ text and is smaller than core.bigFileThreshold, it is treated
+ as text. Otherwise it would generate `Binary files differ`.
String::
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index d2d7c213dd..8475c07932 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -1667,7 +1667,7 @@ linkgit:gittutorial[7],
linkgit:gittutorial-2[7],
linkgit:gitcvs-migration[7],
linkgit:git-help[1],
-link:everyday.html[Everyday git],
+linkgit:giteveryday[7],
link:user-manual.html[The Git User's Manual]
GIT
diff --git a/Documentation/gitcvs-migration.txt b/Documentation/gitcvs-migration.txt
index 5f4e89005c..b06e852a85 100644
--- a/Documentation/gitcvs-migration.txt
+++ b/Documentation/gitcvs-migration.txt
@@ -194,7 +194,7 @@ linkgit:gittutorial[7],
linkgit:gittutorial-2[7],
linkgit:gitcore-tutorial[7],
linkgit:gitglossary[7],
-link:everyday.html[Everyday Git],
+linkgit:giteveryday[7],
link:user-manual.html[The Git User's Manual]
GIT
diff --git a/Documentation/everyday.txt b/Documentation/giteveryday.txt
index b2548ef4e6..7be6e64846 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/giteveryday.txt
@@ -1,22 +1,37 @@
+giteveryday(7)
+===============
+
+NAME
+----
+giteveryday - A useful minimum set of commands for Everyday Git
+
+SYNOPSIS
+--------
+
Everyday Git With 20 Commands Or So
-===================================
-<<Individual Developer (Standalone)>> commands are essential for
-anybody who makes a commit, even for somebody who works alone.
+DESCRIPTION
+-----------
-If you work with other people, you will need commands listed in
-the <<Individual Developer (Participant)>> section as well.
+Git users can broadly be grouped into four categories for the purposes of
+describing here a small set of useful command for everyday Git.
-People who play the <<Integrator>> role need to learn some more
-commands in addition to the above.
+* <<STANDALONE,Individual Developer (Standalone)>> commands are essential
+ for anybody who makes a commit, even for somebody who works alone.
-<<Repository Administration>> commands are for system
-administrators who are responsible for the care and feeding
-of Git repositories.
+* If you work with other people, you will need commands listed in
+ the <<PARTICIPANT,Individual Developer (Participant)>> section as well.
+* People who play the <<INTEGRATOR,Integrator>> role need to learn some
+ more commands in addition to the above.
-Individual Developer (Standalone)[[Individual Developer (Standalone)]]
-----------------------------------------------------------------------
+* <<ADMINISTRATION,Repository Administration>> commands are for system
+ administrators who are responsible for the care and feeding
+ of Git repositories.
+
+
+Individual Developer (Standalone)[[STANDALONE]]
+-----------------------------------------------
A standalone individual developer does not exchange patches with
other people, and works alone in a single repository, using the
@@ -24,8 +39,6 @@ following commands.
* linkgit:git-init[1] to create a new repository.
- * linkgit:git-show-branch[1] to see where you are.
-
* linkgit:git-log[1] to see what happened.
* linkgit:git-checkout[1] and linkgit:git-branch[1] to switch
@@ -45,7 +58,7 @@ following commands.
* linkgit:git-rebase[1] to maintain topic branches.
- * linkgit:git-tag[1] to mark known point.
+ * linkgit:git-tag[1] to mark a known point.
Examples
~~~~~~~~
@@ -75,14 +88,12 @@ $ edit/compile/test
$ git diff HEAD <4>
$ git commit -a -s <5>
$ edit/compile/test
-$ git reset --soft HEAD^ <6>
-$ edit/compile/test
-$ git diff ORIG_HEAD <7>
-$ git commit -a -c ORIG_HEAD <8>
-$ git checkout master <9>
-$ git merge alsa-audio <10>
-$ git log --since='3 days ago' <11>
-$ git log v2.43.. curses/ <12>
+$ git diff HEAD^ <6>
+$ git commit -a --amend <7>
+$ git checkout master <8>
+$ git merge alsa-audio <9>
+$ git log --since='3 days ago' <10>
+$ git log v2.43.. curses/ <11>
------------
+
<1> create a new topic branch.
@@ -90,22 +101,21 @@ $ git log v2.43.. curses/ <12>
<3> you need to tell Git if you added a new file; removal and
modification will be caught if you do `git commit -a` later.
<4> to see what changes you are committing.
-<5> commit everything as you have tested, with your sign-off.
-<6> take the last commit back, keeping what is in the working tree.
-<7> look at the changes since the premature commit we took back.
-<8> redo the commit undone in the previous step, using the message
-you originally wrote.
-<9> switch to the master branch.
-<10> merge a topic branch into your master branch.
-<11> review commit logs; other forms to limit output can be
-combined and include `--max-count=10` (show 10 commits),
+<5> commit everything, as you have tested, with your sign-off.
+<6> look at all your changes including the previous commit.
+<7> amend the previous commit, adding all your new changes,
+using your original message.
+<8> switch to the master branch.
+<9> merge a topic branch into your master branch.
+<10> review commit logs; other forms to limit output can be
+combined and include `-10` (to show up to 10 commits),
`--until=2005-12-10`, etc.
-<12> view only the changes that touch what's in `curses/`
+<11> view only the changes that touch what's in `curses/`
directory, since `v2.43` tag.
-Individual Developer (Participant)[[Individual Developer (Participant)]]
-------------------------------------------------------------------------
+Individual Developer (Participant)[[PARTICIPANT]]
+-------------------------------------------------
A developer working as a participant in a group project needs to
learn how to communicate with others, and uses these commands in
@@ -123,6 +133,13 @@ addition to the ones needed by a standalone developer.
* linkgit:git-format-patch[1] to prepare e-mail submission, if
you adopt Linux kernel-style public forum workflow.
+ * linkgit:git-send-email[1] to send your e-mail submission without
+ corruption by your MUA.
+
+ * linkgit:git-request-pull[1] to create a summary of changes
+ for your upstream to pull.
+
+
Examples
~~~~~~~~
@@ -131,28 +148,34 @@ Clone the upstream and work on it. Feed changes to upstream.::
------------
$ git clone git://git.kernel.org/pub/scm/.../torvalds/linux-2.6 my2.6
$ cd my2.6
-$ edit/compile/test; git commit -a -s <1>
-$ git format-patch origin <2>
-$ git pull <3>
-$ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <4>
-$ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5>
-$ git reset --hard ORIG_HEAD <6>
-$ git gc <7>
-$ git fetch --tags <8>
+$ git checkout -b mine master <1>
+$ edit/compile/test; git commit -a -s <2>
+$ git format-patch master <3>
+$ git send-email --to="person <email@example.com>" 00*.patch <4>
+$ git checkout master <5>
+$ git pull <6>
+$ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <7>
+$ git ls-remote --heads http://git.kernel.org/.../jgarzik/libata-dev.git <8>
+$ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <9>
+$ git reset --hard ORIG_HEAD <10>
+$ git gc <11>
------------
+
-<1> repeat as needed.
-<2> extract patches from your branch for e-mail submission.
-<3> `git pull` fetches from `origin` by default and merges into the
+<1> checkout a new branch `mine` from master.
+<2> repeat as needed.
+<3> extract patches from your branch, relative to master,
+<4> and email them.
+<5> return to `master`, ready to see what's new
+<6> `git pull` fetches from `origin` by default and merges into the
current branch.
-<4> immediately after pulling, look at the changes done upstream
+<7> immediately after pulling, look at the changes done upstream
since last time we checked, only in the
area we are interested in.
-<5> fetch from a specific branch from a specific repository and merge.
-<6> revert the pull.
-<7> garbage collect leftover objects from reverted pull.
-<8> from time to time, obtain official tags from the `origin`
-and store them under `.git/refs/tags/`.
+<8> check the branch names in an external repository (if not known).
+<9> fetch from a specific branch `ALL` from a specific repository
+and merge it.
+<10> revert the pull.
+<11> garbage collect leftover objects from reverted pull.
Push into another repository.::
@@ -166,7 +189,7 @@ remote.origin.fetch refs/heads/*:refs/remotes/origin/*
branch.master.remote origin
branch.master.merge refs/heads/master
satellite$ git config remote.origin.push \
- master:refs/remotes/satellite/master <3>
+ +refs/heads/*:refs/remotes/satellite/* <3>
satellite$ edit/compile/test/commit
satellite$ git push origin <4>
@@ -181,11 +204,12 @@ machine.
<2> clone sets these configuration variables by default.
It arranges `git pull` to fetch and store the branches of mothership
machine to local `remotes/origin/*` remote-tracking branches.
-<3> arrange `git push` to push local `master` branch to
-`remotes/satellite/master` branch of the mothership machine.
-<4> push will stash our work away on `remotes/satellite/master`
-remote-tracking branch on the mothership machine. You could use this
-as a back-up method.
+<3> arrange `git push` to push all local branches to
+their corresponding branch of the mothership machine.
+<4> push will stash all our work away on `remotes/satellite/*`
+remote-tracking branches on the mothership machine. You could use this
+as a back-up method. Likewise, you can pretend that mothership
+"fetched" from you (useful when access is one sided).
<5> on mothership machine, merge the work done on the satellite
machine into the master branch.
@@ -195,17 +219,22 @@ Branch off of a specific tag.::
$ git checkout -b private2.6.14 v2.6.14 <1>
$ edit/compile/test; git commit -a
$ git checkout master
-$ git format-patch -k -m --stdout v2.6.14..private2.6.14 |
- git am -3 -k <2>
+$ git cherry-pick v2.6.14..private2.6.14 <2>
------------
+
<1> create a private branch based on a well known (but somewhat behind)
tag.
<2> forward port all changes in `private2.6.14` branch to `master` branch
-without a formal "merging".
+without a formal "merging". Or longhand +
+`git format-patch -k -m --stdout v2.6.14..private2.6.14 |
+ git am -3 -k`
+An alternate participant submission mechanism is using the
+`git request-pull` or pull-request mechanisms (e.g as used on
+GitHub (www.github.com) to notify your upstream of your
+contribution.
-Integrator[[Integrator]]
+Integrator[[INTEGRATOR]]
------------------------
A fairly central person acting as the integrator in a group
@@ -213,6 +242,13 @@ project receives changes made by others, reviews and integrates
them and publishes the result for others to use, using these
commands in addition to the ones needed by participants.
+This section can also be used by those who respond to `git
+request-pull` or pull-request on GitHub (www.github.com) to
+integrate the work of others into their history. An sub-area
+lieutenant for a repository will act both as a participant and
+as an integrator.
+
+
* linkgit:git-am[1] to apply patches e-mailed in from your
contributors.
@@ -229,19 +265,19 @@ commands in addition to the ones needed by participants.
Examples
~~~~~~~~
-My typical Git day.::
+A typical integrator's Git day.::
+
------------
$ git status <1>
-$ git show-branch <2>
+$ git branch --no-merged master <2>
$ mailx <3>
& s 2 3 4 5 ./+to-apply
& s 7 8 ./+hold-linus
& q
$ git checkout -b topic/one master
-$ git am -3 -i -s -u ./+to-apply <4>
+$ git am -3 -i -s ./+to-apply <4>
$ compile/test
-$ git checkout -b hold/linus && git am -3 -i -s -u ./+hold-linus <5>
+$ git checkout -b hold/linus && git am -3 -i -s ./+hold-linus <5>
$ git checkout topic/one && git rebase master <6>
$ git checkout pu && git reset --hard next <7>
$ git merge topic/one topic/two && git merge hold/linus <8>
@@ -249,51 +285,51 @@ $ git checkout maint
$ git cherry-pick master~4 <9>
$ compile/test
$ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
-$ git fetch ko && git show-branch master maint 'tags/ko-*' <11>
-$ git push ko <12>
-$ git push ko v0.99.9x <13>
+$ git fetch ko && for branch in master maint next pu <11>
+ do
+ git show-branch ko/$branch $branch <12>
+ done
+$ git push --follow-tags ko <13>
------------
+
-<1> see what I was in the middle of doing, if any.
-<2> see what topic branches I have and think about how ready
-they are.
+<1> see what you were in the middle of doing, if anything.
+<2> see which branches haven't been merged into `master` yet.
+Likewise for any other integration branches e.g. `maint`, `next`
+and `pu` (potential updates).
<3> read mails, save ones that are applicable, and save others
-that are not quite ready.
-<4> apply them, interactively, with my sign-offs.
-<5> create topic branch as needed and apply, again with my
-sign-offs.
+that are not quite ready (other mail readers are available).
+<4> apply them, interactively, with your sign-offs.
+<5> create topic branch as needed and apply, again with sign-offs.
<6> rebase internal topic branch that has not been merged to the
master or exposed as a part of a stable branch.
<7> restart `pu` every time from the next.
<8> and bundle topic branches still cooking.
<9> backport a critical fix.
<10> create a signed tag.
-<11> make sure I did not accidentally rewind master beyond what I
-already pushed out. `ko` shorthand points at the repository I have
-at kernel.org, and looks like this:
+<11> make sure master was not accidentally rewound beyond that
+already pushed out. `ko` shorthand points at the Git maintainer's
+repository at kernel.org, and looks like this:
+
------------
-$ cat .git/remotes/ko
-URL: kernel.org:/pub/scm/git/git.git
-Pull: master:refs/tags/ko-master
-Pull: next:refs/tags/ko-next
-Pull: maint:refs/tags/ko-maint
-Push: master
-Push: next
-Push: +pu
-Push: maint
+(in .git/config)
+[remote "ko"]
+ url = kernel.org:/pub/scm/git/git.git
+ fetch = refs/heads/*:refs/remotes/ko/*
+ push = refs/heads/master
+ push = refs/heads/next
+ push = +refs/heads/pu
+ push = refs/heads/maint
------------
+
-In the output from `git show-branch`, `master` should have
-everything `ko-master` has, and `next` should have
-everything `ko-next` has.
-
-<12> push out the bleeding edge.
-<13> push the tag out, too.
+<12> In the output from `git show-branch`, `master` should have
+everything `ko/master` has, and `next` should have
+everything `ko/next` has, etc.
+<13> push out the bleeding edge, together with new tags that point
+into the pushed history.
-Repository Administration[[Repository Administration]]
-------------------------------------------------------
+Repository Administration[[ADMINISTRATION]]
+-------------------------------------------
A repository administrator uses the following tools to set up
and maintain access to the repository by developers.
@@ -304,9 +340,19 @@ and maintain access to the repository by developers.
* linkgit:git-shell[1] can be used as a 'restricted login shell'
for shared central repository users.
+ * linkgit:git-http-backend[1] provides a server side implementation
+ of Git-over-HTTP ("Smart http") allowing both fetch and push services.
+
+ * linkgit:gitweb[1] provides a web front-end to Git repositories,
+ which can be set-up using the linkgit:git-instaweb[1] script.
+
link:howto/update-hook-example.html[update hook howto] has a good
example of managing a shared central repository.
+In addition there are a number of other widely deployed hosting, browsing
+and reviewing solutions such as:
+
+ * gitolite, gerrit code review, cgit and others.
Examples
~~~~~~~~
@@ -335,22 +381,25 @@ $ cat /etc/xinetd.d/git-daemon
# description: The Git server offers access to Git repositories
service git
{
- disable = no
- type = UNLISTED
- port = 9418
- socket_type = stream
- wait = no
- user = nobody
- server = /usr/bin/git-daemon
- server_args = --inetd --export-all --base-path=/pub/scm
- log_on_failure += USERID
+ disable = no
+ type = UNLISTED
+ port = 9418
+ socket_type = stream
+ wait = no
+ user = nobody
+ server = /usr/bin/git-daemon
+ server_args = --inetd --export-all --base-path=/pub/scm
+ log_on_failure += USERID
}
------------
+
Check your xinetd(8) documentation and setup, this is from a Fedora system.
Others might be different.
-Give push/pull only access to developers.::
+Give push/pull only access to developers using git-over-ssh.::
+
+e.g. those using:
+`$ git push/pull ssh://host.xz/pub/scm/project`
+
------------
$ grep git /etc/passwd <1>
@@ -363,8 +412,8 @@ $ grep git /etc/shells <2>
------------
+
<1> log-in shell is set to /usr/bin/git-shell, which does not
-allow anything but `git push` and `git pull`. The users should
-get an ssh access to the machine.
+allow anything but `git push` and `git pull`. The users require
+ssh access to the machine.
<2> in many distributions /etc/shells needs to list what is used
as the login shell.
@@ -401,13 +450,6 @@ for branch policy control.
david is the release manager and is the only person who can
create and push version tags.
-HTTP server to support dumb protocol transfer.::
-+
-------------
-dev$ git update-server-info <1>
-dev$ ftp user@isp.example.com <2>
-ftp> cp -r .git /home/user/myproject.git
-------------
-+
-<1> make sure your info/refs and objects/info/packs are up-to-date
-<2> upload to public HTTP server hosted by your ISP.
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/gitglossary.txt b/Documentation/gitglossary.txt
index e52de7dbb4..212e254adc 100644
--- a/Documentation/gitglossary.txt
+++ b/Documentation/gitglossary.txt
@@ -19,7 +19,7 @@ SEE ALSO
linkgit:gittutorial[7],
linkgit:gittutorial-2[7],
linkgit:gitcvs-migration[7],
-link:everyday.html[Everyday Git],
+linkgit:giteveryday[7],
link:user-manual.html[The Git User's Manual]
GIT
diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt
index 3109ea8aad..f6fbf814fb 100644
--- a/Documentation/gittutorial-2.txt
+++ b/Documentation/gittutorial-2.txt
@@ -403,7 +403,7 @@ What next?
At this point you should know everything necessary to read the man
pages for any of the git commands; one good place to start would be
-with the commands mentioned in link:everyday.html[Everyday Git]. You
+with the commands mentioned in linkgit:giteveryday[7]. You
should be able to find any unknown jargon in linkgit:gitglossary[7].
The link:user-manual.html[Git User's Manual] provides a more
@@ -427,7 +427,7 @@ linkgit:gitcvs-migration[7],
linkgit:gitcore-tutorial[7],
linkgit:gitglossary[7],
linkgit:git-help[1],
-link:everyday.html[Everyday Git],
+linkgit:giteveryday[7],
link:user-manual.html[The Git User's Manual]
GIT
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 8262196318..af9f709ccf 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -656,7 +656,7 @@ digressions that may be interesting at this point are:
* linkgit:gitworkflows[7]: Gives an overview of recommended
workflows.
- * link:everyday.html[Everyday Git with 20 Commands Or So]
+ * linkgit:giteveryday[7]: Everyday Git with 20 Commands Or So.
* linkgit:gitcvs-migration[7]: Git for CVS users.
@@ -668,7 +668,7 @@ linkgit:gitcore-tutorial[7],
linkgit:gitglossary[7],
linkgit:git-help[1],
linkgit:gitworkflows[7],
-link:everyday.html[Everyday Git],
+linkgit:giteveryday[7],
link:user-manual.html[The Git User's Manual]
GIT
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 85d63532a3..dcf7429a47 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -95,7 +95,7 @@ would show something like this:
The author of fe6e0ee was Junio C Hamano, 23 hours ago
The title was >>t4119: test autocomputing -p<n> for traditional diff input.<<
---------
+-------
+
The placeholders are:
@@ -115,19 +115,22 @@ The placeholders are:
- '%aD': author date, RFC2822 style
- '%ar': author date, relative
- '%at': author date, UNIX timestamp
-- '%ai': author date, ISO 8601 format
+- '%ai': author date, ISO 8601-like format
+- '%aI': author date, strict ISO 8601 format
- '%cn': committer name
- '%cN': committer name (respecting .mailmap, see
linkgit:git-shortlog[1] or linkgit:git-blame[1])
- '%ce': committer email
- '%cE': committer email (respecting .mailmap, see
linkgit:git-shortlog[1] or linkgit:git-blame[1])
-- '%cd': committer date
+- '%cd': committer date (format respects --date= option)
- '%cD': committer date, RFC2822 style
- '%cr': committer date, relative
- '%ct': committer date, UNIX timestamp
-- '%ci': committer date, ISO 8601 format
+- '%ci': committer date, ISO 8601-like format
+- '%cI': committer date, strict ISO 8601 format
- '%d': ref names, like the --decorate option of linkgit:git-log[1]
+- '%D': ref names without the " (", ")" wrapping.
- '%e': encoding
- '%s': subject
- '%f': sanitized subject line, suitable for a filename
@@ -182,8 +185,9 @@ The placeholders are:
NOTE: Some placeholders may depend on other options given to the
revision traversal engine. For example, the `%g*` reflog options will
insert an empty string unless we are traversing reflog entries (e.g., by
-`git log -g`). The `%d` placeholder will use the "short" decoration
-format if `--decorate` was not already provided on the command line.
+`git log -g`). The `%d` and `%D` placeholders will use the "short"
+decoration format if `--decorate` was not already provided on the command
+line.
If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
is inserted immediately before the expansion if and only if the
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index deb8cca917..3301fdebf0 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -168,6 +168,15 @@ respectively, and they must begin with `refs/` when applied to `--glob`
or `--all`. If a trailing '/{asterisk}' is intended, it must be given
explicitly.
+--reflog::
+ Pretend as if all objects mentioned by reflogs are listed on the
+ command line as `<commit>`.
+
+--indexed-objects::
+ Pretend as if all trees and blobs used by the index are listed
+ on the command line. Note that you probably want to use
+ `--objects`, too.
+
--ignore-missing::
Upon seeing an invalid object name in the input, pretend as if
the bad input was not given.
@@ -677,7 +686,7 @@ include::pretty-options.txt[]
--relative-date::
Synonym for `--date=relative`.
---date=(relative|local|default|iso|rfc|short|raw)::
+--date=(relative|local|default|iso|iso-strict|rfc|short|raw)::
Only takes effect for dates shown in human-readable format, such
as when using `--pretty`. `log.date` config variable sets a default
value for the log command's `--date` option.
@@ -687,7 +696,16 @@ e.g. ``2 hours ago''.
+
`--date=local` shows timestamps in user's local time zone.
+
-`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
+`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.
+The differences to the strict ISO 8601 format are:
+
+ - a space instead of the `T` date/time delimiter
+ - a space between time and time zone
+ - no colon between hours and minutes of the time zone
+
++
+`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict
+ISO 8601 format.
+
`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
format, often found in email messages.
diff --git a/Documentation/technical/api-allocation-growing.txt b/Documentation/technical/api-allocation-growing.txt
index 542946b1ba..5a59b54844 100644
--- a/Documentation/technical/api-allocation-growing.txt
+++ b/Documentation/technical/api-allocation-growing.txt
@@ -34,3 +34,6 @@ item[nr++] = value you like;
------------
You are responsible for updating the `nr` variable.
+
+If you need to specify the number of elements to allocate explicitly
+then use the macro `REALLOC_ARRAY(item, alloc)` instead of `ALLOC_GROW`.
diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt
index edd5018e15..0d8b99b368 100644
--- a/Documentation/technical/api-config.txt
+++ b/Documentation/technical/api-config.txt
@@ -77,6 +77,99 @@ To read a specific file in git-config format, use
`git_config_from_file`. This takes the same callback and data parameters
as `git_config`.
+Querying For Specific Variables
+-------------------------------
+
+For programs wanting to query for specific variables in a non-callback
+manner, the config API provides two functions `git_config_get_value`
+and `git_config_get_value_multi`. They both read values from an internal
+cache generated previously from reading the config files.
+
+`int git_config_get_value(const char *key, const char **value)`::
+
+ Finds the highest-priority value for the configuration variable `key`,
+ stores the pointer to it in `value` and returns 0. When the
+ configuration variable `key` is not found, returns 1 without touching
+ `value`. The caller should not free or modify `value`, as it is owned
+ by the cache.
+
+`const struct string_list *git_config_get_value_multi(const char *key)`::
+
+ Finds and returns the value list, sorted in order of increasing priority
+ for the configuration variable `key`. When the configuration variable
+ `key` is not found, returns NULL. The caller should not free or modify
+ the returned pointer, as it is owned by the cache.
+
+`void git_config_clear(void)`::
+
+ Resets and invalidates the config cache.
+
+The config API also provides type specific API functions which do conversion
+as well as retrieval for the queried variable, including:
+
+`int git_config_get_int(const char *key, int *dest)`::
+
+ Finds and parses the value to an integer for the configuration variable
+ `key`. Dies on error; otherwise, stores the value of the parsed integer in
+ `dest` and returns 0. When the configuration variable `key` is not found,
+ returns 1 without touching `dest`.
+
+`int git_config_get_ulong(const char *key, unsigned long *dest)`::
+
+ Similar to `git_config_get_int` but for unsigned longs.
+
+`int git_config_get_bool(const char *key, int *dest)`::
+
+ Finds and parses the value into a boolean value, for the configuration
+ variable `key` respecting keywords like "true" and "false". Integer
+ values are converted into true/false values (when they are non-zero or
+ zero, respectively). Other values cause a die(). If parsing is successful,
+ stores the value of the parsed result in `dest` and returns 0. When the
+ configuration variable `key` is not found, returns 1 without touching
+ `dest`.
+
+`int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)`::
+
+ Similar to `git_config_get_bool`, except that integers are copied as-is,
+ and `is_bool` flag is unset.
+
+`int git_config_get_maybe_bool(const char *key, int *dest)`::
+
+ Similar to `git_config_get_bool`, except that it returns -1 on error
+ rather than dying.
+
+`int git_config_get_string_const(const char *key, const char **dest)`::
+
+ Allocates and copies the retrieved string into the `dest` parameter for
+ the configuration variable `key`; if NULL string is given, prints an
+ error message and returns -1. When the configuration variable `key` is
+ not found, returns 1 without touching `dest`.
+
+`int git_config_get_string(const char *key, char **dest)`::
+
+ Similar to `git_config_get_string_const`, except that retrieved value
+ copied into the `dest` parameter is a mutable string.
+
+`int git_config_get_pathname(const char *key, const char **dest)`::
+
+ Similar to `git_config_get_string`, but expands `~` or `~user` into
+ the user's home directory when found at the beginning of the path.
+
+`git_die_config(const char *key, const char *err, ...)`::
+
+ First prints the error message specified by the caller in `err` and then
+ dies printing the line number and the file name of the highest priority
+ value for the configuration variable `key`.
+
+`void git_die_config_linenr(const char *key, const char *filename, int linenr)`::
+
+ Helper function which formats the die error message according to the
+ parameters entered. Used by `git_die_config()`. It can be used by callers
+ handling `git_config_get_value_multi()` to print the correct error message
+ for the desired value.
+
+See test-config.c for usage examples.
+
Value Parsing Helpers
---------------------
@@ -134,6 +227,68 @@ int read_file_with_include(const char *file, config_fn_t fn, void *data)
`git_config` respects includes automatically. The lower-level
`git_config_from_file` does not.
+Custom Configsets
+-----------------
+
+A `config_set` can be used to construct an in-memory cache for
+config-like files that the caller specifies (i.e., files like `.gitmodules`,
+`~/.gitconfig` etc.). For example,
+
+---------------------------------------
+struct config_set gm_config;
+git_configset_init(&gm_config);
+int b;
+/* we add config files to the config_set */
+git_configset_add_file(&gm_config, ".gitmodules");
+git_configset_add_file(&gm_config, ".gitmodules_alt");
+
+if (!git_configset_get_bool(gm_config, "submodule.frotz.ignore", &b)) {
+ /* hack hack hack */
+}
+
+/* when we are done with the configset */
+git_configset_clear(&gm_config);
+----------------------------------------
+
+Configset API provides functions for the above mentioned work flow, including:
+
+`void git_configset_init(struct config_set *cs)`::
+
+ Initializes the config_set `cs`.
+
+`int git_configset_add_file(struct config_set *cs, const char *filename)`::
+
+ Parses the file and adds the variable-value pairs to the `config_set`,
+ dies if there is an error in parsing the file. Returns 0 on success, or
+ -1 if the file does not exist or is inaccessible. The user has to decide
+ if he wants to free the incomplete configset or continue using it when
+ the function returns -1.
+
+`int git_configset_get_value(struct config_set *cs, const char *key, const char **value)`::
+
+ Finds the highest-priority value for the configuration variable `key`
+ and config set `cs`, stores the pointer to it in `value` and returns 0.
+ When the configuration variable `key` is not found, returns 1 without
+ touching `value`. The caller should not free or modify `value`, as it
+ is owned by the cache.
+
+`const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)`::
+
+ Finds and returns the value list, sorted in order of increasing priority
+ for the configuration variable `key` and config set `cs`. When the
+ configuration variable `key` is not found, returns NULL. The caller
+ should not free or modify the returned pointer, as it is owned by the cache.
+
+`void git_configset_clear(struct config_set *cs)`::
+
+ Clears `config_set` structure, removes all saved variable-value pairs.
+
+In addition to above functions, the `config_set` API provides type specific
+functions in the vein of `git_config_get_int` and family but with an extra
+parameter, pointer to struct `config_set`.
+They all behave similarly to the `git_config_get*()` family described in
+"Querying For Specific Variables" above.
+
Writing Config Files
--------------------
diff --git a/Documentation/technical/api-lockfile.txt b/Documentation/technical/api-lockfile.txt
index dd894043ae..93b5f23e4c 100644
--- a/Documentation/technical/api-lockfile.txt
+++ b/Documentation/technical/api-lockfile.txt
@@ -3,20 +3,132 @@ lockfile API
The lockfile API serves two purposes:
-* Mutual exclusion. When we write out a new index file, first
- we create a new file `$GIT_DIR/index.lock`, write the new
- contents into it, and rename it to the final destination
- `$GIT_DIR/index`. We try to create the `$GIT_DIR/index.lock`
- file with O_EXCL so that we can notice and fail when somebody
- else is already trying to update the index file.
-
-* Automatic cruft removal. After we create the "lock" file, we
- may decide to `die()`, and we would want to make sure that we
- remove the file that has not been committed to its final
- destination. This is done by remembering the lockfiles we
- created in a linked list and cleaning them up from an
- `atexit(3)` handler. Outstanding lockfiles are also removed
- when the program dies on a signal.
+* Mutual exclusion and atomic file updates. When we want to change a
+ file, we create a lockfile `<filename>.lock`, write the new file
+ contents into it, and then rename the lockfile to its final
+ destination `<filename>`. We create the `<filename>.lock` file with
+ `O_CREAT|O_EXCL` so that we can notice and fail if somebody else has
+ already locked the file, then atomically rename the lockfile to its
+ final destination to commit the changes and unlock the file.
+
+* Automatic cruft removal. If the program exits after we lock a file
+ but before the changes have been committed, we want to make sure
+ that we remove the lockfile. This is done by remembering the
+ lockfiles we have created in a linked list and setting up an
+ `atexit(3)` handler and a signal handler that clean up the
+ lockfiles. This mechanism ensures that outstanding lockfiles are
+ cleaned up if the program exits (including when `die()` is called)
+ or if the program dies on a signal.
+
+Please note that lockfiles only block other writers. Readers do not
+block, but they are guaranteed to see either the old contents of the
+file or the new contents of the file (assuming that the filesystem
+implements `rename(2)` atomically).
+
+
+Calling sequence
+----------------
+
+The caller:
+
+* Allocates a `struct lock_file` either as a static variable or on the
+ heap, initialized to zeros. Once you use the structure to call the
+ `hold_lock_file_*` family of functions, it belongs to the lockfile
+ subsystem and its storage must remain valid throughout the life of
+ the program (i.e. you cannot use an on-stack variable to hold this
+ structure).
+
+* Attempts to create a lockfile by passing that variable and the path
+ of the final destination (e.g. `$GIT_DIR/index`) to
+ `hold_lock_file_for_update` or `hold_lock_file_for_append`.
+
+* Writes new content for the destination file by either:
+
+ * writing to the file descriptor returned by the `hold_lock_file_*`
+ functions (also available via `lock->fd`).
+
+ * calling `fdopen_lock_file` to get a `FILE` pointer for the open
+ file and writing to the file using stdio.
+
+When finished writing, the caller can:
+
+* Close the file descriptor and rename the lockfile to its final
+ destination by calling `commit_lock_file` or `commit_lock_file_to`.
+
+* Close the file descriptor and remove the lockfile by calling
+ `rollback_lock_file`.
+
+* Close the file descriptor without removing or renaming the lockfile
+ by calling `close_lock_file`, and later call `commit_lock_file`,
+ `commit_lock_file_to`, `rollback_lock_file`, or `reopen_lock_file`.
+
+Even after the lockfile is committed or rolled back, the `lock_file`
+object must not be freed or altered by the caller. However, it may be
+reused; just pass it to another call of `hold_lock_file_for_update` or
+`hold_lock_file_for_append`.
+
+If the program exits before you have called one of `commit_lock_file`,
+`commit_lock_file_to`, `rollback_lock_file`, or `close_lock_file`, an
+`atexit(3)` handler will close and remove the lockfile, rolling back
+any uncommitted changes.
+
+If you need to close the file descriptor you obtained from a
+`hold_lock_file_*` function yourself, do so by calling
+`close_lock_file`. You should never call `close(2)` or `fclose(3)`
+yourself! Otherwise the `struct lock_file` structure would still think
+that the file descriptor needs to be closed, and a commit or rollback
+would result in duplicate calls to `close(2)`. Worse yet, if you close
+and then later open another file descriptor for a completely different
+purpose, then a commit or rollback might close that unrelated file
+descriptor.
+
+
+Error handling
+--------------
+
+The `hold_lock_file_*` functions return a file descriptor on success
+or -1 on failure (unless `LOCK_DIE_ON_ERROR` is used; see below). On
+errors, `errno` describes the reason for failure. Errors can be
+reported by passing `errno` to one of the following helper functions:
+
+unable_to_lock_message::
+
+ Append an appropriate error message to a `strbuf`.
+
+unable_to_lock_error::
+
+ Emit an appropriate error message using `error()`.
+
+unable_to_lock_die::
+
+ Emit an appropriate error message and `die()`.
+
+Similarly, `commit_lock_file`, `commit_lock_file_to`, and
+`close_lock_file` return 0 on success. On failure they set `errno`
+appropriately, do their best to roll back the lockfile, and return -1.
+
+
+Flags
+-----
+
+The following flags can be passed to `hold_lock_file_for_update` or
+`hold_lock_file_for_append`:
+
+LOCK_NO_DEREF::
+
+ Usually symbolic links in the destination path are resolved
+ and the lockfile is created by adding ".lock" to the resolved
+ path. If `LOCK_NO_DEREF` is set, then the lockfile is created
+ by adding ".lock" to the path argument itself. This option is
+ used, for example, when locking a symbolic reference, which
+ for backwards-compatibility reasons can be a symbolic link
+ containing the name of the referred-to-reference.
+
+LOCK_DIE_ON_ERROR::
+
+ If a lock is already taken for the file, `die()` with an error
+ message. If this option is not specified, trying to lock a
+ file that is already locked returns -1 to the caller.
The functions
@@ -24,51 +136,85 @@ The functions
hold_lock_file_for_update::
- Take a pointer to `struct lock_file`, the filename of
- the final destination (e.g. `$GIT_DIR/index`) and a flag
- `die_on_error`. Attempt to create a lockfile for the
- destination and return the file descriptor for writing
- to the file. If `die_on_error` flag is true, it dies if
- a lock is already taken for the file; otherwise it
- returns a negative integer to the caller on failure.
+ Take a pointer to `struct lock_file`, the path of the file to
+ be locked (e.g. `$GIT_DIR/index`) and a flags argument (see
+ above). Attempt to create a lockfile for the destination and
+ return the file descriptor for writing to the file.
+
+hold_lock_file_for_append::
+
+ Like `hold_lock_file_for_update`, but before returning copy
+ the existing contents of the file (if any) to the lockfile and
+ position its write pointer at the end of the file.
+
+fdopen_lock_file::
+
+ Associate a stdio stream with the lockfile. Return NULL
+ (*without* rolling back the lockfile) on error. The stream is
+ closed automatically when `close_lock_file` is called or when
+ the file is committed or rolled back.
+
+get_locked_file_path::
+
+ Return the path of the file that is locked by the specified
+ lock_file object. The caller must free the memory.
commit_lock_file::
- Take a pointer to the `struct lock_file` initialized
- with an earlier call to `hold_lock_file_for_update()`,
- close the file descriptor and rename the lockfile to its
- final destination. Returns 0 upon success, a negative
- value on failure to close(2) or rename(2).
+ Take a pointer to the `struct lock_file` initialized with an
+ earlier call to `hold_lock_file_for_update` or
+ `hold_lock_file_for_append`, close the file descriptor, and
+ rename the lockfile to its final destination. Return 0 upon
+ success. On failure, roll back the lock file and return -1,
+ with `errno` set to the value from the failing call to
+ `close(2)` or `rename(2)`. It is a bug to call
+ `commit_lock_file` for a `lock_file` object that is not
+ currently locked.
+
+commit_lock_file_to::
+
+ Like `commit_lock_file()`, except that it takes an explicit
+ `path` argument to which the lockfile should be renamed. The
+ `path` must be on the same filesystem as the lock file.
rollback_lock_file::
- Take a pointer to the `struct lock_file` initialized
- with an earlier call to `hold_lock_file_for_update()`,
- close the file descriptor and remove the lockfile.
+ Take a pointer to the `struct lock_file` initialized with an
+ earlier call to `hold_lock_file_for_update` or
+ `hold_lock_file_for_append`, close the file descriptor and
+ remove the lockfile. It is a NOOP to call
+ `rollback_lock_file()` for a `lock_file` object that has
+ already been committed or rolled back.
close_lock_file::
- Take a pointer to the `struct lock_file` initialized
- with an earlier call to `hold_lock_file_for_update()`,
- and close the file descriptor. Returns 0 upon success,
- a negative value on failure to close(2).
-
-Because the structure is used in an `atexit(3)` handler, its
-storage has to stay throughout the life of the program. It
-cannot be an auto variable allocated on the stack.
-
-Call `commit_lock_file()` or `rollback_lock_file()` when you are
-done writing to the file descriptor. If you do not call either
-and simply `exit(3)` from the program, an `atexit(3)` handler
-will close and remove the lockfile.
-
-If you need to close the file descriptor you obtained from
-`hold_lock_file_for_update` function yourself, do so by calling
-`close_lock_file()`. You should never call `close(2)` yourself!
-Otherwise the `struct
-lock_file` structure still remembers that the file descriptor
-needs to be closed, and a later call to `commit_lock_file()` or
-`rollback_lock_file()` will result in duplicate calls to
-`close(2)`. Worse yet, if you `close(2)`, open another file
-descriptor for completely different purpose, and then call
-`commit_lock_file()` or `rollback_lock_file()`, they may close
-that unrelated file descriptor.
+
+ Take a pointer to the `struct lock_file` initialized with an
+ earlier call to `hold_lock_file_for_update` or
+ `hold_lock_file_for_append`. Close the file descriptor (and
+ the file pointer if it has been opened using
+ `fdopen_lock_file`). Return 0 upon success. On failure to
+ `close(2)`, return a negative value and roll back the lock
+ file. Usually `commit_lock_file`, `commit_lock_file_to`, or
+ `rollback_lock_file` should eventually be called if
+ `close_lock_file` succeeds.
+
+reopen_lock_file::
+
+ Re-open a lockfile that has been closed (using
+ `close_lock_file`) but not yet committed or rolled back. This
+ can be used to implement a sequence of operations like the
+ following:
+
+ * Lock file.
+
+ * Write new contents to lockfile, then `close_lock_file` to
+ cause the contents to be written to disk.
+
+ * Pass the name of the lockfile to another program to allow it
+ (and nobody else) to inspect the contents you wrote, while
+ still holding the lock yourself.
+
+ * `reopen_lock_file` to reopen the lockfile. Make further
+ updates to the contents.
+
+ * `commit_lock_file` to make the final version permanent.
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 69510ae57a..3f12fcdd4c 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -13,6 +13,10 @@ produces in the caller in order to process it.
Functions
---------
+`child_process_init`
+
+ Initialize a struct child_process variable.
+
`start_command`::
Start a sub-process. Takes a pointer to a `struct child_process`
@@ -96,8 +100,8 @@ command to run in a sub-process.
The caller:
-1. allocates and clears (memset(&chld, 0, sizeof(chld));) a
- struct child_process variable;
+1. allocates and clears (using child_process_init() or
+ CHILD_PROCESS_INIT) a struct child_process variable;
2. initializes the members;
3. calls start_command();
4. processes the data;
@@ -165,6 +169,11 @@ string pointers (NULL terminated) in .env:
. If the string does not contain '=', it names an environment
variable that will be removed from the child process's environment.
+If the .env member is NULL, `start_command` will point it at the
+.env_array `argv_array` (so you may use one or the other, but not both).
+The memory in .env_array will be cleaned up automatically during
+`finish_command` (or during `start_command` when it is unsuccessful).
+
To specify a new initial working directory for the sub-process,
specify it in the .dir member.
diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt
index f9c06a7573..cca6543234 100644
--- a/Documentation/technical/api-strbuf.txt
+++ b/Documentation/technical/api-strbuf.txt
@@ -160,6 +160,10 @@ then they will free() it.
Add a single character to the buffer.
+`strbuf_addchars`::
+
+ Add a character the specified number of times to the buffer.
+
`strbuf_insert`::
Insert data to the given position of the buffer. The remaining contents
@@ -307,6 +311,16 @@ same behaviour as well.
use it unless you need the correct position in the file
descriptor.
+`strbuf_getcwd`::
+
+ Set the buffer to the path of the current working directory.
+
+`strbuf_add_absolute_path`
+
+ Add a path to a buffer, converting a relative path to an
+ absolute one in the process. Symbolic links are not
+ resolved.
+
`stripspace`::
Strip whitespace from a buffer. The second parameter controls if
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index 569c48a352..462e20645f 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -212,9 +212,9 @@ out of what the server said it could do with the first 'want' line.
want-list = first-want
*additional-want
- shallow-line = PKT_LINE("shallow" SP obj-id)
+ shallow-line = PKT-LINE("shallow" SP obj-id)
- depth-request = PKT_LINE("deepen" SP depth)
+ depth-request = PKT-LINE("deepen" SP depth)
first-want = PKT-LINE("want" SP obj-id SP capability-list LF)
additional-want = PKT-LINE("want" SP obj-id LF)
@@ -465,7 +465,7 @@ contain all the objects that the server will need to complete the new
references.
----
- update-request = *shallow command-list [pack-file]
+ update-request = *shallow ( command-list | push-cert ) [pack-file]
shallow = PKT-LINE("shallow" SP obj-id LF)
@@ -481,12 +481,27 @@ references.
old-id = obj-id
new-id = obj-id
+ push-cert = PKT-LINE("push-cert" NUL capability-list LF)
+ PKT-LINE("certificate version 0.1" LF)
+ PKT-LINE("pusher" SP ident LF)
+ PKT-LINE("pushee" SP url LF)
+ PKT-LINE("nonce" SP nonce LF)
+ PKT-LINE(LF)
+ *PKT-LINE(command LF)
+ *PKT-LINE(gpg-signature-lines LF)
+ PKT-LINE("push-cert-end" LF)
+
pack-file = "PACK" 28*(OCTET)
----
If the receiving end does not support delete-refs, the sending end MUST
NOT ask for delete command.
+If the receiving end does not support push-cert, the sending end
+MUST NOT send a push-cert command. When a push-cert command is
+sent, command-list MUST NOT be sent; the commands recorded in the
+push certificate is used instead.
+
The pack-file MUST NOT be sent if the only command used is 'delete'.
A pack-file MUST be sent if either create or update command is used,
@@ -501,6 +516,34 @@ was being processed (the obj-id is still the same as the old-id), and
it will run any update hooks to make sure that the update is acceptable.
If all of that is fine, the server will then update the references.
+Push Certificate
+----------------
+
+A push certificate begins with a set of header lines. After the
+header and an empty line, the protocol commands follow, one per
+line.
+
+Currently, the following header fields are defined:
+
+`pusher` ident::
+ Identify the GPG key in "Human Readable Name <email@address>"
+ format.
+
+`pushee` url::
+ The repository URL (anonymized, if the URL contains
+ authentication material) the user who ran `git push`
+ intended to push into.
+
+`nonce` nonce::
+ The 'nonce' string the receiving repository asked the
+ pushing user to include in the certificate, to prevent
+ replay attacks.
+
+The GPG signature lines are a detached signature for the contents
+recorded in the push certificate before the signature block begins.
+The detached signature is used to certify that the commands were
+given by the pusher, who must be the signer.
+
Report Status
-------------
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index e174343847..0c92deebcc 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -18,8 +18,8 @@ was sent. Server MUST NOT ignore capabilities that client requested
and server advertised. As a consequence of these rules, server MUST
NOT advertise capabilities it does not understand.
-The 'report-status', 'delete-refs', and 'quiet' capabilities are sent and
-recognized by the receive-pack (push to server) process.
+The 'report-status', 'delete-refs', 'quiet', and 'push-cert' capabilities
+are sent and recognized by the receive-pack (push to server) process.
The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
by both upload-pack and receive-pack protocols. The 'agent' capability
@@ -250,3 +250,12 @@ allow-tip-sha1-in-want
If the upload-pack server advertises this capability, fetch-pack may
send "want" lines with SHA-1s that exist at the server but are not
advertised by upload-pack.
+
+push-cert=<nonce>
+-----------------
+
+The receive-pack server that advertises this capability is willing
+to accept a signed push certificate, and asks the <nonce> to be
+included in the push certificate. A send-pack client MUST NOT
+send a push-cert packet unless the receive-pack server advertises
+this capability.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 14b866e9b3..153d55d2b9 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.1.2
+DEF_VER=v2.1.0.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 9f984a9e55..827006ba61 100644
--- a/Makefile
+++ b/Makefile
@@ -14,11 +14,11 @@ all::
# Define INLINE to a suitable substitute (such as '__inline' or '') if git
# fails to compile with errors about undefined inline functions or similar.
#
-# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
+# Define SNPRINTF_RETURNS_BOGUS if you are on a system which snprintf()
# or vsnprintf() return -1 instead of number of characters which would
# have been written to the final string if enough space had been available.
#
-# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# Define FREAD_READS_DIRECTORIES if you are on a system which succeeds
# when attempting to read from an fopen'ed directory.
#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
@@ -317,9 +317,6 @@ all::
# 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.
-#
# Define NATIVE_CRLF if your platform uses CRLF for line endings.
#
# Define XDL_FAST_HASH to use an alternative line-hashing method in
@@ -432,7 +429,6 @@ XDIFF_OBJS =
VCSSVN_OBJS =
GENERATED_H =
EXTRA_CPPFLAGS =
-LIB_H =
LIB_OBJS =
PROGRAM_OBJS =
PROGRAMS =
@@ -551,6 +547,7 @@ PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
TEST_PROGRAMS_NEED_X += test-chmtime
TEST_PROGRAMS_NEED_X += test-ctype
+TEST_PROGRAMS_NEED_X += test-config
TEST_PROGRAMS_NEED_X += test-date
TEST_PROGRAMS_NEED_X += test-delta
TEST_PROGRAMS_NEED_X += test-dump-cache-tree
@@ -571,6 +568,7 @@ TEST_PROGRAMS_NEED_X += test-revision-walking
TEST_PROGRAMS_NEED_X += test-run-command
TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
TEST_PROGRAMS_NEED_X += test-sha1
+TEST_PROGRAMS_NEED_X += test-sha1-array
TEST_PROGRAMS_NEED_X += test-sigchain
TEST_PROGRAMS_NEED_X += test-string-list
TEST_PROGRAMS_NEED_X += test-subprocess
@@ -631,131 +629,11 @@ VCSSVN_LIB = vcs-svn/lib.a
GENERATED_H += common-cmds.h
-LIB_H += advice.h
-LIB_H += archive.h
-LIB_H += argv-array.h
-LIB_H += attr.h
-LIB_H += bisect.h
-LIB_H += blob.h
-LIB_H += branch.h
-LIB_H += builtin.h
-LIB_H += bulk-checkin.h
-LIB_H += bundle.h
-LIB_H += cache-tree.h
-LIB_H += cache.h
-LIB_H += color.h
-LIB_H += column.h
-LIB_H += commit.h
-LIB_H += compat/bswap.h
-LIB_H += compat/mingw.h
-LIB_H += compat/obstack.h
-LIB_H += compat/poll/poll.h
-LIB_H += compat/precompose_utf8.h
-LIB_H += compat/terminal.h
-LIB_H += compat/win32/dirent.h
-LIB_H += compat/win32/pthread.h
-LIB_H += compat/win32/syslog.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
-LIB_H += diff.h
-LIB_H += diffcore.h
-LIB_H += dir.h
-LIB_H += exec_cmd.h
-LIB_H += ewah/ewok.h
-LIB_H += ewah/ewok_rlw.h
-LIB_H += fetch-pack.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 += hashmap.h
-LIB_H += help.h
-LIB_H += http.h
-LIB_H += kwset.h
-LIB_H += levenshtein.h
-LIB_H += line-log.h
-LIB_H += line-range.h
-LIB_H += list-objects.h
-LIB_H += ll-merge.h
-LIB_H += log-tree.h
-LIB_H += mailmap.h
-LIB_H += merge-blobs.h
-LIB_H += merge-recursive.h
-LIB_H += mergesort.h
-LIB_H += notes-cache.h
-LIB_H += notes-merge.h
-LIB_H += notes-utils.h
-LIB_H += notes.h
-LIB_H += object.h
-LIB_H += pack-objects.h
-LIB_H += pack-revindex.h
-LIB_H += pack.h
-LIB_H += pack-bitmap.h
-LIB_H += parse-options.h
-LIB_H += patch-ids.h
-LIB_H += pathspec.h
-LIB_H += pkt-line.h
-LIB_H += prio-queue.h
-LIB_H += progress.h
-LIB_H += prompt.h
-LIB_H += quote.h
-LIB_H += reachable.h
-LIB_H += reflog-walk.h
-LIB_H += refs.h
-LIB_H += remote.h
-LIB_H += rerere.h
-LIB_H += resolve-undo.h
-LIB_H += revision.h
-LIB_H += run-command.h
-LIB_H += send-pack.h
-LIB_H += sequencer.h
-LIB_H += sha1-array.h
-LIB_H += sha1-lookup.h
-LIB_H += shortlog.h
-LIB_H += sideband.h
-LIB_H += sigchain.h
-LIB_H += strbuf.h
-LIB_H += streaming.h
-LIB_H += string-list.h
-LIB_H += submodule.h
-LIB_H += tag.h
-LIB_H += tar.h
-LIB_H += thread-utils.h
-LIB_H += transport.h
-LIB_H += tree-walk.h
-LIB_H += tree.h
-LIB_H += unpack-trees.h
-LIB_H += unicode_width.h
-LIB_H += url.h
-LIB_H += urlmatch.h
-LIB_H += userdiff.h
-LIB_H += utf8.h
-LIB_H += varint.h
-LIB_H += vcs-svn/fast_export.h
-LIB_H += vcs-svn/line_buffer.h
-LIB_H += vcs-svn/repo_tree.h
-LIB_H += vcs-svn/sliding_window.h
-LIB_H += vcs-svn/svndiff.h
-LIB_H += vcs-svn/svndump.h
-LIB_H += walker.h
-LIB_H += wildmatch.h
-LIB_H += wt-status.h
-LIB_H += xdiff-interface.h
-LIB_H += xdiff/xdiff.h
-LIB_H += xdiff/xdiffi.h
-LIB_H += xdiff/xemit.h
-LIB_H += xdiff/xinclude.h
-LIB_H += xdiff/xmacros.h
-LIB_H += xdiff/xprepare.h
-LIB_H += xdiff/xtypes.h
-LIB_H += xdiff/xutils.h
+LIB_H = $(shell $(FIND) . \
+ -name .git -prune -o \
+ -name t -prune -o \
+ -name Documentation -prune -o \
+ -name '*.h' -print)
LIB_OBJS += abspath.o
LIB_OBJS += advice.o
@@ -886,6 +764,7 @@ LIB_OBJS += submodule.o
LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
LIB_OBJS += trace.o
+LIB_OBJS += trailer.o
LIB_OBJS += transport.o
LIB_OBJS += transport-helper.o
LIB_OBJS += tree-diff.o
@@ -950,6 +829,7 @@ BUILTIN_OBJS += builtin/hash-object.o
BUILTIN_OBJS += builtin/help.o
BUILTIN_OBJS += builtin/index-pack.o
BUILTIN_OBJS += builtin/init-db.o
+BUILTIN_OBJS += builtin/interpret-trailers.o
BUILTIN_OBJS += builtin/log.o
BUILTIN_OBJS += builtin/ls-files.o
BUILTIN_OBJS += builtin/ls-remote.o
@@ -1025,11 +905,6 @@ sysconfdir = etc
endif
endif
-ifdef CHECK_HEADER_DEPENDENCIES
-COMPUTE_HEADER_DEPENDENCIES = no
-USE_COMPUTED_HEADER_DEPENDENCIES =
-endif
-
ifndef COMPUTE_HEADER_DEPENDENCIES
COMPUTE_HEADER_DEPENDENCIES = auto
endif
@@ -1381,7 +1256,6 @@ ifdef NO_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
@@ -1405,12 +1279,10 @@ endif
ifdef BLK_SHA1
SHA1_HEADER = "block-sha1/sha1.h"
LIB_OBJS += block-sha1/sha1.o
- LIB_H += block-sha1/sha1.h
else
ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h"
LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
- LIB_H += ppc/sha1.h
else
ifdef APPLE_COMMON_CRYPTO
COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
@@ -1479,6 +1351,9 @@ ifdef NO_REGEX
COMPAT_CFLAGS += -Icompat/regex
COMPAT_OBJS += compat/regex/regex.o
endif
+ifdef NATIVE_CRLF
+ BASIC_CFLAGS += -DNATIVE_CRLF
+endif
ifdef USE_NED_ALLOCATOR
COMPAT_CFLAGS += -Icompat/nedmalloc
@@ -1735,8 +1610,8 @@ git.sp git.s git.o: EXTRA_CPPFLAGS = \
'-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
- $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
- $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) git.o \
+ $(BUILTIN_OBJS) $(LIBS)
help.sp help.s help.o: common-cmds.h
@@ -1937,29 +1812,13 @@ $(dep_dirs):
missing_dep_dirs := $(filter-out $(wildcard $(dep_dirs)),$(dep_dirs))
dep_file = $(dir $@).depend/$(notdir $@).d
dep_args = -MF $(dep_file) -MQ $@ -MMD -MP
-ifdef CHECK_HEADER_DEPENDENCIES
-$(error cannot compute header dependencies outside a normal build. \
-Please unset CHECK_HEADER_DEPENDENCIES and try again)
-endif
endif
ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
-ifndef CHECK_HEADER_DEPENDENCIES
dep_dirs =
missing_dep_dirs =
dep_args =
endif
-endif
-
-ifdef CHECK_HEADER_DEPENDENCIES
-ifndef PRINT_HEADER_DEPENDENCIES
-missing_deps = $(filter-out $(notdir $^), \
- $(notdir $(shell $(MAKE) -s $@ \
- CHECK_HEADER_DEPENDENCIES=YesPlease \
- USE_COMPUTED_HEADER_DEPENDENCIES=YesPlease \
- PRINT_HEADER_DEPENDENCIES=YesPlease)))
-endif
-endif
ASM_SRC := $(wildcard $(OBJECTS:o=S))
ASM_OBJ := $(ASM_SRC:S=o)
@@ -1967,45 +1826,10 @@ C_OBJ := $(filter-out $(ASM_OBJ),$(OBJECTS))
.SUFFIXES:
-ifdef PRINT_HEADER_DEPENDENCIES
-$(C_OBJ): %.o: %.c FORCE
- echo $^
-$(ASM_OBJ): %.o: %.S FORCE
- echo $^
-
-ifndef CHECK_HEADER_DEPENDENCIES
-$(error cannot print header dependencies during a normal build. \
-Please set CHECK_HEADER_DEPENDENCIES and try again)
-endif
-endif
-
-ifndef PRINT_HEADER_DEPENDENCIES
-ifdef CHECK_HEADER_DEPENDENCIES
-$(C_OBJ): %.o: %.c $(dep_files) FORCE
- @set -e; echo CHECK $@; \
- missing_deps="$(missing_deps)"; \
- if test "$$missing_deps"; \
- then \
- echo missing dependencies: $$missing_deps; \
- false; \
- fi
-$(ASM_OBJ): %.o: %.S $(dep_files) FORCE
- @set -e; echo CHECK $@; \
- missing_deps="$(missing_deps)"; \
- if test "$$missing_deps"; \
- then \
- echo missing dependencies: $$missing_deps; \
- false; \
- fi
-endif
-endif
-
-ifndef CHECK_HEADER_DEPENDENCIES
$(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs)
$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
$(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs)
$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
-endif
%.s: %.c GIT-CFLAGS FORCE
$(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
@@ -2132,9 +1956,9 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
--keyword=gettextln --keyword=eval_gettextln
XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
-LOCALIZED_C := $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
-LOCALIZED_SH := $(SCRIPT_SH)
-LOCALIZED_PERL := $(SCRIPT_PERL)
+LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
+LOCALIZED_SH = $(SCRIPT_SH)
+LOCALIZED_PERL = $(SCRIPT_PERL)
ifdef XGETTEXT_INCLUDE_TESTS
LOCALIZED_C += t/t0200/test.c
@@ -2142,7 +1966,7 @@ LOCALIZED_SH += t/t0200/test.sh
LOCALIZED_PERL += t/t0200/test.perl
endif
-po/git.pot: $(LOCALIZED_C)
+po/git.pot: $(GENERATED_H) FORCE
$(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C)
$(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_SH) \
$(LOCALIZED_SH)
diff --git a/README b/README
index 15a8e23501..1083735d1c 100644
--- a/README
+++ b/README
@@ -27,7 +27,7 @@ Torvalds with help of a group of hackers around the net.
Please read the file INSTALL for installation instructions.
See Documentation/gittutorial.txt to get started, then see
-Documentation/everyday.txt for a useful minimum set of commands, and
+Documentation/giteveryday.txt for a useful minimum set of commands, and
Documentation/git-commandname.txt for documentation of each command.
If git has been correctly installed, then the tutorial can also be
read with "man gittutorial" or "git help tutorial", and the
diff --git a/RelNotes b/RelNotes
index f2706be6c1..c473b35ad9 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.1.2.txt \ No newline at end of file
+Documentation/RelNotes/2.2.0.txt \ No newline at end of file
diff --git a/abspath.c b/abspath.c
index ca33558a91..5edb4e7816 100644
--- a/abspath.c
+++ b/abspath.c
@@ -33,7 +33,7 @@ int is_directory(const char *path)
*/
static const char *real_path_internal(const char *path, int die_on_error)
{
- static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
+ static struct strbuf sb = STRBUF_INIT;
char *retval = NULL;
/*
@@ -41,16 +41,14 @@ static const char *real_path_internal(const char *path, int die_on_error)
* here so that we can chdir() back to it at the end of the
* function:
*/
- char cwd[1024] = "";
-
- int buf_index = 1;
+ struct strbuf cwd = STRBUF_INIT;
int depth = MAXDEPTH;
char *last_elem = NULL;
struct stat st;
/* We've already done it */
- if (path == buf || path == next_buf)
+ if (path == sb.buf)
return path;
if (!*path) {
@@ -60,41 +58,38 @@ static const char *real_path_internal(const char *path, int die_on_error)
goto error_out;
}
- if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) {
- if (die_on_error)
- die("Too long path: %.*s", 60, path);
- else
- goto error_out;
- }
+ strbuf_reset(&sb);
+ strbuf_addstr(&sb, path);
while (depth--) {
- if (!is_directory(buf)) {
- char *last_slash = find_last_dir_sep(buf);
+ if (!is_directory(sb.buf)) {
+ char *last_slash = find_last_dir_sep(sb.buf);
if (last_slash) {
last_elem = xstrdup(last_slash + 1);
- last_slash[1] = '\0';
+ strbuf_setlen(&sb, last_slash - sb.buf + 1);
} else {
- last_elem = xstrdup(buf);
- *buf = '\0';
+ last_elem = xmemdupz(sb.buf, sb.len);
+ strbuf_reset(&sb);
}
}
- if (*buf) {
- if (!*cwd && !getcwd(cwd, sizeof(cwd))) {
+ if (sb.len) {
+ if (!cwd.len && strbuf_getcwd(&cwd)) {
if (die_on_error)
die_errno("Could not get current working directory");
else
goto error_out;
}
- if (chdir(buf)) {
+ if (chdir(sb.buf)) {
if (die_on_error)
- die_errno("Could not switch to '%s'", buf);
+ die_errno("Could not switch to '%s'",
+ sb.buf);
else
goto error_out;
}
}
- if (!getcwd(buf, PATH_MAX)) {
+ if (strbuf_getcwd(&sb)) {
if (die_on_error)
die_errno("Could not get current working directory");
else
@@ -102,48 +97,35 @@ static const char *real_path_internal(const char *path, int die_on_error)
}
if (last_elem) {
- size_t len = strlen(buf);
- if (len + strlen(last_elem) + 2 > PATH_MAX) {
- if (die_on_error)
- die("Too long path name: '%s/%s'",
- buf, last_elem);
- else
- goto error_out;
- }
- if (len && !is_dir_sep(buf[len - 1]))
- buf[len++] = '/';
- strcpy(buf + len, last_elem);
+ if (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))
+ strbuf_addch(&sb, '/');
+ strbuf_addstr(&sb, last_elem);
free(last_elem);
last_elem = NULL;
}
- if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
- ssize_t len = readlink(buf, next_buf, PATH_MAX);
+ if (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {
+ struct strbuf next_sb = STRBUF_INIT;
+ ssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);
if (len < 0) {
if (die_on_error)
- die_errno("Invalid symlink '%s'", buf);
- else
- goto error_out;
- }
- if (PATH_MAX <= len) {
- if (die_on_error)
- die("symbolic link too long: %s", buf);
+ die_errno("Invalid symlink '%s'",
+ sb.buf);
else
goto error_out;
}
- next_buf[len] = '\0';
- buf = next_buf;
- buf_index = 1 - buf_index;
- next_buf = bufs[buf_index];
+ strbuf_swap(&sb, &next_sb);
+ strbuf_release(&next_sb);
} else
break;
}
- retval = buf;
+ retval = sb.buf;
error_out:
free(last_elem);
- if (*cwd && chdir(cwd))
- die_errno("Could not change back to '%s'", cwd);
+ if (cwd.len && chdir(cwd.buf))
+ die_errno("Could not change back to '%s'", cwd.buf);
+ strbuf_release(&cwd);
return retval;
}
@@ -158,54 +140,16 @@ const char *real_path_if_valid(const char *path)
return real_path_internal(path, 0);
}
-static const char *get_pwd_cwd(void)
-{
- static char cwd[PATH_MAX + 1];
- char *pwd;
- struct stat cwd_stat, pwd_stat;
- if (getcwd(cwd, PATH_MAX) == NULL)
- return NULL;
- pwd = getenv("PWD");
- if (pwd && strcmp(pwd, cwd)) {
- stat(cwd, &cwd_stat);
- if ((cwd_stat.st_dev || cwd_stat.st_ino) &&
- !stat(pwd, &pwd_stat) &&
- pwd_stat.st_dev == cwd_stat.st_dev &&
- pwd_stat.st_ino == cwd_stat.st_ino) {
- strlcpy(cwd, pwd, PATH_MAX);
- }
- }
- return cwd;
-}
-
/*
* Use this to get an absolute path from a relative one. If you want
* to resolve links, you should use real_path.
- *
- * If the path is already absolute, then return path. As the user is
- * never meant to free the return value, we're safe.
*/
const char *absolute_path(const char *path)
{
- static char buf[PATH_MAX + 1];
-
- if (!*path) {
- die("The empty string is not a valid path");
- } else if (is_absolute_path(path)) {
- if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
- die("Too long path: %.*s", 60, path);
- } else {
- size_t len;
- const char *fmt;
- const char *cwd = get_pwd_cwd();
- if (!cwd)
- die_errno("Cannot determine the current working directory");
- len = strlen(cwd);
- fmt = (len > 0 && is_dir_sep(cwd[len - 1])) ? "%s%s" : "%s/%s";
- if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
- die("Too long path: %.*s", 60, path);
- }
- return buf;
+ static struct strbuf sb = STRBUF_INIT;
+ strbuf_reset(&sb);
+ strbuf_add_absolute_path(&sb, path);
+ return sb.buf;
}
/*
diff --git a/advice.c b/advice.c
index 9b42033192..3b8bf3c6da 100644
--- a/advice.c
+++ b/advice.c
@@ -86,8 +86,7 @@ int error_resolve_conflict(const char *me)
* other commands doing a merge do.
*/
advise(_("Fix them up in the work tree, and then use 'git add/rm <file>'\n"
- "as appropriate to mark resolution and make a commit, or use\n"
- "'git commit -a'."));
+ "as appropriate to mark resolution and make a commit."));
return -1;
}
diff --git a/alias.c b/alias.c
index 758c867149..6aa164a362 100644
--- a/alias.c
+++ b/alias.c
@@ -1,26 +1,13 @@
#include "cache.h"
-static const char *alias_key;
-static char *alias_val;
-
-static int alias_lookup_cb(const char *k, const char *v, void *cb)
-{
- const char *name;
- if (skip_prefix(k, "alias.", &name) && !strcmp(name, alias_key)) {
- if (!v)
- return config_error_nonbool(k);
- alias_val = xstrdup(v);
- return 0;
- }
- return 0;
-}
-
char *alias_lookup(const char *alias)
{
- alias_key = alias;
- alias_val = NULL;
- git_config(alias_lookup_cb, NULL);
- return alias_val;
+ char *v = NULL;
+ struct strbuf key = STRBUF_INIT;
+ strbuf_addf(&key, "alias.%s", alias);
+ git_config_get_string(key.buf, &v);
+ strbuf_release(&key);
+ return v;
}
#define SPLIT_CMDLINE_BAD_ENDING 1
diff --git a/archive-tar.c b/archive-tar.c
index 719b6298e6..0d1e6bd754 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -395,7 +395,7 @@ static int write_tar_filter_archive(const struct archiver *ar,
struct archiver_args *args)
{
struct strbuf cmd = STRBUF_INIT;
- struct child_process filter;
+ struct child_process filter = CHILD_PROCESS_INIT;
const char *argv[2];
int r;
@@ -406,7 +406,6 @@ static int write_tar_filter_archive(const struct archiver *ar,
if (args->compression_level >= 0)
strbuf_addf(&cmd, " -%d", args->compression_level);
- memset(&filter, 0, sizeof(filter));
argv[0] = cmd.buf;
argv[1] = NULL;
filter.argv = argv;
diff --git a/archive.c b/archive.c
index 3fc0fb2928..94a9981adf 100644
--- a/archive.c
+++ b/archive.c
@@ -5,6 +5,7 @@
#include "archive.h"
#include "parse-options.h"
#include "unpack-trees.h"
+#include "dir.h"
static char const * const archive_usage[] = {
N_("git archive [options] <tree-ish> [<path>...]"),
@@ -98,9 +99,19 @@ static void setup_archive_check(struct git_attr_check *check)
check[1].attr = attr_export_subst;
}
+struct directory {
+ struct directory *up;
+ unsigned char sha1[20];
+ int baselen, len;
+ unsigned mode;
+ int stage;
+ char path[FLEX_ARRAY];
+};
+
struct archiver_context {
struct archiver_args *args;
write_archive_entry_fn_t write_entry;
+ struct directory *bottom;
};
static int write_archive_entry(const unsigned char *sha1, const char *base,
@@ -146,6 +157,65 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
return write_entry(args, sha1, path.buf, path.len, mode);
}
+static void queue_directory(const unsigned char *sha1,
+ const char *base, int baselen, const char *filename,
+ unsigned mode, int stage, struct archiver_context *c)
+{
+ struct directory *d;
+ d = xmallocz(sizeof(*d) + baselen + 1 + strlen(filename));
+ d->up = c->bottom;
+ d->baselen = baselen;
+ d->mode = mode;
+ d->stage = stage;
+ c->bottom = d;
+ d->len = sprintf(d->path, "%.*s%s/", baselen, base, filename);
+ hashcpy(d->sha1, sha1);
+}
+
+static int write_directory(struct archiver_context *c)
+{
+ struct directory *d = c->bottom;
+ int ret;
+
+ if (!d)
+ return 0;
+ c->bottom = d->up;
+ d->path[d->len - 1] = '\0'; /* no trailing slash */
+ ret =
+ write_directory(c) ||
+ write_archive_entry(d->sha1, d->path, d->baselen,
+ d->path + d->baselen, d->mode,
+ d->stage, c) != READ_TREE_RECURSIVE;
+ free(d);
+ return ret ? -1 : 0;
+}
+
+static int queue_or_write_archive_entry(const unsigned char *sha1,
+ const char *base, int baselen, const char *filename,
+ unsigned mode, int stage, void *context)
+{
+ struct archiver_context *c = context;
+
+ while (c->bottom &&
+ !(baselen >= c->bottom->len &&
+ !strncmp(base, c->bottom->path, c->bottom->len))) {
+ struct directory *next = c->bottom->up;
+ free(c->bottom);
+ c->bottom = next;
+ }
+
+ if (S_ISDIR(mode)) {
+ queue_directory(sha1, base, baselen, filename,
+ mode, stage, c);
+ return READ_TREE_RECURSIVE;
+ }
+
+ if (write_directory(c))
+ return -1;
+ return write_archive_entry(sha1, base, baselen, filename, mode,
+ stage, context);
+}
+
int write_archive_entries(struct archiver_args *args,
write_archive_entry_fn_t write_entry)
{
@@ -167,6 +237,7 @@ int write_archive_entries(struct archiver_args *args,
return err;
}
+ memset(&context, 0, sizeof(context));
context.args = args;
context.write_entry = write_entry;
@@ -187,9 +258,17 @@ int write_archive_entries(struct archiver_args *args,
}
err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
- write_archive_entry, &context);
+ args->pathspec.has_wildcard ?
+ queue_or_write_archive_entry :
+ write_archive_entry,
+ &context);
if (err == READ_TREE_RECURSIVE)
err = 0;
+ while (context.bottom) {
+ struct directory *next = context.bottom->up;
+ free(context.bottom);
+ context.bottom = next;
+ }
return err;
}
@@ -211,7 +290,16 @@ static int reject_entry(const unsigned char *sha1, const char *base,
int baselen, const char *filename, unsigned mode,
int stage, void *context)
{
- return -1;
+ int ret = -1;
+ if (S_ISDIR(mode)) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, base);
+ strbuf_addstr(&sb, filename);
+ if (!match_pathspec(context, sb.buf, sb.len, 0, NULL, 1))
+ ret = READ_TREE_RECURSIVE;
+ strbuf_release(&sb);
+ }
+ return ret;
}
static int path_exists(struct tree *tree, const char *path)
@@ -221,7 +309,9 @@ static int path_exists(struct tree *tree, const char *path)
int ret;
parse_pathspec(&pathspec, 0, 0, "", paths);
- ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL);
+ pathspec.recursive = 1;
+ ret = read_tree_recursive(tree, "", 0, 0, &pathspec,
+ reject_entry, &pathspec);
free_pathspec(&pathspec);
return ret != 0;
}
@@ -237,6 +327,7 @@ static void parse_pathspec_arg(const char **pathspec,
parse_pathspec(&ar_args->pathspec, 0,
PATHSPEC_PREFER_FULL,
"", pathspec);
+ ar_args->pathspec.recursive = 1;
if (pathspec) {
while (*pathspec) {
if (**pathspec && !path_exists(ar_args->tree, *pathspec))
@@ -402,14 +493,6 @@ static int parse_archive_args(int argc, const char **argv,
return argc;
}
-static int git_default_archive_config(const char *var, const char *value,
- void *cb)
-{
- if (!strcmp(var, "uploadarchive.allowunreachable"))
- remote_allow_unreachable = git_config_bool(var, value);
- return git_default_config(var, value, cb);
-}
-
int write_archive(int argc, const char **argv, const char *prefix,
int setup_prefix, const char *name_hint, int remote)
{
@@ -420,7 +503,9 @@ int write_archive(int argc, const char **argv, const char *prefix,
if (setup_prefix && prefix == NULL)
prefix = setup_git_directory_gently(&nongit);
- git_config(git_default_archive_config, NULL);
+ git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable);
+ git_config(git_default_config, NULL);
+
init_tar_archiver();
init_zip_archiver();
diff --git a/attr.c b/attr.c
index 734222dc45..cd5469770a 100644
--- a/attr.c
+++ b/attr.c
@@ -97,8 +97,7 @@ static struct git_attr *git_attr_internal(const char *name, int len)
a->attr_nr = attr_nr++;
git_attr_hash[pos] = a;
- check_all_attr = xrealloc(check_all_attr,
- sizeof(*check_all_attr) * attr_nr);
+ REALLOC_ARRAY(check_all_attr, attr_nr);
check_all_attr[a->attr_nr].attr = a;
check_all_attr[a->attr_nr].value = ATTR__UNKNOWN;
return a;
diff --git a/bisect.c b/bisect.c
index d6e851d783..df09cbc8ca 100644
--- a/bisect.c
+++ b/bisect.c
@@ -215,11 +215,12 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
}
qsort(array, cnt, sizeof(*array), compare_commit_dist);
for (p = list, i = 0; i < cnt; i++) {
- struct name_decoration *r = xmalloc(sizeof(*r) + 100);
+ char buf[100]; /* enough for dist=%d */
struct object *obj = &(array[i].commit->object);
- sprintf(r->name, "dist=%d", array[i].distance);
- r->next = add_decoration(&name_decoration, obj, r);
+ snprintf(buf, sizeof(buf), "dist=%d", array[i].distance);
+ add_name_decoration(DECORATION_NONE, buf, obj);
+
p->item = array[i].commit;
p = p->next;
}
diff --git a/branch.c b/branch.c
index 46e8aa86df..4bab55a9a8 100644
--- a/branch.c
+++ b/branch.c
@@ -140,33 +140,17 @@ 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;
+ char *v = NULL;
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) {
+ if (git_config_get_string(name.buf, &v)) {
strbuf_release(&name);
return -1;
}
- if (cb.value)
- strbuf_addstr(buf, cb.value);
+ strbuf_addstr(buf, v);
+ free(v);
strbuf_release(&name);
return 0;
}
@@ -186,7 +170,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die(_("Cannot force update the current branch."));
}
@@ -226,7 +210,6 @@ void create_branch(const char *head,
int force, int reflog, int clobber_head,
int quiet, enum branch_track track)
{
- struct ref_lock *lock = NULL;
struct commit *commit;
unsigned char sha1[20];
char *real_ref, msg[PATH_MAX + 20];
@@ -285,15 +268,6 @@ void create_branch(const char *head,
die(_("Not a valid branch point: '%s'."), start_name);
hashcpy(sha1, commit->object.sha1);
- if (!dont_change_ref) {
- lock = lock_any_ref_for_update(ref.buf, NULL, 0, NULL);
- if (!lock)
- die_errno(_("Failed to lock ref for update"));
- }
-
- if (reflog)
- log_all_ref_updates = 1;
-
if (forcing)
snprintf(msg, sizeof msg, "branch: Reset to %s",
start_name);
@@ -301,13 +275,26 @@ void create_branch(const char *head,
snprintf(msg, sizeof msg, "branch: Created from %s",
start_name);
+ if (reflog)
+ log_all_ref_updates = 1;
+
+ if (!dont_change_ref) {
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref.buf, sha1,
+ null_sha1, 0, !forcing, msg, &err) ||
+ ref_transaction_commit(transaction, &err))
+ die("%s", err.buf);
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
+ }
+
if (real_ref && track)
setup_tracking(ref.buf + 11, real_ref, track, quiet);
- if (!dont_change_ref)
- if (write_ref_sha1(lock, sha1, msg) < 0)
- die_errno(_("Failed to write ref"));
-
strbuf_release(&ref);
free(real_ref);
}
diff --git a/builtin.h b/builtin.h
index 5d91f31ca2..b87df70f96 100644
--- a/builtin.h
+++ b/builtin.h
@@ -73,6 +73,7 @@ extern int cmd_hash_object(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);
extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
extern int cmd_init_db(int argc, const char **argv, const char *prefix);
+extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
extern int cmd_log(int argc, const char **argv, const char *prefix);
extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 4baf3a5635..ae6d3e262b 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -5,6 +5,7 @@
*/
#include "cache.h"
#include "builtin.h"
+#include "lockfile.h"
#include "dir.h"
#include "pathspec.h"
#include "exec_cmd.h"
@@ -180,7 +181,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
char *file = git_pathdup("ADD_EDIT.patch");
const char *apply_argv[] = { "apply", "--recount", "--cached",
NULL, NULL };
- struct child_process child;
+ struct child_process child = CHILD_PROCESS_INIT;
struct rev_info rev;
int out;
struct stat st;
@@ -214,7 +215,6 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
if (!st.st_size)
die(_("Empty patch. Aborted."));
- memset(&child, 0, sizeof(child));
child.git_cmd = 1;
child.argv = apply_argv;
if (run_command(&child))
diff --git a/builtin/apply.c b/builtin/apply.c
index 6b7c764918..6696ea4c3f 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -7,6 +7,7 @@
*
*/
#include "cache.h"
+#include "lockfile.h"
#include "cache-tree.h"
#include "quote.h"
#include "blob.h"
@@ -435,7 +436,7 @@ static unsigned long linelen(const char *buffer, unsigned long size)
static int is_dev_null(const char *str)
{
- return !memcmp("/dev/null", str, 9) && isspace(str[9]);
+ return skip_prefix(str, "/dev/null", &str) && isspace(*str);
}
#define TERM_SPACE 1
@@ -2626,7 +2627,7 @@ static void update_image(struct image *img,
* NOTE: this knows that we never call remove_first_line()
* on anything other than pre/post image.
*/
- img->line = xrealloc(img->line, nr * sizeof(*img->line));
+ REALLOC_ARRAY(img->line, nr);
img->line_allocated = img->line;
}
if (preimage_limit != postimage->nr)
@@ -4274,13 +4275,11 @@ static int apply_patch(int fd, const char *filename, int options)
return 0;
}
-static int git_apply_config(const char *var, const char *value, void *cb)
+static void git_apply_config(void)
{
- if (!strcmp(var, "apply.whitespace"))
- return git_config_string(&apply_default_whitespace, var, value);
- else if (!strcmp(var, "apply.ignorewhitespace"))
- return git_config_string(&apply_default_ignorewhitespace, var, value);
- return git_default_config(var, value, cb);
+ git_config_get_string_const("apply.whitespace", &apply_default_whitespace);
+ git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace);
+ git_config(git_default_config, NULL);
}
static int option_parse_exclude(const struct option *opt,
@@ -4428,7 +4427,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
prefix = prefix_;
prefix_length = prefix ? strlen(prefix) : 0;
- git_config(git_apply_config, NULL);
+ git_apply_config();
if (apply_default_whitespace)
parse_whitespace_option(apply_default_whitespace);
if (apply_default_ignorewhitespace)
diff --git a/builtin/blame.c b/builtin/blame.c
index 17d30d00aa..303e217ae9 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2286,7 +2286,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
commit->date = now;
parent_tail = &commit->parents;
- if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
die("no such ref: HEAD");
parent_tail = append_parent(parent_tail, head_sha1);
@@ -2580,6 +2580,9 @@ parse_done:
case DATE_RFC2822:
blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
break;
+ case DATE_ISO8601_STRICT:
+ blame_date_width = sizeof("2006-10-19T16:00:04-07:00");
+ break;
case DATE_ISO8601:
blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
break;
@@ -2700,7 +2703,7 @@ parse_done:
* uninteresting.
*/
if (prepare_revision_walk(&revs))
- die("revision walk setup failed");
+ die(_("revision walk setup failed"));
if (is_null_sha1(sb.final->object.sha1)) {
o = sb.final->util;
diff --git a/builtin/branch.c b/builtin/branch.c
index 0591b22a48..3b79c5087f 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -62,39 +62,40 @@ static unsigned char merge_filter_ref[20];
static struct string_list output = STRING_LIST_INIT_DUP;
static unsigned int colopts;
-static int parse_branch_color_slot(const char *var, int ofs)
+static int parse_branch_color_slot(const char *slot)
{
- if (!strcasecmp(var+ofs, "plain"))
+ if (!strcasecmp(slot, "plain"))
return BRANCH_COLOR_PLAIN;
- if (!strcasecmp(var+ofs, "reset"))
+ if (!strcasecmp(slot, "reset"))
return BRANCH_COLOR_RESET;
- if (!strcasecmp(var+ofs, "remote"))
+ if (!strcasecmp(slot, "remote"))
return BRANCH_COLOR_REMOTE;
- if (!strcasecmp(var+ofs, "local"))
+ if (!strcasecmp(slot, "local"))
return BRANCH_COLOR_LOCAL;
- if (!strcasecmp(var+ofs, "current"))
+ if (!strcasecmp(slot, "current"))
return BRANCH_COLOR_CURRENT;
- if (!strcasecmp(var+ofs, "upstream"))
+ if (!strcasecmp(slot, "upstream"))
return BRANCH_COLOR_UPSTREAM;
return -1;
}
static int git_branch_config(const char *var, const char *value, void *cb)
{
+ const char *slot_name;
+
if (starts_with(var, "column."))
return git_column_config(var, value, "branch", &colopts);
if (!strcmp(var, "color.branch")) {
branch_use_color = git_config_colorbool(var, value);
return 0;
}
- if (starts_with(var, "color.branch.")) {
- int slot = parse_branch_color_slot(var, 13);
+ if (skip_prefix(var, "color.branch.", &slot_name)) {
+ int slot = parse_branch_color_slot(slot_name);
if (slot < 0)
return 0;
if (!value)
return config_error_nonbool(var);
- color_parse(value, var, branch_colors[slot]);
- return 0;
+ return color_parse(value, branch_colors[slot]);
}
return git_color_default_config(var, value, cb);
}
@@ -129,7 +130,8 @@ static int branch_merged(int kind, const char *name,
branch->merge[0] &&
branch->merge[0]->dst &&
(reference_name = reference_name_to_free =
- resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+ resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING,
+ sha1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
if (!reference_rev)
@@ -233,9 +235,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
name = mkpathdup(fmt, bname.buf);
- target = resolve_ref_unsafe(name, sha1, 0, &flags);
- if (!target ||
- (!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) {
+ target = resolve_ref_unsafe(name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ sha1, &flags);
+ if (!target) {
error(remote_branch
? _("remote branch '%s' not found.")
: _("branch '%s' not found."), bname.buf);
@@ -243,7 +248,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
continue;
}
- if (!(flags & REF_ISSYMREF) &&
+ if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
force)) {
ret = 1;
@@ -263,8 +268,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
? _("Deleted remote branch %s (was %s).\n")
: _("Deleted branch %s (was %s).\n"),
bname.buf,
- (flags & REF_ISSYMREF)
- ? target
+ (flags & REF_ISBROKEN) ? "broken"
+ : (flags & REF_ISSYMREF) ? target
: find_unique_abbrev(sha1, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
@@ -280,6 +285,7 @@ struct ref_item {
char *dest;
unsigned int kind, width;
struct commit *commit;
+ int ignore;
};
struct ref_list {
@@ -296,7 +302,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst;
- dst = resolve_ref_unsafe(src, sha1, 0, &flag);
+ dst = resolve_ref_unsafe(src, 0, sha1, &flag);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
if (prefix)
@@ -334,20 +340,18 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
static struct {
int kind;
const char *prefix;
- int pfxlen;
} ref_kind[] = {
- { REF_LOCAL_BRANCH, "refs/heads/", 11 },
- { REF_REMOTE_BRANCH, "refs/remotes/", 13 },
+ { REF_LOCAL_BRANCH, "refs/heads/" },
+ { REF_REMOTE_BRANCH, "refs/remotes/" },
};
/* Detect kind */
for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
prefix = ref_kind[i].prefix;
- if (strncmp(refname, prefix, ref_kind[i].pfxlen))
- continue;
- kind = ref_kind[i].kind;
- refname += ref_kind[i].pfxlen;
- break;
+ if (skip_prefix(refname, prefix, &refname)) {
+ kind = ref_kind[i].kind;
+ break;
+ }
}
if (ARRAY_SIZE(ref_kind) <= i)
return 0;
@@ -385,6 +389,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
newitem->commit = commit;
newitem->width = utf8_strwidth(refname);
newitem->dest = resolve_symref(orig_refname, prefix);
+ newitem->ignore = 0;
/* adjust for "remotes/" */
if (newitem->kind == REF_REMOTE_BRANCH &&
ref_list->kinds != REF_REMOTE_BRANCH)
@@ -484,17 +489,6 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
free(ref);
}
-static int matches_merge_filter(struct commit *commit)
-{
- int is_merged;
-
- if (merge_filter == NO_FILTER)
- return 1;
-
- is_merged = !!(commit->object.flags & UNINTERESTING);
- return (is_merged == (merge_filter == SHOW_MERGED));
-}
-
static void add_verbose_info(struct strbuf *out, struct ref_item *item,
int verbose, int abbrev)
{
@@ -522,10 +516,9 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
{
char c;
int color;
- struct commit *commit = item->commit;
struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
- if (!matches_merge_filter(commit))
+ if (item->ignore)
return;
switch (item->kind) {
@@ -575,7 +568,7 @@ static int calc_maxwidth(struct ref_list *refs)
{
int i, w = 0;
for (i = 0; i < refs->index; i++) {
- if (!matches_merge_filter(refs->list[i].commit))
+ if (refs->list[i].ignore)
continue;
if (refs->list[i].width > w)
w = refs->list[i].width;
@@ -618,6 +611,7 @@ static void show_detached(struct ref_list *ref_list)
item.kind = REF_LOCAL_BRANCH;
item.dest = NULL;
item.commit = head_commit;
+ item.ignore = 0;
if (item.width > ref_list->maxwidth)
ref_list->maxwidth = item.width;
print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
@@ -653,7 +647,23 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
add_pending_object(&ref_list.revs,
(struct object *) filter, "");
ref_list.revs.limited = 1;
- prepare_revision_walk(&ref_list.revs);
+
+ if (prepare_revision_walk(&ref_list.revs))
+ die(_("revision walk setup failed"));
+
+ for (i = 0; i < ref_list.index; i++) {
+ struct ref_item *item = &ref_list.list[i];
+ struct commit *commit = item->commit;
+ int is_merged = !!(commit->object.flags & UNINTERESTING);
+ item->ignore = is_merged != (merge_filter == SHOW_MERGED);
+ }
+
+ for (i = 0; i < ref_list.index; i++) {
+ struct ref_item *item = &ref_list.list[i];
+ clear_commit_marks(item->commit, ALL_REV_FLAGS);
+ }
+ clear_commit_marks(filter, ALL_REV_FLAGS);
+
if (verbose)
ref_list.maxwidth = calc_maxwidth(&ref_list);
}
@@ -862,16 +872,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_refdup("HEAD", head_sha1, 0, NULL);
+ head = resolve_refdup("HEAD", 0, head_sha1, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
- if (!strcmp(head, "HEAD")) {
+ if (!strcmp(head, "HEAD"))
detached = 1;
- } else {
- if (!starts_with(head, "refs/heads/"))
- die(_("HEAD not found below refs/heads!"));
- head += 11;
- }
+ else if (!skip_prefix(head, "refs/heads/", &head))
+ die(_("HEAD not found below refs/heads!"));
hashcpy(merge_filter_ref, head_sha1);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 707330499f..f8d81291b9 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -82,8 +82,9 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
enum object_type type;
unsigned long size;
char *buffer = read_sha1_file(sha1, &type, &size);
- if (memcmp(buffer, "object ", 7) ||
- get_sha1_hex(buffer + 7, blob_sha1))
+ const char *target;
+ if (!skip_prefix(buffer, "object ", &target) ||
+ get_sha1_hex(target, blob_sha1))
die("%s not a valid tag", sha1_to_hex(sha1));
free(buffer);
} else
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 05edd9e1df..383dccf93e 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -5,7 +5,7 @@
*
*/
#include "builtin.h"
-#include "cache.h"
+#include "lockfile.h"
#include "quote.h"
#include "cache-tree.h"
#include "parse-options.h"
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f71e74531d..5410dacea0 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,5 +1,5 @@
-#include "cache.h"
#include "builtin.h"
+#include "lockfile.h"
#include "parse-options.h"
#include "refs.h"
#include "commit.h"
@@ -355,7 +355,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- read_ref_full("HEAD", rev, 0, &flag);
+ read_ref_full("HEAD", 0, rev, &flag);
head = lookup_commit_reference_gently(rev, 1);
errs |= post_checkout_hook(head, head, 0);
@@ -552,6 +552,12 @@ static int merge_working_tree(const struct checkout_opts *opts,
}
}
+ if (!active_cache_tree)
+ active_cache_tree = cache_tree();
+
+ if (!cache_tree_fully_valid(active_cache_tree))
+ cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
@@ -769,7 +775,7 @@ static int switch_branches(const struct checkout_opts *opts,
unsigned char rev[20];
int flag, writeout_error = 0;
memset(&old, 0, sizeof(old));
- old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
+ old.path = path_to_free = resolve_refdup("HEAD", 0, rev, &flag);
old.commit = lookup_commit_reference_gently(rev, 1);
if (!(flag & REF_ISSYMREF))
old.path = NULL;
@@ -1066,7 +1072,7 @@ static int checkout_branch(struct checkout_opts *opts,
unsigned char rev[20];
int flag;
- if (!read_ref_full("HEAD", rev, 0, &flag) &&
+ if (!read_ref_full("HEAD", 0, rev, &flag) &&
(flag & REF_ISSYMREF) && is_null_sha1(rev))
return switch_unborn_to_new_branch(opts);
}
@@ -1144,10 +1150,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
const char *argv0 = argv[0];
if (!argc || !strcmp(argv0, "--"))
die (_("--track needs a branch name"));
- if (starts_with(argv0, "refs/"))
- argv0 += 5;
- if (starts_with(argv0, "remotes/"))
- argv0 += 8;
+ skip_prefix(argv0, "refs/", &argv0);
+ skip_prefix(argv0, "remotes/", &argv0);
argv0 = strchr(argv0, '/');
if (!argv0 || !argv0[1])
die (_("Missing branch name; try -b"));
diff --git a/builtin/clean.c b/builtin/clean.c
index 1032563e5f..77846762b5 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -67,7 +67,7 @@ struct menu_item {
char hotkey;
const char *title;
int selected;
- int (*fn)();
+ int (*fn)(void);
};
enum menu_stuff_type {
@@ -100,6 +100,8 @@ static int parse_clean_color_slot(const char *var)
static int git_clean_config(const char *var, const char *value, void *cb)
{
+ const char *slot_name;
+
if (starts_with(var, "column."))
return git_column_config(var, value, "clean", &colopts);
@@ -109,15 +111,13 @@ static int git_clean_config(const char *var, const char *value, void *cb)
clean_use_color = git_config_colorbool(var, value);
return 0;
}
- if (starts_with(var, "color.interactive.")) {
- int slot = parse_clean_color_slot(var +
- strlen("color.interactive."));
+ if (skip_prefix(var, "color.interactive.", &slot_name)) {
+ int slot = parse_clean_color_slot(slot_name);
if (slot < 0)
return 0;
if (!value)
return config_error_nonbool(var);
- color_parse(value, var, clean_colors[slot]);
- return 0;
+ return color_parse(value, clean_colors[slot]);
}
if (!strcmp(var, "clean.requireforce")) {
diff --git a/builtin/clone.c b/builtin/clone.c
index bbd169ceb4..d5e7532105 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -9,6 +9,7 @@
*/
#include "builtin.h"
+#include "lockfile.h"
#include "parse-options.h"
#include "fetch-pack.h"
#include "refs.h"
@@ -390,7 +391,6 @@ static void clone_local(const char *src_repo, const char *dest_repo)
static const char *junk_work_tree;
static const char *junk_git_dir;
-static pid_t junk_pid;
static enum {
JUNK_LEAVE_NONE,
JUNK_LEAVE_REPO,
@@ -417,8 +417,6 @@ static void remove_junk(void)
break;
}
- if (getpid() != junk_pid)
- return;
if (junk_git_dir) {
strbuf_addstr(&sb, junk_git_dir);
remove_dir_recursively(&sb, 0);
@@ -622,7 +620,7 @@ static int checkout(void)
if (option_no_checkout)
return 0;
- head = resolve_refdup("HEAD", sha1, 1, NULL);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
if (!head) {
warning(_("remote HEAD refers to nonexistent ref, "
"unable to checkout.\n"));
@@ -685,9 +683,10 @@ static void write_config(struct string_list *config)
}
}
-static void write_refspec_config(const char* src_ref_prefix,
- const struct ref* our_head_points_at,
- const struct ref* remote_head_points_at, struct strbuf* branch_top)
+static void write_refspec_config(const char *src_ref_prefix,
+ const struct ref *our_head_points_at,
+ const struct ref *remote_head_points_at,
+ struct strbuf *branch_top)
{
struct strbuf key = STRBUF_INIT;
struct strbuf value = STRBUF_INIT;
@@ -758,8 +757,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct refspec *refspec;
const char *fetch_pattern;
- junk_pid = getpid();
-
packet_trace_identity("clone");
argc = parse_options(argc, argv, prefix, builtin_clone_options,
builtin_clone_usage, 0);
@@ -1004,5 +1001,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_release(&key);
strbuf_release(&value);
junk_mode = JUNK_LEAVE_ALL;
+
+ free(refspec);
return err;
}
diff --git a/builtin/commit.c b/builtin/commit.c
index 5ed60364ce..e108c53015 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -6,6 +6,7 @@
*/
#include "cache.h"
+#include "lockfile.h"
#include "cache-tree.h"
#include "color.h"
#include "dir.h"
@@ -42,7 +43,20 @@ static const char * const builtin_status_usage[] = {
NULL
};
-static const char implicit_ident_advice[] =
+static const char implicit_ident_advice_noconfig[] =
+N_("Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly. Run the\n"
+"following command and follow the instructions in your editor to edit\n"
+"your configuration file:\n"
+"\n"
+" git config --global --edit\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+" git commit --amend --reset-author\n");
+
+static const char implicit_ident_advice_config[] =
N_("Your name and email address were configured automatically based\n"
"on your username and hostname. Please check that they are accurate.\n"
"You can suppress this message by setting them explicitly:\n"
@@ -302,8 +316,8 @@ static void refresh_cache_or_die(int refresh_flags)
die_resolve_conflict("commit");
}
-static char *prepare_index(int argc, const char **argv, const char *prefix,
- const struct commit *current_head, int is_status)
+static const char *prepare_index(int argc, const char **argv, const char *prefix,
+ const struct commit *current_head, int is_status)
{
struct string_list partial;
struct pathspec pathspec;
@@ -328,7 +342,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
die(_("unable to create temporary index"));
old_index_env = getenv(INDEX_ENVIRONMENT);
- setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
+ setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
die(_("interactive add failed"));
@@ -339,10 +353,17 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
unsetenv(INDEX_ENVIRONMENT);
discard_cache();
- read_cache_from(index_lock.filename);
+ read_cache_from(index_lock.filename.buf);
+ if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
+ if (reopen_lock_file(&index_lock) < 0)
+ die(_("unable to write index file"));
+ if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ die(_("unable to update temporary index"));
+ } else
+ warning(_("Failed to update main cache tree"));
commit_style = COMMIT_NORMAL;
- return index_lock.filename;
+ return index_lock.filename.buf;
}
/*
@@ -365,7 +386,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
- return index_lock.filename;
+ return index_lock.filename.buf;
}
/*
@@ -380,8 +401,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
if (!only && !pathspec.nr) {
hold_locked_index(&index_lock, 1);
refresh_cache_or_die(refresh_flags);
- if (active_cache_changed) {
+ if (active_cache_changed
+ || !cache_tree_fully_valid(active_cache_tree)) {
update_main_cache_tree(WRITE_TREE_SILENT);
+ active_cache_changed = 1;
+ }
+ if (active_cache_changed) {
if (write_locked_index(&the_index, &index_lock,
COMMIT_LOCK))
die(_("unable to write new_index file"));
@@ -431,6 +456,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
hold_locked_index(&index_lock, 1);
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
+ update_main_cache_tree(WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to write new_index file"));
@@ -447,9 +473,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
die(_("unable to write temporary index file"));
discard_cache();
- read_cache_from(false_lock.filename);
+ read_cache_from(false_lock.filename.buf);
- return false_lock.filename;
+ return false_lock.filename.buf;
}
static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
@@ -520,77 +546,80 @@ static int sane_ident_split(struct ident_split *person)
return 1;
}
-static int parse_force_date(const char *in, char *out, int len)
+static int parse_force_date(const char *in, struct strbuf *out)
{
- if (len < 1)
- return -1;
- *out++ = '@';
- len--;
+ strbuf_addch(out, '@');
- if (parse_date(in, out, len) < 0) {
+ if (parse_date(in, out) < 0) {
int errors = 0;
unsigned long t = approxidate_careful(in, &errors);
if (errors)
return -1;
- snprintf(out, len, "%lu", t);
+ strbuf_addf(out, "%lu", t);
}
return 0;
}
+static void set_ident_var(char **buf, char *val)
+{
+ free(*buf);
+ *buf = val;
+}
+
+static char *envdup(const char *var)
+{
+ const char *val = getenv(var);
+ return val ? xstrdup(val) : NULL;
+}
+
static void determine_author_info(struct strbuf *author_ident)
{
char *name, *email, *date;
struct ident_split author;
- char date_buf[64];
- name = getenv("GIT_AUTHOR_NAME");
- email = getenv("GIT_AUTHOR_EMAIL");
- date = getenv("GIT_AUTHOR_DATE");
+ name = envdup("GIT_AUTHOR_NAME");
+ email = envdup("GIT_AUTHOR_EMAIL");
+ date = envdup("GIT_AUTHOR_DATE");
if (author_message) {
- const char *a, *lb, *rb, *eol;
+ struct ident_split ident;
size_t len;
+ const char *a;
- a = strstr(author_message_buffer, "\nauthor ");
+ a = find_commit_header(author_message_buffer, "author", &len);
if (!a)
- die(_("invalid commit: %s"), author_message);
-
- lb = strchrnul(a + strlen("\nauthor "), '<');
- rb = strchrnul(lb, '>');
- eol = strchrnul(rb, '\n');
- if (!*lb || !*rb || !*eol)
- die(_("invalid commit: %s"), author_message);
-
- if (lb == a + strlen("\nauthor "))
- /* \nauthor <foo@example.com> */
- name = xcalloc(1, 1);
- else
- name = xmemdupz(a + strlen("\nauthor "),
- (lb - strlen(" ") -
- (a + strlen("\nauthor "))));
- email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
- len = eol - (rb + strlen("> "));
- date = xmalloc(len + 2);
- *date = '@';
- memcpy(date + 1, rb + strlen("> "), len);
- date[len + 1] = '\0';
+ die(_("commit '%s' lacks author header"), author_message);
+ if (split_ident_line(&ident, a, len) < 0)
+ die(_("commit '%s' has malformed author line"), author_message);
+
+ set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+ set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
+
+ if (ident.date_begin) {
+ struct strbuf date_buf = STRBUF_INIT;
+ strbuf_addch(&date_buf, '@');
+ strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin);
+ strbuf_addch(&date_buf, ' ');
+ strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin);
+ set_ident_var(&date, strbuf_detach(&date_buf, NULL));
+ }
}
if (force_author) {
- const char *lb = strstr(force_author, " <");
- const char *rb = strchr(force_author, '>');
+ struct ident_split ident;
- if (!lb || !rb)
+ if (split_ident_line(&ident, force_author, strlen(force_author)) < 0)
die(_("malformed --author parameter"));
- name = xstrndup(force_author, lb - force_author);
- email = xstrndup(lb + 2, rb - (lb + 2));
+ set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+ set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
}
if (force_date) {
- if (parse_force_date(force_date, date_buf, sizeof(date_buf)))
+ struct strbuf date_buf = STRBUF_INIT;
+ if (parse_force_date(force_date, &date_buf))
die(_("invalid date format: %s"), force_date);
- date = date_buf;
+ set_ident_var(&date, strbuf_detach(&date_buf, NULL));
}
strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
@@ -600,6 +629,10 @@ static void determine_author_info(struct strbuf *author_ident)
export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
}
+
+ free(name);
+ free(email);
+ free(date);
}
static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
@@ -1048,7 +1081,8 @@ static const char *find_author_by_nickname(const char *name)
revs.mailmap = &mailmap;
read_mailmap(revs.mailmap, NULL);
- prepare_revision_walk(&revs);
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
commit = get_revision(&revs);
if (commit) {
struct pretty_print_context ctx = {0};
@@ -1238,22 +1272,21 @@ static int dry_run_commit(int argc, const char **argv, const char *prefix,
return commitable ? 0 : 1;
}
-static int parse_status_slot(const char *var, int offset)
+static int parse_status_slot(const char *slot)
{
- if (!strcasecmp(var+offset, "header"))
+ if (!strcasecmp(slot, "header"))
return WT_STATUS_HEADER;
- if (!strcasecmp(var+offset, "branch"))
+ if (!strcasecmp(slot, "branch"))
return WT_STATUS_ONBRANCH;
- if (!strcasecmp(var+offset, "updated")
- || !strcasecmp(var+offset, "added"))
+ if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added"))
return WT_STATUS_UPDATED;
- if (!strcasecmp(var+offset, "changed"))
+ if (!strcasecmp(slot, "changed"))
return WT_STATUS_CHANGED;
- if (!strcasecmp(var+offset, "untracked"))
+ if (!strcasecmp(slot, "untracked"))
return WT_STATUS_UNTRACKED;
- if (!strcasecmp(var+offset, "nobranch"))
+ if (!strcasecmp(slot, "nobranch"))
return WT_STATUS_NOBRANCH;
- if (!strcasecmp(var+offset, "unmerged"))
+ if (!strcasecmp(slot, "unmerged"))
return WT_STATUS_UNMERGED;
return -1;
}
@@ -1261,6 +1294,7 @@ static int parse_status_slot(const char *var, int offset)
static int git_status_config(const char *k, const char *v, void *cb)
{
struct wt_status *s = cb;
+ const char *slot_name;
if (starts_with(k, "column."))
return git_column_config(k, v, "status", &s->colopts);
@@ -1290,14 +1324,14 @@ static int git_status_config(const char *k, const char *v, void *cb)
s->display_comment_prefix = git_config_bool(k, v);
return 0;
}
- if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
- int slot = parse_status_slot(k, 13);
+ if (skip_prefix(k, "status.color.", &slot_name) ||
+ skip_prefix(k, "color.status.", &slot_name)) {
+ int slot = parse_status_slot(slot_name);
if (slot < 0)
return 0;
if (!v)
return config_error_nonbool(k);
- color_parse(v, k, s->color_palette[slot]);
- return 0;
+ return color_parse(v, s->color_palette[slot]);
}
if (!strcmp(k, "status.relativepaths")) {
s->relative_paths = git_config_bool(k, v);
@@ -1402,6 +1436,24 @@ int cmd_status(int argc, const char **argv, const char *prefix)
return 0;
}
+static const char *implicit_ident_advice(void)
+{
+ char *user_config = NULL;
+ char *xdg_config = NULL;
+ int config_exists;
+
+ home_config_paths(&user_config, &xdg_config, "config");
+ config_exists = file_exists(user_config) || file_exists(xdg_config);
+ free(user_config);
+ free(xdg_config);
+
+ if (config_exists)
+ return _(implicit_ident_advice_config);
+ else
+ return _(implicit_ident_advice_noconfig);
+
+}
+
static void print_summary(const char *prefix, const unsigned char *sha1,
int initial_commit)
{
@@ -1440,7 +1492,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
strbuf_addbuf_percentquote(&format, &committer_ident);
if (advice_implicit_identity) {
strbuf_addch(&format, '\n');
- strbuf_addstr(&format, _(implicit_ident_advice));
+ strbuf_addstr(&format, implicit_ident_advice());
}
}
strbuf_release(&author_ident);
@@ -1461,14 +1513,12 @@ 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 ",
- starts_with(head, "refs/heads/") ?
- head + 11 :
- !strcmp(head, "HEAD") ?
- _("detached HEAD") :
- head,
- initial_commit ? _(" (root-commit)") : "");
+ head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
+ if (!strcmp(head, "HEAD"))
+ head = _("detached HEAD");
+ else
+ skip_prefix(head, "refs/heads/", &head);
+ printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
if (!log_tree_commit(&rev, commit)) {
rev.always_show_header = 1;
@@ -1508,7 +1558,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
{
/* oldsha1 SP newsha1 LF NUL */
static char buf[2*40 + 3];
- struct child_process proc;
+ struct child_process proc = CHILD_PROCESS_INIT;
const char *argv[3];
int code;
size_t n;
@@ -1520,7 +1570,6 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
argv[1] = "amend";
argv[2] = NULL;
- memset(&proc, 0, sizeof(proc));
proc.argv = argv;
proc.in = -1;
proc.stdout_to_stderr = 1;
@@ -1620,11 +1669,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
const char *index_file, *reflog_msg;
char *nl;
unsigned char sha1[20];
- struct ref_lock *ref_lock;
struct commit_list *parents = NULL, **pptr = &parents;
struct stat statbuf;
struct commit *current_head = NULL;
struct commit_extra_header *extra = NULL;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_commit_usage, builtin_commit_options);
@@ -1746,16 +1796,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
strbuf_release(&author_ident);
free_commit_extra_headers(extra);
- ref_lock = lock_any_ref_for_update("HEAD",
- !current_head
- ? NULL
- : current_head->object.sha1,
- 0, NULL);
- if (!ref_lock) {
- rollback_index_files();
- die(_("cannot lock HEAD ref"));
- }
-
nl = strchr(sb.buf, '\n');
if (nl)
strbuf_setlen(&sb, nl + 1 - sb.buf);
@@ -1764,10 +1804,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
- if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, "HEAD", sha1,
+ current_head
+ ? current_head->object.sha1 : NULL,
+ 0, !!current_head, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
rollback_index_files();
- die(_("cannot update HEAD ref"));
+ die("%s", err.buf);
}
+ ref_transaction_free(transaction);
unlink(git_path("CHERRY_PICK_HEAD"));
unlink(git_path("REVERT_HEAD"));
@@ -1778,7 +1825,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (commit_index_files())
die (_("Repository has been updated, but unable to write\n"
- "new_index file. Check that disk is not full or quota is\n"
+ "new_index file. Check that disk is not full and quota is\n"
"not exceeded, and then \"git reset HEAD\" to recover."));
rerere(0);
@@ -1796,5 +1843,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (!quiet)
print_summary(prefix, sha1, !current_head);
+ strbuf_release(&err);
return 0;
}
diff --git a/builtin/config.c b/builtin/config.c
index 7bba516383..8cc2604069 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -296,7 +296,8 @@ static int git_get_color_config(const char *var, const char *value, void *cb)
if (!strcmp(var, get_color_slot)) {
if (!value)
config_error_nonbool(var);
- color_parse(value, var, parsed_color);
+ if (color_parse(value, parsed_color) < 0)
+ return -1;
get_color_found = 1;
}
return 0;
@@ -309,8 +310,10 @@ static void get_color(const char *def_color)
git_config_with_options(git_get_color_config, NULL,
&given_config_source, respect_includes);
- if (!get_color_found && def_color)
- color_parse(def_color, "command line", parsed_color);
+ if (!get_color_found && def_color) {
+ if (color_parse(def_color, parsed_color) < 0)
+ die(_("unable to parse default color value"));
+ }
fputs(parsed_color, stdout);
}
@@ -445,6 +448,20 @@ static int get_urlmatch(const char *var, const char *url)
return 0;
}
+static char *default_user_config(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf,
+ _("# This is Git's per-user configuration file.\n"
+ "[core]\n"
+ "# Please adapt and uncomment the following lines:\n"
+ "# user = %s\n"
+ "# email = %s\n"),
+ ident_default_name(),
+ ident_default_email());
+ return strbuf_detach(&buf, NULL);
+}
+
int cmd_config(int argc, const char **argv, const char *prefix)
{
int nongit = !startup_info->have_repository;
@@ -551,6 +568,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
}
else if (actions == ACTION_EDIT) {
+ const char *config_file = given_config_source.file ?
+ given_config_source.file : git_path("config");
check_argc(argc, 0, 0);
if (!given_config_source.file && nongit)
die("not in a git directory");
@@ -559,9 +578,18 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (given_config_source.blob)
die("editing blobs is not supported");
git_config(git_default_config, NULL);
- launch_editor(given_config_source.file ?
- given_config_source.file : git_path("config"),
- NULL, NULL);
+ if (use_global_config) {
+ int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd) {
+ char *content = default_user_config();
+ write_str_in_full(fd, content);
+ free(content);
+ close(fd);
+ }
+ else if (errno != EEXIST)
+ die_errno(_("cannot create configuration file %s"), config_file);
+ }
+ launch_editor(config_file, NULL, NULL);
}
else if (actions == ACTION_SET) {
int ret;
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index a7f70cb858..e47ef0b1af 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -11,6 +11,9 @@
static unsigned long garbage;
static off_t size_garbage;
+static int verbose;
+static unsigned long loose, packed, packed_loose;
+static off_t loose_size;
static void real_report_garbage(const char *desc, const char *path)
{
@@ -21,61 +24,31 @@ static void real_report_garbage(const char *desc, const char *path)
garbage++;
}
-static void count_objects(DIR *d, char *path, int len, int verbose,
- unsigned long *loose,
- off_t *loose_size,
- unsigned long *packed_loose)
+static void loose_garbage(const char *path)
{
- struct dirent *ent;
- while ((ent = readdir(d)) != NULL) {
- char hex[41];
- unsigned char sha1[20];
- const char *cp;
- int bad = 0;
+ if (verbose)
+ report_garbage("garbage found", path);
+}
- if (is_dot_or_dotdot(ent->d_name))
- continue;
- for (cp = ent->d_name; *cp; cp++) {
- int ch = *cp;
- if (('0' <= ch && ch <= '9') ||
- ('a' <= ch && ch <= 'f'))
- continue;
- bad = 1;
- break;
- }
- if (cp - ent->d_name != 38)
- bad = 1;
- else {
- struct stat st;
- memcpy(path + len + 3, ent->d_name, 38);
- path[len + 2] = '/';
- path[len + 41] = 0;
- if (lstat(path, &st) || !S_ISREG(st.st_mode))
- bad = 1;
- else
- (*loose_size) += xsize_t(on_disk_bytes(st));
- }
- if (bad) {
- if (verbose) {
- struct strbuf sb = STRBUF_INIT;
- strbuf_addf(&sb, "%.*s/%s",
- len + 2, path, ent->d_name);
- report_garbage("garbage found", sb.buf);
- strbuf_release(&sb);
- }
- continue;
- }
- (*loose)++;
- if (!verbose)
- continue;
- memcpy(hex, path+len, 2);
- memcpy(hex+2, ent->d_name, 38);
- hex[40] = 0;
- if (get_sha1_hex(hex, sha1))
- die("internal error");
- if (has_sha1_pack(sha1))
- (*packed_loose)++;
+static int count_loose(const unsigned char *sha1, const char *path, void *data)
+{
+ struct stat st;
+
+ if (lstat(path, &st) || !S_ISREG(st.st_mode))
+ loose_garbage(path);
+ else {
+ loose_size += on_disk_bytes(st);
+ loose++;
+ if (verbose && has_sha1_pack(sha1))
+ packed_loose++;
}
+ return 0;
+}
+
+static int count_cruft(const char *basename, const char *path, void *data)
+{
+ loose_garbage(path);
+ return 0;
}
static char const * const count_objects_usage[] = {
@@ -85,12 +58,7 @@ static char const * const count_objects_usage[] = {
int cmd_count_objects(int argc, const char **argv, const char *prefix)
{
- int i, verbose = 0, human_readable = 0;
- const char *objdir = get_object_directory();
- int len = strlen(objdir);
- char *path = xmalloc(len + 50);
- unsigned long loose = 0, packed = 0, packed_loose = 0;
- off_t loose_size = 0;
+ int human_readable = 0;
struct option opts[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT_BOOL('H', "human-readable", &human_readable,
@@ -104,19 +72,10 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
usage_with_options(count_objects_usage, opts);
if (verbose)
report_garbage = real_report_garbage;
- memcpy(path, objdir, len);
- if (len && objdir[len-1] != '/')
- path[len++] = '/';
- for (i = 0; i < 256; i++) {
- DIR *d;
- sprintf(path + len, "%02x", i);
- d = opendir(path);
- if (!d)
- continue;
- count_objects(d, path, len, verbose,
- &loose, &loose_size, &packed_loose);
- closedir(d);
- }
+
+ for_each_loose_file_in_objdir(get_object_directory(),
+ count_loose, count_cruft, NULL, NULL);
+
if (verbose) {
struct packed_git *p;
unsigned long num_pack = 0;
diff --git a/builtin/describe.c b/builtin/describe.c
index ee6a3b998f..9103193b4f 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "commit.h"
#include "tag.h"
#include "refs.h"
diff --git a/builtin/diff.c b/builtin/diff.c
index 0f247d2400..4326fa56bf 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -4,6 +4,7 @@
* Copyright (c) 2006 Junio C Hamano
*/
#include "cache.h"
+#include "lockfile.h"
#include "color.h"
#include "commit.h"
#include "blob.h"
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 92b4624a4b..b8182c241d 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -18,6 +18,7 @@
#include "parse-options.h"
#include "quote.h"
#include "remote.h"
+#include "blob.h"
static const char *fast_export_usage[] = {
N_("git fast-export [rev-list-opts]"),
@@ -34,6 +35,7 @@ static int full_tree;
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
static struct refspec *refspecs;
static int refspecs_nr;
+static int anonymize;
static int parse_opt_signed_tag_mode(const struct option *opt,
const char *arg, int unset)
@@ -81,6 +83,76 @@ static int has_unshown_parent(struct commit *commit)
return 0;
}
+struct anonymized_entry {
+ struct hashmap_entry hash;
+ const char *orig;
+ size_t orig_len;
+ const char *anon;
+ size_t anon_len;
+};
+
+static int anonymized_entry_cmp(const void *va, const void *vb,
+ const void *data)
+{
+ const struct anonymized_entry *a = va, *b = vb;
+ return a->orig_len != b->orig_len ||
+ memcmp(a->orig, b->orig, a->orig_len);
+}
+
+/*
+ * Basically keep a cache of X->Y so that we can repeatedly replace
+ * the same anonymized string with another. The actual generation
+ * is farmed out to the generate function.
+ */
+static const void *anonymize_mem(struct hashmap *map,
+ void *(*generate)(const void *, size_t *),
+ const void *orig, size_t *len)
+{
+ struct anonymized_entry key, *ret;
+
+ if (!map->cmpfn)
+ hashmap_init(map, anonymized_entry_cmp, 0);
+
+ hashmap_entry_init(&key, memhash(orig, *len));
+ key.orig = orig;
+ key.orig_len = *len;
+ ret = hashmap_get(map, &key, NULL);
+
+ if (!ret) {
+ ret = xmalloc(sizeof(*ret));
+ hashmap_entry_init(&ret->hash, key.hash.hash);
+ ret->orig = xstrdup(orig);
+ ret->orig_len = *len;
+ ret->anon = generate(orig, len);
+ ret->anon_len = *len;
+ hashmap_put(map, ret);
+ }
+
+ *len = ret->anon_len;
+ return ret->anon;
+}
+
+/*
+ * We anonymize each component of a path individually,
+ * so that paths a/b and a/c will share a common root.
+ * The paths are cached via anonymize_mem so that repeated
+ * lookups for "a" will yield the same value.
+ */
+static void anonymize_path(struct strbuf *out, const char *path,
+ struct hashmap *map,
+ void *(*generate)(const void *, size_t *))
+{
+ while (*path) {
+ const char *end_of_component = strchrnul(path, '/');
+ size_t len = end_of_component - path;
+ const char *c = anonymize_mem(map, generate, path, &len);
+ strbuf_add(out, c, len);
+ path = end_of_component;
+ if (*path)
+ strbuf_addch(out, *path++);
+ }
+}
+
/* Since intptr_t is C99, we do not use it here */
static inline uint32_t *mark_to_ptr(uint32_t mark)
{
@@ -119,6 +191,26 @@ static void show_progress(void)
printf("progress %d objects\n", counter);
}
+/*
+ * Ideally we would want some transformation of the blob data here
+ * that is unreversible, but would still be the same size and have
+ * the same data relationship to other blobs (so that we get the same
+ * delta and packing behavior as the original). But the first and last
+ * requirements there are probably mutually exclusive, so let's take
+ * the easy way out for now, and just generate arbitrary content.
+ *
+ * There's no need to cache this result with anonymize_mem, since
+ * we already handle blob content caching with marks.
+ */
+static char *anonymize_blob(unsigned long *size)
+{
+ static int counter;
+ struct strbuf out = STRBUF_INIT;
+ strbuf_addf(&out, "anonymous blob %d", counter++);
+ *size = out.len;
+ return strbuf_detach(&out, NULL);
+}
+
static void export_blob(const unsigned char *sha1)
{
unsigned long size;
@@ -137,12 +229,19 @@ static void export_blob(const unsigned char *sha1)
if (object && object->flags & SHOWN)
return;
- buf = read_sha1_file(sha1, &type, &size);
- if (!buf)
- die ("Could not read blob %s", sha1_to_hex(sha1));
- if (check_sha1_signature(sha1, buf, size, typename(type)) < 0)
- die("sha1 mismatch in blob %s", sha1_to_hex(sha1));
- object = parse_object_buffer(sha1, type, size, buf, &eaten);
+ if (anonymize) {
+ buf = anonymize_blob(&size);
+ object = (struct object *)lookup_blob(sha1);
+ eaten = 0;
+ } else {
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ die ("Could not read blob %s", sha1_to_hex(sha1));
+ if (check_sha1_signature(sha1, buf, size, typename(type)) < 0)
+ die("sha1 mismatch in blob %s", sha1_to_hex(sha1));
+ object = parse_object_buffer(sha1, type, size, buf, &eaten);
+ }
+
if (!object)
die("Could not read blob %s", sha1_to_hex(sha1));
@@ -190,7 +289,7 @@ static int depth_first(const void *a_, const void *b_)
return (a->status == 'R') - (b->status == 'R');
}
-static void print_path(const char *path)
+static void print_path_1(const char *path)
{
int need_quote = quote_c_style(path, NULL, NULL, 0);
if (need_quote)
@@ -201,6 +300,43 @@ static void print_path(const char *path)
printf("%s", path);
}
+static void *anonymize_path_component(const void *path, size_t *len)
+{
+ static int counter;
+ struct strbuf out = STRBUF_INIT;
+ strbuf_addf(&out, "path%d", counter++);
+ return strbuf_detach(&out, len);
+}
+
+static void print_path(const char *path)
+{
+ if (!anonymize)
+ print_path_1(path);
+ else {
+ static struct hashmap paths;
+ static struct strbuf anon = STRBUF_INIT;
+
+ anonymize_path(&anon, path, &paths, anonymize_path_component);
+ print_path_1(anon.buf);
+ strbuf_reset(&anon);
+ }
+}
+
+static void *generate_fake_sha1(const void *old, size_t *len)
+{
+ static uint32_t counter = 1; /* avoid null sha1 */
+ unsigned char *out = xcalloc(20, 1);
+ put_be32(out + 16, counter++);
+ return out;
+}
+
+static const unsigned char *anonymize_sha1(const unsigned char *sha1)
+{
+ static struct hashmap sha1s;
+ size_t len = 20;
+ return anonymize_mem(&sha1s, generate_fake_sha1, sha1, &len);
+}
+
static void show_filemodify(struct diff_queue_struct *q,
struct diff_options *options, void *data)
{
@@ -245,7 +381,9 @@ static void show_filemodify(struct diff_queue_struct *q,
*/
if (no_data || S_ISGITLINK(spec->mode))
printf("M %06o %s ", spec->mode,
- sha1_to_hex(spec->sha1));
+ sha1_to_hex(anonymize ?
+ anonymize_sha1(spec->sha1) :
+ spec->sha1));
else {
struct object *object = lookup_object(spec->sha1);
printf("M %06o :%d ", spec->mode,
@@ -279,6 +417,114 @@ static const char *find_encoding(const char *begin, const char *end)
return bol;
}
+static void *anonymize_ref_component(const void *old, size_t *len)
+{
+ static int counter;
+ struct strbuf out = STRBUF_INIT;
+ strbuf_addf(&out, "ref%d", counter++);
+ return strbuf_detach(&out, len);
+}
+
+static const char *anonymize_refname(const char *refname)
+{
+ /*
+ * If any of these prefixes is found, we will leave it intact
+ * so that tags remain tags and so forth.
+ */
+ static const char *prefixes[] = {
+ "refs/heads/",
+ "refs/tags/",
+ "refs/remotes/",
+ "refs/"
+ };
+ static struct hashmap refs;
+ static struct strbuf anon = STRBUF_INIT;
+ int i;
+
+ /*
+ * We also leave "master" as a special case, since it does not reveal
+ * anything interesting.
+ */
+ if (!strcmp(refname, "refs/heads/master"))
+ return refname;
+
+ strbuf_reset(&anon);
+ for (i = 0; i < ARRAY_SIZE(prefixes); i++) {
+ if (skip_prefix(refname, prefixes[i], &refname)) {
+ strbuf_addstr(&anon, prefixes[i]);
+ break;
+ }
+ }
+
+ anonymize_path(&anon, refname, &refs, anonymize_ref_component);
+ return anon.buf;
+}
+
+/*
+ * We do not even bother to cache commit messages, as they are unlikely
+ * to be repeated verbatim, and it is not that interesting when they are.
+ */
+static char *anonymize_commit_message(const char *old)
+{
+ static int counter;
+ return xstrfmt("subject %d\n\nbody\n", counter++);
+}
+
+static struct hashmap idents;
+static void *anonymize_ident(const void *old, size_t *len)
+{
+ static int counter;
+ struct strbuf out = STRBUF_INIT;
+ strbuf_addf(&out, "User %d <user%d@example.com>", counter, counter);
+ counter++;
+ return strbuf_detach(&out, len);
+}
+
+/*
+ * Our strategy here is to anonymize the names and email addresses,
+ * but keep timestamps intact, as they influence things like traversal
+ * order (and by themselves should not be too revealing).
+ */
+static void anonymize_ident_line(const char **beg, const char **end)
+{
+ static struct strbuf buffers[] = { STRBUF_INIT, STRBUF_INIT };
+ static unsigned which_buffer;
+
+ struct strbuf *out;
+ struct ident_split split;
+ const char *end_of_header;
+
+ out = &buffers[which_buffer++];
+ which_buffer %= ARRAY_SIZE(buffers);
+ strbuf_reset(out);
+
+ /* skip "committer", "author", "tagger", etc */
+ end_of_header = strchr(*beg, ' ');
+ if (!end_of_header)
+ die("BUG: malformed line fed to anonymize_ident_line: %.*s",
+ (int)(*end - *beg), *beg);
+ end_of_header++;
+ strbuf_add(out, *beg, end_of_header - *beg);
+
+ if (!split_ident_line(&split, end_of_header, *end - end_of_header) &&
+ split.date_begin) {
+ const char *ident;
+ size_t len;
+
+ len = split.mail_end - split.name_begin;
+ ident = anonymize_mem(&idents, anonymize_ident,
+ split.name_begin, &len);
+ strbuf_add(out, ident, len);
+ strbuf_addch(out, ' ');
+ strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
+ } else {
+ strbuf_addstr(out, "Malformed Ident <malformed@example.com> 0 -0000");
+ }
+
+ *beg = out->buf;
+ *end = out->buf + out->len;
+}
+
static void handle_commit(struct commit *commit, struct rev_info *rev)
{
int saved_output_format = rev->diffopt.output_format;
@@ -287,6 +533,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
const char *encoding, *message;
char *reencoded = NULL;
struct commit_list *p;
+ const char *refname;
int i;
rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
@@ -326,13 +573,22 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
export_blob(diff_queued_diff.queue[i]->two->sha1);
+ refname = commit->util;
+ if (anonymize) {
+ refname = anonymize_refname(refname);
+ anonymize_ident_line(&committer, &committer_end);
+ anonymize_ident_line(&author, &author_end);
+ }
+
mark_next_object(&commit->object);
- if (!is_encoding_utf8(encoding))
+ if (anonymize)
+ reencoded = anonymize_commit_message(message);
+ else if (!is_encoding_utf8(encoding))
reencoded = reencode_string(message, "UTF-8", encoding);
if (!commit->parents)
- printf("reset %s\n", (const char*)commit->util);
+ printf("reset %s\n", refname);
printf("commit %s\nmark :%"PRIu32"\n%.*s\n%.*s\ndata %u\n%s",
- (const char *)commit->util, last_idnum,
+ refname, last_idnum,
(int)(author_end - author), author,
(int)(committer_end - committer), committer,
(unsigned)(reencoded
@@ -363,6 +619,14 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
show_progress();
}
+static void *anonymize_tag(const void *old, size_t *len)
+{
+ static int counter;
+ struct strbuf out = STRBUF_INIT;
+ strbuf_addf(&out, "tag message %d", counter++);
+ return strbuf_detach(&out, len);
+}
+
static void handle_tail(struct object_array *commits, struct rev_info *revs)
{
struct commit *commit;
@@ -419,6 +683,17 @@ static void handle_tag(const char *name, struct tag *tag)
} else {
tagger++;
tagger_end = strchrnul(tagger, '\n');
+ if (anonymize)
+ anonymize_ident_line(&tagger, &tagger_end);
+ }
+
+ if (anonymize) {
+ name = anonymize_refname(name);
+ if (message) {
+ static struct hashmap tags;
+ message = anonymize_mem(&tags, anonymize_tag,
+ message, &message_size);
+ }
}
/* handle signed tags */
@@ -584,6 +859,8 @@ static void handle_tags_and_duplicates(void)
handle_tag(name, (struct tag *)object);
break;
case OBJ_COMMIT:
+ if (anonymize)
+ name = anonymize_refname(name);
/* create refs pointing to already seen commits */
commit = (struct commit *)object;
printf("reset %s\nfrom :%d\n\n", name,
@@ -719,6 +996,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
N_("Apply refspec to exported refs")),
+ OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")),
OPT_END()
};
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 159fb7e916..6ffd02388b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -404,23 +404,37 @@ static int s_update_ref(const char *action,
{
char msg[1024];
char *rla = getenv("GIT_REFLOG_ACTION");
- static struct ref_lock *lock;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+ int ret, df_conflict = 0;
if (dry_run)
return 0;
if (!rla)
rla = default_rla.buf;
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
- lock = lock_any_ref_for_update(ref->name,
- check_old ? ref->old_sha1 : NULL,
- 0, NULL);
- if (!lock)
- return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
- STORE_REF_ERROR_OTHER;
- if (write_ref_sha1(lock, ref->new_sha1, msg) < 0)
- return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
- STORE_REF_ERROR_OTHER;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref->name, ref->new_sha1,
+ ref->old_sha1, 0, check_old, msg, &err))
+ goto fail;
+
+ ret = ref_transaction_commit(transaction, &err);
+ if (ret) {
+ df_conflict = (ret == TRANSACTION_NAME_CONFLICT);
+ goto fail;
+ }
+
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
return 0;
+fail:
+ ref_transaction_free(transaction);
+ error("%s", err.buf);
+ strbuf_release(&err);
+ return df_conflict ? STORE_REF_ERROR_DF_CONFLICT
+ : STORE_REF_ERROR_OTHER;
}
#define REFCOL_WIDTH 10
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 79df05ef52..37177c6c29 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -602,7 +602,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
/* get current branch */
current_branch = current_branch_to_free =
- resolve_refdup("HEAD", head_sha1, 1, NULL);
+ resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
if (!current_branch)
die("No current branch");
if (starts_with(current_branch, "refs/heads/"))
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 47bd624696..603a90e29b 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -138,10 +138,8 @@ static int parse_atom(const char *atom, const char *ep)
/* Add it in, including the deref prefix */
at = used_atom_cnt;
used_atom_cnt++;
- used_atom = xrealloc(used_atom,
- (sizeof *used_atom) * used_atom_cnt);
- used_atom_type = xrealloc(used_atom_type,
- (sizeof(*used_atom_type) * used_atom_cnt));
+ REALLOC_ARRAY(used_atom, used_atom_cnt);
+ REALLOC_ARRAY(used_atom_type, used_atom_cnt);
used_atom[at] = xmemdupz(atom, ep - atom);
used_atom_type[at] = valid_atom[i].cmp_type;
if (*atom == '*')
@@ -633,11 +631,12 @@ static void populate_value(struct refinfo *ref)
unsigned long size;
const unsigned char *tagged;
- ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
+ ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
unsigned char unused1[20];
- ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+ ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
+ unused1, NULL);
if (!ref->symref)
ref->symref = "";
}
@@ -673,7 +672,8 @@ static void populate_value(struct refinfo *ref)
} else if (starts_with(name, "color:")) {
char color[COLOR_MAXLEN] = "";
- color_parse(name + 6, "--format", color);
+ if (color_parse(name + 6, color) < 0)
+ die(_("unable to parse format"));
v->s = xstrdup(color);
continue;
} else if (!strcmp(name, "flag")) {
@@ -695,7 +695,8 @@ static void populate_value(struct refinfo *ref)
const char *head;
unsigned char sha1[20];
- head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ sha1, NULL);
if (!strcmp(ref->refname, head))
v->s = "*";
else
@@ -839,6 +840,11 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
struct refinfo *ref;
int cnt;
+ if (flag & REF_BAD_NAME) {
+ warning("ignoring ref with broken name %s", refname);
+ return 0;
+ }
+
if (*cb->grab_pattern) {
const char **pattern;
int namelen = strlen(refname);
@@ -870,8 +876,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
ref->flag = flag;
cnt = cb->grab_cnt;
- cb->grab_array = xrealloc(cb->grab_array,
- sizeof(*cb->grab_array) * (cnt + 1));
+ REALLOC_ARRAY(cb->grab_array, cnt + 1);
cb->grab_array[cnt++] = ref;
cb->grab_cnt = cnt;
return 0;
@@ -1007,7 +1012,8 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style)
struct atom_value resetv;
char color[COLOR_MAXLEN] = "";
- color_parse("reset", "--format", color);
+ if (color_parse("reset", color) < 0)
+ die("BUG: couldn't parse 'reset' as a color");
resetv.s = color;
print_value(&resetv, quote_style);
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 0928a98a71..a27515aeaa 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -298,7 +298,7 @@ static int fsck_obj(struct object *obj)
if (fsck_walk(obj, mark_used, NULL))
objerror(obj, "broken links");
- if (fsck_object(obj, check_strict, fsck_error_func))
+ if (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))
return -1;
if (obj->type == OBJ_TREE) {
@@ -556,7 +556,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", 0, head_sha1, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
diff --git a/builtin/gc.c b/builtin/gc.c
index 8d219d8c42..005adbebea 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -11,7 +11,7 @@
*/
#include "builtin.h"
-#include "cache.h"
+#include "lockfile.h"
#include "parse-options.h"
#include "run-command.h"
#include "sigchain.h"
@@ -55,44 +55,33 @@ static void remove_pidfile_on_signal(int signo)
raise(signo);
}
-static int gc_config(const char *var, const char *value, void *cb)
+static void gc_config(void)
{
- if (!strcmp(var, "gc.packrefs")) {
+ const char *value;
+
+ if (!git_config_get_value("gc.packrefs", &value)) {
if (value && !strcmp(value, "notbare"))
pack_refs = -1;
else
- pack_refs = git_config_bool(var, value);
- return 0;
- }
- if (!strcmp(var, "gc.aggressivewindow")) {
- aggressive_window = git_config_int(var, value);
- return 0;
- }
- if (!strcmp(var, "gc.aggressivedepth")) {
- aggressive_depth = git_config_int(var, value);
- return 0;
- }
- if (!strcmp(var, "gc.auto")) {
- gc_auto_threshold = git_config_int(var, value);
- return 0;
- }
- if (!strcmp(var, "gc.autopacklimit")) {
- gc_auto_pack_limit = git_config_int(var, value);
- return 0;
+ pack_refs = git_config_bool("gc.packrefs", value);
}
- if (!strcmp(var, "gc.autodetach")) {
- detach_auto = git_config_bool(var, value);
- return 0;
- }
- if (!strcmp(var, "gc.pruneexpire")) {
- if (value && strcmp(value, "now")) {
+
+ git_config_get_int("gc.aggressivewindow", &aggressive_window);
+ git_config_get_int("gc.aggressivedepth", &aggressive_depth);
+ git_config_get_int("gc.auto", &gc_auto_threshold);
+ git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
+ git_config_get_bool("gc.autodetach", &detach_auto);
+
+ if (!git_config_get_string_const("gc.pruneexpire", &prune_expire)) {
+ if (strcmp(prune_expire, "now")) {
unsigned long now = approxidate("now");
- if (approxidate(value) >= now)
- return error(_("Invalid %s: '%s'"), var, value);
+ if (approxidate(prune_expire) >= now) {
+ git_die_config("gc.pruneexpire", _("Invalid gc.pruneexpire: '%s'"),
+ prune_expire);
+ }
}
- return git_config_string(&prune_expire, var, value);
}
- return git_default_config(var, value, cb);
+ git_config(git_default_config, NULL);
}
static int too_many_loose_objects(void)
@@ -301,7 +290,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_pushl(&prune, "prune", "--expire", NULL );
argv_array_pushl(&rerere, "rerere", "gc", NULL);
- git_config(gc_config, NULL);
+ gc_config();
if (pack_refs < 0)
pack_refs = !is_bare_repository();
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index aa72596083..6f4147ad02 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -19,6 +19,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
char buffer[HEADERSIZE];
struct ustar_header *header = (struct ustar_header *)buffer;
char *content = buffer + RECORDSIZE;
+ const char *comment;
ssize_t n;
if (argc != 1)
@@ -29,10 +30,10 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
die("git get-tar-commit-id: read error");
if (header->typeflag[0] != 'g')
return 1;
- if (memcmp(content, "52 comment=", 11))
+ if (!skip_prefix(content, "52 comment=", &comment))
return 1;
- n = write_in_full(1, content + 11, 41);
+ n = write_in_full(1, comment, 41);
if (n < 41)
die_errno("git get-tar-commit-id: write error");
diff --git a/builtin/grep.c b/builtin/grep.c
index c86a142f30..4063882f06 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -456,10 +456,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
}
static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
- struct object *obj, const char *name, struct object_context *oc)
+ struct object *obj, const char *name, const char *path)
{
if (obj->type == OBJ_BLOB)
- return grep_sha1(opt, obj->sha1, name, 0, oc ? oc->path : NULL);
+ return grep_sha1(opt, obj->sha1, name, 0, path);
if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
struct tree_desc tree;
void *data;
@@ -501,7 +501,7 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
for (i = 0; i < nr; i++) {
struct object *real_obj;
real_obj = deref_tag(list->objects[i].item, NULL, 0);
- if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].context)) {
+ if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) {
hit = 1;
if (opt->status_only)
break;
@@ -821,7 +821,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
struct object *object = parse_object_or_die(sha1, arg);
if (!seen_dashdash)
verify_non_filename(prefix, arg);
- add_object_array_with_context(object, arg, &list, xmemdupz(&oc, sizeof(struct object_context)));
+ add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
continue;
}
if (!strcmp(arg, "--")) {
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index d7fcf4c13c..6158363318 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -10,35 +10,55 @@
#include "parse-options.h"
#include "exec_cmd.h"
-static void hash_fd(int fd, const char *type, int write_object, const char *path)
+/*
+ * This is to create corrupt objects for debugging and as such it
+ * needs to bypass the data conversion performed by, and the type
+ * limitation imposed by, index_fd() and its callees.
+ */
+static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigned flags)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int ret;
+
+ if (strbuf_read(&buf, fd, 4096) < 0)
+ ret = -1;
+ else if (flags & HASH_WRITE_OBJECT)
+ ret = write_sha1_file(buf.buf, buf.len, type, sha1);
+ else
+ ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
+ strbuf_release(&buf);
+ return ret;
+}
+
+static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
+ int literally)
{
struct stat st;
unsigned char sha1[20];
- unsigned flags = (HASH_FORMAT_CHECK |
- (write_object ? HASH_WRITE_OBJECT : 0));
if (fstat(fd, &st) < 0 ||
- index_fd(sha1, fd, &st, type_from_string(type), path, flags))
- die(write_object
+ (literally
+ ? hash_literally(sha1, fd, type, flags)
+ : index_fd(sha1, fd, &st, type_from_string(type), path, flags)))
+ die((flags & HASH_WRITE_OBJECT)
? "Unable to add %s to database"
: "Unable to hash %s", path);
printf("%s\n", sha1_to_hex(sha1));
maybe_flush_or_die(stdout, "hash to stdout");
}
-static void hash_object(const char *path, const char *type, int write_object,
- const char *vpath)
+static void hash_object(const char *path, const char *type, const char *vpath,
+ unsigned flags, int literally)
{
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
die_errno("Cannot open '%s'", path);
- hash_fd(fd, type, write_object, vpath);
+ hash_fd(fd, type, vpath, flags, literally);
}
-static int no_filters;
-
-static void hash_stdin_paths(const char *type, int write_objects)
+static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,
+ int literally)
{
struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
@@ -49,47 +69,46 @@ static void hash_stdin_paths(const char *type, int write_objects)
die("line is badly quoted");
strbuf_swap(&buf, &nbuf);
}
- hash_object(buf.buf, type, write_objects,
- no_filters ? NULL : buf.buf);
+ hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags,
+ literally);
}
strbuf_release(&buf);
strbuf_release(&nbuf);
}
-static const char * const hash_object_usage[] = {
- N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."),
- N_("git hash-object --stdin-paths < <list-of-paths>"),
- NULL
-};
-
-static const char *type;
-static int write_object;
-static int hashstdin;
-static int stdin_paths;
-static const char *vpath;
-
-static const struct option hash_object_options[] = {
- OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
- OPT_BOOL('w', NULL, &write_object, N_("write the object into the object database")),
- OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
- OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
- OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
- OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")),
- OPT_END()
-};
-
int cmd_hash_object(int argc, const char **argv, const char *prefix)
{
+ static const char * const hash_object_usage[] = {
+ N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."),
+ N_("git hash-object --stdin-paths < <list-of-paths>"),
+ NULL
+ };
+ const char *type = blob_type;
+ int hashstdin = 0;
+ int stdin_paths = 0;
+ int no_filters = 0;
+ int literally = 0;
+ unsigned flags = HASH_FORMAT_CHECK;
+ const char *vpath = NULL;
+ const struct option hash_object_options[] = {
+ OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
+ OPT_BIT('w', NULL, &flags, N_("write the object into the object database"),
+ HASH_WRITE_OBJECT),
+ OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
+ OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
+ OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
+ OPT_BOOL( 0, "literally", &literally, N_("just hash any random garbage to create corrupt objects for debugging Git")),
+ OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")),
+ OPT_END()
+ };
int i;
int prefix_length = -1;
const char *errstr = NULL;
- type = blob_type;
-
argc = parse_options(argc, argv, NULL, hash_object_options,
hash_object_usage, 0);
- if (write_object) {
+ if (flags & HASH_WRITE_OBJECT) {
prefix = setup_git_directory();
prefix_length = prefix ? strlen(prefix) : 0;
if (vpath && prefix)
@@ -119,19 +138,19 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
}
if (hashstdin)
- hash_fd(0, type, write_object, vpath);
+ hash_fd(0, type, vpath, flags, literally);
for (i = 0 ; i < argc; i++) {
const char *arg = argv[i];
if (0 <= prefix_length)
arg = prefix_filename(prefix, prefix_length, arg);
- hash_object(arg, type, write_object,
- no_filters ? NULL : vpath ? vpath : arg);
+ hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,
+ flags, literally);
}
if (stdin_paths)
- hash_stdin_paths(type, write_object);
+ hash_stdin_paths(type, no_filters, flags, literally);
return 0;
}
diff --git a/builtin/help.c b/builtin/help.c
index 1fdefeb686..b3c818ee01 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -79,12 +79,11 @@ static const char *get_man_viewer_info(const char *name)
static int check_emacsclient_version(void)
{
struct strbuf buffer = STRBUF_INIT;
- struct child_process ec_process;
+ struct child_process ec_process = CHILD_PROCESS_INIT;
const char *argv_ec[] = { "emacsclient", "--version", NULL };
int version;
/* emacsclient prints its version number on stderr */
- memset(&ec_process, 0, sizeof(ec_process));
ec_process.argv = argv_ec;
ec_process.err = -1;
ec_process.stdout_to_stderr = 1;
@@ -422,6 +421,7 @@ static struct {
const char *help;
} common_guides[] = {
{ "attributes", N_("Defining attributes per path") },
+ { "everyday", N_("Everyday Git With 20 Commands Or So") },
{ "glossary", N_("A Git glossary") },
{ "ignore", N_("Specifies intentionally untracked files to ignore") },
{ "modules", N_("Defining submodule properties") },
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index eebf1a8fc2..a369f55353 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -185,6 +185,9 @@ static void cleanup_thread(void)
#define deepest_delta_lock()
#define deepest_delta_unlock()
+#define type_cas_lock()
+#define type_cas_unlock()
+
#endif
@@ -779,7 +782,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
if (!obj)
die(_("invalid %s"), typename(type));
if (do_fsck_object &&
- fsck_object(obj, 1, fsck_error_function))
+ fsck_object(obj, buf, size, 1,
+ fsck_error_function))
die(_("Error in object"));
if (fsck_walk(obj, mark_link, NULL))
die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
@@ -1169,9 +1173,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
int nr_objects_initial = nr_objects;
if (nr_unresolved <= 0)
die(_("confusion beyond insanity"));
- objects = xrealloc(objects,
- (nr_objects + nr_unresolved + 1)
- * sizeof(*objects));
+ REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1);
memset(objects + nr_objects + 1, 0,
nr_unresolved * sizeof(*objects));
f = sha1fd(output_fd, curr_pack);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 56f85e239a..587a5055ed 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -330,12 +330,12 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir,
* moving the target repo later on in separate_git_dir()
*/
git_link = xstrdup(real_path(git_dir));
+ set_git_dir(real_path(real_git_dir));
}
else {
- real_git_dir = real_path(git_dir);
+ set_git_dir(real_path(git_dir));
git_link = NULL;
}
- set_git_dir(real_path(real_git_dir));
return 0;
}
@@ -426,8 +426,9 @@ int init_db(const char *template_dir, unsigned int flags)
static int guess_repository_type(const char *git_dir)
{
- char cwd[PATH_MAX];
const char *slash;
+ char *cwd;
+ int cwd_is_git_dir;
/*
* "GIT_DIR=. git init" is always bare.
@@ -435,9 +436,10 @@ static int guess_repository_type(const char *git_dir)
*/
if (!strcmp(".", git_dir))
return 1;
- if (!getcwd(cwd, sizeof(cwd)))
- die_errno(_("cannot tell cwd"));
- if (!strcmp(git_dir, cwd))
+ cwd = xgetcwd();
+ cwd_is_git_dir = !strcmp(git_dir, cwd);
+ free(cwd);
+ if (cwd_is_git_dir)
return 1;
/*
* "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
@@ -535,10 +537,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
usage(init_db_usage[0]);
}
if (is_bare_repository_cfg == 1) {
- static char git_dir[PATH_MAX+1];
-
- setenv(GIT_DIR_ENVIRONMENT,
- getcwd(git_dir, sizeof(git_dir)), argc > 0);
+ char *cwd = xgetcwd();
+ setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0);
+ free(cwd);
}
if (init_shared_repository != -1)
@@ -572,13 +573,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
git_work_tree_cfg = xstrdup(real_path(rel));
free(rel);
}
- if (!git_work_tree_cfg) {
- git_work_tree_cfg = xcalloc(PATH_MAX, 1);
- if (!getcwd(git_work_tree_cfg, PATH_MAX))
- die_errno (_("Cannot access current working directory"));
- }
+ if (!git_work_tree_cfg)
+ git_work_tree_cfg = xgetcwd();
if (work_tree)
- set_git_work_tree(real_path(work_tree));
+ set_git_work_tree(work_tree);
else
set_git_work_tree(git_work_tree_cfg);
if (access(get_git_work_tree(), X_OK))
@@ -587,7 +585,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
}
else {
if (work_tree)
- set_git_work_tree(real_path(work_tree));
+ set_git_work_tree(work_tree);
}
set_git_dir_init(git_dir, real_git_dir, 1);
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
new file mode 100644
index 0000000000..46838d24a9
--- /dev/null
+++ b/builtin/interpret-trailers.c
@@ -0,0 +1,44 @@
+/*
+ * Builtin "git interpret-trailers"
+ *
+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
+ *
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "trailer.h"
+
+static const char * const git_interpret_trailers_usage[] = {
+ N_("git interpret-trailers [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
+ NULL
+};
+
+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
+{
+ int trim_empty = 0;
+ struct string_list trailers = STRING_LIST_INIT_DUP;
+
+ struct option options[] = {
+ OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
+ OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
+ N_("trailer(s) to add")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_interpret_trailers_usage, 0);
+
+ if (argc) {
+ int i;
+ for (i = 0; i < argc; i++)
+ process_trailers(argv[i], trim_empty, &trailers);
+ } else
+ process_trailers(NULL, trim_empty, &trailers);
+
+ string_list_clear(&trailers, 0);
+
+ return 0;
+}
diff --git a/builtin/log.c b/builtin/log.c
index 4389722b4b..734aab3a73 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -78,7 +78,7 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse
decoration_style = DECORATE_SHORT_REFS;
if (decoration_style < 0)
- die("invalid --decorate option: %s", arg);
+ die(_("invalid --decorate option: %s"), arg);
decoration_given = 1;
@@ -130,7 +130,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback},
OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
- "Process line range n,m in file, counting from 1",
+ N_("Process line range n,m in file, counting from 1"),
log_line_range_callback),
OPT_END()
};
@@ -150,7 +150,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
/* Any arguments at this point are not recognized */
if (argc > 1)
- die("unrecognized argument: %s", argv[1]);
+ die(_("unrecognized argument: %s"), argv[1]);
memset(&w, 0, sizeof(w));
userformat_find_requirements(NULL, &w);
@@ -368,6 +368,8 @@ static int cmd_log_walk(struct rev_info *rev)
static int git_log_config(const char *var, const char *value, void *cb)
{
+ const char *slot_name;
+
if (!strcmp(var, "format.pretty"))
return git_config_string(&fmt_pretty, var, value);
if (!strcmp(var, "format.subjectprefix"))
@@ -388,8 +390,8 @@ static int git_log_config(const char *var, const char *value, void *cb)
default_show_root = git_config_bool(var, value);
return 0;
}
- if (starts_with(var, "color.decorate."))
- return parse_decorate_color_config(var, 15, value);
+ if (skip_prefix(var, "color.decorate.", &slot_name))
+ return parse_decorate_color_config(var, slot_name, value);
if (!strcmp(var, "log.mailmap")) {
use_mailmap_config = git_config_bool(var, value);
return 0;
@@ -447,13 +449,13 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con
return stream_blob_to_fd(1, sha1, NULL, 0);
if (get_sha1_with_context(obj_name, 0, sha1c, &obj_context))
- die("Not a valid object name %s", obj_name);
+ die(_("Not a valid object name %s"), obj_name);
if (!obj_context.path[0] ||
!textconv_object(obj_context.path, obj_context.mode, sha1c, 1, &buf, &size))
return stream_blob_to_fd(1, sha1, NULL, 0);
if (!buf)
- die("git show %s: bad file", obj_name);
+ die(_("git show %s: bad file"), obj_name);
write_or_die(1, buf, size);
return 0;
@@ -864,6 +866,7 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
strbuf_addbuf(buf, &desc);
strbuf_addch(buf, '\n');
}
+ strbuf_release(&desc);
}
static char *find_branch_name(struct rev_info *rev)
@@ -1397,7 +1400,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (check_head) {
unsigned char sha1[20];
const char *ref, *v;
- ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+ ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ sha1, NULL);
if (ref && skip_prefix(ref, "refs/heads/", &v))
branch_name = xstrdup(v);
else
@@ -1439,7 +1443,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
continue;
nr++;
- list = xrealloc(list, nr * sizeof(list[0]));
+ REALLOC_ARRAY(list, nr);
list[nr - 1] = commit;
}
if (nr == 0)
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 47c38808a2..99cee20fb0 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -474,7 +474,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
OPT_BOOL('k', "killed", &show_killed,
N_("show files on the filesystem that need to be removed")),
OPT_BIT(0, "directory", &dir.flags,
- N_("show 'other' directories' name only"),
+ N_("show 'other' directories' names only"),
DIR_SHOW_OTHER_DIRECTORIES),
OPT_NEGBIT(0, "empty-directory", &dir.flags,
N_("don't show empty directories"),
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 06296d4bdf..8e02ea109a 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -53,14 +53,15 @@ static int keep_cr;
*/
static int split_one(FILE *mbox, const char *name, int allow_bare)
{
- FILE *output = NULL;
+ FILE *output;
int fd;
int status = 0;
int is_bare = !is_from_line(buf.buf, buf.len);
- if (is_bare && !allow_bare)
- goto corrupt;
-
+ if (is_bare && !allow_bare) {
+ fprintf(stderr, "corrupt mailbox\n");
+ exit(1);
+ }
fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0)
die_errno("cannot open output file '%s'", name);
@@ -91,13 +92,6 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
}
fclose(output);
return status;
-
- corrupt:
- if (output)
- fclose(output);
- unlink(name);
- fprintf(stderr, "corrupt mailbox\n");
- exit(1);
}
static int populate_maildir_list(struct string_list *list, const char *path)
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 61cbde4094..f9ab48597e 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -25,7 +25,7 @@ static void add_merge_entry(struct merge_list *entry)
merge_result_end = &entry->next;
}
-static void merge_trees_recursive(struct tree_desc t[3], const char *base, int df_conflict);
+static void merge_trees(struct tree_desc t[3], const char *base);
static const char *explanation(struct merge_list *entry)
{
@@ -195,8 +195,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
add_merge_entry(final);
}
-static void unresolved_directory(const struct traverse_info *info, struct name_entry n[3],
- int df_conflict)
+static void unresolved_directory(const struct traverse_info *info,
+ struct name_entry n[3])
{
char *newbase;
struct name_entry *p;
@@ -218,7 +218,7 @@ static void unresolved_directory(const struct traverse_info *info, struct name_e
buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2));
#undef ENTRY_SHA1
- merge_trees_recursive(t, newbase, df_conflict);
+ merge_trees(t, newbase);
free(buf0);
free(buf1);
@@ -259,7 +259,7 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])
dirmask |= (1 << i);
}
- unresolved_directory(info, n, dirmask && (dirmask != mask));
+ unresolved_directory(info, n);
if (dirmask == mask)
return;
@@ -335,21 +335,15 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
return mask;
}
-static void merge_trees_recursive(struct tree_desc t[3], const char *base, int df_conflict)
+static void merge_trees(struct tree_desc t[3], const char *base)
{
struct traverse_info info;
setup_traverse_info(&info, base);
- info.data = &df_conflict;
info.fn = threeway_callback;
traverse_trees(3, t, &info);
}
-static void merge_trees(struct tree_desc t[3], const char *base)
-{
- merge_trees_recursive(t, base, 0);
-}
-
static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)
{
unsigned char sha1[20];
diff --git a/builtin/merge.c b/builtin/merge.c
index ce82eb297d..bebbe5b308 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -9,6 +9,7 @@
#include "cache.h"
#include "parse-options.h"
#include "builtin.h"
+#include "lockfile.h"
#include "run-command.h"
#include "diff.h"
#include "refs.h"
@@ -237,11 +238,10 @@ static void drop_save(void)
static int save_state(unsigned char *stash)
{
int len;
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buffer = STRBUF_INIT;
const char *argv[] = {"stash", "create", NULL};
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.out = -1;
cp.git_cmd = 1;
@@ -557,7 +557,7 @@ static void parse_branch_merge_options(char *bmo)
if (argc < 0)
die(_("Bad branch.%s.mergeoptions string: %s"), branch,
split_cmdline_strerror(argc));
- argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+ REALLOC_ARRAY(argv, argc + 2);
memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
argc++;
argv[0] = "branch.*.mergeoptions";
@@ -657,19 +657,18 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
struct commit_list *remoteheads,
struct commit *head, const char *head_arg)
{
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ static struct lock_file lock;
- hold_locked_index(lock, 1);
+ hold_locked_index(&lock, 1);
refresh_cache(REFRESH_QUIET);
if (active_cache_changed &&
- write_locked_index(&the_index, lock, COMMIT_LOCK))
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
return error(_("Unable to write index."));
- rollback_lock_file(lock);
+ rollback_lock_file(&lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
int clean, x;
struct commit *result;
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
struct commit_list *reversed = NULL;
struct merge_options o;
struct commit_list *j;
@@ -697,13 +696,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
for (j = common; j; j = j->next)
commit_list_insert(j->item, &reversed);
- hold_locked_index(lock, 1);
+ hold_locked_index(&lock, 1);
clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result);
if (active_cache_changed &&
- write_locked_index(&the_index, lock, COMMIT_LOCK))
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
die (_("unable to write %s"), get_index_file());
- rollback_lock_file(lock);
+ rollback_lock_file(&lock);
return clean ? 0 : 1;
} else {
return try_merge_command(strategy, xopts_nr, xopts,
@@ -1102,7 +1101,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 = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
@@ -1144,14 +1143,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
*/
if (advice_resolve_conflict)
die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
- "Please, commit your changes before you can merge."));
+ "Please, commit your changes before you merge."));
else
die(_("You have not concluded your merge (MERGE_HEAD exists)."));
}
if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
if (advice_resolve_conflict)
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
- "Please, commit your changes before you can merge."));
+ "Please, commit your changes before you merge."));
else
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
}
diff --git a/builtin/mv.c b/builtin/mv.c
index 6ffe540c20..563d05ba1a 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2006 Johannes Schindelin
*/
-#include "cache.h"
#include "builtin.h"
+#include "lockfile.h"
#include "dir.h"
#include "cache-tree.h"
#include "string-list.h"
@@ -61,6 +61,46 @@ static const char *add_slash(const char *path)
static struct lock_file lock_file;
#define SUBMODULE_WITH_GITDIR ((const char *)1)
+static void prepare_move_submodule(const char *src, int first,
+ const char **submodule_gitfile)
+{
+ struct strbuf submodule_dotgit = STRBUF_INIT;
+ if (!S_ISGITLINK(active_cache[first]->ce_mode))
+ die(_("Directory %s is in index and no submodule?"), src);
+ if (!is_staging_gitmodules_ok())
+ die(_("Please stage your changes to .gitmodules or stash them to proceed"));
+ strbuf_addf(&submodule_dotgit, "%s/.git", src);
+ *submodule_gitfile = read_gitfile(submodule_dotgit.buf);
+ if (*submodule_gitfile)
+ *submodule_gitfile = xstrdup(*submodule_gitfile);
+ else
+ *submodule_gitfile = SUBMODULE_WITH_GITDIR;
+ strbuf_release(&submodule_dotgit);
+}
+
+static int index_range_of_same_dir(const char *src, int length,
+ int *first_p, int *last_p)
+{
+ const char *src_w_slash = add_slash(src);
+ int first, last, len_w_slash = length + 1;
+
+ first = cache_name_pos(src_w_slash, len_w_slash);
+ if (first >= 0)
+ die(_("%.*s is in index"), len_w_slash, src_w_slash);
+
+ first = -1 - first;
+ for (last = first; last < active_nr; last++) {
+ const char *path = active_cache[last]->name;
+ if (strncmp(path, src_w_slash, len_w_slash))
+ break;
+ }
+ if (src_w_slash != src)
+ free((char *)src_w_slash);
+ *first_p = first;
+ *last_p = last;
+ return last - first;
+}
+
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, gitmodules_modified = 0;
@@ -108,7 +148,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
} else {
if (argc != 1)
- die("destination '%s' is not a directory", dest_path[0]);
+ die(_("destination '%s' is not a directory"), dest_path[0]);
destination = dest_path;
}
@@ -131,75 +171,36 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
&& lstat(dst, &st) == 0)
bad = _("cannot move directory over file");
else if (src_is_dir) {
- int first = cache_name_pos(src, length);
- if (first >= 0) {
- struct strbuf submodule_dotgit = STRBUF_INIT;
- if (!S_ISGITLINK(active_cache[first]->ce_mode))
- die (_("Huh? Directory %s is in index and no submodule?"), src);
- if (!is_staging_gitmodules_ok())
- die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
- strbuf_addf(&submodule_dotgit, "%s/.git", src);
- submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);
- if (submodule_gitfile[i])
- submodule_gitfile[i] = xstrdup(submodule_gitfile[i]);
- else
- submodule_gitfile[i] = SUBMODULE_WITH_GITDIR;
- strbuf_release(&submodule_dotgit);
- } else {
- const char *src_w_slash = add_slash(src);
- int last, len_w_slash = length + 1;
+ int first = cache_name_pos(src, length), last;
- modes[i] = WORKING_DIRECTORY;
+ if (first >= 0)
+ prepare_move_submodule(src, first,
+ submodule_gitfile + i);
+ else if (index_range_of_same_dir(src, length,
+ &first, &last) < 1)
+ bad = _("source directory is empty");
+ else { /* last - first >= 1 */
+ int j, dst_len, n;
- first = cache_name_pos(src_w_slash, len_w_slash);
- if (first >= 0)
- die (_("Huh? %.*s is in index?"),
- len_w_slash, src_w_slash);
-
- first = -1 - first;
- for (last = first; last < active_nr; last++) {
- const char *path = active_cache[last]->name;
- if (strncmp(path, src_w_slash, len_w_slash))
- break;
- }
- if (src_w_slash != src)
- free((char *)src_w_slash);
-
- if (last - first < 1)
- bad = _("source directory is empty");
- else {
- int j, dst_len;
-
- if (last - first > 0) {
- source = xrealloc(source,
- (argc + last - first)
- * sizeof(char *));
- destination = xrealloc(destination,
- (argc + last - first)
- * sizeof(char *));
- modes = xrealloc(modes,
- (argc + last - first)
- * sizeof(enum update_mode));
- submodule_gitfile = xrealloc(submodule_gitfile,
- (argc + last - first)
- * sizeof(char *));
- }
+ modes[i] = WORKING_DIRECTORY;
+ n = argc + last - first;
+ REALLOC_ARRAY(source, n);
+ REALLOC_ARRAY(destination, n);
+ REALLOC_ARRAY(modes, n);
+ REALLOC_ARRAY(submodule_gitfile, n);
- dst = add_slash(dst);
- dst_len = strlen(dst);
+ dst = add_slash(dst);
+ dst_len = strlen(dst);
- for (j = 0; j < last - first; j++) {
- const char *path =
- active_cache[first + j]->name;
- source[argc + j] = path;
- destination[argc + j] =
- prefix_path(dst, dst_len,
- path + length + 1);
- modes[argc + j] = INDEX;
- submodule_gitfile[argc + j] = NULL;
- }
- argc += last - first;
+ for (j = 0; j < last - first; j++) {
+ const char *path = active_cache[first + j]->name;
+ source[argc + j] = path;
+ destination[argc + j] =
+ prefix_path(dst, dst_len, path + length + 1);
+ modes[argc + j] = INDEX;
+ submodule_gitfile[argc + j] = NULL;
}
+ argc += last - first;
}
} else if (cache_name_pos(src, length) < 0)
bad = _("not under version control");
@@ -225,24 +226,22 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
else
string_list_insert(&src_for_dst, dst);
- if (bad) {
- if (ignore_errors) {
- if (--argc > 0) {
- memmove(source + i, source + i + 1,
- (argc - i) * sizeof(char *));
- memmove(destination + i,
- destination + i + 1,
- (argc - i) * sizeof(char *));
- memmove(modes + i, modes + i + 1,
- (argc - i) * sizeof(enum update_mode));
- memmove(submodule_gitfile + i,
- submodule_gitfile + i + 1,
- (argc - i) * sizeof(char *));
- i--;
- }
- } else
- die (_("%s, source=%s, destination=%s"),
- bad, src, dst);
+ if (!bad)
+ continue;
+ if (!ignore_errors)
+ die(_("%s, source=%s, destination=%s"),
+ bad, src, dst);
+ if (--argc > 0) {
+ int n = argc - i;
+ memmove(source + i, source + i + 1,
+ n * sizeof(char *));
+ memmove(destination + i, destination + i + 1,
+ n * sizeof(char *));
+ memmove(modes + i, modes + i + 1,
+ n * sizeof(enum update_mode));
+ memmove(submodule_gitfile + i, submodule_gitfile + i + 1,
+ n * sizeof(char *));
+ i--;
}
}
@@ -254,7 +253,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
printf(_("Renaming %s to %s\n"), src, dst);
if (!show_only && mode != INDEX) {
if (rename(src, dst) < 0 && !ignore_errors)
- die_errno (_("renaming '%s' failed"), src);
+ die_errno(_("renaming '%s' failed"), src);
if (submodule_gitfile[i]) {
if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
@@ -275,10 +274,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
if (gitmodules_modified)
stage_updated_gitmodules();
- if (active_cache_changed) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("Unable to write new index file"));
- }
+ if (active_cache_changed &&
+ write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ die(_("Unable to write new index file"));
return 0;
}
diff --git a/builtin/notes.c b/builtin/notes.c
index 820c34135c..68b6cd8cc1 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -122,12 +122,11 @@ static void write_commented_object(int fd, const unsigned char *object)
{
const char *show_args[5] =
{"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
- struct child_process show;
+ struct child_process show = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
struct strbuf cbuf = STRBUF_INIT;
/* Invoke "git show --stat --no-notes $object" */
- memset(&show, 0, sizeof(show));
show.argv = show_args;
show.no_stdin = 1;
show.out = -1;
@@ -211,7 +210,7 @@ static void create_note(const unsigned char *object, struct msg_arg *msg,
if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
error(_("unable to write note object"));
if (path)
- error(_("The note contents has been left in %s"),
+ error(_("The note contents have been left in %s"),
path);
exit(128);
}
@@ -703,7 +702,7 @@ static int merge_commit(struct notes_merge_options *o)
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
o->local_ref = local_ref_to_free =
- resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
+ resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL);
if (!o->local_ref)
die("Failed to resolve NOTES_MERGE_REF");
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index b59f5d895e..3f9f5c7760 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -20,6 +20,9 @@
#include "streaming.h"
#include "thread-utils.h"
#include "pack-bitmap.h"
+#include "reachable.h"
+#include "sha1-array.h"
+#include "argv-array.h"
static const char *pack_usage[] = {
N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@ -89,8 +92,7 @@ static void index_commit_for_bitmap(struct commit *commit)
{
if (indexed_commits_nr >= indexed_commits_alloc) {
indexed_commits_alloc = (indexed_commits_alloc + 32) * 2;
- indexed_commits = xrealloc(indexed_commits,
- indexed_commits_alloc * sizeof(struct commit *));
+ REALLOC_ARRAY(indexed_commits, indexed_commits_alloc);
}
indexed_commits[indexed_commits_nr++] = commit;
@@ -812,6 +814,7 @@ static void write_pack_file(void)
fixup_pack_header_footer(fd, sha1, pack_tmp_name,
nr_written, sha1, offset);
close(fd);
+ write_bitmap_index = 0;
}
if (!pack_to_stdout) {
@@ -1973,8 +1976,6 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
init_threaded_search();
- if (!delta_search_threads) /* --threads=0 means autodetect */
- delta_search_threads = online_cpus();
if (delta_search_threads <= 1) {
find_deltas(list, &list_size, window, depth, processed);
cleanup_threaded_search();
@@ -2408,6 +2409,27 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
return 0;
}
+/*
+ * Store a list of sha1s that are should not be discarded
+ * because they are either written too recently, or are
+ * reachable from another object that was.
+ *
+ * This is filled by get_object_list.
+ */
+static struct sha1_array recent_objects;
+
+static int loosened_object_can_be_discarded(const unsigned char *sha1,
+ unsigned long mtime)
+{
+ if (!unpack_unreachable_expiration)
+ return 0;
+ if (mtime > unpack_unreachable_expiration)
+ return 0;
+ if (sha1_array_lookup(&recent_objects, sha1) >= 0)
+ return 0;
+ return 1;
+}
+
static void loosen_unused_packed_objects(struct rev_info *revs)
{
struct packed_git *p;
@@ -2418,17 +2440,14 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
if (!p->pack_local || p->pack_keep)
continue;
- if (unpack_unreachable_expiration &&
- p->mtime < unpack_unreachable_expiration)
- continue;
-
if (open_pack_index(p))
die("cannot open pack index");
for (i = 0; i < p->num_objects; i++) {
sha1 = nth_packed_object_sha1(p, i);
if (!packlist_find(&to_pack, sha1, NULL) &&
- !has_sha1_pack_kept_or_nonlocal(sha1))
+ !has_sha1_pack_kept_or_nonlocal(sha1) &&
+ !loosened_object_can_be_discarded(sha1, p->mtime))
if (force_object_loose(sha1, p->mtime))
die("unable to force loose object");
}
@@ -2464,6 +2483,19 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
return 0;
}
+static void record_recent_object(struct object *obj,
+ const struct name_path *path,
+ const char *last,
+ void *data)
+{
+ sha1_array_append(&recent_objects, obj->sha1);
+}
+
+static void record_recent_commit(struct commit *commit, void *data)
+{
+ sha1_array_append(&recent_objects, commit->object.sha1);
+}
+
static void get_object_list(int ac, const char **av)
{
struct rev_info revs;
@@ -2511,10 +2543,23 @@ static void get_object_list(int ac, const char **av)
mark_edges_uninteresting(&revs, show_edge);
traverse_commit_list(&revs, show_commit, show_object, NULL);
+ if (unpack_unreachable_expiration) {
+ revs.ignore_missing_links = 1;
+ if (add_unseen_recent_objects_to_traversal(&revs,
+ unpack_unreachable_expiration))
+ die("unable to add recent objects");
+ if (prepare_revision_walk(&revs))
+ die("revision walk setup failed");
+ traverse_commit_list(&revs, record_recent_commit,
+ record_recent_object, NULL);
+ }
+
if (keep_unreachable)
add_objects_in_unpacked_packs(&revs);
if (unpack_unreachable)
loosen_unused_packed_objects(&revs);
+
+ sha1_array_clear(&recent_objects);
}
static int option_parse_index_version(const struct option *opt,
@@ -2569,9 +2614,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
int use_internal_rev_list = 0;
int thin = 0;
int all_progress_implied = 0;
- const char *rp_av[6];
- int rp_ac = 0;
+ struct argv_array rp = ARGV_ARRAY_INIT;
int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
+ int rev_list_index = 0;
struct option pack_objects_options[] = {
OPT_SET_INT('q', "quiet", &progress,
N_("do not show progress meter"), 0),
@@ -2618,6 +2663,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
{ OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
N_("include objects referred by reflog entries"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+ { OPTION_SET_INT, 0, "indexed-objects", &rev_list_index, NULL,
+ N_("include objects referred to by the index"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
OPT_BOOL(0, "stdout", &pack_to_stdout,
N_("output pack to stdout")),
OPT_BOOL(0, "include-tag", &include_tag,
@@ -2660,24 +2708,28 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (pack_to_stdout != !base_name || argc)
usage_with_options(pack_usage, pack_objects_options);
- rp_av[rp_ac++] = "pack-objects";
+ argv_array_push(&rp, "pack-objects");
if (thin) {
use_internal_rev_list = 1;
- rp_av[rp_ac++] = "--objects-edge";
+ argv_array_push(&rp, "--objects-edge");
} else
- rp_av[rp_ac++] = "--objects";
+ argv_array_push(&rp, "--objects");
if (rev_list_all) {
use_internal_rev_list = 1;
- rp_av[rp_ac++] = "--all";
+ argv_array_push(&rp, "--all");
}
if (rev_list_reflog) {
use_internal_rev_list = 1;
- rp_av[rp_ac++] = "--reflog";
+ argv_array_push(&rp, "--reflog");
+ }
+ if (rev_list_index) {
+ use_internal_rev_list = 1;
+ argv_array_push(&rp, "--indexed-objects");
}
if (rev_list_unpacked) {
use_internal_rev_list = 1;
- rp_av[rp_ac++] = "--unpacked";
+ argv_array_push(&rp, "--unpacked");
}
if (!reuse_object)
@@ -2686,6 +2738,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
pack_compression_level = Z_DEFAULT_COMPRESSION;
else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
die("bad pack compression level %d", pack_compression_level);
+
+ if (!delta_search_threads) /* --threads=0 means autodetect */
+ delta_search_threads = online_cpus();
+
#ifdef NO_PTHREADS
if (delta_search_threads != 1)
warning("no threads support, ignoring --threads");
@@ -2704,6 +2760,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (keep_unreachable && unpack_unreachable)
die("--keep-unreachable and --unpack-unreachable are incompatible.");
+ if (!rev_list_all || !rev_list_reflog || !rev_list_index)
+ unpack_unreachable_expiration = 0;
if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow())
use_bitmap_index = 0;
@@ -2721,8 +2779,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!use_internal_rev_list)
read_object_list_from_stdin();
else {
- rp_av[rp_ac] = NULL;
- get_object_list(rp_ac, rp_av);
+ get_object_list(rp.argc, rp.argv);
+ argv_array_clear(&rp);
}
cleanup_preferred_base();
if (include_tag && nr_result)
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index 6879468c46..f24a2c2bdc 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -10,63 +10,41 @@ static const char * const prune_packed_usage[] = {
static struct progress *progress;
-static void prune_dir(int i, DIR *dir, struct strbuf *pathname, int opts)
+static int prune_subdir(int nr, const char *path, void *data)
{
- struct dirent *de;
- char hex[40];
- int top_len = pathname->len;
+ int *opts = data;
+ display_progress(progress, nr + 1);
+ if (!(*opts & PRUNE_PACKED_DRY_RUN))
+ rmdir(path);
+ return 0;
+}
+
+static int prune_object(const unsigned char *sha1, const char *path,
+ void *data)
+{
+ int *opts = data;
- sprintf(hex, "%02x", i);
- while ((de = readdir(dir)) != NULL) {
- unsigned char sha1[20];
- if (strlen(de->d_name) != 38)
- continue;
- memcpy(hex + 2, de->d_name, 38);
- if (get_sha1_hex(hex, sha1))
- continue;
- if (!has_sha1_pack(sha1))
- continue;
+ if (!has_sha1_pack(sha1))
+ return 0;
- strbuf_add(pathname, de->d_name, 38);
- if (opts & PRUNE_PACKED_DRY_RUN)
- printf("rm -f %s\n", pathname->buf);
- else
- unlink_or_warn(pathname->buf);
- display_progress(progress, i + 1);
- strbuf_setlen(pathname, top_len);
- }
+ if (*opts & PRUNE_PACKED_DRY_RUN)
+ printf("rm -f %s\n", path);
+ else
+ unlink_or_warn(path);
+ return 0;
}
void prune_packed_objects(int opts)
{
- int i;
- const char *dir = get_object_directory();
- struct strbuf pathname = STRBUF_INIT;
- int top_len;
-
- strbuf_addstr(&pathname, dir);
if (opts & PRUNE_PACKED_VERBOSE)
progress = start_progress_delay(_("Removing duplicate objects"),
256, 95, 2);
- if (pathname.len && pathname.buf[pathname.len - 1] != '/')
- strbuf_addch(&pathname, '/');
-
- top_len = pathname.len;
- for (i = 0; i < 256; i++) {
- DIR *d;
+ for_each_loose_file_in_objdir(get_object_directory(),
+ prune_object, NULL, prune_subdir, &opts);
- display_progress(progress, i + 1);
- strbuf_setlen(&pathname, top_len);
- strbuf_addf(&pathname, "%02x/", i);
- d = opendir(pathname.buf);
- if (!d)
- continue;
- prune_dir(i, d, &pathname, opts);
- closedir(d);
- strbuf_setlen(&pathname, top_len + 2);
- rmdir(pathname.buf);
- }
+ /* Ensure we show 100% before finishing progress */
+ display_progress(progress, 256);
stop_progress(&progress);
}
diff --git a/builtin/prune.c b/builtin/prune.c
index 144a3bdb33..04d3b12ae4 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -31,11 +31,23 @@ static int prune_tmp_file(const char *fullpath)
return 0;
}
-static int prune_object(const char *fullpath, const unsigned char *sha1)
+static int prune_object(const unsigned char *sha1, const char *fullpath,
+ void *data)
{
struct stat st;
- if (lstat(fullpath, &st))
- return error("Could not stat '%s'", fullpath);
+
+ /*
+ * Do we know about this object?
+ * It must have been reachable
+ */
+ if (lookup_object(sha1))
+ return 0;
+
+ if (lstat(fullpath, &st)) {
+ /* report errors, but do not stop pruning */
+ error("Could not stat '%s'", fullpath);
+ return 0;
+ }
if (st.st_mtime > expire)
return 0;
if (show_only || verbose) {
@@ -48,68 +60,20 @@ static int prune_object(const char *fullpath, const unsigned char *sha1)
return 0;
}
-static int prune_dir(int i, struct strbuf *path)
+static int prune_cruft(const char *basename, const char *path, void *data)
{
- size_t baselen = path->len;
- DIR *dir = opendir(path->buf);
- struct dirent *de;
-
- if (!dir)
- return 0;
-
- while ((de = readdir(dir)) != NULL) {
- char name[100];
- unsigned char sha1[20];
-
- if (is_dot_or_dotdot(de->d_name))
- continue;
- if (strlen(de->d_name) == 38) {
- sprintf(name, "%02x", i);
- memcpy(name+2, de->d_name, 39);
- if (get_sha1_hex(name, sha1) < 0)
- break;
-
- /*
- * Do we know about this object?
- * It must have been reachable
- */
- if (lookup_object(sha1))
- continue;
-
- strbuf_addf(path, "/%s", de->d_name);
- prune_object(path->buf, sha1);
- strbuf_setlen(path, baselen);
- continue;
- }
- if (starts_with(de->d_name, "tmp_obj_")) {
- strbuf_addf(path, "/%s", de->d_name);
- prune_tmp_file(path->buf);
- strbuf_setlen(path, baselen);
- continue;
- }
- fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name);
- }
- closedir(dir);
- if (!show_only)
- rmdir(path->buf);
+ if (starts_with(basename, "tmp_obj_"))
+ prune_tmp_file(path);
+ else
+ fprintf(stderr, "bad sha1 file: %s\n", path);
return 0;
}
-static void prune_object_dir(const char *path)
+static int prune_subdir(int nr, const char *path, void *data)
{
- struct strbuf buf = STRBUF_INIT;
- size_t baselen;
- int i;
-
- strbuf_addstr(&buf, path);
- strbuf_addch(&buf, '/');
- baselen = buf.len;
-
- for (i = 0; i < 256; i++) {
- strbuf_addf(&buf, "%02x", i);
- prune_dir(i, &buf);
- strbuf_setlen(&buf, baselen);
- }
+ if (!show_only)
+ rmdir(path);
+ return 0;
}
/*
@@ -171,9 +135,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
if (show_progress)
progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2);
- mark_reachable_objects(&revs, 1, progress);
+ mark_reachable_objects(&revs, 1, expire, progress);
stop_progress(&progress);
- prune_object_dir(get_object_directory());
+ for_each_loose_file_in_objdir(get_object_directory(), prune_object,
+ prune_cruft, prune_subdir, NULL);
prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0);
remove_temporary_files(get_object_directory());
diff --git a/builtin/push.c b/builtin/push.c
index f50e3d5e77..a076b1964d 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -471,6 +471,17 @@ static int option_parse_recurse_submodules(const struct option *opt,
return 0;
}
+static int git_push_config(const char *k, const char *v, void *cb)
+{
+ struct wt_status *s = cb;
+ int status;
+
+ status = git_gpg_config(k, v, NULL);
+ if (status)
+ return status;
+ return git_default_config(k, v, s);
+}
+
int cmd_push(int argc, const char **argv, const char *prefix)
{
int flags = 0;
@@ -506,11 +517,12 @@ int cmd_push(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
TRANSPORT_PUSH_FOLLOW_TAGS),
+ OPT_BIT(0, "signed", &flags, N_("GPG sign the push"), TRANSPORT_PUSH_CERT),
OPT_END()
};
packet_trace_identity("push");
- git_config(git_default_config, NULL);
+ git_config(git_push_config, NULL);
argc = parse_options(argc, argv, prefix, options, push_usage, 0);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index e7e1c33a7f..43b47f72f1 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -5,6 +5,7 @@
*/
#include "cache.h"
+#include "lockfile.h"
#include "object.h"
#include "tree.h"
#include "tree-walk.h"
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index f93ac454b4..fc0393779c 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "lockfile.h"
#include "pack.h"
#include "refs.h"
#include "pkt-line.h"
@@ -15,6 +16,9 @@
#include "connected.h"
#include "argv-array.h"
#include "version.h"
+#include "tag.h"
+#include "gpg-interface.h"
+#include "sigchain.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -41,11 +45,27 @@ static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
static int fix_thin = 1;
+static int stateless_rpc;
+static const char *service_dir;
static const char *head_name;
static void *head_name_to_free;
static int sent_capabilities;
static int shallow_update;
static const char *alt_shallow_file;
+static struct strbuf push_cert = STRBUF_INIT;
+static unsigned char push_cert_sha1[20];
+static struct signature_check sigcheck;
+static const char *push_cert_nonce;
+static const char *cert_nonce_seed;
+
+static const char *NONCE_UNSOLICITED = "UNSOLICITED";
+static const char *NONCE_BAD = "BAD";
+static const char *NONCE_MISSING = "MISSING";
+static const char *NONCE_OK = "OK";
+static const char *NONCE_SLOP = "SLOP";
+static const char *nonce_status;
+static long nonce_stamp_slop;
+static unsigned long nonce_stamp_slop_limit;
static enum deny_action parse_deny_action(const char *var, const char *value)
{
@@ -129,6 +149,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "receive.certnonceseed") == 0)
+ return git_config_string(&cert_nonce_seed, var, value);
+
+ if (strcmp(var, "receive.certnonceslop") == 0) {
+ nonce_stamp_slop_limit = git_config_ulong(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -137,15 +165,23 @@ static void show_ref(const char *path, const unsigned char *sha1)
if (ref_is_hidden(path))
return;
- if (sent_capabilities)
+ if (sent_capabilities) {
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
- else
- packet_write(1, "%s %s%c%s%s agent=%s\n",
- sha1_to_hex(sha1), path, 0,
- " report-status delete-refs side-band-64k quiet",
- prefer_ofs_delta ? " ofs-delta" : "",
- git_user_agent_sanitized());
- sent_capabilities = 1;
+ } else {
+ struct strbuf cap = STRBUF_INIT;
+
+ strbuf_addstr(&cap,
+ "report-status delete-refs side-band-64k quiet");
+ if (prefer_ofs_delta)
+ strbuf_addstr(&cap, " ofs-delta");
+ if (push_cert_nonce)
+ strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
+ strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
+ packet_write(1, "%s %s%c%s\n",
+ sha1_to_hex(sha1), path, 0, cap.buf);
+ strbuf_release(&cap);
+ sent_capabilities = 1;
+ }
}
static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *unused)
@@ -252,10 +288,231 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
+#define HMAC_BLOCK_SIZE 64
+
+static void hmac_sha1(unsigned char *out,
+ const char *key_in, size_t key_len,
+ const char *text, size_t text_len)
+{
+ unsigned char key[HMAC_BLOCK_SIZE];
+ unsigned char k_ipad[HMAC_BLOCK_SIZE];
+ unsigned char k_opad[HMAC_BLOCK_SIZE];
+ int i;
+ git_SHA_CTX ctx;
+
+ /* RFC 2104 2. (1) */
+ memset(key, '\0', HMAC_BLOCK_SIZE);
+ if (HMAC_BLOCK_SIZE < key_len) {
+ git_SHA1_Init(&ctx);
+ git_SHA1_Update(&ctx, key_in, key_len);
+ git_SHA1_Final(key, &ctx);
+ } else {
+ memcpy(key, key_in, key_len);
+ }
+
+ /* RFC 2104 2. (2) & (5) */
+ for (i = 0; i < sizeof(key); i++) {
+ k_ipad[i] = key[i] ^ 0x36;
+ k_opad[i] = key[i] ^ 0x5c;
+ }
+
+ /* RFC 2104 2. (3) & (4) */
+ git_SHA1_Init(&ctx);
+ git_SHA1_Update(&ctx, k_ipad, sizeof(k_ipad));
+ git_SHA1_Update(&ctx, text, text_len);
+ git_SHA1_Final(out, &ctx);
+
+ /* RFC 2104 2. (6) & (7) */
+ git_SHA1_Init(&ctx);
+ git_SHA1_Update(&ctx, k_opad, sizeof(k_opad));
+ git_SHA1_Update(&ctx, out, 20);
+ git_SHA1_Final(out, &ctx);
+}
+
+static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+{
+ struct strbuf buf = STRBUF_INIT;
+ unsigned char sha1[20];
+
+ strbuf_addf(&buf, "%s:%lu", path, stamp);
+ hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
+ strbuf_release(&buf);
+
+ /* RFC 2104 5. HMAC-SHA1-80 */
+ strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+ return strbuf_detach(&buf, NULL);
+}
+
+/*
+ * NEEDSWORK: reuse find_commit_header() from jk/commit-author-parsing
+ * after dropping "_commit" from its name and possibly moving it out
+ * of commit.c
+ */
+static char *find_header(const char *msg, size_t len, const char *key)
+{
+ int key_len = strlen(key);
+ const char *line = msg;
+
+ while (line && line < msg + len) {
+ const char *eol = strchrnul(line, '\n');
+
+ if ((msg + len <= eol) || line == eol)
+ return NULL;
+ if (line + key_len < eol &&
+ !memcmp(line, key, key_len) && line[key_len] == ' ') {
+ int offset = key_len + 1;
+ return xmemdupz(line + offset, (eol - line) - offset);
+ }
+ line = *eol ? eol + 1 : NULL;
+ }
+ return NULL;
+}
+
+static const char *check_nonce(const char *buf, size_t len)
+{
+ char *nonce = find_header(buf, len, "nonce");
+ unsigned long stamp, ostamp;
+ char *bohmac, *expect = NULL;
+ const char *retval = NONCE_BAD;
+
+ if (!nonce) {
+ retval = NONCE_MISSING;
+ goto leave;
+ } else if (!push_cert_nonce) {
+ retval = NONCE_UNSOLICITED;
+ goto leave;
+ } else if (!strcmp(push_cert_nonce, nonce)) {
+ retval = NONCE_OK;
+ goto leave;
+ }
+
+ if (!stateless_rpc) {
+ /* returned nonce MUST match what we gave out earlier */
+ retval = NONCE_BAD;
+ goto leave;
+ }
+
+ /*
+ * In stateless mode, we may be receiving a nonce issued by
+ * another instance of the server that serving the same
+ * repository, and the timestamps may not match, but the
+ * nonce-seed and dir should match, so we can recompute and
+ * report the time slop.
+ *
+ * In addition, when a nonce issued by another instance has
+ * timestamp within receive.certnonceslop seconds, we pretend
+ * as if we issued that nonce when reporting to the hook.
+ */
+
+ /* nonce is concat(<seconds-since-epoch>, "-", <hmac>) */
+ if (*nonce <= '0' || '9' < *nonce) {
+ retval = NONCE_BAD;
+ goto leave;
+ }
+ stamp = strtoul(nonce, &bohmac, 10);
+ if (bohmac == nonce || bohmac[0] != '-') {
+ retval = NONCE_BAD;
+ goto leave;
+ }
+
+ expect = prepare_push_cert_nonce(service_dir, stamp);
+ if (strcmp(expect, nonce)) {
+ /* Not what we would have signed earlier */
+ retval = NONCE_BAD;
+ goto leave;
+ }
+
+ /*
+ * By how many seconds is this nonce stale? Negative value
+ * would mean it was issued by another server with its clock
+ * skewed in the future.
+ */
+ ostamp = strtoul(push_cert_nonce, NULL, 10);
+ nonce_stamp_slop = (long)ostamp - (long)stamp;
+
+ if (nonce_stamp_slop_limit &&
+ abs(nonce_stamp_slop) <= nonce_stamp_slop_limit) {
+ /*
+ * Pretend as if the received nonce (which passes the
+ * HMAC check, so it is not a forged by third-party)
+ * is what we issued.
+ */
+ free((void *)push_cert_nonce);
+ push_cert_nonce = xstrdup(nonce);
+ retval = NONCE_OK;
+ } else {
+ retval = NONCE_SLOP;
+ }
+
+leave:
+ free(nonce);
+ free(expect);
+ return retval;
+}
+
+static void prepare_push_cert_sha1(struct child_process *proc)
+{
+ static int already_done;
+
+ if (!push_cert.len)
+ return;
+
+ if (!already_done) {
+ struct strbuf gpg_output = STRBUF_INIT;
+ struct strbuf gpg_status = STRBUF_INIT;
+ int bogs /* beginning_of_gpg_sig */;
+
+ already_done = 1;
+ if (write_sha1_file(push_cert.buf, push_cert.len, "blob", push_cert_sha1))
+ hashclr(push_cert_sha1);
+
+ memset(&sigcheck, '\0', sizeof(sigcheck));
+ sigcheck.result = 'N';
+
+ bogs = parse_signature(push_cert.buf, push_cert.len);
+ if (verify_signed_buffer(push_cert.buf, bogs,
+ push_cert.buf + bogs, push_cert.len - bogs,
+ &gpg_output, &gpg_status) < 0) {
+ ; /* error running gpg */
+ } else {
+ sigcheck.payload = push_cert.buf;
+ sigcheck.gpg_output = gpg_output.buf;
+ sigcheck.gpg_status = gpg_status.buf;
+ parse_gpg_output(&sigcheck);
+ }
+
+ strbuf_release(&gpg_output);
+ strbuf_release(&gpg_status);
+ nonce_status = check_nonce(push_cert.buf, bogs);
+ }
+ if (!is_null_sha1(push_cert_sha1)) {
+ argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT=%s",
+ sha1_to_hex(push_cert_sha1));
+ argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s",
+ sigcheck.signer ? sigcheck.signer : "");
+ argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s",
+ sigcheck.key ? sigcheck.key : "");
+ argv_array_pushf(&proc->env_array, "GIT_PUSH_CERT_STATUS=%c",
+ sigcheck.result);
+ if (push_cert_nonce) {
+ argv_array_pushf(&proc->env_array,
+ "GIT_PUSH_CERT_NONCE=%s",
+ push_cert_nonce);
+ argv_array_pushf(&proc->env_array,
+ "GIT_PUSH_CERT_NONCE_STATUS=%s",
+ nonce_status);
+ if (nonce_status == NONCE_SLOP)
+ argv_array_pushf(&proc->env_array,
+ "GIT_PUSH_CERT_NONCE_SLOP=%ld",
+ nonce_stamp_slop);
+ }
+ }
+}
+
typedef int (*feed_fn)(void *, const char **, size_t *);
static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
{
- struct child_process proc;
+ struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
const char *argv[2];
int code;
@@ -266,11 +523,12 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
argv[1] = NULL;
- memset(&proc, 0, sizeof(proc));
proc.argv = argv;
proc.in = -1;
proc.stdout_to_stderr = 1;
+ prepare_push_cert_sha1(&proc);
+
if (use_sideband) {
memset(&muxer, 0, sizeof(muxer));
muxer.proc = copy_to_sideband;
@@ -288,6 +546,8 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
return code;
}
+ sigchain_push(SIGPIPE, SIG_IGN);
+
while (1) {
const char *buf;
size_t n;
@@ -299,6 +559,9 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
close(proc.in);
if (use_sideband)
finish_async(&muxer);
+
+ sigchain_pop(SIGPIPE);
+
return finish_command(&proc);
}
@@ -350,7 +613,7 @@ static int run_receive_hook(struct command *commands, const char *hook_name,
static int run_update_hook(struct command *cmd)
{
const char *argv[5];
- struct child_process proc;
+ struct child_process proc = CHILD_PROCESS_INIT;
int code;
argv[0] = find_hook("update");
@@ -362,7 +625,6 @@ static int run_update_hook(struct command *cmd)
argv[3] = sha1_to_hex(cmd->new_sha1);
argv[4] = NULL;
- memset(&proc, 0, sizeof(proc));
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
@@ -475,7 +737,6 @@ static const char *update(struct command *cmd, struct shallow_info *si)
const char *namespaced_name;
unsigned char *old_sha1 = cmd->old_sha1;
unsigned char *new_sha1 = cmd->new_sha1;
- struct ref_lock *lock;
/* only refs/... are allowed */
if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@@ -576,19 +837,28 @@ static const char *update(struct command *cmd, struct shallow_info *si)
return NULL; /* good */
}
else {
+ struct strbuf err = STRBUF_INIT;
+ struct ref_transaction *transaction;
+
if (shallow_update && si->shallow_ref[cmd->index] &&
update_shallow_ref(cmd, si))
return "shallow error";
- lock = lock_any_ref_for_update(namespaced_name, old_sha1,
- 0, NULL);
- if (!lock) {
- rp_error("failed to lock %s", name);
- return "failed to lock";
- }
- if (write_ref_sha1(lock, new_sha1, "push")) {
- return "failed to write"; /* error() already called */
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, namespaced_name,
+ new_sha1, old_sha1, 0, 1, "push",
+ &err) ||
+ ref_transaction_commit(transaction, &err)) {
+ ref_transaction_free(transaction);
+
+ rp_error("%s", err.buf);
+ strbuf_release(&err);
+ return "failed to update ref";
}
+
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
return NULL; /* good */
}
}
@@ -598,7 +868,7 @@ static void run_update_post_hook(struct command *commands)
struct command *cmd;
int argc;
const char **argv;
- struct child_process proc;
+ struct child_process proc = CHILD_PROCESS_INIT;
char *hook;
hook = find_hook("post-update");
@@ -621,7 +891,6 @@ static void run_update_post_hook(struct command *commands)
}
argv[argc] = NULL;
- memset(&proc, 0, sizeof(proc));
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
@@ -645,7 +914,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_unsafe(buf.buf, sha1, 0, &flag);
+ dst_name = resolve_ref_unsafe(buf.buf, 0, sha1, &flag);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
@@ -806,7 +1075,7 @@ static void execute_commands(struct command *commands,
check_aliased_updates(commands);
free(head_name_to_free);
- head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
+ head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
checked_connectivity = 1;
for (cmd = commands; cmd; cmd = cmd->next) {
@@ -831,40 +1100,79 @@ static void execute_commands(struct command *commands,
"the reported refs above");
}
+static struct command **queue_command(struct command **tail,
+ const char *line,
+ int linelen)
+{
+ unsigned char old_sha1[20], new_sha1[20];
+ struct command *cmd;
+ const char *refname;
+ int reflen;
+
+ if (linelen < 83 ||
+ line[40] != ' ' ||
+ line[81] != ' ' ||
+ get_sha1_hex(line, old_sha1) ||
+ get_sha1_hex(line + 41, new_sha1))
+ die("protocol error: expected old/new/ref, got '%s'", line);
+
+ refname = line + 82;
+ reflen = linelen - 82;
+ cmd = xcalloc(1, sizeof(struct command) + reflen + 1);
+ hashcpy(cmd->old_sha1, old_sha1);
+ hashcpy(cmd->new_sha1, new_sha1);
+ memcpy(cmd->ref_name, refname, reflen);
+ cmd->ref_name[reflen] = '\0';
+ *tail = cmd;
+ return &cmd->next;
+}
+
+static void queue_commands_from_cert(struct command **tail,
+ struct strbuf *push_cert)
+{
+ const char *boc, *eoc;
+
+ if (*tail)
+ die("protocol error: got both push certificate and unsigned commands");
+
+ boc = strstr(push_cert->buf, "\n\n");
+ if (!boc)
+ die("malformed push certificate %.*s", 100, push_cert->buf);
+ else
+ boc += 2;
+ eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len);
+
+ while (boc < eoc) {
+ const char *eol = memchr(boc, '\n', eoc - boc);
+ tail = queue_command(tail, boc, eol ? eol - boc : eoc - eol);
+ boc = eol ? eol + 1 : eoc;
+ }
+}
+
static struct command *read_head_info(struct sha1_array *shallow)
{
struct command *commands = NULL;
struct command **p = &commands;
for (;;) {
char *line;
- unsigned char old_sha1[20], new_sha1[20];
- struct command *cmd;
- char *refname;
- int len, reflen;
+ int len, linelen;
line = packet_read_line(0, &len);
if (!line)
break;
if (len == 48 && starts_with(line, "shallow ")) {
- if (get_sha1_hex(line + 8, old_sha1))
- die("protocol error: expected shallow sha, got '%s'", line + 8);
- sha1_array_append(shallow, old_sha1);
+ unsigned char sha1[20];
+ if (get_sha1_hex(line + 8, sha1))
+ die("protocol error: expected shallow sha, got '%s'",
+ line + 8);
+ sha1_array_append(shallow, sha1);
continue;
}
- if (len < 83 ||
- line[40] != ' ' ||
- line[81] != ' ' ||
- get_sha1_hex(line, old_sha1) ||
- get_sha1_hex(line + 41, new_sha1))
- die("protocol error: expected old/new/ref, got '%s'",
- line);
-
- refname = line + 82;
- reflen = strlen(refname);
- if (reflen + 82 < len) {
- const char *feature_list = refname + reflen + 1;
+ linelen = strlen(line);
+ if (linelen < len) {
+ const char *feature_list = line + linelen + 1;
if (parse_feature_request(feature_list, "report-status"))
report_status = 1;
if (parse_feature_request(feature_list, "side-band-64k"))
@@ -872,13 +1180,34 @@ static struct command *read_head_info(struct sha1_array *shallow)
if (parse_feature_request(feature_list, "quiet"))
quiet = 1;
}
- cmd = xcalloc(1, sizeof(struct command) + len - 80);
- hashcpy(cmd->old_sha1, old_sha1);
- hashcpy(cmd->new_sha1, new_sha1);
- memcpy(cmd->ref_name, line + 82, len - 81);
- *p = cmd;
- p = &cmd->next;
+
+ if (!strcmp(line, "push-cert")) {
+ int true_flush = 0;
+ char certbuf[1024];
+
+ for (;;) {
+ len = packet_read(0, NULL, NULL,
+ certbuf, sizeof(certbuf), 0);
+ if (!len) {
+ true_flush = 1;
+ break;
+ }
+ if (!strcmp(certbuf, "push-cert-end\n"))
+ break; /* end of cert */
+ strbuf_addstr(&push_cert, certbuf);
+ }
+
+ if (true_flush)
+ break;
+ continue;
+ }
+
+ p = queue_command(p, line, linelen);
}
+
+ if (push_cert.len)
+ queue_commands_from_cert(p, &push_cert);
+
return commands;
}
@@ -907,11 +1236,10 @@ static const char *pack_lockfile;
static const char *unpack(int err_fd, struct shallow_info *si)
{
struct pack_header hdr;
- struct argv_array av = ARGV_ARRAY_INIT;
const char *hdr_err;
int status;
char hdr_arg[38];
- struct child_process child;
+ struct child_process child = CHILD_PROCESS_INIT;
int fsck_objects = (receive_fsck_objects >= 0
? receive_fsck_objects
: transfer_fsck_objects >= 0
@@ -930,17 +1258,16 @@ static const char *unpack(int err_fd, struct shallow_info *si)
if (si->nr_ours || si->nr_theirs) {
alt_shallow_file = setup_temporary_shallow(si->shallow);
- argv_array_pushl(&av, "--shallow-file", alt_shallow_file, NULL);
+ argv_array_push(&child.args, "--shallow-file");
+ argv_array_push(&child.args, alt_shallow_file);
}
- memset(&child, 0, sizeof(child));
if (ntohl(hdr.hdr_entries) < unpack_limit) {
- argv_array_pushl(&av, "unpack-objects", hdr_arg, NULL);
+ argv_array_pushl(&child.args, "unpack-objects", hdr_arg, NULL);
if (quiet)
- argv_array_push(&av, "-q");
+ argv_array_push(&child.args, "-q");
if (fsck_objects)
- argv_array_push(&av, "--strict");
- child.argv = av.argv;
+ argv_array_push(&child.args, "--strict");
child.no_stdout = 1;
child.err = err_fd;
child.git_cmd = 1;
@@ -955,13 +1282,12 @@ static const char *unpack(int err_fd, struct shallow_info *si)
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
- argv_array_pushl(&av, "index-pack",
+ argv_array_pushl(&child.args, "index-pack",
"--stdin", hdr_arg, keep_arg, NULL);
if (fsck_objects)
- argv_array_push(&av, "--strict");
+ argv_array_push(&child.args, "--strict");
if (fix_thin)
- argv_array_push(&av, "--fix-thin");
- child.argv = av.argv;
+ argv_array_push(&child.args, "--fix-thin");
child.out = -1;
child.err = err_fd;
child.git_cmd = 1;
@@ -1120,9 +1446,7 @@ static int delete_only(struct command *commands)
int cmd_receive_pack(int argc, const char **argv, const char *prefix)
{
int advertise_refs = 0;
- int stateless_rpc = 0;
int i;
- const char *dir = NULL;
struct command *commands;
struct sha1_array shallow = SHA1_ARRAY_INIT;
struct sha1_array ref = SHA1_ARRAY_INIT;
@@ -1155,19 +1479,21 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
usage(receive_pack_usage);
}
- if (dir)
+ if (service_dir)
usage(receive_pack_usage);
- dir = arg;
+ service_dir = arg;
}
- if (!dir)
+ if (!service_dir)
usage(receive_pack_usage);
setup_path();
- if (!enter_repo(dir, 0))
- die("'%s' does not appear to be a git repository", dir);
+ if (!enter_repo(service_dir, 0))
+ die("'%s' does not appear to be a git repository", service_dir);
git_config(receive_pack_config, NULL);
+ if (cert_nonce_seed)
+ push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL));
if (0 <= transfer_unpack_limit)
unpack_limit = transfer_unpack_limit;
@@ -1212,5 +1538,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
packet_flush(1);
sha1_array_clear(&shallow);
sha1_array_clear(&ref);
+ free((void *)push_cert_nonce);
return 0;
}
diff --git a/builtin/reflog.c b/builtin/reflog.c
index e8a8fb13b9..2d85d260ca 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -1,5 +1,5 @@
-#include "cache.h"
#include "builtin.h"
+#include "lockfile.h"
#include "commit.h"
#include "refs.h"
#include "dir.h"
@@ -431,7 +431,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
write_str_in_full(lock->lock_fd, "\n") != 1 ||
close_ref(lock) < 0)) {
status |= error("Couldn't write %s",
- lock->lk->filename);
+ lock->lk->filename.buf);
unlink(newlog_path);
} else if (rename(newlog_path, log_file)) {
status |= error("cannot rename %s to %s",
@@ -649,7 +649,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, NULL);
+ mark_reachable_objects(&cb.revs, 0, 0, NULL);
if (cb.verbose)
putchar('\n');
}
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index 692c834d9d..3b8c22cc75 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -30,16 +30,14 @@ static char *strip_escapes(const char *str, const char *service,
size_t rpos = 0;
int escape = 0;
char special = 0;
- size_t psoff = 0;
+ const char *service_noprefix = service;
struct strbuf ret = STRBUF_INIT;
- /* Calculate prefix length for \s and lengths for \s and \S */
- if (!strncmp(service, "git-", 4))
- psoff = 4;
+ skip_prefix(service_noprefix, "git-", &service_noprefix);
/* Pass the service to command. */
setenv("GIT_EXT_SERVICE", service, 1);
- setenv("GIT_EXT_SERVICE_NOPREFIX", service + psoff, 1);
+ setenv("GIT_EXT_SERVICE_NOPREFIX", service_noprefix, 1);
/* Scan the length of argument. */
while (str[rpos] && (escape || str[rpos] != ' ')) {
@@ -85,7 +83,7 @@ static char *strip_escapes(const char *str, const char *service,
strbuf_addch(&ret, str[rpos]);
break;
case 's':
- strbuf_addstr(&ret, service + psoff);
+ strbuf_addstr(&ret, service_noprefix);
break;
case 'S':
strbuf_addstr(&ret, service);
@@ -179,9 +177,8 @@ static void send_git_request(int stdin_fd, const char *serv, const char *repo,
static int run_child(const char *arg, const char *service)
{
int r;
- struct child_process child;
+ struct child_process child = CHILD_PROCESS_INIT;
- memset(&child, 0, sizeof(child));
child.in = -1;
child.out = -1;
child.err = 0;
diff --git a/builtin/remote.c b/builtin/remote.c
index 9a4640dbf0..7f28f92a37 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -567,7 +567,8 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
- symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
+ orig_sha1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
@@ -703,7 +704,7 @@ static int mv(int argc, const char **argv)
int flag = 0;
unsigned char sha1[20];
- read_ref_full(item->string, sha1, 1, &flag);
+ read_ref_full(item->string, RESOLVE_REF_READING, sha1, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(item->string, NULL, REF_NODEREF))
@@ -748,13 +749,16 @@ static int mv(int argc, const char **argv)
static int remove_branches(struct string_list *branches)
{
+ struct strbuf err = STRBUF_INIT;
const char **branch_names;
int i, result = 0;
branch_names = xmalloc(branches->nr * sizeof(*branch_names));
for (i = 0; i < branches->nr; i++)
branch_names[i] = branches->items[i].string;
- result |= repack_without_refs(branch_names, branches->nr, NULL);
+ if (repack_without_refs(branch_names, branches->nr, &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
free(branch_names);
for (i = 0; i < branches->nr; i++) {
@@ -1331,9 +1335,13 @@ static int prune_remote(const char *remote, int dry_run)
delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
for (i = 0; i < states.stale.nr; i++)
delete_refs[i] = states.stale.items[i].util;
- if (!dry_run)
- result |= repack_without_refs(delete_refs,
- states.stale.nr, NULL);
+ if (!dry_run) {
+ struct strbuf err = STRBUF_INIT;
+ if (repack_without_refs(delete_refs, states.stale.nr,
+ &err))
+ result |= error("%s", err.buf);
+ strbuf_release(&err);
+ }
free(delete_refs);
}
diff --git a/builtin/repack.c b/builtin/repack.c
index a77e743b94..28456206c5 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -133,7 +133,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
{".idx"},
{".bitmap", 1},
};
- struct child_process cmd;
+ struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
struct argv_array cmd_args = ARGV_ARRAY_INIT;
struct string_list names = STRING_LIST_INIT_DUP;
@@ -209,6 +209,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd_args, "--non-empty");
argv_array_push(&cmd_args, "--all");
argv_array_push(&cmd_args, "--reflog");
+ argv_array_push(&cmd_args, "--indexed-objects");
if (window)
argv_array_pushf(&cmd_args, "--window=%s", window);
if (window_memory)
@@ -250,7 +251,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd_args, packtmp);
- memset(&cmd, 0, sizeof(cmd));
cmd.argv = cmd_args.argv;
cmd.git_cmd = 1;
cmd.out = -1;
@@ -378,6 +378,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
/* End of pack replacement. */
if (delete_redundant) {
+ int opts = 0;
sort_string_list(&names);
for_each_string_list_item(item, &existing_packs) {
char *sha1;
@@ -388,25 +389,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (!string_list_has_string(&names, sha1))
remove_redundant_pack(packdir, item->string);
}
- argv_array_push(&cmd_args, "prune-packed");
- if (quiet)
- argv_array_push(&cmd_args, "--quiet");
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.argv = cmd_args.argv;
- cmd.git_cmd = 1;
- run_command(&cmd);
- argv_array_clear(&cmd_args);
+ if (!quiet && isatty(2))
+ opts |= PRUNE_PACKED_VERBOSE;
+ prune_packed_objects(opts);
}
- if (!no_update_server_info) {
- argv_array_push(&cmd_args, "update-server-info");
- memset(&cmd, 0, sizeof(cmd));
- cmd.argv = cmd_args.argv;
- cmd.git_cmd = 1;
- run_command(&cmd);
- argv_array_clear(&cmd_args);
- }
+ if (!no_update_server_info)
+ update_server_info(0);
remove_temporary_files();
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);
diff --git a/builtin/replace.c b/builtin/replace.c
index 294b61b97e..85d39b58d8 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -155,7 +155,8 @@ static int replace_object_sha1(const char *object_ref,
unsigned char prev[20];
enum object_type obj_type, repl_type;
char ref[PATH_MAX];
- struct ref_lock *lock;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
obj_type = sha1_object_info(object, NULL);
repl_type = sha1_object_info(repl, NULL);
@@ -168,12 +169,14 @@ static int replace_object_sha1(const char *object_ref,
check_ref_valid(object, prev, ref, sizeof(ref), force);
- lock = lock_any_ref_for_update(ref, prev, 0, NULL);
- if (!lock)
- die("%s: cannot lock the ref", ref);
- if (write_ref_sha1(lock, repl, NULL) < 0)
- die("%s: cannot update the ref", ref);
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref, repl, prev,
+ 0, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err))
+ die("%s", err.buf);
+ ref_transaction_free(transaction);
return 0;
}
@@ -197,7 +200,7 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
static void export_object(const unsigned char *sha1, enum object_type type,
int raw, const char *filename)
{
- struct child_process cmd = { NULL };
+ struct child_process cmd = CHILD_PROCESS_INIT;
int fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
@@ -234,7 +237,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
if (!raw && type == OBJ_TREE) {
const char *argv[] = { "mktree", NULL };
- struct child_process cmd = { argv };
+ struct child_process cmd = CHILD_PROCESS_INIT;
struct strbuf result = STRBUF_INIT;
cmd.argv = argv;
diff --git a/builtin/reset.c b/builtin/reset.c
index 855d478e3b..4c08ddc1ca 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -8,6 +8,7 @@
* Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
*/
#include "builtin.h"
+#include "lockfile.h"
#include "tag.h"
#include "object.h"
#include "commit.h"
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index d85e08cc9c..35d3c43ed6 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -508,7 +508,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
int has_dashdash = 0;
int output_prefix = 0;
unsigned char sha1[20];
+ unsigned int flags = 0;
const char *name = NULL;
+ struct object_context unused;
if (argc > 1 && !strcmp("--parseopt", argv[1]))
return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -596,6 +598,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1;
+ flags |= GET_SHA1_QUIETLY;
continue;
}
if (!strcmp(arg, "--short") ||
@@ -736,7 +739,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--git-dir")) {
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
- static char cwd[PATH_MAX];
+ char *cwd;
int len;
if (gitdir) {
puts(gitdir);
@@ -746,10 +749,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
puts(".git");
continue;
}
- if (!getcwd(cwd, PATH_MAX))
- die_errno("unable to get current working directory");
+ cwd = xgetcwd();
len = strlen(cwd);
printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
+ free(cwd);
continue;
}
if (!strcmp(arg, "--resolve-git-dir")) {
@@ -818,7 +821,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
name++;
type = REVERSED;
}
- if (!get_sha1(name, sha1)) {
+ if (!get_sha1_with_context(name, flags, sha1, &unused)) {
if (verify)
revs_count++;
else
diff --git a/builtin/rm.c b/builtin/rm.c
index bc6490b8bc..d8a9c86dd1 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,8 +3,8 @@
*
* Copyright (C) Linus Torvalds 2006
*/
-#include "cache.h"
#include "builtin.h"
+#include "lockfile.h"
#include "dir.h"
#include "cache-tree.h"
#include "tree-walk.h"
@@ -65,7 +65,7 @@ static void error_removing_concrete_submodules(struct string_list *files, int *e
Q_("the following submodule (or one of its nested "
"submodules)\n"
"uses a .git directory:",
- "the following submodules (or one of its nested "
+ "the following submodules (or one of their nested "
"submodules)\n"
"use a .git directory:", files->nr),
_("\n(use 'rm -rf' if you really want to remove "
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 4b1bc0fef7..b564a77845 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -154,6 +154,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.verbose = 1;
continue;
}
+ if (!strcmp(arg, "--signed")) {
+ args.push_cert = 1;
+ continue;
+ }
if (!strcmp(arg, "--progress")) {
progress = 1;
continue;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 298c95e3f8..270e39c6c1 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -563,7 +563,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
default_arg[default_num++] = "show-branch";
} else if (default_alloc <= default_num + 1) {
default_alloc = default_alloc * 3 / 2 + 20;
- default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);
+ REALLOC_ARRAY(default_arg, default_alloc);
}
default_arg[default_num++] = xstrdup(value);
default_arg[default_num] = NULL;
@@ -723,11 +723,14 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
char nth_desc[256];
char *ref;
int base = 0;
+ unsigned int flags = 0;
if (ac == 0) {
static const char *fake_av[2];
- fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
+ fake_av[0] = resolve_refdup("HEAD",
+ RESOLVE_REF_READING,
+ sha1, NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
@@ -749,7 +752,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
/* Ah, that is a date spec... */
unsigned long at;
at = approxidate(reflog_base);
- read_ref_at(ref, at, -1, sha1, NULL,
+ read_ref_at(ref, flags, at, -1, sha1, NULL,
NULL, NULL, &base);
}
}
@@ -760,7 +763,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
unsigned long timestamp;
int tz;
- if (read_ref_at(ref, 0, base+i, sha1, &logmsg,
+ if (read_ref_at(ref, flags, 0, base+i, sha1, &logmsg,
&timestamp, &tz, NULL)) {
reflog = i;
break;
@@ -788,7 +791,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
}
- head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
+ head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ head_sha1, NULL);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index b6a711d319..29fb3f1c20 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -13,7 +13,7 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print)
{
unsigned char sha1[20];
int flag;
- const char *refname = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
+ const char *refname = resolve_ref_unsafe(HEAD, 0, sha1, &flag);
if (!refname)
die("No such ref: %s", HEAD);
diff --git a/builtin/tag.c b/builtin/tag.c
index 19eb747820..e633f4efdb 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -576,7 +576,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct strbuf ref = STRBUF_INIT;
unsigned char object[20], prev[20];
const char *object_ref, *tag;
- struct ref_lock *lock;
struct create_tag_options opt;
char *cleanup_arg = NULL;
int annotate = 0, force = 0, lines = -1;
@@ -584,6 +583,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
struct option options[] = {
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
{ OPTION_INTEGER, 'n', NULL, &lines, N_("n"),
@@ -729,14 +730,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (annotate)
create_tag(object, tag, &buf, &opt, prev, object);
- lock = lock_any_ref_for_update(ref.buf, prev, 0, NULL);
- if (!lock)
- die(_("%s: cannot lock the ref"), ref.buf);
- if (write_ref_sha1(lock, object, NULL) < 0)
- die(_("%s: cannot update the ref"), ref.buf);
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, ref.buf, object, prev,
+ 0, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err))
+ die("%s", err.buf);
+ ref_transaction_free(transaction);
if (force && !is_null_sha1(prev) && hashcmp(prev, object))
printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
+ strbuf_release(&err);
strbuf_release(&buf);
strbuf_release(&ref);
return 0;
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 99cde45879..855d94b90b 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -164,10 +164,10 @@ static unsigned nr_objects;
* Called only from check_object() after it verified this object
* is Ok.
*/
-static void write_cached_object(struct object *obj)
+static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
{
unsigned char sha1[20];
- struct obj_buffer *obj_buf = lookup_object_buffer(obj);
+
if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
die("failed to write object %s", sha1_to_hex(obj->sha1));
obj->flags |= FLAG_WRITTEN;
@@ -180,6 +180,8 @@ static void write_cached_object(struct object *obj)
*/
static int check_object(struct object *obj, int type, void *data)
{
+ struct obj_buffer *obj_buf;
+
if (!obj)
return 1;
@@ -198,11 +200,15 @@ static int check_object(struct object *obj, int type, void *data)
return 0;
}
- if (fsck_object(obj, 1, fsck_error_function))
+ obj_buf = lookup_object_buffer(obj);
+ if (!obj_buf)
+ die("Whoops! Cannot find object '%s'", sha1_to_hex(obj->sha1));
+ if (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,
+ fsck_error_function))
die("Error in object");
if (fsck_walk(obj, check_object, NULL))
die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
- write_cached_object(obj);
+ write_cached_object(obj, obj_buf);
return 0;
}
diff --git a/builtin/update-index.c b/builtin/update-index.c
index e8c7fd4d49..b0e3dc9105 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -4,6 +4,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "lockfile.h"
#include "quote.h"
#include "cache-tree.h"
#include "tree-walk.h"
@@ -942,7 +943,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
if (newfd < 0) {
if (refresh_args.flags & REFRESH_QUIET)
exit(128);
- unable_to_lock_index_die(get_index_file(), lock_error);
+ unable_to_lock_die(get_index_file(), lock_error);
}
if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
die("Unable to write new index file");
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 3067b11310..6c9be05128 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -12,11 +12,9 @@ static const char * const git_update_ref_usage[] = {
NULL
};
-static struct ref_transaction *transaction;
-
static char line_termination = '\n';
static int update_flags;
-static struct strbuf err = STRBUF_INIT;
+static const char *msg;
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
@@ -177,8 +175,10 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
* depending on how line_termination is set.
*/
-static const char *parse_cmd_update(struct strbuf *input, const char *next)
+static const char *parse_cmd_update(struct ref_transaction *transaction,
+ struct strbuf *input, const char *next)
{
+ struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char new_sha1[20];
unsigned char old_sha1[20];
@@ -199,17 +199,20 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
die("update %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
free(refname);
+ strbuf_release(&err);
return next;
}
-static const char *parse_cmd_create(struct strbuf *input, const char *next)
+static const char *parse_cmd_create(struct ref_transaction *transaction,
+ struct strbuf *input, const char *next)
{
+ struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char new_sha1[20];
@@ -226,16 +229,21 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next)
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
- ref_transaction_create(transaction, refname, new_sha1, update_flags);
+ if (ref_transaction_create(transaction, refname, new_sha1,
+ update_flags, msg, &err))
+ die("%s", err.buf);
update_flags = 0;
free(refname);
+ strbuf_release(&err);
return next;
}
-static const char *parse_cmd_delete(struct strbuf *input, const char *next)
+static const char *parse_cmd_delete(struct ref_transaction *transaction,
+ struct strbuf *input, const char *next)
{
+ struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char old_sha1[20];
int have_old;
@@ -256,17 +264,21 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
if (*next != line_termination)
die("delete %s: extra input: %s", refname, next);
- ref_transaction_delete(transaction, refname, old_sha1,
- update_flags, have_old);
+ if (ref_transaction_delete(transaction, refname, old_sha1,
+ update_flags, have_old, msg, &err))
+ die("%s", err.buf);
update_flags = 0;
free(refname);
+ strbuf_release(&err);
return next;
}
-static const char *parse_cmd_verify(struct strbuf *input, const char *next)
+static const char *parse_cmd_verify(struct ref_transaction *transaction,
+ struct strbuf *input, const char *next)
{
+ struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char new_sha1[20];
unsigned char old_sha1[20];
@@ -289,11 +301,12 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
die("verify %s: extra input: %s", refname, next);
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- update_flags, have_old, &err))
+ update_flags, have_old, msg, &err))
die("%s", err.buf);
update_flags = 0;
free(refname);
+ strbuf_release(&err);
return next;
}
@@ -307,7 +320,7 @@ static const char *parse_cmd_option(struct strbuf *input, const char *next)
return next + 8;
}
-static void update_refs_stdin(void)
+static void update_refs_stdin(struct ref_transaction *transaction)
{
struct strbuf input = STRBUF_INIT;
const char *next;
@@ -322,13 +335,13 @@ static void update_refs_stdin(void)
else if (isspace(*next))
die("whitespace before command: %s", next);
else if (starts_with(next, "update "))
- next = parse_cmd_update(&input, next + 7);
+ next = parse_cmd_update(transaction, &input, next + 7);
else if (starts_with(next, "create "))
- next = parse_cmd_create(&input, next + 7);
+ next = parse_cmd_create(transaction, &input, next + 7);
else if (starts_with(next, "delete "))
- next = parse_cmd_delete(&input, next + 7);
+ next = parse_cmd_delete(transaction, &input, next + 7);
else if (starts_with(next, "verify "))
- next = parse_cmd_verify(&input, next + 7);
+ next = parse_cmd_verify(transaction, &input, next + 7);
else if (starts_with(next, "option "))
next = parse_cmd_option(&input, next + 7);
else
@@ -342,7 +355,7 @@ static void update_refs_stdin(void)
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
- const char *refname, *oldval, *msg = NULL;
+ const char *refname, *oldval;
unsigned char sha1[20], oldsha1[20];
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0;
struct option options[] = {
@@ -362,15 +375,21 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("Refusing to perform update with empty message.");
if (read_stdin) {
- transaction = ref_transaction_begin();
+ struct strbuf err = STRBUF_INIT;
+ struct ref_transaction *transaction;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction)
+ die("%s", err.buf);
if (delete || no_deref || argc > 0)
usage_with_options(git_update_ref_usage, options);
if (end_null)
line_termination = '\0';
- update_refs_stdin();
- if (ref_transaction_commit(transaction, msg, &err))
+ update_refs_stdin(transaction);
+ if (ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
+ strbuf_release(&err);
return 0;
}
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index 972579f33c..7747537beb 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -8,7 +8,7 @@
static int verify_one_pack(const char *path, unsigned int flags)
{
- struct child_process index_pack;
+ struct child_process index_pack = CHILD_PROCESS_INIT;
const char *argv[] = {"index-pack", NULL, NULL, NULL };
struct strbuf arg = STRBUF_INIT;
int verbose = flags & VERIFY_PACK_VERBOSE;
@@ -32,7 +32,6 @@ static int verify_one_pack(const char *path, unsigned int flags)
strbuf_addstr(&arg, ".pack");
argv[2] = arg.buf;
- memset(&index_pack, 0, sizeof(index_pack));
index_pack.argv = argv;
index_pack.git_cmd = 1;
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 98e651c284..0c4b8a7cad 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2011, Google Inc.
*/
+#include "cache.h"
#include "bulk-checkin.h"
#include "csum-file.h"
#include "pack.h"
diff --git a/bulk-checkin.h b/bulk-checkin.h
index 4f599f8841..fbd40fc98c 100644
--- a/bulk-checkin.h
+++ b/bulk-checkin.h
@@ -4,8 +4,6 @@
#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);
diff --git a/bundle.c b/bundle.c
index b708906cdb..fa67057e60 100644
--- a/bundle.c
+++ b/bundle.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "bundle.h"
#include "object.h"
#include "commit.h"
@@ -209,26 +210,29 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
{
unsigned long size;
enum object_type type;
- char *buf, *line, *lineend;
+ char *buf = NULL, *line, *lineend;
unsigned long date;
+ int result = 1;
if (revs->max_age == -1 && revs->min_age == -1)
- return 1;
+ goto out;
buf = read_sha1_file(tag->sha1, &type, &size);
if (!buf)
- return 1;
+ goto out;
line = memmem(buf, size, "\ntagger ", 8);
if (!line++)
- return 1;
+ goto out;
lineend = memchr(line, '\n', buf + size - line);
line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
if (!line++)
- return 1;
+ goto out;
date = strtoul(line, NULL, 10);
- free(buf);
- return (revs->max_age == -1 || revs->max_age < date) &&
+ result = (revs->max_age == -1 || revs->max_age < date) &&
(revs->min_age == -1 || revs->min_age > date);
+out:
+ free(buf);
+ return result;
}
int create_bundle(struct bundle_header *header, const char *path,
@@ -240,7 +244,7 @@ int create_bundle(struct bundle_header *header, const char *path,
int i, ref_count = 0;
struct strbuf buf = STRBUF_INIT;
struct rev_info revs;
- struct child_process rls;
+ struct child_process rls = CHILD_PROCESS_INIT;
FILE *rls_fout;
bundle_to_stdout = !strcmp(path, "-");
@@ -258,7 +262,6 @@ int create_bundle(struct bundle_header *header, const char *path,
init_revisions(&revs, NULL);
/* write prerequisites */
- memset(&rls, 0, sizeof(rls));
argv_array_pushl(&rls.args,
"rev-list", "--boundary", "--pretty=oneline",
NULL);
@@ -307,7 +310,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 (read_ref_full(e->name, sha1, 1, &flag))
+ if (read_ref_full(e->name, RESOLVE_REF_READING, sha1, &flag))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
@@ -417,14 +420,13 @@ int unbundle(struct bundle_header *header, int bundle_fd, int flags)
{
const char *argv_index_pack[] = {"index-pack",
"--fix-thin", "--stdin", NULL, NULL};
- struct child_process ip;
+ struct child_process ip = CHILD_PROCESS_INIT;
if (flags & BUNDLE_VERBOSE)
argv_index_pack[3] = "-v";
if (verify_bundle(header, 0))
return -1;
- memset(&ip, 0, sizeof(ip));
ip.argv = argv_index_pack;
ip.in = bundle_fd;
ip.no_stdout = 1;
diff --git a/cache-tree.c b/cache-tree.c
index c53f7de2b1..215202c42d 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "tree.h"
#include "tree-walk.h"
#include "cache-tree.h"
@@ -246,9 +247,12 @@ static int update_one(struct cache_tree *it,
struct strbuf buffer;
int missing_ok = flags & WRITE_TREE_MISSING_OK;
int dryrun = flags & WRITE_TREE_DRY_RUN;
+ int repair = flags & WRITE_TREE_REPAIR;
int to_invalidate = 0;
int i;
+ assert(!(dryrun && repair));
+
*skip_count = 0;
if (0 <= it->entry_count && has_sha1_file(it->sha1))
@@ -320,6 +324,7 @@ static int update_one(struct cache_tree *it,
int pathlen, entlen;
const unsigned char *sha1;
unsigned mode;
+ int expected_missing = 0;
path = ce->name;
pathlen = ce_namelen(ce);
@@ -336,8 +341,10 @@ static int update_one(struct cache_tree *it,
i += sub->count;
sha1 = sub->cache_tree->sha1;
mode = S_IFDIR;
- if (sub->cache_tree->entry_count < 0)
+ if (sub->cache_tree->entry_count < 0) {
to_invalidate = 1;
+ expected_missing = 1;
+ }
}
else {
sha1 = ce->sha1;
@@ -347,6 +354,8 @@ static int update_one(struct cache_tree *it,
}
if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
strbuf_release(&buffer);
+ if (expected_missing)
+ return -1;
return error("invalid object %06o %s for '%.*s'",
mode, sha1_to_hex(sha1), entlen+baselen, path);
}
@@ -381,7 +390,14 @@ static int update_one(struct cache_tree *it,
#endif
}
- if (dryrun)
+ if (repair) {
+ unsigned char sha1[20];
+ hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1);
+ if (has_sha1_file(sha1))
+ hashcpy(it->sha1, sha1);
+ else
+ to_invalidate = 1;
+ } else if (dryrun)
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
strbuf_release(&buffer);
diff --git a/cache-tree.h b/cache-tree.h
index b47ccec7f6..aa7b3e4a0a 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -39,6 +39,7 @@ int update_main_cache_tree(int);
#define WRITE_TREE_IGNORE_CACHE_TREE 2
#define WRITE_TREE_DRY_RUN 4
#define WRITE_TREE_SILENT 8
+#define WRITE_TREE_REPAIR 16
/* error return codes */
#define WRITE_TREE_UNREADABLE_INDEX (-1)
diff --git a/cache.h b/cache.h
index dcf3a2afe9..99ed096aed 100644
--- a/cache.h
+++ b/cache.h
@@ -8,6 +8,7 @@
#include "gettext.h"
#include "convert.h"
#include "trace.h"
+#include "string-list.h"
#include SHA1_HEADER
#ifndef git_SHA_CTX
@@ -481,7 +482,7 @@ extern int daemonize(void);
alloc = (nr); \
else \
alloc = alloc_nr(alloc); \
- x = xrealloc((x), alloc * sizeof(*(x))); \
+ REALLOC_ARRAY(x, alloc); \
} \
} while (0)
@@ -569,28 +570,11 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
-struct lock_file {
- struct lock_file *next;
- int fd;
- pid_t owner;
- char on_list;
- char filename[PATH_MAX];
-};
-#define LOCK_DIE_ON_ERROR 1
-#define LOCK_NODEREF 2
-extern int unable_to_lock_error(const char *path, int err);
-extern void unable_to_lock_message(const char *path, int err,
- struct strbuf *buf);
-extern NORETURN void unable_to_lock_index_die(const char *path, int err);
-extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
-extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
-extern int commit_lock_file(struct lock_file *);
extern void update_index_if_able(struct index_state *, struct lock_file *);
extern int hold_locked_index(struct lock_file *, int);
extern void set_alternate_index_output(const char *);
-extern int close_lock_file(struct lock_file *);
-extern void rollback_lock_file(struct lock_file *);
+
extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
/* Environment bits from configuration mechanism */
@@ -966,8 +950,8 @@ extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
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 *refname, unsigned char *sha1,
- int reading, int *flags);
+extern int read_ref_full(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags);
extern int read_ref(const char *refname, unsigned char *sha1);
/*
@@ -979,29 +963,49 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* or the input ref.
*
* If the reference cannot be resolved to an object, the behavior
- * depends on the "reading" argument:
+ * depends on the RESOLVE_REF_READING flag:
*
- * - If reading is set, return NULL.
+ * - If RESOLVE_REF_READING is set, return NULL.
*
- * - If reading is not set, clear sha1 and return the name of the last
- * reference name in the chain, which will either be a non-symbolic
+ * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
+ * the last reference name in the chain, which will either be a non-symbolic
* reference or an undefined reference. If this is a prelude to
* "writing" to the ref, the return value is the name of the ref
* that will actually be created or changed.
*
- * If flag is non-NULL, set the value that it points to the
+ * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
+ * level of symbolic reference. The value stored in sha1 for a symbolic
+ * reference will always be null_sha1 in this case, and the return
+ * value is the reference that the symref refers to directly.
+ *
+ * If flags is non-NULL, set the value that it points to the
* combination of REF_ISPACKED (if the reference was found among the
- * packed references) and REF_ISSYMREF (if the initial reference was a
- * symbolic reference).
+ * packed references), REF_ISSYMREF (if the initial reference was a
+ * symbolic reference), REF_BAD_NAME (if the reference name is ill
+ * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
+ * (if the ref is malformed or has a bad name). See refs.h for more detail
+ * on each flag.
*
* If ref is not a properly-formatted, normalized reference, return
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
* give up and return NULL.
*
- * errno is set to something meaningful on error.
+ * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
+ * name is invalid according to git-check-ref-format(1). If the name
+ * is bad then the value stored in sha1 will be null_sha1 and the two
+ * flags REF_ISBROKEN and REF_BAD_NAME will be set.
+ *
+ * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
+ * directory and do not consist of all caps and underscores cannot be
+ * resolved. The function returns NULL for such ref names.
+ * Caps and underscores refers to the special refs, such as HEAD,
+ * FETCH_HEAD and friends, that all live outside of the refs/ directory.
*/
-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);
+#define RESOLVE_REF_READING 0x01
+#define RESOLVE_REF_NO_RECURSE 0x02
+#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
+extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
+extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
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);
@@ -1037,6 +1041,7 @@ enum date_mode {
DATE_SHORT,
DATE_LOCAL,
DATE_ISO8601,
+ DATE_ISO8601_STRICT,
DATE_RFC2822,
DATE_RAW
};
@@ -1044,10 +1049,10 @@ enum date_mode {
const char *show_date(unsigned long time, int timezone, enum date_mode mode);
void show_date_relative(unsigned long time, int tz, const struct timeval *now,
struct strbuf *timebuf);
-int parse_date(const char *date, char *buf, int bufsize);
+int parse_date(const char *date, struct strbuf *out);
int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
int parse_expiry_date(const char *date, unsigned long *timestamp);
-void datestamp(char *buf, int bufsize);
+void datestamp(struct strbuf *out);
#define approxidate(s) approxidate_careful((s), NULL)
unsigned long approxidate_careful(const char *, int *);
unsigned long approxidate_relative(const char *date, const struct timeval *now);
@@ -1061,6 +1066,7 @@ extern const char *git_author_info(int);
extern const char *git_committer_info(int);
extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
extern const char *fmt_name(const char *name, const char *email);
+extern const char *ident_default_name(void);
extern const char *ident_default_email(void);
extern const char *git_editor(void);
extern const char *git_pager(int stdout_is_tty);
@@ -1139,7 +1145,7 @@ extern void prepare_alt_odb(void);
extern void read_info_alternates(const char * relative_base, int depth);
extern void add_to_alternates_file(const char *reference);
typedef int alt_odb_fn(struct alternate_object_database *, void *);
-extern void foreach_alt_odb(alt_odb_fn, void*);
+extern int foreach_alt_odb(alt_odb_fn, void*);
struct pack_window {
struct pack_window *next;
@@ -1235,6 +1241,50 @@ extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsig
extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
+/*
+ * Iterate over the files in the loose-object parts of the object
+ * directory "path", triggering the following callbacks:
+ *
+ * - loose_object is called for each loose object we find.
+ *
+ * - loose_cruft is called for any files that do not appear to be
+ * loose objects. Note that we only look in the loose object
+ * directories "objects/[0-9a-f]{2}/", so we will not report
+ * "objects/foobar" as cruft.
+ *
+ * - loose_subdir is called for each top-level hashed subdirectory
+ * of the object directory (e.g., "$OBJDIR/f0"). It is called
+ * after the objects in the directory are processed.
+ *
+ * Any callback that is NULL will be ignored. Callbacks returning non-zero
+ * will end the iteration.
+ */
+typedef int each_loose_object_fn(const unsigned char *sha1,
+ const char *path,
+ void *data);
+typedef int each_loose_cruft_fn(const char *basename,
+ const char *path,
+ void *data);
+typedef int each_loose_subdir_fn(int nr,
+ const char *path,
+ void *data);
+int for_each_loose_file_in_objdir(const char *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data);
+
+/*
+ * Iterate over loose and packed objects in both the local
+ * repository and any alternates repositories.
+ */
+typedef int each_packed_object_fn(const unsigned char *sha1,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *data);
+extern int for_each_loose_object(each_loose_object_fn, void *);
+extern int for_each_packed_object(each_packed_object_fn, void *);
+
struct object_info {
/* Request */
enum object_type *typep;
@@ -1296,7 +1346,7 @@ extern int git_config_from_buf(config_fn_t fn, const char *name,
const char *buf, size_t len, void *data);
extern void git_config_push_parameter(const char *text);
extern int git_config_from_parameters(config_fn_t fn, void *data);
-extern int git_config(config_fn_t fn, void *);
+extern void git_config(config_fn_t fn, void *);
extern int git_config_with_options(config_fn_t fn, void *,
struct git_config_source *config_source,
int respect_includes);
@@ -1320,6 +1370,7 @@ extern int git_config_rename_section_in_file(const char *, const char *, const c
extern const char *git_etc_gitconfig(void);
extern int check_repository_format_version(const char *var, const char *value, void *cb);
extern int git_env_bool(const char *, int);
+extern unsigned long git_env_ulong(const char *, unsigned long);
extern int git_config_system(void);
extern int config_error_nonbool(const char *);
#if defined(__GNUC__)
@@ -1353,6 +1404,69 @@ extern int parse_config_key(const char *var,
const char **subsection, int *subsection_len,
const char **key);
+struct config_set_element {
+ struct hashmap_entry ent;
+ char *key;
+ struct string_list value_list;
+};
+
+struct configset_list_item {
+ struct config_set_element *e;
+ int value_index;
+};
+
+/*
+ * the contents of the list are ordered according to their
+ * position in the config files and order of parsing the files.
+ * (i.e. key-value pair at the last position of .git/config will
+ * be at the last item of the list)
+ */
+struct configset_list {
+ struct configset_list_item *items;
+ unsigned int nr, alloc;
+};
+
+struct config_set {
+ struct hashmap config_hash;
+ int hash_initialized;
+ struct configset_list list;
+};
+
+extern void git_configset_init(struct config_set *cs);
+extern int git_configset_add_file(struct config_set *cs, const char *filename);
+extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value);
+extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+extern void git_configset_clear(struct config_set *cs);
+extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
+extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
+extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
+extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
+extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
+extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
+extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
+extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+
+extern int git_config_get_value(const char *key, const char **value);
+extern const struct string_list *git_config_get_value_multi(const char *key);
+extern void git_config_clear(void);
+extern void git_config_iter(config_fn_t fn, void *data);
+extern int git_config_get_string_const(const char *key, const char **dest);
+extern int git_config_get_string(const char *key, char **dest);
+extern int git_config_get_int(const char *key, int *dest);
+extern int git_config_get_ulong(const char *key, unsigned long *dest);
+extern int git_config_get_bool(const char *key, int *dest);
+extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
+extern int git_config_get_maybe_bool(const char *key, int *dest);
+extern int git_config_get_pathname(const char *key, const char **dest);
+
+struct key_value_info {
+ const char *filename;
+ int linenr;
+};
+
+extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
+extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
+
extern int committer_ident_sufficiently_given(void);
extern int author_ident_sufficiently_given(void);
@@ -1363,6 +1477,8 @@ extern const char *git_mailmap_blob;
/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
+__attribute__((format (printf, 2, 3)))
+extern void fprintf_or_die(FILE *, const char *fmt, ...);
extern int copy_fd(int ifd, int ofd);
extern int copy_file(const char *dst, const char *src, int mode);
extern int copy_file_with_time(const char *dst, const char *src, int mode);
diff --git a/color.c b/color.c
index f672885b71..7941e932d2 100644
--- a/color.c
+++ b/color.c
@@ -60,13 +60,12 @@ static int parse_attr(const char *name, int len)
return -1;
}
-void color_parse(const char *value, const char *var, char *dst)
+int color_parse(const char *value, char *dst)
{
- color_parse_mem(value, strlen(value), var, dst);
+ return color_parse_mem(value, strlen(value), dst);
}
-void color_parse_mem(const char *value, int value_len, const char *var,
- char *dst)
+int color_parse_mem(const char *value, int value_len, char *dst)
{
const char *ptr = value;
int len = value_len;
@@ -76,7 +75,7 @@ void color_parse_mem(const char *value, int value_len, const char *var,
if (!strncasecmp(value, "reset", len)) {
strcpy(dst, GIT_COLOR_RESET);
- return;
+ return 0;
}
/* [fg [bg]] [attr]... */
@@ -153,9 +152,9 @@ void color_parse_mem(const char *value, int value_len, const char *var,
*dst++ = 'm';
}
*dst = 0;
- return;
+ return 0;
bad:
- die("bad color value '%.*s' for variable '%s'", value_len, value, var);
+ return error(_("invalid color value: %.*s"), value_len, value);
}
int git_config_colorbool(const char *var, const char *value)
diff --git a/color.h b/color.h
index 9a8495bb7f..f5beab1ed7 100644
--- a/color.h
+++ b/color.h
@@ -77,8 +77,8 @@ int git_color_default_config(const char *var, const char *value, void *cb);
int git_config_colorbool(const char *var, const char *value);
int want_color(int var);
-void color_parse(const char *value, const char *var, char *dst);
-void color_parse_mem(const char *value, int len, const char *var, char *dst);
+int color_parse(const char *value, char *dst);
+int color_parse_mem(const char *value, int len, char *dst);
__attribute__((format (printf, 3, 4)))
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
__attribute__((format (printf, 3, 4)))
diff --git a/column.c b/column.c
index ca878bcea7..8082a944fd 100644
--- a/column.c
+++ b/column.c
@@ -81,8 +81,7 @@ static void compute_column_width(struct column_data *data)
*/
static void shrink_columns(struct column_data *data)
{
- data->width = xrealloc(data->width,
- sizeof(*data->width) * data->cols);
+ REALLOC_ARRAY(data->width, data->cols);
while (data->rows > 1) {
int x, total_width, cols, rows;
rows = data->rows;
@@ -91,8 +90,7 @@ static void shrink_columns(struct column_data *data)
data->rows--;
data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
if (data->cols != cols)
- data->width = xrealloc(data->width,
- sizeof(*data->width) * data->cols);
+ REALLOC_ARRAY(data->width, data->cols);
compute_column_width(data);
total_width = strlen(data->opts.indent);
@@ -367,7 +365,7 @@ int parseopt_column_callback(const struct option *opt,
}
static int fd_out = -1;
-static struct child_process column_process;
+static struct child_process column_process = CHILD_PROCESS_INIT;
int run_column_filter(int colopts, const struct column_options *opts)
{
diff --git a/command-list.txt b/command-list.txt
index a3ff0c9e60..f1eae0810d 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -62,6 +62,7 @@ git-imap-send foreignscminterface
git-index-pack plumbingmanipulators
git-init mainporcelain common
git-instaweb ancillaryinterrogators
+git-interpret-trailers purehelpers
gitk mainporcelain
git-log mainporcelain common
git-ls-files plumbinginterrogators
diff --git a/commit-slab.h b/commit-slab.h
index 375c9c751a..f37ec3831f 100644
--- a/commit-slab.h
+++ b/commit-slab.h
@@ -90,8 +90,7 @@ static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s, \
\
if (s->slab_count <= nth_slab) { \
int i; \
- s->slab = xrealloc(s->slab, \
- (nth_slab + 1) * sizeof(*s->slab)); \
+ REALLOC_ARRAY(s->slab, nth_slab + 1); \
stat_ ##slabname## realloc++; \
for (i = s->slab_count; i <= nth_slab; i++) \
s->slab[i] = NULL; \
diff --git a/commit.c b/commit.c
index ae7f2b10f4..19cf8f9c67 100644
--- a/commit.c
+++ b/commit.c
@@ -584,25 +584,19 @@ define_commit_slab(author_date_slab, unsigned long);
static void record_author_date(struct author_date_slab *author_date,
struct commit *commit)
{
- const char *buf, *line_end, *ident_line;
const char *buffer = get_commit_buffer(commit, NULL);
struct ident_split ident;
+ const char *ident_line;
+ size_t ident_len;
char *date_end;
unsigned long date;
- for (buf = buffer; buf; buf = line_end + 1) {
- line_end = strchrnul(buf, '\n');
- if (!skip_prefix(buf, "author ", &ident_line)) {
- if (!line_end[0] || line_end[1] == '\n')
- return; /* end of header */
- continue;
- }
- if (split_ident_line(&ident,
- ident_line, line_end - ident_line) ||
- !ident.date_begin || !ident.date_end)
- goto fail_exit; /* malformed "author" line */
- break;
- }
+ ident_line = find_commit_header(buffer, "author", &ident_len);
+ if (!ident_line)
+ goto fail_exit; /* no author line */
+ if (split_ident_line(&ident, ident_line, ident_len) ||
+ !ident.date_begin || !ident.date_end)
+ goto fail_exit; /* malformed "author" line */
date = strtoul(ident.date_begin, &date_end, 10);
if (date_end != ident.date_end)
@@ -1220,43 +1214,7 @@ free_return:
free(buf);
}
-static struct {
- char result;
- const char *check;
-} sigcheck_gpg_status[] = {
- { 'G', "\n[GNUPG:] GOODSIG " },
- { 'B', "\n[GNUPG:] BADSIG " },
- { 'U', "\n[GNUPG:] TRUST_NEVER" },
- { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
-};
-
-static void parse_gpg_output(struct signature_check *sigc)
-{
- const char *buf = sigc->gpg_status;
- int i;
-
- /* Iterate over all search strings */
- for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
- const char *found, *next;
-
- if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
- found = strstr(buf, sigcheck_gpg_status[i].check);
- if (!found)
- continue;
- found += strlen(sigcheck_gpg_status[i].check);
- }
- sigc->result = sigcheck_gpg_status[i].result;
- /* The trust messages are not followed by key/signer information */
- if (sigc->result != 'U') {
- sigc->key = xmemdupz(found, 16);
- found += 17;
- next = strchrnul(found, '\n');
- sigc->signer = xmemdupz(found, next - found);
- }
- }
-}
-
-void check_commit_signature(const struct commit* commit, struct signature_check *sigc)
+void check_commit_signature(const struct commit *commit, struct signature_check *sigc)
{
struct strbuf payload = STRBUF_INIT;
struct strbuf signature = STRBUF_INIT;
@@ -1660,3 +1618,25 @@ void print_commit_list(struct commit_list *list,
printf(format, sha1_to_hex(list->item->object.sha1));
}
}
+
+const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
+{
+ int key_len = strlen(key);
+ const char *line = msg;
+
+ while (line) {
+ const char *eol = strchrnul(line, '\n');
+
+ if (line == eol)
+ return NULL;
+
+ if (eol - line > key_len &&
+ !strncmp(line, key, key_len) &&
+ line[key_len] == ' ') {
+ *out_len = eol - line - key_len - 1;
+ return line + key_len + 1;
+ }
+ line = *eol ? eol + 1 : NULL;
+ }
+ return NULL;
+}
diff --git a/commit.h b/commit.h
index aa8c3ca50a..bc68ccbe69 100644
--- a/commit.h
+++ b/commit.h
@@ -26,13 +26,25 @@ extern int save_commit_buffer;
extern const char *commit_type;
/* While we can decorate any object with a name, it's only used for commits.. */
-extern struct decoration name_decoration;
struct name_decoration {
struct name_decoration *next;
int type;
- char name[1];
+ char name[FLEX_ARRAY];
};
+enum decoration_type {
+ DECORATION_NONE = 0,
+ DECORATION_REF_LOCAL,
+ DECORATION_REF_REMOTE,
+ DECORATION_REF_TAG,
+ DECORATION_REF_STASH,
+ DECORATION_REF_HEAD,
+ DECORATION_GRAFTED,
+};
+
+void add_name_decoration(enum decoration_type type, const char *name, struct object *obj);
+const struct name_decoration *get_name_decoration(const struct object *obj);
+
struct commit *lookup_commit(const unsigned char *sha1);
struct commit *lookup_commit_reference(const unsigned char *sha1);
struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
@@ -314,6 +326,17 @@ extern struct commit_extra_header *read_commit_extra_headers(struct commit *, co
extern void free_commit_extra_headers(struct commit_extra_header *extra);
+/*
+ * Search the commit object contents given by "msg" for the header "key".
+ * Returns a pointer to the start of the header contents, or NULL. The length
+ * of the header, up to the first newline, is returned via out_len.
+ *
+ * Note that some headers (like mergetag) may be multi-line. It is the caller's
+ * responsibility to parse further in this case!
+ */
+extern const char *find_commit_header(const char *msg, const char *key,
+ size_t *out_len);
+
typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
void *cb_data);
@@ -347,7 +370,7 @@ extern void print_commit_list(struct commit_list *list,
* at all. This may allocate memory for sig->gpg_output, sig->gpg_status,
* sig->signer and sig->key.
*/
-extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
+extern void check_commit_signature(const struct commit *commit, struct signature_check *sigc);
int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
diff --git a/compat/bswap.h b/compat/bswap.h
index f6fd9a6a6c..7fed637ed0 100644
--- a/compat/bswap.h
+++ b/compat/bswap.h
@@ -122,6 +122,10 @@ static inline uint64_t git_bswap64(uint64_t x)
# define GIT_BYTE_ORDER GIT_BIG_ENDIAN
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
# define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN
+# elif defined(__THW_BIG_ENDIAN__) && !defined(__THW_LITTLE_ENDIAN__)
+# define GIT_BYTE_ORDER GIT_BIG_ENDIAN
+# elif defined(__THW_LITTLE_ENDIAN__) && !defined(__THW_BIG_ENDIAN__)
+# define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN
# else
# error "Cannot determine endianness"
# endif
diff --git a/compat/mingw.h b/compat/mingw.h
index df0e3203ab..5e499cfb71 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -69,7 +69,6 @@ struct sigaction {
sig_handler_t sa_handler;
unsigned sa_flags;
};
-#define sigemptyset(x) (void)0
#define SA_RESTART 0
struct itimerval {
@@ -116,6 +115,12 @@ static inline int fcntl(int fd, int cmd, ...)
}
/* bash cannot reliably detect negative return codes as failure */
#define exit(code) exit((code) & 0xff)
+#define sigemptyset(x) (void)0
+static inline int sigaddset(sigset_t *set, int signum)
+{ return 0; }
+#define SIG_UNBLOCK 0
+static inline int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
+{ return 0; }
/*
* simple adaptors
diff --git a/config.c b/config.c
index 9e42d3832b..15a2983577 100644
--- a/config.c
+++ b/config.c
@@ -6,9 +6,12 @@
*
*/
#include "cache.h"
+#include "lockfile.h"
#include "exec_cmd.h"
#include "strbuf.h"
#include "quote.h"
+#include "hashmap.h"
+#include "string-list.h"
struct config_source {
struct config_source *prev;
@@ -37,6 +40,13 @@ static struct config_source *cf;
static int zlib_compression_seen;
+/*
+ * Default config_set that contains key-value pairs from the usual set of config
+ * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
+ * config file and the global /etc/gitconfig)
+ */
+static struct config_set the_config_set;
+
static int config_file_fgetc(struct config_source *conf)
{
return fgetc(conf->u.file);
@@ -127,7 +137,6 @@ static int handle_path_include(const char *path, struct config_include_data *inc
int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
- const char *type;
int ret;
/*
@@ -138,10 +147,7 @@ int git_config_include(const char *var, const char *value, void *data)
if (ret < 0)
return ret;
- if (!skip_prefix(var, "include.", &type))
- return ret;
-
- if (!strcmp(type, "path"))
+ if (!strcmp(var, "include.path"))
ret = handle_path_include(value, inc);
return ret;
}
@@ -237,6 +243,7 @@ static int get_next_char(void)
cf->linenr++;
if (c == EOF) {
cf->eof = 1;
+ cf->linenr++;
c = '\n';
}
return c;
@@ -312,6 +319,7 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
{
int c;
char *value;
+ int ret;
/* Get the full name */
for (;;) {
@@ -334,7 +342,15 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
if (!value)
return -1;
}
- return fn(name->buf, value, data);
+ /*
+ * We already consumed the \n, but we need linenr to point to
+ * the line we just parsed during the call to fn to get
+ * accurate line number in error messages.
+ */
+ cf->linenr--;
+ ret = fn(name->buf, value, data);
+ cf->linenr++;
+ return ret;
}
static int get_extended_base_var(struct strbuf *name, int c)
@@ -450,9 +466,9 @@ static int git_parse_source(config_fn_t fn, void *data)
break;
}
if (cf->die_on_error)
- die("bad config file line %d in %s", cf->linenr, cf->name);
+ die(_("bad config file line %d in %s"), cf->linenr, cf->name);
else
- return error("bad config file line %d in %s", cf->linenr, cf->name);
+ return error(_("bad config file line %d in %s"), cf->linenr, cf->name);
}
static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -568,9 +584,9 @@ static void die_bad_number(const char *name, const char *value)
value = "";
if (cf && cf->name)
- die("bad numeric config value '%s' for '%s' in %s: %s",
+ die(_("bad numeric config value '%s' for '%s' in %s: %s"),
value, name, cf->name, reason);
- die("bad numeric config value '%s' for '%s': %s", value, name, reason);
+ die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
}
int git_config_int(const char *name, const char *value)
@@ -655,7 +671,7 @@ int git_config_pathname(const char **dest, const char *var, const char *value)
return config_error_nonbool(var);
*dest = expand_user_path(value);
if (!*dest)
- die("Failed to expand user dir in: '%s'", value);
+ die(_("failed to expand user dir in: '%s'"), value);
return 0;
}
@@ -733,7 +749,7 @@ static int git_default_core_config(const char *var, const char *value)
if (level == -1)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
- die("bad zlib compression level %d", level);
+ die(_("bad zlib compression level %d"), level);
zlib_compression_level = level;
zlib_compression_seen = 1;
return 0;
@@ -744,7 +760,7 @@ static int git_default_core_config(const char *var, const char *value)
if (level == -1)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
- die("bad zlib compression level %d", level);
+ die(_("bad zlib compression level %d"), level);
core_compression_level = level;
core_compression_seen = 1;
if (!zlib_compression_seen)
@@ -866,7 +882,7 @@ static int git_default_core_config(const char *var, const char *value)
else if (!strcmp(value, "link"))
object_creation_mode = OBJECT_CREATION_USES_HARDLINKS;
else
- die("Invalid mode for object creation: %s", value);
+ die(_("invalid mode for object creation: %s"), value);
return 0;
}
@@ -1124,12 +1140,28 @@ const char *git_etc_gitconfig(void)
return system_wide;
}
+/*
+ * Parse environment variable 'k' as a boolean (in various
+ * possible spellings); if missing, use the default value 'def'.
+ */
int git_env_bool(const char *k, int def)
{
const char *v = getenv(k);
return v ? git_config_bool(k, v) : def;
}
+/*
+ * Parse environment variable 'k' as ulong with possibly a unit
+ * suffix; if missing, use the default value 'val'.
+ */
+unsigned long git_env_ulong(const char *k, unsigned long val)
+{
+ const char *v = getenv(k);
+ if (v && !git_parse_ulong(v, &val))
+ die("failed to parse %s", k);
+ return val;
+}
+
int git_config_system(void)
{
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
@@ -1166,7 +1198,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
switch (git_config_from_parameters(fn, data)) {
case -1: /* error */
- die("unable to parse command-line config");
+ die(_("unable to parse command-line config"));
break;
case 0: /* found nothing */
break;
@@ -1213,9 +1245,365 @@ int git_config_with_options(config_fn_t fn, void *data,
return ret;
}
-int git_config(config_fn_t fn, void *data)
+static void git_config_raw(config_fn_t fn, void *data)
+{
+ if (git_config_with_options(fn, data, NULL, 1) < 0)
+ /*
+ * git_config_with_options() normally returns only
+ * positive values, as most errors are fatal, and
+ * non-fatal potential errors are guarded by "if"
+ * statements that are entered only when no error is
+ * possible.
+ *
+ * If we ever encounter a non-fatal error, it means
+ * something went really wrong and we should stop
+ * immediately.
+ */
+ die(_("unknown error occured while reading the configuration files"));
+}
+
+static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+{
+ int i, value_index;
+ struct string_list *values;
+ struct config_set_element *entry;
+ struct configset_list *list = &cs->list;
+ struct key_value_info *kv_info;
+
+ for (i = 0; i < list->nr; i++) {
+ entry = list->items[i].e;
+ value_index = list->items[i].value_index;
+ values = &entry->value_list;
+ if (fn(entry->key, values->items[value_index].string, data) < 0) {
+ kv_info = values->items[value_index].util;
+ git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr);
+ }
+ }
+}
+
+static void git_config_check_init(void);
+
+void git_config(config_fn_t fn, void *data)
+{
+ git_config_check_init();
+ configset_iter(&the_config_set, fn, data);
+}
+
+static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+{
+ struct config_set_element k;
+ struct config_set_element *found_entry;
+ char *normalized_key;
+ int ret;
+ /*
+ * `key` may come from the user, so normalize it before using it
+ * for querying entries from the hashmap.
+ */
+ ret = git_config_parse_key(key, &normalized_key, NULL);
+
+ if (ret)
+ return NULL;
+
+ hashmap_entry_init(&k, strhash(normalized_key));
+ k.key = normalized_key;
+ found_entry = hashmap_get(&cs->config_hash, &k, NULL);
+ free(normalized_key);
+ return found_entry;
+}
+
+static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+{
+ struct config_set_element *e;
+ struct string_list_item *si;
+ struct configset_list_item *l_item;
+ struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
+
+ e = configset_find_element(cs, key);
+ /*
+ * Since the keys are being fed by git_config*() callback mechanism, they
+ * are already normalized. So simply add them without any further munging.
+ */
+ if (!e) {
+ e = xmalloc(sizeof(*e));
+ hashmap_entry_init(e, strhash(key));
+ e->key = xstrdup(key);
+ string_list_init(&e->value_list, 1);
+ hashmap_add(&cs->config_hash, e);
+ }
+ si = string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL);
+
+ ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
+ l_item = &cs->list.items[cs->list.nr++];
+ l_item->e = e;
+ l_item->value_index = e->value_list.nr - 1;
+
+ if (cf) {
+ kv_info->filename = strintern(cf->name);
+ kv_info->linenr = cf->linenr;
+ } else {
+ /* for values read from `git_config_from_parameters()` */
+ kv_info->filename = NULL;
+ kv_info->linenr = -1;
+ }
+ si->util = kv_info;
+
+ return 0;
+}
+
+static int config_set_element_cmp(const struct config_set_element *e1,
+ const struct config_set_element *e2, const void *unused)
+{
+ return strcmp(e1->key, e2->key);
+}
+
+void git_configset_init(struct config_set *cs)
+{
+ hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0);
+ cs->hash_initialized = 1;
+ cs->list.nr = 0;
+ cs->list.alloc = 0;
+ cs->list.items = NULL;
+}
+
+void git_configset_clear(struct config_set *cs)
+{
+ struct config_set_element *entry;
+ struct hashmap_iter iter;
+ if (!cs->hash_initialized)
+ return;
+
+ hashmap_iter_init(&cs->config_hash, &iter);
+ while ((entry = hashmap_iter_next(&iter))) {
+ free(entry->key);
+ string_list_clear(&entry->value_list, 1);
+ }
+ hashmap_free(&cs->config_hash, 1);
+ cs->hash_initialized = 0;
+ free(cs->list.items);
+ cs->list.nr = 0;
+ cs->list.alloc = 0;
+ cs->list.items = NULL;
+}
+
+static int config_set_callback(const char *key, const char *value, void *cb)
+{
+ struct config_set *cs = cb;
+ configset_add_value(cs, key, value);
+ return 0;
+}
+
+int git_configset_add_file(struct config_set *cs, const char *filename)
+{
+ return git_config_from_file(config_set_callback, filename, cs);
+}
+
+int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+{
+ const struct string_list *values = NULL;
+ /*
+ * Follows "last one wins" semantic, i.e., if there are multiple matches for the
+ * queried key in the files of the configset, the value returned will be the last
+ * value in the value list for that key.
+ */
+ values = git_configset_get_value_multi(cs, key);
+
+ if (!values)
+ return 1;
+ assert(values->nr > 0);
+ *value = values->items[values->nr - 1].string;
+ return 0;
+}
+
+const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
{
- return git_config_with_options(fn, data, NULL, 1);
+ struct config_set_element *e = configset_find_element(cs, key);
+ return e ? &e->value_list : NULL;
+}
+
+int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest)
+{
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value))
+ return git_config_string(dest, key, value);
+ else
+ return 1;
+}
+
+int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+{
+ return git_configset_get_string_const(cs, key, (const char **)dest);
+}
+
+int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+{
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value)) {
+ *dest = git_config_int(key, value);
+ return 0;
+ } else
+ return 1;
+}
+
+int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+{
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value)) {
+ *dest = git_config_ulong(key, value);
+ return 0;
+ } else
+ return 1;
+}
+
+int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+{
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value)) {
+ *dest = git_config_bool(key, value);
+ return 0;
+ } else
+ return 1;
+}
+
+int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+ int *is_bool, int *dest)
+{
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value)) {
+ *dest = git_config_bool_or_int(key, value, is_bool);
+ return 0;
+ } else
+ return 1;
+}
+
+int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+{
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value)) {
+ *dest = git_config_maybe_bool(key, value);
+ if (*dest == -1)
+ return -1;
+ return 0;
+ } else
+ return 1;
+}
+
+int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+{
+ const char *value;
+ if (!git_configset_get_value(cs, key, &value))
+ return git_config_pathname(dest, key, value);
+ else
+ return 1;
+}
+
+static void git_config_check_init(void)
+{
+ if (the_config_set.hash_initialized)
+ return;
+ git_configset_init(&the_config_set);
+ git_config_raw(config_set_callback, &the_config_set);
+}
+
+void git_config_clear(void)
+{
+ if (!the_config_set.hash_initialized)
+ return;
+ git_configset_clear(&the_config_set);
+}
+
+int git_config_get_value(const char *key, const char **value)
+{
+ git_config_check_init();
+ return git_configset_get_value(&the_config_set, key, value);
+}
+
+const struct string_list *git_config_get_value_multi(const char *key)
+{
+ git_config_check_init();
+ return git_configset_get_value_multi(&the_config_set, key);
+}
+
+int git_config_get_string_const(const char *key, const char **dest)
+{
+ int ret;
+ git_config_check_init();
+ ret = git_configset_get_string_const(&the_config_set, key, dest);
+ if (ret < 0)
+ git_die_config(key, NULL);
+ return ret;
+}
+
+int git_config_get_string(const char *key, char **dest)
+{
+ git_config_check_init();
+ return git_config_get_string_const(key, (const char **)dest);
+}
+
+int git_config_get_int(const char *key, int *dest)
+{
+ git_config_check_init();
+ return git_configset_get_int(&the_config_set, key, dest);
+}
+
+int git_config_get_ulong(const char *key, unsigned long *dest)
+{
+ git_config_check_init();
+ return git_configset_get_ulong(&the_config_set, key, dest);
+}
+
+int git_config_get_bool(const char *key, int *dest)
+{
+ git_config_check_init();
+ return git_configset_get_bool(&the_config_set, key, dest);
+}
+
+int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)
+{
+ git_config_check_init();
+ return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest);
+}
+
+int git_config_get_maybe_bool(const char *key, int *dest)
+{
+ git_config_check_init();
+ return git_configset_get_maybe_bool(&the_config_set, key, dest);
+}
+
+int git_config_get_pathname(const char *key, const char **dest)
+{
+ int ret;
+ git_config_check_init();
+ ret = git_configset_get_pathname(&the_config_set, key, dest);
+ if (ret < 0)
+ git_die_config(key, NULL);
+ return ret;
+}
+
+NORETURN
+void git_die_config_linenr(const char *key, const char *filename, int linenr)
+{
+ if (!filename)
+ die(_("unable to parse '%s' from command-line config"), key);
+ else
+ die(_("bad config variable '%s' in file '%s' at line %d"),
+ key, filename, linenr);
+}
+
+NORETURN __attribute__((format(printf, 2, 3)))
+void git_die_config(const char *key, const char *err, ...)
+{
+ const struct string_list *values;
+ struct key_value_info *kv_info;
+
+ if (err) {
+ va_list params;
+ va_start(params, err);
+ vreportf("error: ", err, params);
+ va_end(params);
+ }
+ values = git_config_get_value_multi(key);
+ kv_info = values->items[values->nr - 1].util;
+ git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
}
/*
@@ -1256,7 +1644,7 @@ static int store_aux(const char *key, const char *value, void *cb)
case KEY_SEEN:
if (matches(key, value)) {
if (store.seen == 1 && store.multi_replace == 0) {
- warning("%s has multiple values", key);
+ warning(_("%s has multiple values"), key);
}
ALLOC_GROW(store.offset, store.seen + 1,
@@ -1653,9 +2041,9 @@ int git_config_set_multivar_in_file(const char *config_filename,
MAP_PRIVATE, in_fd, 0);
close(in_fd);
- if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+ if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
error("chmod on %s failed: %s",
- lock->filename, strerror(errno));
+ lock->filename.buf, strerror(errno));
ret = CONFIG_NO_WRITE;
goto out_free;
}
@@ -1712,6 +2100,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
if (commit_lock_file(lock) < 0) {
error("could not commit config file %s", config_filename);
ret = CONFIG_NO_WRITE;
+ lock = NULL;
goto out_free;
}
@@ -1724,6 +2113,9 @@ int git_config_set_multivar_in_file(const char *config_filename,
lock = NULL;
ret = 0;
+ /* Invalidate the config cache */
+ git_config_clear();
+
out_free:
if (lock)
rollback_lock_file(lock);
@@ -1731,7 +2123,7 @@ out_free:
return ret;
write_err_out:
- ret = write_error(lock->filename);
+ ret = write_error(lock->filename.buf);
goto out_free;
}
@@ -1832,9 +2224,9 @@ int git_config_rename_section_in_file(const char *config_filename,
fstat(fileno(config_file), &st);
- if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+ if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
ret = error("chmod on %s failed: %s",
- lock->filename, strerror(errno));
+ lock->filename.buf, strerror(errno));
goto out;
}
@@ -1855,7 +2247,7 @@ int git_config_rename_section_in_file(const char *config_filename,
}
store.baselen = strlen(new_name);
if (!store_write_section(out_fd, new_name)) {
- ret = write_error(lock->filename);
+ ret = write_error(lock->filename.buf);
goto out;
}
/*
@@ -1881,7 +2273,7 @@ int git_config_rename_section_in_file(const char *config_filename,
continue;
length = strlen(output);
if (write_in_full(out_fd, output, length) != length) {
- ret = write_error(lock->filename);
+ ret = write_error(lock->filename.buf);
goto out;
}
}
diff --git a/config.mak.uname b/config.mak.uname
index 15ee15e98c..a2f380fd8d 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -89,8 +89,13 @@ ifeq ($(uname_S),Darwin)
NEEDS_CRYPTO_WITH_SSL = YesPlease
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = YesPlease
+ # Note: $(uname_R) gives us the underlying Darwin version.
+ # - MacOS 10.0.* and MacOS 10.1.0 = Darwin 1.*
+ # - MacOS 10.x.* = Darwin (x+4).* for (1 <= x)
+ # i.e. "begins with [15678] and a dot" means "10.4.* or older".
ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
OLD_ICONV = UnfortunatelyYes
+ NO_APPLE_COMMON_CRYPTO = YesPlease
endif
ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
NO_STRLCPY = YesPlease
diff --git a/configure.ac b/configure.ac
index 4b1ae7c3c9..6af964797f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -746,6 +746,14 @@ case $ac_cv_type_socklen_t in
esac
GIT_CONF_SUBST([SOCKLEN_T])
+#
+# Define NO_STRUCT_ITIMERVAL if you don't have struct itimerval.
+AC_CHECK_TYPES([struct itimerval],
+[NO_STRUCT_ITIMERVAL=],
+[NO_STRUCT_ITIMERVAL=UnfortunatelyYes],
+[#include <sys/time.h>])
+GIT_CONF_SUBST([NO_STRUCT_ITIMERVAL])
+#
# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
AC_CHECK_MEMBER(struct dirent.d_ino,
[NO_D_INO_IN_DIRENT=],
@@ -903,6 +911,12 @@ AC_CHECK_LIB([iconv], [locale_charset],
[CHARSET_LIB=-lcharset])])
GIT_CONF_SUBST([CHARSET_LIB])
#
+# Define NO_SETITIMER if you don't have setitimer.
+GIT_CHECK_FUNC(setitimer,
+[NO_SETITIMER=],
+[NO_SETITIMER=YesPlease])
+GIT_CONF_SUBST([NO_SETITIMER])
+#
# Define NO_STRCASESTR if you don't have strcasestr.
GIT_CHECK_FUNC(strcasestr,
[NO_STRCASESTR=],
diff --git a/connect.c b/connect.c
index 5047402a1a..d47d0ec604 100644
--- a/connect.c
+++ b/connect.c
@@ -13,28 +13,24 @@
static char *server_capabilities;
static const char *parse_feature_value(const char *, const char *, int *);
-static int check_ref(const char *name, int len, unsigned int flags)
+static int check_ref(const char *name, unsigned int flags)
{
if (!flags)
return 1;
- if (len < 5 || memcmp(name, "refs/", 5))
+ if (!skip_prefix(name, "refs/", &name))
return 0;
- /* Skip the "refs/" part */
- name += 5;
- len -= 5;
-
/* REF_NORMAL means that we don't want the magic fake tag refs */
if ((flags & REF_NORMAL) && check_refname_format(name, 0))
return 0;
/* REF_HEADS means that we want regular branch heads */
- if ((flags & REF_HEADS) && !memcmp(name, "heads/", 6))
+ if ((flags & REF_HEADS) && starts_with(name, "heads/"))
return 1;
/* REF_TAGS means that we want tags */
- if ((flags & REF_TAGS) && !memcmp(name, "tags/", 5))
+ if ((flags & REF_TAGS) && starts_with(name, "tags/"))
return 1;
/* All type bits clear means that we are ok with anything */
@@ -43,7 +39,7 @@ static int check_ref(const char *name, int len, unsigned int flags)
int check_ref_type(const struct ref *ref, int flags)
{
- return check_ref(ref->name, strlen(ref->name), flags);
+ return check_ref(ref->name, flags);
}
static void die_initial_contact(int got_at_least_one_head)
@@ -167,7 +163,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
continue;
}
- if (!check_ref(name, name_len, flags))
+ if (!check_ref(name, flags))
continue;
ref = alloc_ref(buffer + 41);
hashcpy(ref->old_sha1, old_sha1);
@@ -537,7 +533,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)
get_host_and_port(&host, &port);
- proxy = xcalloc(1, sizeof(*proxy));
+ proxy = xmalloc(sizeof(*proxy));
+ child_process_init(proxy);
argv_array_push(&proxy->args, git_proxy_command);
argv_array_push(&proxy->args, host);
argv_array_push(&proxy->args, port);
@@ -639,7 +636,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
return protocol;
}
-static struct child_process no_fork;
+static struct child_process no_fork = CHILD_PROCESS_INIT;
/*
* This returns a dummy child_process if the transport protocol does not
@@ -694,7 +691,8 @@ struct child_process *git_connect(int fd[2], const char *url,
target_host, 0);
free(target_host);
} else {
- conn = xcalloc(1, sizeof(*conn));
+ conn = xmalloc(sizeof(*conn));
+ child_process_init(conn);
strbuf_addstr(&cmd, prog);
strbuf_addch(&cmd, ' ');
diff --git a/connected.c b/connected.c
index dae9c9972e..299c56090b 100644
--- a/connected.c
+++ b/connected.c
@@ -25,7 +25,7 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
struct transport *transport,
const char *shallow_file)
{
- struct child_process rev_list;
+ struct child_process rev_list = CHILD_PROCESS_INIT;
const char *argv[9];
char commit[41];
unsigned char sha1[20];
@@ -60,7 +60,6 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
argv[ac++] = "--quiet";
argv[ac] = NULL;
- memset(&rev_list, 0, sizeof(rev_list));
rev_list.argv = argv;
rev_list.git_cmd = 1;
rev_list.in = -1;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 06bf262087..2fece98c60 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -281,16 +281,12 @@ __gitcomp_file ()
# argument, and using the options specified in the second argument.
__git_ls_files_helper ()
{
- (
- test -n "${CDPATH+set}" && unset CDPATH
- cd "$1"
- if [ "$2" == "--committable" ]; then
- git diff-index --name-only --relative HEAD
- else
- # NOTE: $2 is not quoted in order to support multiple options
- git ls-files --exclude-standard $2
- fi
- ) 2>/dev/null
+ if [ "$2" == "--committable" ]; then
+ git -C "$1" diff-index --name-only --relative HEAD
+ else
+ # NOTE: $2 is not quoted in order to support multiple options
+ git -C "$1" ls-files --exclude-standard $2
+ fi 2>/dev/null
}
@@ -388,7 +384,8 @@ __git_refs ()
;;
*)
echo "HEAD"
- git for-each-ref --format="%(refname:short)" -- "refs/remotes/$dir/" | sed -e "s#^$dir/##"
+ git for-each-ref --format="%(refname:short)" -- \
+ "refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
;;
esac
}
@@ -522,7 +519,7 @@ __git_complete_index_file ()
;;
esac
- __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
+ __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_"
}
__git_complete_file ()
@@ -1175,8 +1172,8 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
--full-index --binary --abbrev --diff-filter=
--find-copies-harder
--text --ignore-space-at-eol --ignore-space-change
- --ignore-all-space --exit-code --quiet --ext-diff
- --no-ext-diff
+ --ignore-all-space --ignore-blank-lines --exit-code
+ --quiet --ext-diff --no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
--inter-hunk-context=
--patience --histogram --minimal
@@ -1207,7 +1204,7 @@ _git_diff ()
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
- tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3 codecompare
+ tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
"
_git_difftool ()
@@ -1467,6 +1464,7 @@ _git_log ()
--abbrev-commit --abbrev=
--relative-date --date=
--pretty= --format= --oneline
+ --show-signature
--cherry-pick
--graph
--decorate --decorate=
@@ -2344,6 +2342,7 @@ _git_show ()
;;
--*)
__gitcomp "--pretty= --format= --abbrev-commit --oneline
+ --show-signature
$__git_diff_common_options
"
return
diff --git a/contrib/contacts/.gitignore b/contrib/contacts/.gitignore
new file mode 100644
index 0000000000..f385ee643c
--- /dev/null
+++ b/contrib/contacts/.gitignore
@@ -0,0 +1,3 @@
+git-contacts.1
+git-contacts.html
+git-contacts.xml
diff --git a/contrib/contacts/Makefile b/contrib/contacts/Makefile
new file mode 100644
index 0000000000..a2990f0dcb
--- /dev/null
+++ b/contrib/contacts/Makefile
@@ -0,0 +1,71 @@
+# The default target of this Makefile is...
+all::
+
+-include ../../config.mak.autogen
+-include ../../config.mak
+
+prefix ?= /usr/local
+gitexecdir ?= $(prefix)/libexec/git-core
+mandir ?= $(prefix)/share/man
+man1dir ?= $(mandir)/man1
+htmldir ?= $(prefix)/share/doc/git-doc
+
+../../GIT-VERSION-FILE: FORCE
+ $(MAKE) -C ../../ GIT-VERSION-FILE
+
+-include ../../GIT-VERSION-FILE
+
+# this should be set to a 'standard' bsd-type install program
+INSTALL ?= install
+RM ?= rm -f
+
+ASCIIDOC = asciidoc
+XMLTO = xmlto
+
+ifndef SHELL_PATH
+ SHELL_PATH = /bin/sh
+endif
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+ASCIIDOC_CONF = ../../Documentation/asciidoc.conf
+MANPAGE_XSL = ../../Documentation/manpage-normal.xsl
+
+GIT_CONTACTS := git-contacts
+
+GIT_CONTACTS_DOC := git-contacts.1
+GIT_CONTACTS_XML := git-contacts.xml
+GIT_CONTACTS_TXT := git-contacts.txt
+GIT_CONTACTS_HTML := git-contacts.html
+
+doc: $(GIT_CONTACTS_DOC) $(GIT_CONTACTS_HTML)
+
+install: $(GIT_CONTACTS)
+ $(INSTALL) -d -m 755 $(DESTDIR)$(gitexecdir)
+ $(INSTALL) -m 755 $(GIT_CONTACTS) $(DESTDIR)$(gitexecdir)
+
+install-doc: install-man install-html
+
+install-man: $(GIT_CONTACTS_DOC)
+ $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
+ $(INSTALL) -m 644 $^ $(DESTDIR)$(man1dir)
+
+install-html: $(GIT_CONTACTS_HTML)
+ $(INSTALL) -d -m 755 $(DESTDIR)$(htmldir)
+ $(INSTALL) -m 644 $^ $(DESTDIR)$(htmldir)
+
+$(GIT_CONTACTS_DOC): $(GIT_CONTACTS_XML)
+ $(XMLTO) -m $(MANPAGE_XSL) man $^
+
+$(GIT_CONTACTS_XML): $(GIT_CONTACTS_TXT)
+ $(ASCIIDOC) -b docbook -d manpage -f $(ASCIIDOC_CONF) \
+ -agit_version=$(GIT_VERSION) $^
+
+$(GIT_CONTACTS_HTML): $(GIT_CONTACTS_TXT)
+ $(ASCIIDOC) -b xhtml11 -d manpage -f $(ASCIIDOC_CONF) \
+ -agit_version=$(GIT_VERSION) $^
+
+clean:
+ $(RM) $(GIT_CONTACTS)
+ $(RM) *.xml *.html *.1
+
+.PHONY: FORCE
diff --git a/contrib/subtree/.gitignore b/contrib/subtree/.gitignore
index 91360a3d7f..0b9381abca 100644
--- a/contrib/subtree/.gitignore
+++ b/contrib/subtree/.gitignore
@@ -1,6 +1,7 @@
*~
git-subtree
-git-subtree.xml
git-subtree.1
+git-subtree.html
+git-subtree.xml
mainline
subproj
diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile
index d9a0ce2c63..3071baf493 100644
--- a/contrib/subtree/Makefile
+++ b/contrib/subtree/Makefile
@@ -1,10 +1,14 @@
+# The default target of this Makefile is...
+all::
+
-include ../../config.mak.autogen
-include ../../config.mak
prefix ?= /usr/local
-mandir ?= $(prefix)/share/man
gitexecdir ?= $(prefix)/libexec/git-core
+mandir ?= $(prefix)/share/man
man1dir ?= $(mandir)/man1
+htmldir ?= $(prefix)/share/doc/git-doc
../../GIT-VERSION-FILE: FORCE
$(MAKE) -C ../../ GIT-VERSION-FILE
@@ -34,7 +38,7 @@ GIT_SUBTREE_XML := git-subtree.xml
GIT_SUBTREE_TXT := git-subtree.txt
GIT_SUBTREE_HTML := git-subtree.html
-all: $(GIT_SUBTREE)
+all:: $(GIT_SUBTREE)
$(GIT_SUBTREE): $(GIT_SUBTREE_SH)
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' $< >$@
@@ -46,12 +50,16 @@ install: $(GIT_SUBTREE)
$(INSTALL) -d -m 755 $(DESTDIR)$(gitexecdir)
$(INSTALL) -m 755 $(GIT_SUBTREE) $(DESTDIR)$(gitexecdir)
-install-doc: install-man
+install-doc: install-man install-html
install-man: $(GIT_SUBTREE_DOC)
$(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
$(INSTALL) -m 644 $^ $(DESTDIR)$(man1dir)
+install-html: $(GIT_SUBTREE_HTML)
+ $(INSTALL) -d -m 755 $(DESTDIR)$(htmldir)
+ $(INSTALL) -m 644 $^ $(DESTDIR)$(htmldir)
+
$(GIT_SUBTREE_DOC): $(GIT_SUBTREE_XML)
$(XMLTO) -m $(MANPAGE_XSL) man $^
diff --git a/contrib/svn-fe/Makefile b/contrib/svn-fe/Makefile
index 360d8da417..e8651aaf4b 100644
--- a/contrib/svn-fe/Makefile
+++ b/contrib/svn-fe/Makefile
@@ -1,18 +1,58 @@
all:: svn-fe$X
-CC = gcc
+CC = cc
RM = rm -f
MV = mv
CFLAGS = -g -O2 -Wall
LDFLAGS =
-ALL_CFLAGS = $(CFLAGS)
-ALL_LDFLAGS = $(LDFLAGS)
-EXTLIBS =
+EXTLIBS = -lz
+
+include ../../config.mak.uname
+-include ../../config.mak.autogen
+-include ../../config.mak
+
+ifeq ($(uname_S),Darwin)
+ ifndef NO_FINK
+ ifeq ($(shell test -d /sw/lib && echo y),y)
+ CFLAGS += -I/sw/include
+ LDFLAGS += -L/sw/lib
+ endif
+ endif
+ ifndef NO_DARWIN_PORTS
+ ifeq ($(shell test -d /opt/local/lib && echo y),y)
+ CFLAGS += -I/opt/local/include
+ LDFLAGS += -L/opt/local/lib
+ endif
+ endif
+endif
+
+ifndef NO_OPENSSL
+ EXTLIBS += -lssl
+ ifdef NEEDS_CRYPTO_WITH_SSL
+ EXTLIBS += -lcrypto
+ endif
+endif
+
+ifndef NO_PTHREADS
+ CFLAGS += $(PTHREADS_CFLAGS)
+ EXTLIBS += $(PTHREAD_LIBS)
+endif
+
+ifdef HAVE_CLOCK_GETTIME
+ CFLAGS += -DHAVE_CLOCK_GETTIME
+ EXTLIBS += -lrt
+endif
+
+ifdef NEEDS_LIBICONV
+ EXTLIBS += -liconv
+endif
GIT_LIB = ../../libgit.a
VCSSVN_LIB = ../../vcs-svn/lib.a
-LIBS = $(VCSSVN_LIB) $(GIT_LIB) $(EXTLIBS)
+XDIFF_LIB = ../../xdiff/lib.a
+
+LIBS = $(VCSSVN_LIB) $(GIT_LIB) $(XDIFF_LIB)
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
QUIET_SUBDIR1 =
@@ -33,12 +73,11 @@ ifndef V
endif
endif
-svn-fe$X: svn-fe.o $(VCSSVN_LIB) $(GIT_LIB)
- $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ svn-fe.o \
- $(ALL_LDFLAGS) $(LIBS)
+svn-fe$X: svn-fe.o $(VCSSVN_LIB) $(XDIFF_LIB) $(GIT_LIB)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(EXTLIBS) -o $@ svn-fe.o $(LIBS)
svn-fe.o: svn-fe.c ../../vcs-svn/svndump.h
- $(QUIET_CC)$(CC) -I../../vcs-svn -o $*.o -c $(ALL_CFLAGS) $<
+ $(QUIET_CC)$(CC) $(CFLAGS) -I../../vcs-svn -o $*.o -c $<
svn-fe.html: svn-fe.txt
$(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
@@ -54,6 +93,9 @@ svn-fe.1: svn-fe.txt
../../vcs-svn/lib.a: FORCE
$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) vcs-svn/lib.a
+../../xdiff/lib.a: FORCE
+ $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) xdiff/lib.a
+
../../libgit.a: FORCE
$(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) libgit.a
diff --git a/convert.c b/convert.c
index cb5fbb45ea..9a5612e93d 100644
--- a/convert.c
+++ b/convert.c
@@ -312,16 +312,17 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
struct filter_params {
const char *src;
unsigned long size;
+ int fd;
const char *cmd;
const char *path;
};
-static int filter_buffer(int in, int out, void *data)
+static int filter_buffer_or_fd(int in, int out, void *data)
{
/*
* Spawn cmd and feed the buffer contents through its stdin.
*/
- struct child_process child_process;
+ struct child_process child_process = CHILD_PROCESS_INIT;
struct filter_params *params = (struct filter_params *)data;
int write_err, status;
const char *argv[] = { NULL, NULL };
@@ -344,7 +345,6 @@ static int filter_buffer(int in, int out, void *data)
argv[0] = cmd.buf;
- memset(&child_process, 0, sizeof(child_process));
child_process.argv = argv;
child_process.use_shell = 1;
child_process.in = -1;
@@ -355,7 +355,12 @@ static int filter_buffer(int in, int out, void *data)
sigchain_push(SIGPIPE, SIG_IGN);
- write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+ if (params->src) {
+ write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+ } else {
+ write_err = copy_fd(params->fd, child_process.in);
+ }
+
if (close(child_process.in))
write_err = 1;
if (write_err)
@@ -371,7 +376,7 @@ static int filter_buffer(int in, int out, void *data)
return (write_err || status);
}
-static int apply_filter(const char *path, const char *src, size_t len,
+static int apply_filter(const char *path, const char *src, size_t len, int fd,
struct strbuf *dst, const char *cmd)
{
/*
@@ -392,11 +397,12 @@ static int apply_filter(const char *path, const char *src, size_t len,
return 1;
memset(&async, 0, sizeof(async));
- async.proc = filter_buffer;
+ async.proc = filter_buffer_or_fd;
async.data = &params;
async.out = -1;
params.src = src;
params.size = len;
+ params.fd = fd;
params.cmd = cmd;
params.path = path;
@@ -747,6 +753,25 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
}
}
+int would_convert_to_git_filter_fd(const char *path)
+{
+ struct conv_attrs ca;
+
+ convert_attrs(&ca, path);
+ if (!ca.drv)
+ return 0;
+
+ /*
+ * Apply a filter to an fd only if the filter is required to succeed.
+ * We must die if the filter fails, because the original data before
+ * filtering is not available.
+ */
+ if (!ca.drv->required)
+ return 0;
+
+ return apply_filter(path, NULL, 0, -1, NULL, ca.drv->clean);
+}
+
int convert_to_git(const char *path, const char *src, size_t len,
struct strbuf *dst, enum safe_crlf checksafe)
{
@@ -761,7 +786,7 @@ int convert_to_git(const char *path, const char *src, size_t len,
required = ca.drv->required;
}
- ret |= apply_filter(path, src, len, dst, filter);
+ ret |= apply_filter(path, src, len, -1, dst, filter);
if (!ret && required)
die("%s: clean filter '%s' failed", path, ca.drv->name);
@@ -778,6 +803,23 @@ int convert_to_git(const char *path, const char *src, size_t len,
return ret | ident_to_git(path, src, len, dst, ca.ident);
}
+void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst,
+ enum safe_crlf checksafe)
+{
+ struct conv_attrs ca;
+ convert_attrs(&ca, path);
+
+ assert(ca.drv);
+ assert(ca.drv->clean);
+
+ if (!apply_filter(path, NULL, 0, fd, dst, ca.drv->clean))
+ die("%s: clean filter '%s' failed", path, ca.drv->name);
+
+ ca.crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
+ crlf_to_git(path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
+ ident_to_git(path, dst->buf, dst->len, dst, ca.ident);
+}
+
static int convert_to_working_tree_internal(const char *path, const char *src,
size_t len, struct strbuf *dst,
int normalizing)
@@ -811,7 +853,7 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
}
}
- ret_filter = apply_filter(path, src, len, dst, filter);
+ ret_filter = apply_filter(path, src, len, -1, dst, filter);
if (!ret_filter && required)
die("%s: smudge filter %s failed", path, ca.drv->name);
diff --git a/convert.h b/convert.h
index 0c2143c152..d9d853cd3d 100644
--- a/convert.h
+++ b/convert.h
@@ -40,11 +40,15 @@ extern int convert_to_working_tree(const char *path, const char *src,
size_t len, struct strbuf *dst);
extern int renormalize_buffer(const char *path, const char *src, size_t len,
struct strbuf *dst);
-static inline int would_convert_to_git(const char *path, const char *src,
- size_t len, enum safe_crlf checksafe)
+static inline int would_convert_to_git(const char *path)
{
- return convert_to_git(path, src, len, NULL, checksafe);
+ return convert_to_git(path, NULL, 0, NULL, 0);
}
+/* Precondition: would_convert_to_git_filter_fd(path) == true */
+extern void convert_to_git_filter_fd(const char *path, int fd,
+ struct strbuf *dst,
+ enum safe_crlf checksafe);
+extern int would_convert_to_git_filter_fd(const char *path);
/*****************************************************************
*
diff --git a/copy.c b/copy.c
index a7f58fd905..f2970ec462 100644
--- a/copy.c
+++ b/copy.c
@@ -4,34 +4,17 @@ int copy_fd(int ifd, int ofd)
{
while (1) {
char buffer[8192];
- char *buf = buffer;
ssize_t len = xread(ifd, buffer, sizeof(buffer));
if (!len)
break;
if (len < 0) {
- int read_error = errno;
- close(ifd);
return error("copy-fd: read returned %s",
- strerror(read_error));
- }
- while (len) {
- int written = xwrite(ofd, buf, len);
- if (written > 0) {
- buf += written;
- len -= written;
- }
- else if (!written) {
- close(ifd);
- return error("copy-fd: write returned 0");
- } else {
- int write_error = errno;
- close(ifd);
- return error("copy-fd: write returned %s",
- strerror(write_error));
- }
+ strerror(errno));
}
+ if (write_in_full(ofd, buffer, len) < 0)
+ return error("copy-fd: write returned %s",
+ strerror(errno));
}
- close(ifd);
return 0;
}
@@ -60,6 +43,7 @@ int copy_file(const char *dst, const char *src, int mode)
return fdo;
}
status = copy_fd(fdi, fdo);
+ close(fdi);
if (close(fdo) != 0)
return error("%s: close error: %s", dst, strerror(errno));
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 3b370ca5e5..c2f00498f6 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -2,6 +2,7 @@
#include "credential.h"
#include "unix-socket.h"
#include "sigchain.h"
+#include "parse-options.h"
static const char *socket_path;
@@ -201,7 +202,7 @@ static int serve_cache_loop(int fd)
return 1;
}
-static void serve_cache(const char *socket_path)
+static void serve_cache(const char *socket_path, int debug)
{
int fd;
@@ -211,6 +212,10 @@ static void serve_cache(const char *socket_path)
printf("ok\n");
fclose(stdout);
+ if (!debug) {
+ if (!freopen("/dev/null", "w", stderr))
+ die_errno("unable to point stderr to /dev/null");
+ }
while (serve_cache_loop(fd))
; /* nothing */
@@ -252,16 +257,28 @@ static void check_socket_directory(const char *path)
int main(int argc, const char **argv)
{
- socket_path = argv[1];
+ static const char *usage[] = {
+ "git-credential-cache--daemon [opts] <socket_path>",
+ NULL
+ };
+ int debug = 0;
+ const struct option options[] = {
+ OPT_BOOL(0, "debug", &debug,
+ N_("print debugging messages to stderr")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, NULL, options, usage, 0);
+ socket_path = argv[0];
if (!socket_path)
- die("usage: git-credential-cache--daemon <socket_path>");
+ usage_with_options(usage, options);
check_socket_directory(socket_path);
atexit(cleanup_socket);
sigchain_push_common(cleanup_socket_on_signal);
- serve_cache(socket_path);
+ serve_cache(socket_path, debug);
return 0;
}
diff --git a/credential-cache.c b/credential-cache.c
index 9a03792c7d..8689a1519a 100644
--- a/credential-cache.c
+++ b/credential-cache.c
@@ -37,12 +37,11 @@ static int send_request(const char *socket, const struct strbuf *out)
static void spawn_daemon(const char *socket)
{
- struct child_process daemon;
+ struct child_process daemon = CHILD_PROCESS_INIT;
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;
diff --git a/credential-store.c b/credential-store.c
index f9146e576f..d435514cbe 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "credential.h"
#include "string-list.h"
#include "parse-options.h"
diff --git a/credential.c b/credential.c
index 4d79d320f8..1886ea50b3 100644
--- a/credential.c
+++ b/credential.c
@@ -205,11 +205,10 @@ static int run_credential_helper(struct credential *c,
const char *cmd,
int want_output)
{
- struct child_process helper;
+ struct child_process helper = CHILD_PROCESS_INIT;
const char *argv[] = { NULL, NULL };
FILE *fp;
- memset(&helper, 0, sizeof(helper));
argv[0] = cmd;
helper.argv = argv;
helper.use_shell = 1;
diff --git a/daemon.c b/daemon.c
index e6b51ed998..54a03bd527 100644
--- a/daemon.c
+++ b/daemon.c
@@ -230,23 +230,6 @@ struct daemon_service {
int overridable;
};
-static struct daemon_service *service_looking_at;
-static int service_enabled;
-
-static int git_daemon_config(const char *var, const char *value, void *cb)
-{
- const char *service;
-
- if (skip_prefix(var, "daemon.", &service) &&
- !strcmp(service, service_looking_at->config_name)) {
- service_enabled = git_config_bool(var, value);
- return 0;
- }
-
- /* we are not interested in parsing any other configuration here */
- return 0;
-}
-
static int daemon_error(const char *dir, const char *msg)
{
if (!informative_errors)
@@ -259,7 +242,7 @@ static const char *access_hook;
static int run_access_hook(struct daemon_service *service, const char *dir, const char *path)
{
- struct child_process child;
+ struct child_process child = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
const char *argv[8];
const char **arg = argv;
@@ -277,7 +260,6 @@ static int run_access_hook(struct daemon_service *service, const char *dir, cons
*arg = NULL;
#undef STRARG
- memset(&child, 0, sizeof(child));
child.use_shell = 1;
child.argv = argv;
child.no_stdin = 1;
@@ -324,6 +306,7 @@ static int run_service(const char *dir, struct daemon_service *service)
{
const char *path;
int enabled = service->enabled;
+ struct strbuf var = STRBUF_INIT;
loginfo("Request %s for '%s'", service->name, dir);
@@ -354,11 +337,9 @@ static int run_service(const char *dir, struct daemon_service *service)
}
if (service->overridable) {
- service_looking_at = service;
- service_enabled = -1;
- git_config(git_daemon_config, NULL);
- if (0 <= service_enabled)
- enabled = service_enabled;
+ strbuf_addf(&var, "daemon.%s", service->config_name);
+ git_config_get_bool(var.buf, &enabled);
+ strbuf_release(&var);
}
if (!enabled) {
logerror("'%s': service not enabled for '%s'",
@@ -406,9 +387,8 @@ static void copy_to_log(int fd)
static int run_service_command(const char **argv)
{
- struct child_process cld;
+ struct child_process cld = CHILD_PROCESS_INIT;
- memset(&cld, 0, sizeof(cld));
cld.argv = argv;
cld.git_cmd = 1;
cld.err = -1;
@@ -573,20 +553,21 @@ static void parse_host_arg(char *extra_args, int buflen)
static char addrbuf[HOST_NAME_MAX + 1];
hent = gethostbyname(hostname);
+ if (hent) {
+ ap = hent->h_addr_list;
+ memset(&sa, 0, sizeof sa);
+ sa.sin_family = hent->h_addrtype;
+ sa.sin_port = htons(0);
+ memcpy(&sa.sin_addr, *ap, hent->h_length);
+
+ inet_ntop(hent->h_addrtype, &sa.sin_addr,
+ addrbuf, sizeof(addrbuf));
- ap = hent->h_addr_list;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = hent->h_addrtype;
- sa.sin_port = htons(0);
- memcpy(&sa.sin_addr, *ap, hent->h_length);
-
- inet_ntop(hent->h_addrtype, &sa.sin_addr,
- addrbuf, sizeof(addrbuf));
-
- free(canon_hostname);
- canon_hostname = xstrdup(hent->h_name);
- free(ip_address);
- ip_address = xstrdup(addrbuf);
+ free(canon_hostname);
+ canon_hostname = xstrdup(hent->h_name);
+ free(ip_address);
+ ip_address = xstrdup(addrbuf);
+ }
#endif
}
}
@@ -733,7 +714,7 @@ static void check_dead_children(void)
static char **cld_argv;
static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
{
- struct child_process cld = { NULL };
+ struct child_process cld = CHILD_PROCESS_INIT;
char addrbuf[300] = "REMOTE_ADDR=", portbuf[300];
char *env[] = { addrbuf, portbuf, NULL };
@@ -834,7 +815,6 @@ static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)
static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
{
int socknum = 0;
- int maxfd = -1;
char pbuf[NI_MAXSERV];
struct addrinfo hints, *ai0, *ai;
int gai;
@@ -902,9 +882,6 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
socklist->list[socklist->nr++] = sockfd;
socknum++;
-
- if (maxfd < sockfd)
- maxfd = sockfd;
}
freeaddrinfo(ai0);
@@ -943,7 +920,7 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
}
if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
- logerror("Could not listen to %s: %s",
+ logerror("Could not bind to %s: %s",
ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
strerror(errno));
close(sockfd);
diff --git a/date.c b/date.c
index 782de95d90..59dfe579c6 100644
--- a/date.c
+++ b/date.c
@@ -200,7 +200,16 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tz);
- else if (mode == DATE_RFC2822)
+ else if (mode == DATE_ISO8601_STRICT) {
+ char sign = (tz >= 0) ? '+' : '-';
+ tz = abs(tz);
+ strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ sign, tz / 100, tz % 100);
+ } else if (mode == DATE_RFC2822)
strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
weekday_names[tm->tm_wday], tm->tm_mday,
month_names[tm->tm_mon], tm->tm_year + 1900,
@@ -605,7 +614,7 @@ static int match_tz(const char *date, int *offp)
return end - date;
}
-static int date_string(unsigned long date, int offset, char *buf, int len)
+static void date_string(unsigned long date, int offset, struct strbuf *buf)
{
int sign = '+';
@@ -613,7 +622,7 @@ static int date_string(unsigned long date, int offset, char *buf, int len)
offset = -offset;
sign = '-';
}
- return snprintf(buf, len, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
+ strbuf_addf(buf, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
}
/*
@@ -735,13 +744,14 @@ int parse_expiry_date(const char *date, unsigned long *timestamp)
return errors;
}
-int parse_date(const char *date, char *result, int maxlen)
+int parse_date(const char *date, struct strbuf *result)
{
unsigned long timestamp;
int offset;
if (parse_date_basic(date, &timestamp, &offset))
return -1;
- return date_string(timestamp, offset, result, maxlen);
+ date_string(timestamp, offset, result);
+ return 0;
}
enum date_mode parse_date_format(const char *format)
@@ -751,6 +761,9 @@ enum date_mode parse_date_format(const char *format)
else if (!strcmp(format, "iso8601") ||
!strcmp(format, "iso"))
return DATE_ISO8601;
+ else if (!strcmp(format, "iso8601-strict") ||
+ !strcmp(format, "iso-strict"))
+ return DATE_ISO8601_STRICT;
else if (!strcmp(format, "rfc2822") ||
!strcmp(format, "rfc"))
return DATE_RFC2822;
@@ -766,7 +779,7 @@ enum date_mode parse_date_format(const char *format)
die("unknown date format %s", format);
}
-void datestamp(char *buf, int bufsize)
+void datestamp(struct strbuf *out)
{
time_t now;
int offset;
@@ -776,7 +789,7 @@ void datestamp(char *buf, int bufsize)
offset = tm_to_time_t(localtime(&now)) - now;
offset /= 60;
- date_string(now, offset, buf, bufsize);
+ date_string(now, offset, out);
}
/*
diff --git a/diff.c b/diff.c
index 867f034b8f..d1bd534cae 100644
--- a/diff.c
+++ b/diff.c
@@ -248,8 +248,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
return 0;
if (!value)
return config_error_nonbool(var);
- color_parse(value, var, diff_colors[slot]);
- return 0;
+ return color_parse(value, diff_colors[slot]);
}
/* like GNU diff's --suppress-blank-empty option */
@@ -376,7 +375,7 @@ static unsigned long diff_filespec_size(struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one))
return 0;
- diff_populate_filespec(one, 1);
+ diff_populate_filespec(one, CHECK_SIZE_ONLY);
return one->size;
}
@@ -1910,11 +1909,11 @@ static void show_dirstat(struct diff_options *options)
diff_free_filespec_data(p->one);
diff_free_filespec_data(p->two);
} else if (DIFF_FILE_VALID(p->one)) {
- diff_populate_filespec(p->one, 1);
+ diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
copied = added = 0;
diff_free_filespec_data(p->one);
} else if (DIFF_FILE_VALID(p->two)) {
- diff_populate_filespec(p->two, 1);
+ diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
copied = 0;
added = p->two->size;
diff_free_filespec_data(p->two);
@@ -2188,8 +2187,8 @@ int diff_filespec_is_binary(struct diff_filespec *one)
one->is_binary = one->driver->binary;
else {
if (!one->data && DIFF_FILE_VALID(one))
- diff_populate_filespec(one, 0);
- if (one->data)
+ diff_populate_filespec(one, CHECK_BINARY);
+ if (one->is_binary == -1 && one->data)
one->is_binary = buffer_is_binary(one->data,
one->size);
if (one->is_binary == -1)
@@ -2324,6 +2323,19 @@ static void builtin_diff(const char *name_a,
} else if (!DIFF_OPT_TST(o, TEXT) &&
( (!textconv_one && diff_filespec_is_binary(one)) ||
(!textconv_two && diff_filespec_is_binary(two)) )) {
+ if (!one->data && !two->data &&
+ S_ISREG(one->mode) && S_ISREG(two->mode) &&
+ !DIFF_OPT_TST(o, BINARY)) {
+ if (!hashcmp(one->sha1, two->sha1)) {
+ if (must_show_header)
+ fprintf(o->file, "%s", header.buf);
+ goto free_ab_and_return;
+ }
+ fprintf(o->file, "%s", header.buf);
+ fprintf(o->file, "%sBinary files %s and %s differ\n",
+ line_prefix, lbl[0], lbl[1]);
+ goto free_ab_and_return;
+ }
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
/* Quite common confusing case */
@@ -2668,8 +2680,9 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
* grab the data for the blob (or file) for our own in-core comparison.
* diff_filespec has data and size fields for this purpose.
*/
-int diff_populate_filespec(struct diff_filespec *s, int size_only)
+int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
{
+ int size_only = flags & CHECK_SIZE_ONLY;
int err = 0;
/*
* demote FAIL to WARN to allow inspecting the situation
@@ -2724,6 +2737,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
}
if (size_only)
return 0;
+ if ((flags & CHECK_BINARY) &&
+ s->size > big_file_threshold && s->is_binary == -1) {
+ s->is_binary = 1;
+ return 0;
+ }
fd = open(s->path, O_RDONLY);
if (fd < 0)
goto err_empty;
@@ -2745,16 +2763,21 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
}
else {
enum object_type type;
- if (size_only) {
+ if (size_only || (flags & CHECK_BINARY)) {
type = sha1_object_info(s->sha1, &s->size);
if (type < 0)
die("unable to read %s", sha1_to_hex(s->sha1));
- } else {
- s->data = read_sha1_file(s->sha1, &type, &s->size);
- if (!s->data)
- die("unable to read %s", sha1_to_hex(s->sha1));
- s->should_free = 1;
+ if (size_only)
+ return 0;
+ if (s->size > big_file_threshold && s->is_binary == -1) {
+ s->is_binary = 1;
+ return 0;
+ }
}
+ s->data = read_sha1_file(s->sha1, &type, &s->size);
+ if (!s->data)
+ die("unable to read %s", sha1_to_hex(s->sha1));
+ s->should_free = 1;
}
return 0;
}
@@ -4688,8 +4711,8 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
!DIFF_FILE_VALID(p->two) ||
(p->one->sha1_valid && p->two->sha1_valid) ||
(p->one->mode != p->two->mode) ||
- diff_populate_filespec(p->one, 1) ||
- diff_populate_filespec(p->two, 1) ||
+ diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
+ diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
(p->one->size != p->two->size) ||
!diff_filespec_is_identical(p->one, p->two)) /* (2) */
p->skip_stat_unmatch_result = 1;
@@ -4931,7 +4954,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
struct diff_tempfile *temp;
const char *argv[3];
const char **arg = argv;
- struct child_process child;
+ struct child_process child = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
int err = 0;
@@ -4940,7 +4963,6 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
*arg++ = temp->name;
*arg = NULL;
- memset(&child, 0, sizeof(child));
child.use_shell = 1;
child.argv = argv;
child.out = -1;
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 2e44a37459..4e132f1fdb 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -147,9 +147,11 @@ static int estimate_similarity(struct diff_filespec *src,
* is a possible size - we really should have a flag to
* say whether the size is valid or not!)
*/
- if (!src->cnt_data && diff_populate_filespec(src, 1))
+ if (!src->cnt_data &&
+ diff_populate_filespec(src, CHECK_SIZE_ONLY))
return 0;
- if (!dst->cnt_data && diff_populate_filespec(dst, 1))
+ if (!dst->cnt_data &&
+ diff_populate_filespec(dst, CHECK_SIZE_ONLY))
return 0;
max_size = ((src->size > dst->size) ? src->size : dst->size);
diff --git a/diffcore.h b/diffcore.h
index c876dac71a..33ea2de348 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -55,7 +55,9 @@ extern void free_filespec(struct diff_filespec *);
extern void fill_filespec(struct diff_filespec *, const unsigned char *,
int, unsigned short);
-extern int diff_populate_filespec(struct diff_filespec *, int);
+#define CHECK_SIZE_ONLY 1
+#define CHECK_BINARY 2
+extern int diff_populate_filespec(struct diff_filespec *, unsigned int);
extern void diff_free_filespec_data(struct diff_filespec *);
extern void diff_free_filespec_blob(struct diff_filespec *);
extern int diff_filespec_is_binary(struct diff_filespec *);
diff --git a/dir.c b/dir.c
index fcb68729b1..3f7a0256b6 100644
--- a/dir.c
+++ b/dir.c
@@ -826,9 +826,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
current = stk ? stk->baselen : -1;
strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
while (current < baselen) {
- struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
const char *cp;
+ stk = xcalloc(1, sizeof(*stk));
if (current < 0) {
cp = base;
current = 0;
@@ -1507,12 +1507,16 @@ int dir_inside_of(const char *subdir, const char *dir)
int is_inside_dir(const char *dir)
{
- char cwd[PATH_MAX];
+ char *cwd;
+ int rc;
+
if (!dir)
return 0;
- if (!getcwd(cwd, sizeof(cwd)))
- die_errno("can't find the current directory");
- return dir_inside_of(cwd, dir) >= 0;
+
+ cwd = xgetcwd();
+ rc = (dir_inside_of(cwd, dir) >= 0);
+ free(cwd);
+ return rc;
}
int is_empty_dir(const char *path)
diff --git a/editor.c b/editor.c
index 0abbd8dc3a..01c644cddb 100644
--- a/editor.c
+++ b/editor.c
@@ -38,10 +38,9 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
if (strcmp(editor, ":")) {
const char *args[] = { editor, real_path(path), NULL };
- struct child_process p;
+ struct child_process p = CHILD_PROCESS_INIT;
int ret, sig;
- memset(&p, 0, sizeof(p));
p.argv = args;
p.env = env;
p.use_shell = 1;
diff --git a/exec_cmd.c b/exec_cmd.c
index 125fa6fabf..698e7526c4 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -86,11 +86,7 @@ const char *git_exec_path(void)
static void add_path(struct strbuf *out, const char *path)
{
if (path && *path) {
- if (is_absolute_path(path))
- strbuf_addstr(out, path);
- else
- strbuf_addstr(out, absolute_path(path));
-
+ strbuf_add_absolute_path(out, path);
strbuf_addch(out, PATH_SEP);
}
}
diff --git a/fast-import.c b/fast-import.c
index a40b4ea2d0..d0bd285a16 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -153,6 +153,7 @@ Format of STDIN stream:
#include "builtin.h"
#include "cache.h"
+#include "lockfile.h"
#include "object.h"
#include "blob.h"
#include "tree.h"
@@ -878,7 +879,7 @@ static void start_packfile(void)
pack_size = sizeof(hdr);
object_count = 0;
- all_packs = xrealloc(all_packs, sizeof(*all_packs) * (pack_id + 1));
+ REALLOC_ARRAY(all_packs, pack_id + 1);
all_packs[pack_id] = p;
}
@@ -1686,8 +1687,9 @@ found_entry:
static int update_branch(struct branch *b)
{
static const char *msg = "fast-import";
- struct ref_lock *lock;
+ struct ref_transaction *transaction;
unsigned char old_sha1[20];
+ struct strbuf err = STRBUF_INIT;
if (read_ref(b->name, old_sha1))
hashclr(old_sha1);
@@ -1696,29 +1698,33 @@ static int update_branch(struct branch *b)
delete_ref(b->name, old_sha1, 0);
return 0;
}
- lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
- if (!lock)
- return error("Unable to lock %s", b->name);
if (!force_update && !is_null_sha1(old_sha1)) {
struct commit *old_cmit, *new_cmit;
old_cmit = lookup_commit_reference_gently(old_sha1, 0);
new_cmit = lookup_commit_reference_gently(b->sha1, 0);
- if (!old_cmit || !new_cmit) {
- unlock_ref(lock);
+ if (!old_cmit || !new_cmit)
return error("Branch %s is missing commits.", b->name);
- }
if (!in_merge_bases(old_cmit, new_cmit)) {
- unlock_ref(lock);
warning("Not updating %s"
" (new tip %s does not contain %s)",
b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
return -1;
}
}
- if (write_ref_sha1(lock, b->sha1, msg) < 0)
- return error("Unable to update %s", b->name);
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
+ 0, 1, msg, &err) ||
+ ref_transaction_commit(transaction, &err)) {
+ ref_transaction_free(transaction);
+ error("%s", err.buf);
+ strbuf_release(&err);
+ return -1;
+ }
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
return 0;
}
@@ -1737,17 +1743,32 @@ static void dump_tags(void)
{
static const char *msg = "fast-import";
struct tag *t;
- struct ref_lock *lock;
struct strbuf ref_name = STRBUF_INIT;
+ struct strbuf err = STRBUF_INIT;
+ struct ref_transaction *transaction;
+ transaction = ref_transaction_begin(&err);
+ if (!transaction) {
+ failure |= error("%s", err.buf);
+ goto cleanup;
+ }
for (t = first_tag; t; t = t->next_tag) {
strbuf_reset(&ref_name);
- strbuf_addf(&ref_name, "tags/%s", t->name);
- lock = lock_ref_sha1(ref_name.buf, NULL);
- if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
- failure |= error("Unable to update %s", ref_name.buf);
+ strbuf_addf(&ref_name, "refs/tags/%s", t->name);
+
+ if (ref_transaction_update(transaction, ref_name.buf, t->sha1,
+ NULL, 0, 0, msg, &err)) {
+ failure |= error("%s", err.buf);
+ goto cleanup;
+ }
}
+ if (ref_transaction_commit(transaction, &err))
+ failure |= error("%s", err.buf);
+
+ cleanup:
+ ref_transaction_free(transaction);
strbuf_release(&ref_name);
+ strbuf_release(&err);
}
static void dump_marks_helper(FILE *f,
@@ -1773,20 +1794,18 @@ static void dump_marks_helper(FILE *f,
static void dump_marks(void)
{
static struct lock_file mark_lock;
- int mark_fd;
FILE *f;
if (!export_marks_file)
return;
- mark_fd = hold_lock_file_for_update(&mark_lock, export_marks_file, 0);
- if (mark_fd < 0) {
+ if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
failure |= error("Unable to write marks file %s: %s",
export_marks_file, strerror(errno));
return;
}
- f = fdopen(mark_fd, "w");
+ f = fdopen_lock_file(&mark_lock, "w");
if (!f) {
int saved_errno = errno;
rollback_lock_file(&mark_lock);
@@ -1795,27 +1814,10 @@ static void dump_marks(void)
return;
}
- /*
- * Since the lock file was fdopen()'ed, it should not be close()'ed.
- * Assign -1 to the lock file descriptor so that commit_lock_file()
- * won't try to close() it.
- */
- mark_lock.fd = -1;
-
dump_marks_helper(f, 0, marks);
- if (ferror(f) || fclose(f)) {
- int saved_errno = errno;
- rollback_lock_file(&mark_lock);
- failure |= error("Unable to write marks file %s: %s",
- export_marks_file, strerror(saved_errno));
- return;
- }
-
if (commit_lock_file(&mark_lock)) {
- int saved_errno = errno;
- rollback_lock_file(&mark_lock);
failure |= error("Unable to commit marks file %s: %s",
- export_marks_file, strerror(saved_errno));
+ export_marks_file, strerror(errno));
return;
}
}
@@ -1980,7 +1982,7 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
return 1;
}
-static int validate_raw_date(const char *src, char *result, int maxlen)
+static int validate_raw_date(const char *src, struct strbuf *result)
{
const char *orig_src = src;
char *endp;
@@ -1998,11 +2000,10 @@ static int validate_raw_date(const char *src, char *result, int maxlen)
return -1;
num = strtoul(src + 1, &endp, 10);
- if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
- 1400 < num)
+ if (errno || endp == src + 1 || *endp || 1400 < num)
return -1;
- strcpy(result, orig_src);
+ strbuf_addstr(result, orig_src);
return 0;
}
@@ -2010,7 +2011,7 @@ static char *parse_ident(const char *buf)
{
const char *ltgt;
size_t name_len;
- char *ident;
+ struct strbuf ident = STRBUF_INIT;
/* ensure there is a space delimiter even if there is no name */
if (*buf == '<')
@@ -2029,26 +2030,25 @@ static char *parse_ident(const char *buf)
die("Missing space after > in ident string: %s", buf);
ltgt++;
name_len = ltgt - buf;
- ident = xmalloc(name_len + 24);
- strncpy(ident, buf, name_len);
+ strbuf_add(&ident, buf, name_len);
switch (whenspec) {
case WHENSPEC_RAW:
- if (validate_raw_date(ltgt, ident + name_len, 24) < 0)
+ if (validate_raw_date(ltgt, &ident) < 0)
die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
break;
case WHENSPEC_RFC2822:
- if (parse_date(ltgt, ident + name_len, 24) < 0)
+ if (parse_date(ltgt, &ident) < 0)
die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf);
break;
case WHENSPEC_NOW:
if (strcmp("now", ltgt))
die("Date in ident must be 'now': %s", buf);
- datestamp(ident + name_len, 24);
+ datestamp(&ident);
break;
}
- return ident;
+ return strbuf_detach(&ident, NULL);
}
static void parse_and_store_blob(
@@ -3283,36 +3283,34 @@ static void parse_option(const char *option)
die("This version of fast-import does not support option: %s", option);
}
-static int git_pack_config(const char *k, const char *v, void *cb)
+static void git_pack_config(void)
{
- if (!strcmp(k, "pack.depth")) {
- max_depth = git_config_int(k, v);
+ int indexversion_value;
+ unsigned long packsizelimit_value;
+
+ if (!git_config_get_ulong("pack.depth", &max_depth)) {
if (max_depth > MAX_DEPTH)
max_depth = MAX_DEPTH;
- return 0;
}
- if (!strcmp(k, "pack.compression")) {
- int level = git_config_int(k, v);
- if (level == -1)
- level = Z_DEFAULT_COMPRESSION;
- else if (level < 0 || level > Z_BEST_COMPRESSION)
- die("bad pack compression level %d", level);
- pack_compression_level = level;
+ if (!git_config_get_int("pack.compression", &pack_compression_level)) {
+ if (pack_compression_level == -1)
+ pack_compression_level = Z_DEFAULT_COMPRESSION;
+ else if (pack_compression_level < 0 ||
+ pack_compression_level > Z_BEST_COMPRESSION)
+ git_die_config("pack.compression",
+ "bad pack compression level %d", pack_compression_level);
pack_compression_seen = 1;
- return 0;
}
- if (!strcmp(k, "pack.indexversion")) {
- pack_idx_opts.version = git_config_int(k, v);
+ if (!git_config_get_int("pack.indexversion", &indexversion_value)) {
+ pack_idx_opts.version = indexversion_value;
if (pack_idx_opts.version > 2)
- die("bad pack.indexversion=%"PRIu32,
- pack_idx_opts.version);
- return 0;
+ git_die_config("pack.indexversion",
+ "bad pack.indexversion=%"PRIu32, pack_idx_opts.version);
}
- if (!strcmp(k, "pack.packsizelimit")) {
- max_packsize = git_config_ulong(k, v);
- return 0;
- }
- return git_default_config(k, v, cb);
+ if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
+ max_packsize = packsizelimit_value;
+
+ git_config(git_default_config, NULL);
}
static const char fast_import_usage[] =
@@ -3365,7 +3363,7 @@ int main(int argc, char **argv)
setup_git_directory();
reset_pack_idx_option(&pack_idx_opts);
- git_config(git_pack_config, NULL);
+ git_pack_config();
if (!pack_compression_seen && core_compression_seen)
pack_compression_level = core_compression_level;
diff --git a/fetch-pack.c b/fetch-pack.c
index b8a58fa7a5..655ee64256 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "refs.h"
#include "pkt-line.h"
#include "commit.h"
@@ -666,7 +667,7 @@ static int get_pack(struct fetch_pack_args *args,
char hdr_arg[256];
const char **av, *cmd_name;
int do_keep = args->keep_pack;
- struct child_process cmd;
+ struct child_process cmd = CHILD_PROCESS_INIT;
int ret;
memset(&demux, 0, sizeof(demux));
@@ -685,7 +686,6 @@ static int get_pack(struct fetch_pack_args *args,
else
demux.out = xd[0];
- memset(&cmd, 0, sizeof(cmd));
cmd.argv = argv;
av = argv;
*hdr_arg = 0;
@@ -869,34 +869,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
return ref;
}
-static int fetch_pack_config(const char *var, const char *value, void *cb)
+static void fetch_pack_config(void)
{
- if (strcmp(var, "fetch.unpacklimit") == 0) {
- fetch_unpack_limit = git_config_int(var, value);
- return 0;
- }
-
- if (strcmp(var, "transfer.unpacklimit") == 0) {
- transfer_unpack_limit = git_config_int(var, value);
- return 0;
- }
-
- if (strcmp(var, "repack.usedeltabaseoffset") == 0) {
- prefer_ofs_delta = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "fetch.fsckobjects")) {
- fetch_fsck_objects = git_config_bool(var, value);
- return 0;
- }
-
- if (!strcmp(var, "transfer.fsckobjects")) {
- transfer_fsck_objects = git_config_bool(var, value);
- return 0;
- }
+ git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit);
+ git_config_get_int("transfer.unpacklimit", &transfer_unpack_limit);
+ git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
+ git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
+ git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
- return git_default_config(var, value, cb);
+ git_config(git_default_config, NULL);
}
static void fetch_pack_setup(void)
@@ -904,7 +885,7 @@ static void fetch_pack_setup(void)
static int did_setup;
if (did_setup)
return;
- git_config(fetch_pack_config, NULL);
+ fetch_pack_config();
if (0 <= transfer_unpack_limit)
unpack_limit = transfer_unpack_limit;
else if (0 <= fetch_unpack_limit)
diff --git a/fsck.c b/fsck.c
index 56156fff44..2fffa434a5 100644
--- a/fsck.c
+++ b/fsck.c
@@ -6,6 +6,7 @@
#include "commit.h"
#include "tag.h"
#include "fsck.h"
+#include "refs.h"
static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)
{
@@ -237,6 +238,26 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
return retval;
}
+static int require_end_of_header(const void *data, unsigned long size,
+ struct object *obj, fsck_error error_func)
+{
+ const char *buffer = (const char *)data;
+ unsigned long i;
+
+ for (i = 0; i < size; i++) {
+ switch (buffer[i]) {
+ case '\0':
+ return error_func(obj, FSCK_ERROR,
+ "unterminated header: NUL at offset %d", i);
+ case '\n':
+ if (i + 1 < size && buffer[i + 1] == '\n')
+ return 0;
+ }
+ }
+
+ return error_func(obj, FSCK_ERROR, "unterminated header");
+}
+
static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)
{
char *end;
@@ -277,13 +298,16 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f
}
static int fsck_commit_buffer(struct commit *commit, const char *buffer,
- fsck_error error_func)
+ unsigned long size, fsck_error error_func)
{
unsigned char tree_sha1[20], sha1[20];
struct commit_graft *graft;
unsigned parent_count, parent_line_count = 0;
int err;
+ if (require_end_of_header(buffer, size, &commit->object, error_func))
+ return -1;
+
if (!skip_prefix(buffer, "tree ", &buffer))
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n')
@@ -322,24 +346,111 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
return 0;
}
-static int fsck_commit(struct commit *commit, fsck_error error_func)
+static int fsck_commit(struct commit *commit, const char *data,
+ unsigned long size, fsck_error error_func)
{
- const char *buffer = get_commit_buffer(commit, NULL);
- int ret = fsck_commit_buffer(commit, buffer, error_func);
- unuse_commit_buffer(commit, buffer);
+ const char *buffer = data ? data : get_commit_buffer(commit, &size);
+ int ret = fsck_commit_buffer(commit, buffer, size, error_func);
+ if (!data)
+ unuse_commit_buffer(commit, buffer);
return ret;
}
-static int fsck_tag(struct tag *tag, fsck_error error_func)
+static int fsck_tag_buffer(struct tag *tag, const char *data,
+ unsigned long size, fsck_error error_func)
+{
+ unsigned char sha1[20];
+ int ret = 0;
+ const char *buffer;
+ char *to_free = NULL, *eol;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (data)
+ buffer = data;
+ else {
+ enum object_type type;
+
+ buffer = to_free =
+ read_sha1_file(tag->object.sha1, &type, &size);
+ if (!buffer)
+ return error_func(&tag->object, FSCK_ERROR,
+ "cannot read tag object");
+
+ if (type != OBJ_TAG) {
+ ret = error_func(&tag->object, FSCK_ERROR,
+ "expected tag got %s",
+ typename(type));
+ goto done;
+ }
+ }
+
+ if (require_end_of_header(buffer, size, &tag->object, error_func))
+ goto done;
+
+ if (!skip_prefix(buffer, "object ", &buffer)) {
+ ret = error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'object' line");
+ goto done;
+ }
+ if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') {
+ ret = error_func(&tag->object, FSCK_ERROR, "invalid 'object' line format - bad sha1");
+ goto done;
+ }
+ buffer += 41;
+
+ if (!skip_prefix(buffer, "type ", &buffer)) {
+ ret = error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'type' line");
+ goto done;
+ }
+ eol = strchr(buffer, '\n');
+ if (!eol) {
+ ret = error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line");
+ goto done;
+ }
+ if (type_from_string_gently(buffer, eol - buffer, 1) < 0)
+ ret = error_func(&tag->object, FSCK_ERROR, "invalid 'type' value");
+ if (ret)
+ goto done;
+ buffer = eol + 1;
+
+ if (!skip_prefix(buffer, "tag ", &buffer)) {
+ ret = error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'tag' line");
+ goto done;
+ }
+ eol = strchr(buffer, '\n');
+ if (!eol) {
+ ret = error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line");
+ goto done;
+ }
+ strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer);
+ if (check_refname_format(sb.buf, 0))
+ error_func(&tag->object, FSCK_WARN, "invalid 'tag' name: %s", buffer);
+ buffer = eol + 1;
+
+ if (!skip_prefix(buffer, "tagger ", &buffer))
+ /* early tags do not contain 'tagger' lines; warn only */
+ error_func(&tag->object, FSCK_WARN, "invalid format - expected 'tagger' line");
+ else
+ ret = fsck_ident(&buffer, &tag->object, error_func);
+
+done:
+ strbuf_release(&sb);
+ free(to_free);
+ return ret;
+}
+
+static int fsck_tag(struct tag *tag, const char *data,
+ unsigned long size, fsck_error error_func)
{
struct object *tagged = tag->tagged;
if (!tagged)
return error_func(&tag->object, FSCK_ERROR, "could not load tagged object");
- return 0;
+
+ return fsck_tag_buffer(tag, data, size, error_func);
}
-int fsck_object(struct object *obj, int strict, fsck_error error_func)
+int fsck_object(struct object *obj, void *data, unsigned long size,
+ int strict, fsck_error error_func)
{
if (!obj)
return error_func(obj, FSCK_ERROR, "no valid object to fsck");
@@ -349,9 +460,11 @@ int fsck_object(struct object *obj, int strict, fsck_error error_func)
if (obj->type == OBJ_TREE)
return fsck_tree((struct tree *) obj, strict, error_func);
if (obj->type == OBJ_COMMIT)
- return fsck_commit((struct commit *) obj, error_func);
+ return fsck_commit((struct commit *) obj, (const char *) data,
+ size, error_func);
if (obj->type == OBJ_TAG)
- return fsck_tag((struct tag *) obj, error_func);
+ return fsck_tag((struct tag *) obj, (const char *) data,
+ size, error_func);
return error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)",
obj->type);
diff --git a/fsck.h b/fsck.h
index 1e4f527318..d1e6387a44 100644
--- a/fsck.h
+++ b/fsck.h
@@ -28,6 +28,8 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);
* 0 everything OK
*/
int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);
-int fsck_object(struct object *obj, int strict, fsck_error error_func);
+/* If NULL is passed for data, we assume the object is local and read it. */
+int fsck_object(struct object *obj, void *data, unsigned long size,
+ int strict, fsck_error error_func);
#endif
diff --git a/git-bisect.sh b/git-bisect.sh
index 1e0d602f4b..6cda2b5a60 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -286,11 +286,11 @@ bisect_next_check() {
if test -s "$GIT_DIR/BISECT_START"
then
- gettextln "You need to give me at least one good and one bad revisions.
+ gettextln "You need to give me at least one good and one bad revision.
(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
else
gettextln "You need to start by \"git bisect start\".
-You then need to give me at least one good and one bad revisions.
+You then need to give me at least one good and one bad revision.
(You can use \"git bisect bad\" and \"git bisect good\" for that.)" >&2
fi
exit 1 ;;
diff --git a/git-compat-util.h b/git-compat-util.h
index f587749b7c..400e921086 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -82,6 +82,7 @@
#define _ALL_SOURCE 1
#define _GNU_SOURCE 1
#define _BSD_SOURCE 1
+#define _DEFAULT_SOURCE 1
#define _NETBSD_SOURCE 1
#define _SGI_SOURCE 1
@@ -191,7 +192,7 @@ extern int compat_mkdir_wo_trailing_slash(const char*, mode_t);
struct itimerval {
struct timeval it_interval;
struct timeval it_value;
-}
+};
#endif
#ifdef NO_SETITIMER
@@ -264,19 +265,35 @@ extern char *gitbasename(char *);
#endif
#ifndef has_dos_drive_prefix
-#define has_dos_drive_prefix(path) 0
+static inline int git_has_dos_drive_prefix(const char *path)
+{
+ return 0;
+}
+#define has_dos_drive_prefix git_has_dos_drive_prefix
#endif
-#ifndef offset_1st_component
-#define offset_1st_component(path) (is_dir_sep((path)[0]))
+#ifndef is_dir_sep
+static inline int git_is_dir_sep(int c)
+{
+ return c == '/';
+}
+#define is_dir_sep git_is_dir_sep
#endif
-#ifndef is_dir_sep
-#define is_dir_sep(c) ((c) == '/')
+#ifndef offset_1st_component
+static inline int git_offset_1st_component(const char *path)
+{
+ return is_dir_sep(path[0]);
+}
+#define offset_1st_component git_offset_1st_component
#endif
#ifndef find_last_dir_sep
-#define find_last_dir_sep(path) strrchr(path, '/')
+static inline char *git_find_last_dir_sep(const char *path)
+{
+ return strrchr(path, '/');
+}
+#define find_last_dir_sep git_find_last_dir_sep
#endif
#if defined(__HP_cc) && (__HP_cc >= 61000)
@@ -309,6 +326,8 @@ extern char *gitbasename(char *);
#include "wildmatch.h"
+struct strbuf;
+
/* General helper functions */
extern void vreportf(const char *prefix, const char *err, va_list params);
extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
@@ -577,6 +596,11 @@ int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
#endif
+#ifdef NO_PTHREADS
+#define atexit git_atexit
+extern int git_atexit(void (*handler)(void));
+#endif
+
extern void release_pack_memory(size_t);
typedef void (*try_to_free_t)(size_t);
@@ -593,6 +617,7 @@ extern try_to_free_t set_try_to_free_routine(try_to_free_t);
extern char *xstrdup(const char *str);
extern void *xmalloc(size_t size);
extern void *xmallocz(size_t size);
+extern void *xmallocz_gently(size_t size);
extern void *xmemdupz(const void *data, size_t len);
extern char *xstrndup(const char *str, size_t len);
extern void *xrealloc(void *ptr, size_t size);
@@ -607,6 +632,9 @@ extern int xmkstemp(char *template);
extern int xmkstemp_mode(char *template, int mode);
extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1);
+extern char *xgetcwd(void);
+
+#define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), (alloc) * sizeof(*(x)))
static inline size_t xsize_t(off_t len)
{
@@ -656,7 +684,7 @@ extern const unsigned char sane_ctype[256];
#define iscntrl(x) (sane_istest(x,GIT_CNTRL))
#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \
GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC)
-#define isxdigit(x) (hexval_table[x] != -1)
+#define isxdigit(x) (hexval_table[(unsigned char)(x)] != -1)
#define tolower(x) sane_case((unsigned char)(x), 0x20)
#define toupper(x) sane_case((unsigned char)(x), 0)
#define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
@@ -750,17 +778,27 @@ void git_qsort(void *base, size_t nmemb, size_t size,
#endif
#endif
-#if defined(__GNUC__) || (_MSC_VER >= 1400)
+#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__C99_MACRO_WITH_VA_ARGS)
#define HAVE_VARIADIC_MACROS 1
#endif
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
- * Always returns the return value of unlink(2).
+ * Returns 0 on success, which includes trying to unlink an object that does
+ * not exist.
*/
int unlink_or_warn(const char *path);
+ /*
+ * Tries to unlink file. Returns 0 if unlink succeeded
+ * or the file already didn't exist. Returns -1 and
+ * appends a message to err suitable for
+ * 'error("%s", err->buf)' on error.
+ */
+int unlink_or_msg(const char *file, struct strbuf *err);
/*
- * Likewise for rmdir(2).
+ * Preserves errno, prints a message, but gives no warning for ENOENT.
+ * Returns 0 on success, which includes trying to remove a directory that does
+ * not exist.
*/
int rmdir_or_warn(const char *path);
/*
diff --git a/git-difftool.perl b/git-difftool.perl
index 18ca61e8d0..598fcc23b9 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -47,13 +47,9 @@ sub find_worktree
sub print_tool_help
{
- my $cmd = 'TOOL_MODE=diff';
- $cmd .= ' && . "$(git --exec-path)/git-mergetool--lib"';
- $cmd .= ' && show_tool_help';
-
# See the comment at the bottom of file_diff() for the reason behind
# using system() followed by exit() instead of exec().
- my $rc = system('sh', '-c', $cmd);
+ my $rc = system(qw(git mergetool --tool-help=diff));
exit($rc | ($rc >> 8));
}
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index c45a020301..a40d3dfea6 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -250,7 +250,7 @@ list_merge_tool_candidates () {
tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
fi
tools="$tools gvimdiff diffuse diffmerge ecmerge"
- tools="$tools p4merge araxis bc3 codecompare"
+ tools="$tools p4merge araxis bc codecompare"
fi
case "${VISUAL:-$EDITOR}" in
*vim*)
diff --git a/git-mergetool.sh b/git-mergetool.sh
index 9a046b75d1..ff050e58ff 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -10,11 +10,11 @@
USAGE='[--tool=tool] [--tool-help] [-y|--no-prompt|--prompt] [file to merge] ...'
SUBDIRECTORY_OK=Yes
+NONGIT_OK=Yes
OPTIONS_SPEC=
TOOL_MODE=merge
. git-sh-setup
. git-mergetool--lib
-require_work_tree
# Returns true if the mode reflects a symlink
is_symlink () {
@@ -37,6 +37,19 @@ base_present () {
test -n "$base_mode"
}
+mergetool_tmpdir_init () {
+ if test "$(git config --bool mergetool.writeToTemp)" != true
+ then
+ MERGETOOL_TMPDIR=.
+ return 0
+ fi
+ if MERGETOOL_TMPDIR=$(mktemp -d -t "git-mergetool-XXXXXX" 2>/dev/null)
+ then
+ return 0
+ fi
+ die "error: mktemp is needed when 'mergetool.writeToTemp' is true"
+}
+
cleanup_temp_files () {
if test "$1" = --save-backup
then
@@ -46,6 +59,10 @@ cleanup_temp_files () {
else
rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
fi
+ if test "$MERGETOOL_TMPDIR" != "."
+ then
+ rmdir "$MERGETOOL_TMPDIR"
+ fi
}
describe_file () {
@@ -228,11 +245,27 @@ merge_file () {
return 1
fi
- ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
- BACKUP="./$MERGED.BACKUP.$ext"
- LOCAL="./$MERGED.LOCAL.$ext"
- REMOTE="./$MERGED.REMOTE.$ext"
- BASE="./$MERGED.BASE.$ext"
+ if BASE=$(expr "$MERGED" : '\(.*\)\.[^/]*$')
+ then
+ ext=$(expr "$MERGED" : '.*\(\.[^/]*\)$')
+ else
+ BASE=$MERGED
+ ext=
+ fi
+
+ mergetool_tmpdir_init
+
+ if test "$MERGETOOL_TMPDIR" != "."
+ then
+ # If we're using a temporary directory then write to the
+ # top-level of that directory.
+ BASE=${BASE##*/}
+ fi
+
+ BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
+ LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
+ REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
+ BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
@@ -321,6 +354,10 @@ guessed_merge_tool=false
while test $# != 0
do
case "$1" in
+ --tool-help=*)
+ TOOL_MODE=${1#--tool-help=}
+ show_tool_help
+ ;;
--tool-help)
show_tool_help
;;
@@ -372,6 +409,9 @@ prompt_after_failed_merge () {
done
}
+git_dir_init
+require_work_tree
+
if test -z "$merge_tool"
then
# Check if a merge tool has been configured
diff --git a/git-pull.sh b/git-pull.sh
index 18a394fcc4..4d4fc77b05 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -20,7 +20,7 @@ die_conflict () {
if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
die "$(gettext "Pull is not possible because you have unmerged files.
Please, fix them up in the work tree, and then use 'git add/rm <file>'
-as appropriate to mark resolution, or use 'git commit -a'.")"
+as appropriate to mark resolution and make a commit.")"
else
die "$(gettext "Pull is not possible because you have unmerged files.")"
fi
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 9447980330..d968760139 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -330,8 +330,7 @@ esac
# Make sure we are in a valid repository of a vintage we understand,
# if we require to be in a git repository.
-if test -z "$NONGIT_OK"
-then
+git_dir_init () {
GIT_DIR=$(git rev-parse --git-dir) || exit
if [ -z "$SUBDIRECTORY_OK" ]
then
@@ -346,6 +345,11 @@ then
exit 1
}
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
+}
+
+if test -z "$NONGIT_OK"
+then
+ git_dir_init
fi
peel_committish () {
diff --git a/git-stash.sh b/git-stash.sh
index bcc757b390..d4cf818be9 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -50,7 +50,7 @@ clear_stash () {
then
die "$(gettext "git stash clear with parameters is unimplemented")"
fi
- if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
+ if current=$(git rev-parse --verify --quiet $ref_stash)
then
git update-ref -d $ref_stash $current
fi
@@ -292,12 +292,12 @@ save_stash () {
}
have_stash () {
- git rev-parse --verify $ref_stash >/dev/null 2>&1
+ git rev-parse --verify --quiet $ref_stash >/dev/null
}
list_stash () {
have_stash || return 0
- git log --format="%gd: %gs" -g "$@" $ref_stash --
+ git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash --
}
show_stash () {
@@ -392,12 +392,12 @@ parse_flags_and_rev()
;;
esac
- REV=$(git rev-parse --quiet --symbolic --verify "$1" 2>/dev/null) || {
+ REV=$(git rev-parse --symbolic --verify --quiet "$1") || {
reference="$1"
- die "$(eval_gettext "\$reference is not valid reference")"
+ die "$(eval_gettext "\$reference is not a valid reference")"
}
- i_commit=$(git rev-parse --quiet --verify "$REV^2" 2>/dev/null) &&
+ i_commit=$(git rev-parse --verify --quiet "$REV^2") &&
set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
s=$1 &&
w_commit=$1 &&
@@ -409,7 +409,7 @@ parse_flags_and_rev()
test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
IS_STASH_REF=t
- u_commit=$(git rev-parse --quiet --verify "$REV^3" 2>/dev/null) &&
+ u_commit=$(git rev-parse --verify --quiet "$REV^3") &&
u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
}
@@ -531,7 +531,8 @@ drop_stash () {
die "$(eval_gettext "\${REV}: Could not drop stash entry")"
# clear_stash if we just dropped the last stash entry
- git rev-parse --verify "$ref_stash@{0}" >/dev/null 2>&1 || clear_stash
+ git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
+ clear_stash
}
apply_to_branch () {
diff --git a/git-svn.perl b/git-svn.perl
index 0a323722a6..b6e2186cef 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -260,8 +260,8 @@ my %cmd = (
} ],
'find-rev' => [ \&cmd_find_rev,
"Translate between SVN revision numbers and tree-ish",
- { 'before' => \$_before,
- 'after' => \$_after } ],
+ { 'B|before' => \$_before,
+ 'A|after' => \$_after } ],
'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory",
{ 'merge|m|M' => \$_merge,
'verbose|v' => \$_verbose,
@@ -306,13 +306,16 @@ sub readline {
}
package main;
-my $term = eval {
- $ENV{"GIT_SVN_NOTTY"}
- ? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT
- : new Term::ReadLine 'git-svn';
-};
-if ($@) {
- $term = new FakeTerm "$@: going non-interactive";
+my $term;
+sub term_init {
+ $term = eval {
+ $ENV{"GIT_SVN_NOTTY"}
+ ? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT
+ : new Term::ReadLine 'git-svn';
+ };
+ if ($@) {
+ $term = new FakeTerm "$@: going non-interactive";
+ }
}
my $cmd;
@@ -424,6 +427,7 @@ sub ask {
my $default = $arg{default};
my $resp;
my $i = 0;
+ term_init() unless $term;
if ( !( defined($term->IN)
&& defined( fileno($term->IN) )
@@ -1161,7 +1165,9 @@ sub cmd_branch {
::_req_svn();
my $ctx = SVN::Client->new(
- auth => Git::SVN::Ra::_auth_providers(),
+ config => SVN::Core::config_get_config(
+ $Git::SVN::Ra::config_dir
+ ),
log_msg => sub {
${ $_[0] } = defined $_message
? $_message
@@ -1475,10 +1481,37 @@ sub cmd_commit_diff {
}
}
-
sub cmd_info {
- my $path = canonicalize_path(defined($_[0]) ? $_[0] : ".");
- my $fullpath = canonicalize_path($cmd_dir_prefix . $path);
+ my $path_arg = defined($_[0]) ? $_[0] : '.';
+ my $path = $path_arg;
+ if (File::Spec->file_name_is_absolute($path)) {
+ $path = canonicalize_path($path);
+
+ my $toplevel = eval {
+ my @cmd = qw/rev-parse --show-toplevel/;
+ command_oneline(\@cmd, STDERR => 0);
+ };
+
+ # remove $toplevel from the absolute path:
+ my ($vol, $dirs, $file) = File::Spec->splitpath($path);
+ my (undef, $tdirs, $tfile) = File::Spec->splitpath($toplevel);
+ my @dirs = File::Spec->splitdir($dirs);
+ my @tdirs = File::Spec->splitdir($tdirs);
+ pop @dirs if $dirs[-1] eq '';
+ pop @tdirs if $tdirs[-1] eq '';
+ push @dirs, $file;
+ push @tdirs, $tfile;
+ while (@tdirs && @dirs && $tdirs[0] eq $dirs[0]) {
+ shift @dirs;
+ shift @tdirs;
+ }
+ $dirs = File::Spec->catdir(@dirs);
+ $path = File::Spec->catpath($vol, $dirs);
+
+ $path = canonicalize_path($path);
+ } else {
+ $path = canonicalize_path($cmd_dir_prefix . $path);
+ }
if (exists $_[1]) {
die "Too many arguments specified\n";
}
@@ -1499,14 +1532,14 @@ sub cmd_info {
# canonicalize_path() will return "" to make libsvn 1.5.x happy,
$path = "." if $path eq "";
- my $full_url = canonicalize_url( add_path_to_url( $url, $fullpath ) );
+ my $full_url = canonicalize_url( add_path_to_url( $url, $path ) );
if ($_url) {
print "$full_url\n";
return;
}
- my $result = "Path: $path\n";
+ my $result = "Path: $path_arg\n";
$result .= "Name: " . basename($path) . "\n" if $file_type ne "dir";
$result .= "URL: $full_url\n";
@@ -1537,7 +1570,7 @@ sub cmd_info {
}
my ($lc_author, $lc_rev, $lc_date_utc);
- my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $fullpath);
+ my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path);
my $log = command_output_pipe(@args);
my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
while (<$log>) {
diff --git a/git.c b/git.c
index 9c49519831..18fbf79430 100644
--- a/git.c
+++ b/git.c
@@ -14,13 +14,13 @@ const char git_usage_string[] =
" <command> [<args>]";
const char git_more_info_string[] =
- N_("'git help -a' and 'git help -g' lists available subcommands and some\n"
+ N_("'git help -a' and 'git help -g' list available subcommands and some\n"
"concept guides. See 'git help <command>' or 'git help <concept>'\n"
"to read about a specific subcommand or concept.");
static struct startup_info git_startup_info;
static int use_pager = -1;
-static char orig_cwd[PATH_MAX];
+static char *orig_cwd;
static const char *env_names[] = {
GIT_DIR_ENVIRONMENT,
GIT_WORK_TREE_ENVIRONMENT,
@@ -36,8 +36,7 @@ static void save_env(void)
if (saved_environment)
return;
saved_environment = 1;
- if (!getcwd(orig_cwd, sizeof(orig_cwd)))
- die_errno("cannot getcwd");
+ orig_cwd = xgetcwd();
for (i = 0; i < ARRAY_SIZE(env_names); i++) {
orig_env[i] = getenv(env_names[i]);
if (orig_env[i])
@@ -48,8 +47,9 @@ static void save_env(void)
static void restore_env(void)
{
int i;
- if (*orig_cwd && chdir(orig_cwd))
+ if (orig_cwd && chdir(orig_cwd))
die_errno("could not move to %s", orig_cwd);
+ free(orig_cwd);
for (i = 0; i < ARRAY_SIZE(env_names); i++) {
if (orig_env[i])
setenv(env_names[i], orig_env[i], 1);
@@ -161,9 +161,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--bare")) {
- static char git_dir[PATH_MAX+1];
+ char *cwd = xgetcwd();
is_bare_repository_cfg = 1;
- setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
+ setenv(GIT_DIR_ENVIRONMENT, cwd, 0);
+ free(cwd);
setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
if (envchanged)
*envchanged = 1;
@@ -281,8 +282,7 @@ static int handle_alias(int *argcp, const char ***argv)
"trace: alias expansion: %s =>",
alias_command);
- new_argv = xrealloc(new_argv, sizeof(char *) *
- (count + *argcp));
+ REALLOC_ARRAY(new_argv, count + *argcp);
/* insert after command name */
memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
@@ -417,6 +417,7 @@ static struct cmd_struct commands[] = {
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
{ "init", cmd_init_db, NO_SETUP },
{ "init-db", cmd_init_db, NO_SETUP },
+ { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP },
{ "log", cmd_log, RUN_SETUP },
{ "ls-files", cmd_ls_files, RUN_SETUP },
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
@@ -592,6 +593,26 @@ static int run_argv(int *argcp, const char ***argv)
return done_alias;
}
+/*
+ * Many parts of Git have subprograms communicate via pipe, expect the
+ * upstream of a pipe to die with SIGPIPE when the downstream of a
+ * pipe does not need to read all that is written. Some third-party
+ * programs that ignore or block SIGPIPE for their own reason forget
+ * to restore SIGPIPE handling to the default before spawning Git and
+ * break this carefully orchestrated machinery.
+ *
+ * Restore the way SIGPIPE is handled to default, which is what we
+ * expect.
+ */
+static void restore_sigpipe_to_default(void)
+{
+ sigset_t unblock;
+
+ sigemptyset(&unblock);
+ sigaddset(&unblock, SIGPIPE);
+ sigprocmask(SIG_UNBLOCK, &unblock, NULL);
+ signal(SIGPIPE, SIG_DFL);
+}
int main(int argc, char **av)
{
@@ -611,6 +632,8 @@ int main(int argc, char **av)
*/
sanitize_stdfds();
+ restore_sigpipe_to_default();
+
git_setup_gettext();
trace_command_performance(argv);
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index a9f57d6f90..ccf75169dd 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4100,7 +4100,7 @@ sub print_search_form {
if ($use_pathinfo) {
$action .= "/".esc_url($project);
}
- print $cgi->startform(-method => "get", -action => $action) .
+ print $cgi->start_form(-method => "get", -action => $action) .
"<div class=\"search\">\n" .
(!$use_pathinfo &&
$cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
@@ -5510,7 +5510,7 @@ sub git_project_search_form {
}
print "<div class=\"projsearch\">\n";
- print $cgi->startform(-method => 'get', -action => $my_uri) .
+ print $cgi->start_form(-method => 'get', -action => $my_uri) .
$cgi->hidden(-name => 'a', -value => 'project_list') . "\n";
print $cgi->hidden(-name => 'pf', -value => $project_filter). "\n"
if (defined $project_filter);
diff --git a/gpg-interface.c b/gpg-interface.c
index ff07012726..68b0c814f7 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -7,6 +7,9 @@
static char *configured_signing_key;
static const char *gpg_program = "gpg";
+#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
+#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
+
void signature_check_clear(struct signature_check *sigc)
{
free(sigc->payload);
@@ -21,6 +24,60 @@ void signature_check_clear(struct signature_check *sigc)
sigc->key = NULL;
}
+static struct {
+ char result;
+ const char *check;
+} sigcheck_gpg_status[] = {
+ { 'G', "\n[GNUPG:] GOODSIG " },
+ { 'B', "\n[GNUPG:] BADSIG " },
+ { 'U', "\n[GNUPG:] TRUST_NEVER" },
+ { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
+};
+
+void parse_gpg_output(struct signature_check *sigc)
+{
+ const char *buf = sigc->gpg_status;
+ int i;
+
+ /* Iterate over all search strings */
+ for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
+ const char *found, *next;
+
+ if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
+ found = strstr(buf, sigcheck_gpg_status[i].check);
+ if (!found)
+ continue;
+ found += strlen(sigcheck_gpg_status[i].check);
+ }
+ sigc->result = sigcheck_gpg_status[i].result;
+ /* The trust messages are not followed by key/signer information */
+ if (sigc->result != 'U') {
+ sigc->key = xmemdupz(found, 16);
+ found += 17;
+ next = strchrnul(found, '\n');
+ sigc->signer = xmemdupz(found, next - found);
+ }
+ }
+}
+
+/*
+ * Look at GPG signed content (e.g. a signed tag object), whose
+ * payload is followed by a detached signature on it. 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;
+ size_t len = 0;
+ while (len < size && !starts_with(buf + len, PGP_SIGNATURE) &&
+ !starts_with(buf + len, PGP_MESSAGE)) {
+ eol = memchr(buf + len, '\n', size - len);
+ len += eol ? eol - (buf + len) + 1 : size - len;
+ }
+ return len;
+}
+
void set_signing_key(const char *key)
{
free(configured_signing_key);
@@ -55,12 +112,11 @@ const char *get_signing_key(void)
*/
int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
{
- struct child_process gpg;
+ struct child_process gpg = CHILD_PROCESS_INIT;
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;
@@ -116,7 +172,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
const char *signature, size_t signature_size,
struct strbuf *gpg_output, struct strbuf *gpg_status)
{
- struct child_process gpg;
+ struct child_process gpg = CHILD_PROCESS_INIT;
const char *args_gpg[] = {NULL, "--status-fd=1", "--verify", "FILE", "-", NULL};
char path[PATH_MAX];
int fd, ret;
@@ -133,7 +189,6 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
path, strerror(errno));
close(fd);
- memset(&gpg, 0, sizeof(gpg));
gpg.argv = args_gpg;
gpg.in = -1;
gpg.out = -1;
diff --git a/gpg-interface.h b/gpg-interface.h
index 37c23daff0..87a4f2e3fa 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -5,16 +5,23 @@ struct signature_check {
char *payload;
char *gpg_output;
char *gpg_status;
- char result; /* 0 (not checked),
- * N (checked but no further result),
- * U (untrusted good),
- * G (good)
- * B (bad) */
+
+ /*
+ * possible "result":
+ * 0 (not checked)
+ * N (checked but no further result)
+ * U (untrusted good)
+ * G (good)
+ * B (bad)
+ */
+ char result;
char *signer;
char *key;
};
extern void signature_check_clear(struct signature_check *sigc);
+extern size_t parse_signature(const char *buf, unsigned long size);
+extern void parse_gpg_output(struct signature_check *);
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, struct strbuf *gpg_status);
extern int git_gpg_config(const char *, const char *, void *);
diff --git a/graph.c b/graph.c
index 640433166b..c25a09a8fd 100644
--- a/graph.c
+++ b/graph.c
@@ -267,16 +267,10 @@ static void graph_ensure_capacity(struct git_graph *graph, int num_columns)
graph->column_capacity *= 2;
} while (graph->column_capacity < num_columns);
- graph->columns = xrealloc(graph->columns,
- sizeof(struct column) *
- graph->column_capacity);
- graph->new_columns = xrealloc(graph->new_columns,
- sizeof(struct column) *
- graph->column_capacity);
- graph->mapping = xrealloc(graph->mapping,
- sizeof(int) * 2 * graph->column_capacity);
- graph->new_mapping = xrealloc(graph->new_mapping,
- sizeof(int) * 2 * graph->column_capacity);
+ REALLOC_ARRAY(graph->columns, graph->column_capacity);
+ REALLOC_ARRAY(graph->new_columns, graph->column_capacity);
+ REALLOC_ARRAY(graph->mapping, graph->column_capacity * 2);
+ REALLOC_ARRAY(graph->new_mapping, graph->column_capacity * 2);
}
/*
@@ -1145,7 +1139,7 @@ int graph_next_line(struct git_graph *graph, struct strbuf *sb)
static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
{
- int i, j;
+ int i;
if (graph->state != GRAPH_COMMIT) {
graph_next_line(graph, sb);
@@ -1161,21 +1155,11 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
*/
for (i = 0; i < graph->num_columns; i++) {
struct column *col = &graph->columns[i];
- struct commit *col_commit = col->commit;
- if (col_commit == graph->commit) {
- strbuf_write_column(sb, col, '|');
-
- if (graph->num_parents < 3)
- strbuf_addch(sb, ' ');
- else {
- int num_spaces = ((graph->num_parents - 2) * 2);
- for (j = 0; j < num_spaces; j++)
- strbuf_addch(sb, ' ');
- }
- } else {
- strbuf_write_column(sb, col, '|');
+ strbuf_write_column(sb, col, '|');
+ if (col->commit == graph->commit && graph->num_parents > 2)
+ strbuf_addchars(sb, ' ', (graph->num_parents - 2) * 2);
+ else
strbuf_addch(sb, ' ');
- }
}
graph_pad_horizontally(graph, sb, graph->num_columns);
diff --git a/grep.c b/grep.c
index 99217dc04f..4dc31ea386 100644
--- a/grep.c
+++ b/grep.c
@@ -111,7 +111,7 @@ int grep_config(const char *var, const char *value, void *cb)
if (color) {
if (!value)
return config_error_nonbool(var);
- color_parse(value, var, color);
+ return color_parse(value, color);
}
return 0;
}
diff --git a/help.c b/help.c
index 7af65e205e..2072a873e2 100644
--- a/help.c
+++ b/help.c
@@ -305,7 +305,7 @@ const char *help_unknown_cmd(const char *cmd)
add_cmd_list(&main_cmds, &aliases);
add_cmd_list(&main_cmds, &other_cmds);
qsort(main_cmds.names, main_cmds.cnt,
- sizeof(main_cmds.names), cmdname_compare);
+ sizeof(*main_cmds.names), cmdname_compare);
uniq(&main_cmds);
/* This abuses cmdname->len for levenshtein distance */
diff --git a/http-backend.c b/http-backend.c
index 80790bbaef..b6c0484fb2 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -219,29 +219,22 @@ static void get_idx_file(char *name)
send_local_file("application/x-git-packed-objects-toc", name);
}
-static int http_config(const char *var, const char *value, void *cb)
+static void http_config(void)
{
- const char *p;
+ int i, value = 0;
+ struct strbuf var = STRBUF_INIT;
- if (!strcmp(var, "http.getanyfile")) {
- getanyfile = git_config_bool(var, value);
- return 0;
- }
+ git_config_get_bool("http.getanyfile", &getanyfile);
- if (skip_prefix(var, "http.", &p)) {
- int i;
-
- for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
- struct rpc_service *svc = &rpc_service[i];
- if (!strcmp(p, svc->config_name)) {
- svc->enabled = git_config_bool(var, value);
- return 0;
- }
- }
+ for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
+ struct rpc_service *svc = &rpc_service[i];
+ strbuf_addf(&var, "http.%s", svc->config_name);
+ if (!git_config_get_bool(var.buf, &value))
+ svc->enabled = value;
+ strbuf_reset(&var);
}
- /* we are not interested in parsing any other configuration here */
- return 0;
+ strbuf_release(&var);
}
static struct rpc_service *select_service(const char *name)
@@ -321,9 +314,8 @@ static void run_service(const char **argv)
const char *encoding = getenv("HTTP_CONTENT_ENCODING");
const char *user = getenv("REMOTE_USER");
const char *host = getenv("REMOTE_ADDR");
- struct argv_array env = ARGV_ARRAY_INIT;
int gzipped_request = 0;
- struct child_process cld;
+ struct child_process cld = CHILD_PROCESS_INIT;
if (encoding && !strcmp(encoding, "gzip"))
gzipped_request = 1;
@@ -336,14 +328,12 @@ static void run_service(const char **argv)
host = "(none)";
if (!getenv("GIT_COMMITTER_NAME"))
- argv_array_pushf(&env, "GIT_COMMITTER_NAME=%s", user);
+ argv_array_pushf(&cld.env_array, "GIT_COMMITTER_NAME=%s", user);
if (!getenv("GIT_COMMITTER_EMAIL"))
- argv_array_pushf(&env, "GIT_COMMITTER_EMAIL=%s@http.%s",
- user, host);
+ argv_array_pushf(&cld.env_array,
+ "GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
- memset(&cld, 0, sizeof(cld));
cld.argv = argv;
- cld.env = env.argv;
if (gzipped_request)
cld.in = -1;
cld.git_cmd = 1;
@@ -358,7 +348,6 @@ static void run_service(const char **argv)
if (finish_command(&cld))
exit(1);
- argv_array_clear(&env);
}
static int show_text_ref(const char *name, const unsigned char *sha1,
@@ -420,7 +409,9 @@ static int show_head_ref(const char *refname, const unsigned char *sha1,
if (flag & REF_ISSYMREF) {
unsigned char unused[20];
- const char *target = resolve_ref_unsafe(refname, unused, 1, NULL);
+ const char *target = resolve_ref_unsafe(refname,
+ RESOLVE_REF_READING,
+ unused, NULL);
const char *target_nons = strip_namespace(target);
strbuf_addf(buf, "ref: %s\n", target_nons);
@@ -627,7 +618,7 @@ int main(int argc, char **argv)
access("git-daemon-export-ok", F_OK) )
not_found("Repository not exported: '%s'", dir);
- git_config(http_config, NULL);
+ http_config();
cmd->imp(cmd_arg);
return 0;
}
diff --git a/http-walker.c b/http-walker.c
index dbddfaa177..88da5468e7 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -230,7 +230,6 @@ static void process_alternates_response(void *callback_data)
int okay = 0;
int serverlen = 0;
struct alt_base *newalt;
- char *target = NULL;
if (data[i] == '/') {
/*
* This counts
@@ -287,17 +286,15 @@ static void process_alternates_response(void *callback_data)
}
/* skip "objects\n" at end */
if (okay) {
- target = xmalloc(serverlen + posn - i - 6);
- memcpy(target, base, serverlen);
- memcpy(target + serverlen, data + i,
- posn - i - 7);
- target[serverlen + posn - i - 7] = 0;
+ struct strbuf target = STRBUF_INIT;
+ strbuf_add(&target, base, serverlen);
+ strbuf_add(&target, data + i, posn - i - 7);
if (walker->get_verbosely)
- fprintf(stderr,
- "Also look at %s\n", target);
+ fprintf(stderr, "Also look at %s\n",
+ target.buf);
newalt = xmalloc(sizeof(*newalt));
newalt->next = NULL;
- newalt->base = target;
+ newalt->base = strbuf_detach(&target, NULL);
newalt->got_indices = 0;
newalt->packs = NULL;
diff --git a/http.c b/http.c
index c8cd50dd0c..040f362a6a 100644
--- a/http.c
+++ b/http.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
#include "http.h"
#include "pack.h"
#include "sideband.h"
@@ -300,6 +301,9 @@ static CURL *get_curl_handle(void)
{
CURL *result = curl_easy_init();
+ if (!result)
+ die("curl_easy_init failed");
+
if (!curl_ssl_verify) {
curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
@@ -399,7 +403,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
git_config(urlmatch_config_entry, &config);
free(normalized_url);
- curl_global_init(CURL_GLOBAL_ALL);
+ if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
+ die("curl_global_init failed");
http_proactive_auth = proactive_auth;
@@ -417,10 +422,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
}
curlm = curl_multi_init();
- if (curlm == NULL) {
- fprintf(stderr, "Error creating curl multi handle.\n");
- exit(1);
- }
+ if (!curlm)
+ die("curl_multi_init failed");
#endif
if (getenv("GIT_SSL_NO_VERIFY"))
@@ -1332,7 +1335,7 @@ int finish_http_pack_request(struct http_pack_request *preq)
struct packed_git **lst;
struct packed_git *p = preq->target;
char *tmp_idx;
- struct child_process ip;
+ struct child_process ip = CHILD_PROCESS_INIT;
const char *ip_argv[8];
close_pack_index(p);
@@ -1355,7 +1358,6 @@ int finish_http_pack_request(struct http_pack_request *preq)
ip_argv[3] = preq->tmpfile;
ip_argv[4] = NULL;
- memset(&ip, 0, sizeof(ip));
ip.argv = ip_argv;
ip.git_cmd = 1;
ip.no_stdin = 1;
diff --git a/ident.c b/ident.c
index 1d9b6e770d..5ff1aadaaa 100644
--- a/ident.c
+++ b/ident.c
@@ -9,7 +9,7 @@
static struct strbuf git_default_name = STRBUF_INIT;
static struct strbuf git_default_email = STRBUF_INIT;
-static char git_default_date[50];
+static struct strbuf git_default_date = STRBUF_INIT;
#define IDENT_NAME_GIVEN 01
#define IDENT_MAIL_GIVEN 02
@@ -102,7 +102,7 @@ static void copy_email(const struct passwd *pw, struct strbuf *email)
add_domainname(email);
}
-static const char *ident_default_name(void)
+const char *ident_default_name(void)
{
if (!git_default_name.len) {
copy_gecos(xgetpwuid_self(), &git_default_name);
@@ -129,9 +129,9 @@ const char *ident_default_email(void)
static const char *ident_default_date(void)
{
- if (!git_default_date[0])
- datestamp(git_default_date, sizeof(git_default_date));
- return git_default_date;
+ if (!git_default_date.len)
+ datestamp(&git_default_date);
+ return git_default_date.buf;
}
static int crud(unsigned char c)
@@ -292,7 +292,6 @@ const char *fmt_ident(const char *name, const char *email,
const char *date_str, int flag)
{
static struct strbuf ident = STRBUF_INIT;
- char date[50];
int strict = (flag & IDENT_STRICT);
int want_date = !(flag & IDENT_NO_DATE);
int want_name = !(flag & IDENT_NO_NAME);
@@ -320,15 +319,6 @@ const char *fmt_ident(const char *name, const char *email,
die("unable to auto-detect email address (got '%s')", email);
}
- if (want_date) {
- if (date_str && date_str[0]) {
- if (parse_date(date_str, date, sizeof(date)) < 0)
- die("invalid date format: %s", date_str);
- }
- else
- strcpy(date, ident_default_date());
- }
-
strbuf_reset(&ident);
if (want_name) {
strbuf_addstr_without_crud(&ident, name);
@@ -339,8 +329,14 @@ const char *fmt_ident(const char *name, const char *email,
strbuf_addch(&ident, '>');
if (want_date) {
strbuf_addch(&ident, ' ');
- strbuf_addstr_without_crud(&ident, date);
+ if (date_str && date_str[0]) {
+ if (parse_date(date_str, &ident) < 0)
+ die("invalid date format: %s", date_str);
+ }
+ else
+ strbuf_addstr(&ident, ident_default_date());
}
+
return ident.buf;
}
diff --git a/imap-send.c b/imap-send.c
index 524fbabc96..70bcc7a4e5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,6 +69,7 @@ struct imap_server_conf {
char *tunnel;
char *host;
int port;
+ char *folder;
char *user;
char *pass;
int use_ssl;
@@ -82,6 +83,7 @@ static struct imap_server_conf server = {
NULL, /* tunnel */
NULL, /* host */
0, /* port */
+ NULL, /* folder */
NULL, /* user */
NULL, /* pass */
0, /* use_ssl */
@@ -128,7 +130,6 @@ struct imap_cmd_cb {
char *data;
int dlen;
int uid;
- unsigned create:1, trycreate:1;
};
struct imap_cmd {
@@ -493,9 +494,9 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
return ret;
}
-static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
- struct imap_cmd_cb *cb,
- const char *fmt, va_list ap)
+static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
+ struct imap_cmd_cb *cb,
+ const char *fmt, va_list ap)
{
struct imap *imap = ctx->imap;
struct imap_cmd *cmd;
@@ -524,7 +525,7 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
if (Verbose) {
if (imap->num_in_progress)
printf("(%d in progress) ", imap->num_in_progress);
- if (memcmp(cmd->cmd, "LOGIN", 5))
+ if (!starts_with(cmd->cmd, "LOGIN"))
printf(">>> %s", buf);
else
printf(">>> %d LOGIN <user> <pass>\n", cmd->tag);
@@ -559,20 +560,6 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
}
__attribute__((format (printf, 3, 4)))
-static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
- struct imap_cmd_cb *cb,
- const char *fmt, ...)
-{
- struct imap_cmd *ret;
- va_list ap;
-
- va_start(ap, fmt);
- ret = v_issue_imap_cmd(ctx, cb, fmt, ap);
- va_end(ap);
- return ret;
-}
-
-__attribute__((format (printf, 3, 4)))
static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
const char *fmt, ...)
{
@@ -580,7 +567,7 @@ static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
struct imap_cmd *cmdp;
va_start(ap, fmt);
- cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+ cmdp = issue_imap_cmd(ctx, cb, fmt, ap);
va_end(ap);
if (!cmdp)
return RESP_BAD;
@@ -596,7 +583,7 @@ static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
struct imap_cmd *cmdp;
va_start(ap, fmt);
- cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+ cmdp = issue_imap_cmd(ctx, cb, fmt, ap);
va_end(ap);
if (!cmdp)
return DRV_STORE_BAD;
@@ -714,8 +701,8 @@ static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
{
struct imap *imap = ctx->imap;
- struct imap_cmd *cmdp, **pcmdp, *ncmdp;
- char *cmd, *arg, *arg1, *p;
+ struct imap_cmd *cmdp, **pcmdp;
+ char *cmd, *arg, *arg1;
int n, resp, resp2, tag;
for (;;) {
@@ -801,39 +788,17 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
if (!strcmp("OK", arg))
resp = DRV_OK;
else {
- if (!strcmp("NO", arg)) {
- if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
- p = strchr(cmdp->cmd, '"');
- if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", (int)(strchr(p + 1, '"') - p + 1), p)) {
- resp = RESP_BAD;
- goto normal;
- }
- /* not waiting here violates the spec, but a server that does not
- grok this nonetheless violates it too. */
- cmdp->cb.create = 0;
- if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) {
- resp = RESP_BAD;
- goto normal;
- }
- free(cmdp->cmd);
- free(cmdp);
- if (!tcmd)
- return 0; /* ignored */
- if (cmdp == tcmd)
- tcmd = ncmdp;
- continue;
- }
+ if (!strcmp("NO", arg))
resp = RESP_NO;
- } else /*if (!strcmp("BAD", arg))*/
+ else /*if (!strcmp("BAD", arg))*/
resp = RESP_BAD;
fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n",
- memcmp(cmdp->cmd, "LOGIN", 5) ?
+ !starts_with(cmdp->cmd, "LOGIN") ?
cmdp->cmd : "LOGIN <user> <pass>",
arg, cmd ? cmd : "");
}
if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
resp = resp2;
- normal:
if (cmdp->cb.done)
cmdp->cb.done(ctx, cmdp, resp);
free(cmdp->cb.data);
@@ -944,7 +909,7 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha
return 0;
}
-static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
+static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *folder)
{
struct credential cred = CREDENTIAL_INIT;
struct imap_store *ctx;
@@ -954,24 +919,23 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
ctx = xcalloc(1, sizeof(*ctx));
- ctx->imap = imap = xcalloc(sizeof(*imap), 1);
+ ctx->imap = imap = xcalloc(1, sizeof(*imap));
imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
imap->in_progress_append = &imap->in_progress;
/* open connection to IMAP server */
if (srvc->tunnel) {
- const char *argv[] = { srvc->tunnel, NULL };
- struct child_process tunnel = {NULL};
+ struct child_process tunnel = CHILD_PROCESS_INIT;
imap_info("Starting tunnel '%s'... ", srvc->tunnel);
- tunnel.argv = argv;
+ argv_array_push(&tunnel.args, srvc->tunnel);
tunnel.use_shell = 1;
tunnel.in = -1;
tunnel.out = -1;
if (start_command(&tunnel))
- die("cannot start proxy %s", argv[0]);
+ die("cannot start proxy %s", srvc->tunnel);
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
@@ -1156,6 +1120,25 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
credential_approve(&cred);
credential_clear(&cred);
+ /* check the target mailbox exists */
+ ctx->name = folder;
+ switch (imap_exec(ctx, NULL, "EXAMINE \"%s\"", ctx->name)) {
+ case RESP_OK:
+ /* ok */
+ break;
+ case RESP_BAD:
+ fprintf(stderr, "IMAP error: could not check mailbox\n");
+ goto out;
+ case RESP_NO:
+ if (imap_exec(ctx, NULL, "CREATE \"%s\"", ctx->name) == RESP_OK) {
+ imap_info("Created missing mailbox\n");
+ } else {
+ fprintf(stderr, "IMAP error: could not create missing mailbox\n");
+ goto out;
+ }
+ break;
+ }
+
ctx->prefix = "";
return ctx;
@@ -1164,6 +1147,7 @@ bail:
credential_reject(&cred);
credential_clear(&cred);
+ out:
imap_close_store(ctx);
return NULL;
}
@@ -1219,7 +1203,6 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
box = ctx->name;
prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
- cb.create = 0;
ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" ", prefix, box);
imap->caps = imap->rcaps;
if (ret != DRV_OK)
@@ -1324,45 +1307,35 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
return 1;
}
-static char *imap_folder;
-
-static int git_imap_config(const char *key, const char *val, void *cb)
+static void git_imap_config(void)
{
- if (!skip_prefix(key, "imap.", &key))
- return 0;
+ const char *val = NULL;
+
+ git_config_get_bool("imap.sslverify", &server.ssl_verify);
+ git_config_get_bool("imap.preformattedhtml", &server.use_html);
+ git_config_get_string("imap.folder", &server.folder);
- /* check booleans first, and barf on others */
- if (!strcmp("sslverify", key))
- server.ssl_verify = git_config_bool(key, val);
- else if (!strcmp("preformattedhtml", key))
- server.use_html = git_config_bool(key, val);
- else if (!val)
- return config_error_nonbool(key);
-
- if (!strcmp("folder", key)) {
- imap_folder = xstrdup(val);
- } else if (!strcmp("host", key)) {
- if (starts_with(val, "imap:"))
- val += 5;
- else if (starts_with(val, "imaps:")) {
- val += 6;
- server.use_ssl = 1;
+ if (!git_config_get_value("imap.host", &val)) {
+ if (!val) {
+ git_die_config("imap.host", "Missing value for 'imap.host'");
+ } else {
+ if (starts_with(val, "imap:"))
+ val += 5;
+ else if (starts_with(val, "imaps:")) {
+ val += 6;
+ server.use_ssl = 1;
+ }
+ if (starts_with(val, "//"))
+ val += 2;
+ server.host = xstrdup(val);
}
- if (starts_with(val, "//"))
- val += 2;
- server.host = xstrdup(val);
- } else if (!strcmp("user", key))
- server.user = xstrdup(val);
- else if (!strcmp("pass", key))
- server.pass = xstrdup(val);
- else if (!strcmp("port", key))
- server.port = git_config_int(key, val);
- else if (!strcmp("tunnel", key))
- server.tunnel = xstrdup(val);
- else if (!strcmp("authmethod", key))
- server.auth_method = xstrdup(val);
+ }
- return 0;
+ git_config_get_string("imap.user", &server.user);
+ git_config_get_string("imap.pass", &server.pass);
+ git_config_get_int("imap.port", &server.port);
+ git_config_get_string("imap.tunnel", &server.tunnel);
+ git_config_get_string("imap.authmethod", &server.auth_method);
}
int main(int argc, char **argv)
@@ -1383,12 +1356,12 @@ int main(int argc, char **argv)
usage(imap_send_usage);
setup_git_directory_gently(&nongit_ok);
- git_config(git_imap_config, NULL);
+ git_imap_config();
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!imap_folder) {
+ if (!server.folder) {
fprintf(stderr, "no imap store specified\n");
return 1;
}
@@ -1418,14 +1391,13 @@ int main(int argc, char **argv)
}
/* write it to the imap server */
- ctx = imap_open_store(&server);
+ ctx = imap_open_store(&server, server.folder);
if (!ctx) {
fprintf(stderr, "failed to open store\n");
return 1;
}
fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
- ctx->name = imap_folder;
while (1) {
unsigned percent = n * 100 / total;
diff --git a/khash.h b/khash.h
index 06c7906549..376475a5ea 100644
--- a/khash.h
+++ b/khash.h
@@ -121,13 +121,9 @@ static const double __ac_HASH_UPPER = 0.77;
if (!new_flags) return -1; \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
- khkey_t *new_keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
- if (!new_keys) return -1; \
- h->keys = new_keys; \
+ REALLOC_ARRAY(h->keys, new_n_buckets); \
if (kh_is_map) { \
- khval_t *new_vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
- if (!new_vals) return -1; \
- h->vals = new_vals; \
+ REALLOC_ARRAY(h->vals, new_n_buckets); \
} \
} /* otherwise shrink */ \
} \
@@ -160,8 +156,8 @@ static const double __ac_HASH_UPPER = 0.77;
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
- h->keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
- if (kh_is_map) h->vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+ REALLOC_ARRAY(h->keys, new_n_buckets); \
+ if (kh_is_map) REALLOC_ARRAY(h->vals, new_n_buckets); \
} \
free(h->flags); /* free the working space */ \
h->flags = new_flags; \
diff --git a/line-log.c b/line-log.c
index 1008e72258..038c58a395 100644
--- a/line-log.c
+++ b/line-log.c
@@ -533,7 +533,7 @@ static void fill_line_ends(struct diff_filespec *spec, long *lines,
}
/* shrink the array to fit the elements */
- ends = xrealloc(ends, cur * sizeof(*ends));
+ REALLOC_ARRAY(ends, cur);
*lines = cur-1;
*line_ends = ends;
}
diff --git a/list-objects.c b/list-objects.c
index 3595ee7a22..2910becd6c 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -208,6 +208,7 @@ void traverse_commit_list(struct rev_info *revs,
struct object_array_entry *pending = revs->pending.objects + i;
struct object *obj = pending->item;
const char *name = pending->name;
+ const char *path = pending->path;
if (obj->flags & (UNINTERESTING | SEEN))
continue;
if (obj->type == OBJ_TAG) {
@@ -215,24 +216,21 @@ void traverse_commit_list(struct rev_info *revs,
show_object(obj, NULL, name, data);
continue;
}
+ if (!path)
+ path = "";
if (obj->type == OBJ_TREE) {
process_tree(revs, (struct tree *)obj, show_object,
- NULL, &base, name, data);
+ NULL, &base, path, data);
continue;
}
if (obj->type == OBJ_BLOB) {
process_blob(revs, (struct blob *)obj, show_object,
- NULL, name, data);
+ NULL, path, data);
continue;
}
die("unknown pending object %s (%s)",
sha1_to_hex(obj->sha1), name);
}
- if (revs->pending.nr) {
- free(revs->pending.objects);
- revs->pending.nr = 0;
- revs->pending.alloc = 0;
- revs->pending.objects = NULL;
- }
+ object_array_clear(&revs->pending);
strbuf_release(&base);
}
diff --git a/ll-merge.c b/ll-merge.c
index fb61ea66a1..8ea03e536a 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -225,11 +225,8 @@ static int read_merge_config(const char *var, const char *value, void *cb)
const char *key, *name;
int namelen;
- if (!strcmp(var, "merge.default")) {
- if (value)
- default_ll_merge = xstrdup(value);
- return 0;
- }
+ if (!strcmp(var, "merge.default"))
+ return git_config_string(&default_ll_merge, var, value);
/*
* We are not interested in anything but "merge.<name>.variable";
@@ -254,12 +251,8 @@ static int read_merge_config(const char *var, const char *value, void *cb)
ll_user_merge_tail = &(fn->next);
}
- if (!strcmp("name", key)) {
- if (!value)
- return error("%s: lacks value", var);
- fn->description = xstrdup(value);
- return 0;
- }
+ if (!strcmp("name", key))
+ return git_config_string(&fn->description, var, value);
if (!strcmp("driver", key)) {
if (!value)
@@ -285,12 +278,8 @@ static int read_merge_config(const char *var, const char *value, void *cb)
return 0;
}
- if (!strcmp("recursive", key)) {
- if (!value)
- return error("%s: lacks value", var);
- fn->recursive = xstrdup(value);
- return 0;
- }
+ if (!strcmp("recursive", key))
+ return git_config_string(&fn->recursive, var, value);
return 0;
}
diff --git a/lockfile.c b/lockfile.c
index 2564a7f544..4f16ee78ce 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -2,59 +2,61 @@
* Copyright (c) 2005, Junio C Hamano
*/
#include "cache.h"
+#include "lockfile.h"
#include "sigchain.h"
-static struct lock_file *lock_file_list;
+static struct lock_file *volatile lock_file_list;
-static void remove_lock_file(void)
+static void remove_lock_files(int skip_fclose)
{
pid_t me = getpid();
while (lock_file_list) {
- if (lock_file_list->owner == me &&
- lock_file_list->filename[0]) {
- if (lock_file_list->fd >= 0)
- close(lock_file_list->fd);
- unlink_or_warn(lock_file_list->filename);
+ if (lock_file_list->owner == me) {
+ /* fclose() is not safe to call in a signal handler */
+ if (skip_fclose)
+ lock_file_list->fp = NULL;
+ rollback_lock_file(lock_file_list);
}
lock_file_list = lock_file_list->next;
}
}
-static void remove_lock_file_on_signal(int signo)
+static void remove_lock_files_on_exit(void)
{
- remove_lock_file();
+ remove_lock_files(0);
+}
+
+static void remove_lock_files_on_signal(int signo)
+{
+ remove_lock_files(1);
sigchain_pop(signo);
raise(signo);
}
/*
- * p = absolute or relative path name
+ * path = absolute or relative path name
*
- * Return a pointer into p showing the beginning of the last path name
- * element. If p is empty or the root directory ("/"), just return p.
+ * Remove the last path name element from path (leaving the preceding
+ * "/", if any). If path is empty or the root directory ("/"), set
+ * path to the empty string.
*/
-static char *last_path_elm(char *p)
+static void trim_last_path_component(struct strbuf *path)
{
- /* r starts pointing to null at the end of the string */
- char *r = strchr(p, '\0');
-
- if (r == p)
- return p; /* just return empty string */
-
- r--; /* back up to last non-null character */
+ int i = path->len;
/* back up past trailing slashes, if any */
- while (r > p && *r == '/')
- r--;
+ while (i && path->buf[i - 1] == '/')
+ i--;
/*
- * then go backwards until I hit a slash, or the beginning of
- * the string
+ * then go backwards until a slash, or the beginning of the
+ * string
*/
- while (r > p && *(r-1) != '/')
- r--;
- return r;
+ while (i && path->buf[i - 1] != '/')
+ i--;
+
+ strbuf_setlen(path, i);
}
@@ -62,103 +64,88 @@ static char *last_path_elm(char *p)
#define MAXDEPTH 5
/*
- * p = path that may be a symlink
- * s = full size of p
- *
- * If p is a symlink, attempt to overwrite p with a path to the real
- * file or directory (which may or may not exist), following a chain of
- * symlinks if necessary. Otherwise, leave p unmodified.
+ * path contains a path that might be a symlink.
*
- * This is a best-effort routine. If an error occurs, p will either be
- * left unmodified or will name a different symlink in a symlink chain
- * that started with p's initial contents.
+ * If path is a symlink, attempt to overwrite it with a path to the
+ * real file or directory (which may or may not exist), following a
+ * chain of symlinks if necessary. Otherwise, leave path unmodified.
*
- * Always returns p.
+ * This is a best-effort routine. If an error occurs, path will
+ * either be left unmodified or will name a different symlink in a
+ * symlink chain that started with the original path.
*/
-
-static char *resolve_symlink(char *p, size_t s)
+static void resolve_symlink(struct strbuf *path)
{
int depth = MAXDEPTH;
+ static struct strbuf link = STRBUF_INIT;
while (depth--) {
- char link[PATH_MAX];
- int link_len = readlink(p, link, sizeof(link));
- if (link_len < 0) {
- /* not a symlink anymore */
- return p;
- }
- else if (link_len < sizeof(link))
- /* readlink() never null-terminates */
- link[link_len] = '\0';
- else {
- warning("%s: symlink too long", p);
- return p;
- }
+ if (strbuf_readlink(&link, path->buf, path->len) < 0)
+ break;
- if (is_absolute_path(link)) {
+ if (is_absolute_path(link.buf))
/* absolute path simply replaces p */
- if (link_len < s)
- strcpy(p, link);
- else {
- warning("%s: symlink too long", p);
- return p;
- }
- } else {
+ strbuf_reset(path);
+ else
/*
- * link is a relative path, so I must replace the
+ * link is a relative path, so replace the
* last element of p with it.
*/
- char *r = (char *)last_path_elm(p);
- if (r - p + link_len < s)
- strcpy(r, link);
- else {
- warning("%s: symlink too long", p);
- return p;
- }
- }
+ trim_last_path_component(path);
+
+ strbuf_addbuf(path, &link);
}
- return p;
+ strbuf_reset(&link);
}
/* Make sure errno contains a meaningful value on error */
static int lock_file(struct lock_file *lk, const char *path, int flags)
{
- /*
- * subtract 5 from size to make sure there's room for adding
- * ".lock" for the lock file name
- */
- static const size_t max_path_len = sizeof(lk->filename) - 5;
+ size_t pathlen = strlen(path);
+
+ if (!lock_file_list) {
+ /* One-time initialization */
+ sigchain_push_common(remove_lock_files_on_signal);
+ atexit(remove_lock_files_on_exit);
+ }
- if (strlen(path) >= max_path_len) {
- errno = ENAMETOOLONG;
+ if (lk->active)
+ die("BUG: cannot lock_file(\"%s\") using active struct lock_file",
+ path);
+ if (!lk->on_list) {
+ /* Initialize *lk and add it to lock_file_list: */
+ lk->fd = -1;
+ lk->fp = NULL;
+ lk->active = 0;
+ lk->owner = 0;
+ strbuf_init(&lk->filename, pathlen + LOCK_SUFFIX_LEN);
+ lk->next = lock_file_list;
+ lock_file_list = lk;
+ lk->on_list = 1;
+ } else if (lk->filename.len) {
+ /* This shouldn't happen, but better safe than sorry. */
+ die("BUG: lock_file(\"%s\") called with improperly-reset lock_file object",
+ path);
+ }
+
+ strbuf_add(&lk->filename, path, pathlen);
+ if (!(flags & LOCK_NO_DEREF))
+ resolve_symlink(&lk->filename);
+ strbuf_addstr(&lk->filename, LOCK_SUFFIX);
+ lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (lk->fd < 0) {
+ strbuf_reset(&lk->filename);
return -1;
}
- strcpy(lk->filename, path);
- if (!(flags & LOCK_NODEREF))
- resolve_symlink(lk->filename, max_path_len);
- strcat(lk->filename, ".lock");
- lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
- if (0 <= lk->fd) {
- if (!lock_file_list) {
- sigchain_push_common(remove_lock_file_on_signal);
- atexit(remove_lock_file);
- }
- lk->owner = getpid();
- if (!lk->on_list) {
- lk->next = lock_file_list;
- lock_file_list = lk;
- lk->on_list = 1;
- }
- if (adjust_shared_perm(lk->filename)) {
- int save_errno = errno;
- error("cannot fix permission bits on %s",
- lk->filename);
- errno = save_errno;
- return -1;
- }
+ lk->owner = getpid();
+ lk->active = 1;
+ if (adjust_shared_perm(lk->filename.buf)) {
+ int save_errno = errno;
+ error("cannot fix permission bits on %s", lk->filename.buf);
+ rollback_lock_file(lk);
+ errno = save_errno;
+ return -1;
}
- else
- lk->filename[0] = 0;
return lk->fd;
}
@@ -175,17 +162,7 @@ void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
absolute_path(path), strerror(err));
}
-int unable_to_lock_error(const char *path, int err)
-{
- struct strbuf buf = STRBUF_INIT;
-
- unable_to_lock_message(path, err, &buf);
- error("%s", buf.buf);
- strbuf_release(&buf);
- return -1;
-}
-
-NORETURN void unable_to_lock_index_die(const char *path, int err)
+NORETURN void unable_to_lock_die(const char *path, int err)
{
struct strbuf buf = STRBUF_INIT;
@@ -198,7 +175,7 @@ int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
{
int fd = lock_file(lk, path, flags);
if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
- unable_to_lock_index_die(path, errno);
+ unable_to_lock_die(path, errno);
return fd;
}
@@ -209,63 +186,147 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
fd = lock_file(lk, path, flags);
if (fd < 0) {
if (flags & LOCK_DIE_ON_ERROR)
- unable_to_lock_index_die(path, errno);
+ unable_to_lock_die(path, errno);
return fd;
}
orig_fd = open(path, O_RDONLY);
if (orig_fd < 0) {
if (errno != ENOENT) {
+ int save_errno = errno;
+
if (flags & LOCK_DIE_ON_ERROR)
die("cannot open '%s' for copying", path);
- close(fd);
- return error("cannot open '%s' for copying", path);
+ rollback_lock_file(lk);
+ error("cannot open '%s' for copying", path);
+ errno = save_errno;
+ return -1;
}
} else if (copy_fd(orig_fd, fd)) {
+ int save_errno = errno;
+
if (flags & LOCK_DIE_ON_ERROR)
exit(128);
- close(fd);
+ close(orig_fd);
+ rollback_lock_file(lk);
+ errno = save_errno;
return -1;
+ } else {
+ close(orig_fd);
}
return fd;
}
+FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
+{
+ if (!lk->active)
+ die("BUG: fdopen_lock_file() called for unlocked object");
+ if (lk->fp)
+ die("BUG: fdopen_lock_file() called twice for file '%s'", lk->filename.buf);
+
+ lk->fp = fdopen(lk->fd, mode);
+ return lk->fp;
+}
+
+char *get_locked_file_path(struct lock_file *lk)
+{
+ if (!lk->active)
+ die("BUG: get_locked_file_path() called for unlocked object");
+ if (lk->filename.len <= LOCK_SUFFIX_LEN)
+ die("BUG: get_locked_file_path() called for malformed lock object");
+ return xmemdupz(lk->filename.buf, lk->filename.len - LOCK_SUFFIX_LEN);
+}
+
int close_lock_file(struct lock_file *lk)
{
int fd = lk->fd;
+ FILE *fp = lk->fp;
+ int err;
+
+ if (fd < 0)
+ return 0;
+
lk->fd = -1;
- return close(fd);
+ if (fp) {
+ lk->fp = NULL;
+
+ /*
+ * Note: no short-circuiting here; we want to fclose()
+ * in any case!
+ */
+ err = ferror(fp) | fclose(fp);
+ } else {
+ err = close(fd);
+ }
+
+ if (err) {
+ int save_errno = errno;
+ rollback_lock_file(lk);
+ errno = save_errno;
+ return -1;
+ }
+
+ return 0;
}
-int commit_lock_file(struct lock_file *lk)
+int reopen_lock_file(struct lock_file *lk)
+{
+ if (0 <= lk->fd)
+ die(_("BUG: reopen a lockfile that is still open"));
+ if (!lk->active)
+ die(_("BUG: reopen a lockfile that has been committed"));
+ lk->fd = open(lk->filename.buf, O_WRONLY);
+ return lk->fd;
+}
+
+int commit_lock_file_to(struct lock_file *lk, const char *path)
{
- char result_file[PATH_MAX];
- size_t i;
- if (lk->fd >= 0 && close_lock_file(lk))
+ if (!lk->active)
+ die("BUG: attempt to commit unlocked object to \"%s\"", path);
+
+ if (close_lock_file(lk))
return -1;
- strcpy(result_file, lk->filename);
- i = strlen(result_file) - 5; /* .lock */
- result_file[i] = 0;
- if (rename(lk->filename, result_file))
+
+ if (rename(lk->filename.buf, path)) {
+ int save_errno = errno;
+ rollback_lock_file(lk);
+ errno = save_errno;
return -1;
- lk->filename[0] = 0;
+ }
+
+ lk->active = 0;
+ strbuf_reset(&lk->filename);
return 0;
}
-int hold_locked_index(struct lock_file *lk, int die_on_error)
+int commit_lock_file(struct lock_file *lk)
{
- return hold_lock_file_for_update(lk, get_index_file(),
- die_on_error
- ? LOCK_DIE_ON_ERROR
- : 0);
+ static struct strbuf result_file = STRBUF_INIT;
+ int err;
+
+ if (!lk->active)
+ die("BUG: attempt to commit unlocked object");
+
+ if (lk->filename.len <= LOCK_SUFFIX_LEN ||
+ strcmp(lk->filename.buf + lk->filename.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
+ die("BUG: lockfile filename corrupt");
+
+ /* remove ".lock": */
+ strbuf_add(&result_file, lk->filename.buf,
+ lk->filename.len - LOCK_SUFFIX_LEN);
+ err = commit_lock_file_to(lk, result_file.buf);
+ strbuf_reset(&result_file);
+ return err;
}
void rollback_lock_file(struct lock_file *lk)
{
- if (lk->filename[0]) {
- if (lk->fd >= 0)
- close(lk->fd);
- unlink_or_warn(lk->filename);
+ if (!lk->active)
+ return;
+
+ if (!close_lock_file(lk)) {
+ unlink_or_warn(lk->filename.buf);
+ lk->active = 0;
+ strbuf_reset(&lk->filename);
}
- lk->filename[0] = 0;
}
diff --git a/lockfile.h b/lockfile.h
new file mode 100644
index 0000000000..cd2ec95d30
--- /dev/null
+++ b/lockfile.h
@@ -0,0 +1,87 @@
+#ifndef LOCKFILE_H
+#define LOCKFILE_H
+
+/*
+ * File write-locks as used by Git.
+ *
+ * For an overview of how to use the lockfile API, please see
+ *
+ * Documentation/technical/api-lockfile.txt
+ *
+ * This module keeps track of all locked files in lock_file_list for
+ * use at cleanup. This list and the lock_file objects that comprise
+ * it must be kept in self-consistent states at all time, because the
+ * program can be interrupted any time by a signal, in which case the
+ * signal handler will walk through the list attempting to clean up
+ * any open lock files.
+ *
+ * A lockfile is owned by the process that created it. The lock_file
+ * object has an "owner" field that records its owner. This field is
+ * used to prevent a forked process from closing a lockfile created by
+ * its parent.
+ *
+ * The possible states of a lock_file object are as follows:
+ *
+ * - Uninitialized. In this state the object's on_list field must be
+ * zero but the rest of its contents need not be initialized. As
+ * soon as the object is used in any way, it is irrevocably
+ * registered in the lock_file_list, and on_list is set.
+ *
+ * - Locked, lockfile open (after hold_lock_file_for_update(),
+ * hold_lock_file_for_append(), or reopen_lock_file()). In this
+ * state:
+ * - the lockfile exists
+ * - active is set
+ * - filename holds the filename of the lockfile
+ * - fd holds a file descriptor open for writing to the lockfile
+ * - fp holds a pointer to an open FILE object if and only if
+ * fdopen_lock_file() has been called on the object
+ * - owner holds the PID of the process that locked the file
+ *
+ * - Locked, lockfile closed (after successful close_lock_file()).
+ * Same as the previous state, except that the lockfile is closed
+ * and fd is -1.
+ *
+ * - Unlocked (after commit_lock_file(), commit_lock_file_to(),
+ * rollback_lock_file(), a failed attempt to lock, or a failed
+ * close_lock_file()). In this state:
+ * - active is unset
+ * - filename is empty (usually, though there are transitory
+ * states in which this condition doesn't hold). Client code should
+ * *not* rely on the filename being empty in this state.
+ * - fd is -1
+ * - the object is left registered in the lock_file_list, and
+ * on_list is set.
+ */
+
+struct lock_file {
+ struct lock_file *volatile next;
+ volatile sig_atomic_t active;
+ volatile int fd;
+ FILE *volatile fp;
+ volatile pid_t owner;
+ char on_list;
+ struct strbuf filename;
+};
+
+/* String appended to a filename to derive the lockfile name: */
+#define LOCK_SUFFIX ".lock"
+#define LOCK_SUFFIX_LEN 5
+
+#define LOCK_DIE_ON_ERROR 1
+#define LOCK_NO_DEREF 2
+
+extern void unable_to_lock_message(const char *path, int err,
+ struct strbuf *buf);
+extern NORETURN void unable_to_lock_die(const char *path, int err);
+extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
+extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
+extern FILE *fdopen_lock_file(struct lock_file *, const char *mode);
+extern char *get_locked_file_path(struct lock_file *);
+extern int commit_lock_file_to(struct lock_file *, const char *path);
+extern int commit_lock_file(struct lock_file *);
+extern int reopen_lock_file(struct lock_file *);
+extern int close_lock_file(struct lock_file *);
+extern void rollback_lock_file(struct lock_file *);
+
+#endif /* LOCKFILE_H */
diff --git a/log-tree.c b/log-tree.c
index 95e9b1da25..7f0890e4ac 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -12,17 +12,7 @@
#include "sequencer.h"
#include "line-log.h"
-struct decoration name_decoration = { "object names" };
-
-enum decoration_type {
- DECORATION_NONE = 0,
- DECORATION_REF_LOCAL,
- DECORATION_REF_REMOTE,
- DECORATION_REF_TAG,
- DECORATION_REF_STASH,
- DECORATION_REF_HEAD,
- DECORATION_GRAFTED,
-};
+static struct decoration name_decoration = { "object names" };
static char decoration_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
@@ -66,15 +56,14 @@ static int parse_decorate_color_slot(const char *slot)
return -1;
}
-int parse_decorate_color_config(const char *var, const int ofs, const char *value)
+int parse_decorate_color_config(const char *var, const char *slot_name, const char *value)
{
- int slot = parse_decorate_color_slot(var + ofs);
+ int slot = parse_decorate_color_slot(slot_name);
if (slot < 0)
return 0;
if (!value)
return config_error_nonbool(var);
- color_parse(value, var, decoration_colors[slot]);
- return 0;
+ return color_parse(value, decoration_colors[slot]);
}
/*
@@ -84,15 +73,20 @@ int parse_decorate_color_config(const char *var, const int ofs, const char *valu
#define decorate_get_color_opt(o, ix) \
decorate_get_color((o)->use_color, ix)
-static void add_name_decoration(enum decoration_type type, const char *name, struct object *obj)
+void add_name_decoration(enum decoration_type type, const char *name, struct object *obj)
{
int nlen = strlen(name);
- struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + nlen);
+ struct name_decoration *res = xmalloc(sizeof(*res) + nlen + 1);
memcpy(res->name, name, nlen + 1);
res->type = type;
res->next = add_decoration(&name_decoration, obj, res);
}
+const struct name_decoration *get_name_decoration(const struct object *obj)
+{
+ return lookup_decoration(&name_decoration, obj);
+}
+
static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
struct object *obj;
@@ -179,24 +173,25 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre
}
/*
- * The caller makes sure there is no funny color before
- * calling. format_decorations makes sure the same after return.
+ * The caller makes sure there is no funny color before calling.
+ * format_decorations_extended makes sure the same after return.
*/
-void format_decorations(struct strbuf *sb,
+void format_decorations_extended(struct strbuf *sb,
const struct commit *commit,
- int use_color)
+ int use_color,
+ const char *prefix,
+ const char *separator,
+ const char *suffix)
{
- const char *prefix;
- struct name_decoration *decoration;
+ const struct name_decoration *decoration;
const char *color_commit =
diff_get_color(use_color, DIFF_COMMIT);
const char *color_reset =
decorate_get_color(use_color, DECORATION_NONE);
- decoration = lookup_decoration(&name_decoration, &commit->object);
+ decoration = get_name_decoration(&commit->object);
if (!decoration)
return;
- prefix = " (";
while (decoration) {
strbuf_addstr(sb, color_commit);
strbuf_addstr(sb, prefix);
@@ -205,11 +200,11 @@ void format_decorations(struct strbuf *sb,
strbuf_addstr(sb, "tag: ");
strbuf_addstr(sb, decoration->name);
strbuf_addstr(sb, color_reset);
- prefix = ", ";
+ prefix = separator;
decoration = decoration->next;
}
strbuf_addstr(sb, color_commit);
- strbuf_addch(sb, ')');
+ strbuf_addstr(sb, suffix);
strbuf_addstr(sb, color_reset);
}
diff --git a/log-tree.h b/log-tree.h
index d6ecd4dc46..c8116e60cd 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -7,13 +7,19 @@ struct log_info {
struct commit *commit, *parent;
};
-int parse_decorate_color_config(const char *var, const int ofs, const char *value);
+int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
void init_log_tree_opt(struct rev_info *);
int log_tree_diff_flush(struct rev_info *);
int log_tree_commit(struct rev_info *, struct commit *);
int log_tree_opt_parse(struct rev_info *, const char **, int);
void show_log(struct rev_info *opt);
-void format_decorations(struct strbuf *sb, const struct commit *commit, int use_color);
+void format_decorations_extended(struct strbuf *sb, const struct commit *commit,
+ int use_color,
+ const char *prefix,
+ const char *separator,
+ const char *suffix);
+#define format_decorations(strbuf, commit, color) \
+ format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")")
void show_decorations(struct rev_info *opt, struct commit *commit);
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
const char **subject_p,
diff --git a/merge-recursive.c b/merge-recursive.c
index 1d332b8bbb..fdb7d0f10b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,8 +3,9 @@
* Fredrik Kuivinen.
* The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
*/
-#include "advice.h"
#include "cache.h"
+#include "advice.h"
+#include "lockfile.h"
#include "cache-tree.h"
#include "commit.h"
#include "blob.h"
@@ -163,9 +164,7 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)
if (!show(o, v))
return;
- strbuf_grow(&o->obuf, o->call_depth * 2 + 2);
- memset(o->obuf.buf + o->obuf.len, ' ', o->call_depth * 2);
- strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2);
+ strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
va_start(ap, fmt);
strbuf_vaddf(&o->obuf, fmt, ap);
@@ -1557,7 +1556,7 @@ static int blob_unchanged(const unsigned char *o_sha,
* unchanged since their sha1s have already been compared.
*/
if (renormalize_buffer(path, o.buf, o.len, &o) |
- renormalize_buffer(path, a.buf, o.len, &a))
+ renormalize_buffer(path, a.buf, a.len, &a))
ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
error_return:
@@ -1688,10 +1687,6 @@ static int merge_content(struct merge_options *o,
static int process_entry(struct merge_options *o,
const char *path, struct stage_data *entry)
{
- /*
- printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
- print_index_entry("\tpath: ", entry);
- */
int clean_merge = 1;
int normalize = o->renormalize;
unsigned o_mode = entry->stages[1].mode;
@@ -2026,22 +2021,12 @@ int merge_recursive_generic(struct merge_options *o,
return clean ? 0 : 1;
}
-static int merge_recursive_config(const char *var, const char *value, void *cb)
+static void merge_recursive_config(struct merge_options *o)
{
- struct merge_options *o = cb;
- if (!strcmp(var, "merge.verbosity")) {
- o->verbosity = git_config_int(var, value);
- return 0;
- }
- if (!strcmp(var, "diff.renamelimit")) {
- o->diff_rename_limit = git_config_int(var, value);
- return 0;
- }
- if (!strcmp(var, "merge.renamelimit")) {
- o->merge_rename_limit = git_config_int(var, value);
- return 0;
- }
- return git_xmerge_config(var, value, cb);
+ git_config_get_int("merge.verbosity", &o->verbosity);
+ git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
+ git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
+ git_config(git_xmerge_config, NULL);
}
void init_merge_options(struct merge_options *o)
@@ -2052,7 +2037,7 @@ void init_merge_options(struct merge_options *o)
o->diff_rename_limit = -1;
o->merge_rename_limit = -1;
o->renormalize = 0;
- git_config(merge_recursive_config, o);
+ merge_recursive_config(o);
if (getenv("GIT_MERGE_VERBOSITY"))
o->verbosity =
strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
diff --git a/merge.c b/merge.c
index 74ced7f70b..fcff632bd6 100644
--- a/merge.c
+++ b/merge.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "commit.h"
#include "run-command.h"
#include "resolve-undo.h"
diff --git a/mergetools/bc b/mergetools/bc
new file mode 100644
index 0000000000..b6319d206e
--- /dev/null
+++ b/mergetools/bc
@@ -0,0 +1,25 @@
+diff_cmd () {
+ "$merge_tool_path" "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+ touch "$BACKUP"
+ if $base_present
+ then
+ "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" \
+ -mergeoutput="$MERGED"
+ else
+ "$merge_tool_path" "$LOCAL" "$REMOTE" \
+ -mergeoutput="$MERGED"
+ fi
+ check_unchanged
+}
+
+translate_merge_tool_path() {
+ if type bcomp >/dev/null 2>/dev/null
+ then
+ echo bcomp
+ else
+ echo bcompare
+ fi
+}
diff --git a/mergetools/bc3 b/mergetools/bc3
index b6319d206e..5d8dd48184 100644
--- a/mergetools/bc3
+++ b/mergetools/bc3
@@ -1,25 +1 @@
-diff_cmd () {
- "$merge_tool_path" "$LOCAL" "$REMOTE"
-}
-
-merge_cmd () {
- touch "$BACKUP"
- if $base_present
- then
- "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" \
- -mergeoutput="$MERGED"
- else
- "$merge_tool_path" "$LOCAL" "$REMOTE" \
- -mergeoutput="$MERGED"
- fi
- check_unchanged
-}
-
-translate_merge_tool_path() {
- if type bcomp >/dev/null 2>/dev/null
- then
- echo bcomp
- else
- echo bcompare
- fi
-}
+. "$MERGE_TOOLS_DIR/bc"
diff --git a/mergetools/meld b/mergetools/meld
index cb672a5519..83ebdfb4c3 100644
--- a/mergetools/meld
+++ b/mergetools/meld
@@ -18,13 +18,18 @@ merge_cmd () {
check_unchanged
}
-# Check whether 'meld --output <file>' is supported
+# Check whether we should use 'meld --output <file>'
check_meld_for_output_version () {
meld_path="$(git config mergetool.meld.path)"
meld_path="${meld_path:-meld}"
- if "$meld_path" --help 2>&1 | grep -e --output >/dev/null
+ if meld_has_output_option=$(git config --bool mergetool.meld.hasOutput)
then
+ : use configured value
+ elif "$meld_path" --help 2>&1 |
+ grep -e '--output=' -e '\[OPTION\.\.\.\]' >/dev/null
+ then
+ : old ones mention --output and new ones just say OPTION...
meld_has_output_option=true
else
meld_has_output_option=false
diff --git a/notes-merge.c b/notes-merge.c
index fd5fae255d..7eb9d7a010 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -549,7 +549,7 @@ int notes_merge(struct notes_merge_options *o,
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (read_ref_full(o->local_ref, local_sha1, 0, NULL))
+ if (read_ref_full(o->local_ref, 0, local_sha1, 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))
diff --git a/object.c b/object.c
index a16b9f9e93..23d6c96719 100644
--- a/object.c
+++ b/object.c
@@ -33,13 +33,20 @@ const char *typename(unsigned int type)
return object_type_strings[type];
}
-int type_from_string(const char *str)
+int type_from_string_gently(const char *str, ssize_t len, int gentle)
{
int i;
+ if (len < 0)
+ len = strlen(str);
+
for (i = 1; i < ARRAY_SIZE(object_type_strings); i++)
- if (!strcmp(str, object_type_strings[i]))
+ if (!strncmp(str, object_type_strings[i], len))
return i;
+
+ if (gentle)
+ return -1;
+
die("invalid object type \"%s\"", str);
}
@@ -300,10 +307,9 @@ int object_list_contains(struct object_list *list, struct object *obj)
*/
static char object_array_slopbuf[1];
-static void add_object_array_with_mode_context(struct object *obj, const char *name,
- struct object_array *array,
- unsigned mode,
- struct object_context *context)
+void add_object_array_with_path(struct object *obj, const char *name,
+ struct object_array *array,
+ unsigned mode, const char *path)
{
unsigned nr = array->nr;
unsigned alloc = array->alloc;
@@ -312,7 +318,7 @@ static void add_object_array_with_mode_context(struct object *obj, const char *n
if (nr >= alloc) {
alloc = (alloc + 32) * 2;
- objects = xrealloc(objects, alloc * sizeof(*objects));
+ REALLOC_ARRAY(objects, alloc);
array->alloc = alloc;
array->objects = objects;
}
@@ -326,26 +332,27 @@ static void add_object_array_with_mode_context(struct object *obj, const char *n
else
entry->name = xstrdup(name);
entry->mode = mode;
- entry->context = context;
+ if (path)
+ entry->path = xstrdup(path);
+ else
+ entry->path = NULL;
array->nr = ++nr;
}
void add_object_array(struct object *obj, const char *name, struct object_array *array)
{
- add_object_array_with_mode(obj, name, array, S_IFINVALID);
-}
-
-void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode)
-{
- add_object_array_with_mode_context(obj, name, array, mode, NULL);
+ add_object_array_with_path(obj, name, array, S_IFINVALID, NULL);
}
-void add_object_array_with_context(struct object *obj, const char *name, struct object_array *array, struct object_context *context)
+/*
+ * Free all memory associated with an entry; the result is
+ * in an unspecified state and should not be examined.
+ */
+static void object_array_release_entry(struct object_array_entry *ent)
{
- if (context)
- add_object_array_with_mode_context(obj, name, array, context->mode, context);
- else
- add_object_array_with_mode_context(obj, name, array, S_IFINVALID, context);
+ if (ent->name != object_array_slopbuf)
+ free(ent->name);
+ free(ent->path);
}
void object_array_filter(struct object_array *array,
@@ -360,13 +367,22 @@ void object_array_filter(struct object_array *array,
objects[dst] = objects[src];
dst++;
} else {
- if (objects[src].name != object_array_slopbuf)
- free(objects[src].name);
+ object_array_release_entry(&objects[src]);
}
}
array->nr = dst;
}
+void object_array_clear(struct object_array *array)
+{
+ int i;
+ for (i = 0; i < array->nr; i++)
+ object_array_release_entry(&array->objects[i]);
+ free(array->objects);
+ array->objects = NULL;
+ array->nr = array->alloc = 0;
+}
+
/*
* Return true iff array already contains an entry with name.
*/
@@ -393,8 +409,7 @@ void object_array_remove_duplicates(struct object_array *array)
objects[array->nr] = objects[src];
array->nr++;
} else {
- if (objects[src].name != object_array_slopbuf)
- free(objects[src].name);
+ object_array_release_entry(&objects[src]);
}
}
}
diff --git a/object.h b/object.h
index 5e8d8ee548..6416247def 100644
--- a/object.h
+++ b/object.h
@@ -18,8 +18,8 @@ struct object_array {
* empty string.
*/
char *name;
+ char *path;
unsigned mode;
- struct object_context *context;
} *objects;
};
@@ -53,7 +53,8 @@ struct object {
};
extern const char *typename(unsigned int type);
-extern int type_from_string(const char *str);
+extern int type_from_string_gently(const char *str, ssize_t, int gentle);
+#define type_from_string(str) type_from_string_gently(str, -1, 0)
/*
* Return the current number of buckets in the object hashmap.
@@ -113,8 +114,7 @@ int object_list_contains(struct object_list *list, struct object *obj);
/* Object array handling .. */
void add_object_array(struct object *obj, const char *name, struct object_array *array);
-void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);
-void add_object_array_with_context(struct object *obj, const char *name, struct object_array *array, struct object_context *context);
+void add_object_array_with_path(struct object *obj, const char *name, struct object_array *array, unsigned mode, const char *path);
typedef int (*object_array_each_func_t)(struct object_array_entry *, void *);
@@ -132,6 +132,12 @@ void object_array_filter(struct object_array *array,
*/
void object_array_remove_duplicates(struct object_array *array);
+/*
+ * Remove any objects from the array, freeing all used memory; afterwards
+ * the array is ready to store more objects with add_object_array().
+ */
+void object_array_clear(struct object_array *array);
+
void clear_object_flags(unsigned flags);
#endif /* OBJECT_H */
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 5f1791a59c..8029ae3561 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -111,8 +111,7 @@ static inline void push_bitmapped_commit(struct commit *commit, struct ewah_bitm
{
if (writer.selected_nr >= writer.selected_alloc) {
writer.selected_alloc = (writer.selected_alloc + 32) * 2;
- writer.selected = xrealloc(writer.selected,
- writer.selected_alloc * sizeof(struct bitmapped_commit));
+ REALLOC_ARRAY(writer.selected, writer.selected_alloc);
}
writer.selected[writer.selected_nr].commit = commit;
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 91e4101531..a1f3c0d34f 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -400,10 +400,8 @@ static int ext_index_add_object(struct object *object, const char *name)
if (hash_ret > 0) {
if (eindex->count >= eindex->alloc) {
eindex->alloc = (eindex->alloc + 16) * 3 / 2;
- eindex->objects = xrealloc(eindex->objects,
- eindex->alloc * sizeof(struct object *));
- eindex->hashes = xrealloc(eindex->hashes,
- eindex->alloc * sizeof(uint32_t));
+ REALLOC_ARRAY(eindex->objects, eindex->alloc);
+ REALLOC_ARRAY(eindex->hashes, eindex->alloc);
}
bitmap_pos = eindex->count;
diff --git a/pack-objects.c b/pack-objects.c
index 9992f3ecf2..6398a8aa96 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -92,8 +92,7 @@ struct object_entry *packlist_alloc(struct packing_data *pdata,
if (pdata->nr_objects >= pdata->nr_alloc) {
pdata->nr_alloc = (pdata->nr_alloc + 1024) * 3 / 2;
- pdata->objects = xrealloc(pdata->objects,
- pdata->nr_alloc * sizeof(*new_entry));
+ REALLOC_ARRAY(pdata->objects, pdata->nr_alloc);
}
new_entry = pdata->objects + pdata->nr_objects++;
diff --git a/pack-write.c b/pack-write.c
index 9ccf80419b..33293ce2a6 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -288,13 +288,12 @@ char *index_pack_lockfile(int ip_out)
* case, we need it to remove the corresponding .keep file
* later on. If we don't get that then tough luck with it.
*/
- if (read_in_full(ip_out, packname, 46) == 46 && packname[45] == '\n' &&
- memcmp(packname, "keep\t", 5) == 0) {
- char path[PATH_MAX];
+ if (read_in_full(ip_out, packname, 46) == 46 && packname[45] == '\n') {
+ const char *name;
packname[45] = 0;
- snprintf(path, sizeof(path), "%s/pack/pack-%s.keep",
- get_object_directory(), packname + 5);
- return xstrdup(path);
+ if (skip_prefix(packname, "keep\t", &name))
+ return xstrfmt("%s/pack/pack-%s.keep",
+ get_object_directory(), name);
}
return NULL;
}
diff --git a/pager.c b/pager.c
index 8b5cbc56e4..f6e8c33192 100644
--- a/pager.c
+++ b/pager.c
@@ -6,19 +6,13 @@
#define DEFAULT_PAGER "less"
#endif
-struct pager_config {
- const char *cmd;
- int want;
- char *value;
-};
-
/*
* This is split up from the rest of git so that we can do
* something different on Windows.
*/
static const char *pager_argv[] = { NULL, NULL };
-static struct child_process pager_process;
+static struct child_process pager_process = CHILD_PROCESS_INIT;
static void wait_for_pager(void)
{
@@ -80,17 +74,10 @@ void setup_pager(void)
pager_process.use_shell = 1;
pager_process.argv = pager_argv;
pager_process.in = -1;
- if (!getenv("LESS") || !getenv("LV")) {
- static const char *env[3];
- int i = 0;
-
- if (!getenv("LESS"))
- env[i++] = "LESS=FRX";
- if (!getenv("LV"))
- env[i++] = "LV=-c";
- env[i] = NULL;
- pager_process.env = env;
- }
+ if (!getenv("LESS"))
+ argv_array_push(&pager_process.env_array, "LESS=FRX");
+ if (!getenv("LV"))
+ argv_array_push(&pager_process.env_array, "LV=-c");
if (start_command(&pager_process))
return;
@@ -155,30 +142,22 @@ int decimal_width(int number)
return width;
}
-static int pager_command_config(const char *var, const char *value, void *data)
+/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
+int check_pager_config(const char *cmd)
{
- struct pager_config *c = data;
- if (starts_with(var, "pager.") && !strcmp(var + 6, c->cmd)) {
- int b = git_config_maybe_bool(var, value);
+ int want = -1;
+ struct strbuf key = STRBUF_INIT;
+ const char *value = NULL;
+ strbuf_addf(&key, "pager.%s", cmd);
+ if (!git_config_get_value(key.buf, &value)) {
+ int b = git_config_maybe_bool(key.buf, value);
if (b >= 0)
- c->want = b;
+ want = b;
else {
- c->want = 1;
- c->value = xstrdup(value);
+ want = 1;
+ pager_program = xstrdup(value);
}
}
- return 0;
-}
-
-/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
-int check_pager_config(const char *cmd)
-{
- struct pager_config c;
- c.cmd = cmd;
- c.want = -1;
- c.value = NULL;
- git_config(pager_command_config, &c);
- if (c.value)
- pager_program = c.value;
- return c.want;
+ strbuf_release(&key);
+ return want;
}
diff --git a/parse-options.c b/parse-options.c
index e7dafa80d5..80106c06bc 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -14,8 +14,12 @@ static int parse_options_usage(struct parse_opt_ctx_t *ctx,
int optbug(const struct option *opt, const char *reason)
{
- if (opt->long_name)
+ if (opt->long_name) {
+ if (opt->short_name)
+ return error("BUG: switch '%c' (--%s) %s",
+ opt->short_name, opt->long_name, reason);
return error("BUG: option '%s' %s", opt->long_name, reason);
+ }
return error("BUG: switch '%c' %s", opt->short_name, reason);
}
@@ -347,12 +351,20 @@ static void check_typos(const char *arg, const struct option *options)
static void parse_options_check(const struct option *opts)
{
int err = 0;
+ char short_opts[128];
+ memset(short_opts, '\0', sizeof(short_opts));
for (; opts->type != OPTION_END; opts++) {
if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
(opts->flags & PARSE_OPT_OPTARG))
err |= optbug(opts, "uses incompatible flags "
"LASTARG_DEFAULT and OPTARG");
+ if (opts->short_name) {
+ if (0x7F <= opts->short_name)
+ err |= optbug(opts, "invalid short name");
+ else if (short_opts[opts->short_name]++)
+ err |= optbug(opts, "short name already used");
+ }
if (opts->flags & PARSE_OPT_NODASH &&
((opts->flags & PARSE_OPT_OPTARG) ||
!(opts->flags & PARSE_OPT_NOARG) ||
diff --git a/path.c b/path.c
index 3afcdb432a..f68df0cf88 100644
--- a/path.c
+++ b/path.c
@@ -148,10 +148,12 @@ void home_config_paths(char **global, char **xdg, char *file)
*global = mkpathdup("%s/.gitconfig", home);
}
- if (!xdg_home)
- *xdg = NULL;
- else
- *xdg = mkpathdup("%s/git/%s", xdg_home, file);
+ if (xdg) {
+ if (!xdg_home)
+ *xdg = NULL;
+ else
+ *xdg = mkpathdup("%s/git/%s", xdg_home, file);
+ }
free(to_free);
}
diff --git a/pretty.c b/pretty.c
index 31fc76b2fd..9d34d02db1 100644
--- a/pretty.c
+++ b/pretty.c
@@ -70,11 +70,12 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
commit_format->name = xstrdup(name);
commit_format->format = CMIT_FMT_USERFORMAT;
- git_config_string(&fmt, var, value);
- if (starts_with(fmt, "format:") || starts_with(fmt, "tformat:")) {
- commit_format->is_tformat = fmt[0] == 't';
- fmt = strchr(fmt, ':') + 1;
- } else if (strchr(fmt, '%'))
+ if (git_config_string(&fmt, var, value))
+ return -1;
+
+ if (skip_prefix(fmt, "format:", &fmt))
+ commit_format->is_tformat = 0;
+ else if (skip_prefix(fmt, "tformat:", &fmt) || strchr(fmt, '%'))
commit_format->is_tformat = 1;
else
commit_format->is_alias = 1;
@@ -155,12 +156,12 @@ void get_commit_format(const char *arg, struct rev_info *rev)
rev->commit_format = CMIT_FMT_DEFAULT;
return;
}
- if (starts_with(arg, "format:") || starts_with(arg, "tformat:")) {
- save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
+ if (skip_prefix(arg, "format:", &arg)) {
+ save_user_format(rev, arg, 0);
return;
}
- if (!*arg || strchr(arg, '%')) {
+ if (!*arg || skip_prefix(arg, "tformat:", &arg) || strchr(arg, '%')) {
save_user_format(rev, arg, 1);
return;
}
@@ -552,31 +553,11 @@ static void add_merge_info(const struct pretty_print_context *pp,
strbuf_addch(sb, '\n');
}
-static char *get_header(const struct commit *commit, const char *msg,
- const char *key)
+static char *get_header(const char *msg, const char *key)
{
- int key_len = strlen(key);
- const char *line = msg;
-
- while (line) {
- const char *eol = strchrnul(line, '\n'), *next;
-
- if (line == eol)
- return NULL;
- if (!*eol) {
- warning("malformed commit (header is missing newline): %s",
- sha1_to_hex(commit->object.sha1));
- next = NULL;
- } else
- next = eol + 1;
- if (eol - line > key_len &&
- !strncmp(line, key, key_len) &&
- line[key_len] == ' ') {
- return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
- }
- line = next;
- }
- return NULL;
+ size_t len;
+ const char *v = find_commit_header(msg, key, &len);
+ return v ? xmemdupz(v, len) : NULL;
}
static char *replace_encoding_header(char *buf, const char *encoding)
@@ -622,11 +603,10 @@ const char *logmsg_reencode(const struct commit *commit,
if (!output_encoding || !*output_encoding) {
if (commit_encoding)
- *commit_encoding =
- get_header(commit, msg, "encoding");
+ *commit_encoding = get_header(msg, "encoding");
return msg;
}
- encoding = get_header(commit, msg, "encoding");
+ encoding = get_header(msg, "encoding");
if (commit_encoding)
*commit_encoding = encoding;
use_encoding = encoding ? encoding : utf8;
@@ -736,9 +716,12 @@ static size_t format_person_part(struct strbuf *sb, char part,
case 'r': /* date, relative */
strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
return placeholder_len;
- case 'i': /* date, ISO 8601 */
+ case 'i': /* date, ISO 8601-like */
strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
return placeholder_len;
+ case 'I': /* date, ISO 8601 strict */
+ strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601_STRICT));
+ return placeholder_len;
}
skip:
@@ -825,18 +808,19 @@ static void parse_commit_header(struct format_commit_context *context)
int i;
for (i = 0; msg[i]; i++) {
+ const char *name;
int eol;
for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
; /* do nothing */
if (i == eol) {
break;
- } else if (starts_with(msg + i, "author ")) {
- context->author.off = i + 7;
- context->author.len = eol - i - 7;
- } else if (starts_with(msg + i, "committer ")) {
- context->committer.off = i + 10;
- context->committer.len = eol - i - 10;
+ } else if (skip_prefix(msg + i, "author ", &name)) {
+ context->author.off = name - msg;
+ context->author.len = msg + eol - name;
+ } else if (skip_prefix(msg + i, "committer ", &name)) {
+ context->committer.off = name - msg;
+ context->committer.len = msg + eol - name;
}
i = eol;
}
@@ -967,6 +951,8 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
const char *placeholder,
struct format_commit_context *c)
{
+ const char *rest = placeholder;
+
if (placeholder[1] == '(') {
const char *begin = placeholder + 2;
const char *end = strchr(begin, ')');
@@ -974,31 +960,24 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
if (!end)
return 0;
- if (starts_with(begin, "auto,")) {
+ if (skip_prefix(begin, "auto,", &begin)) {
if (!want_color(c->pretty_ctx->color))
return end - placeholder + 1;
- begin += 5;
}
- color_parse_mem(begin,
- end - begin,
- "--pretty format", color);
+ if (color_parse_mem(begin, end - begin, color) < 0)
+ die(_("unable to parse --pretty format"));
strbuf_addstr(sb, color);
return end - placeholder + 1;
}
- if (starts_with(placeholder + 1, "red")) {
+ if (skip_prefix(placeholder + 1, "red", &rest))
strbuf_addstr(sb, GIT_COLOR_RED);
- return 4;
- } else if (starts_with(placeholder + 1, "green")) {
+ else if (skip_prefix(placeholder + 1, "green", &rest))
strbuf_addstr(sb, GIT_COLOR_GREEN);
- return 6;
- } else if (starts_with(placeholder + 1, "blue")) {
+ else if (skip_prefix(placeholder + 1, "blue", &rest))
strbuf_addstr(sb, GIT_COLOR_BLUE);
- return 5;
- } else if (starts_with(placeholder + 1, "reset")) {
+ else if (skip_prefix(placeholder + 1, "reset", &rest))
strbuf_addstr(sb, GIT_COLOR_RESET);
- return 6;
- } else
- return 0;
+ return rest - placeholder;
}
static size_t parse_padding_placeholder(struct strbuf *sb,
@@ -1195,6 +1174,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
load_ref_decorations(DECORATE_SHORT_REFS);
format_decorations(sb, commit, c->auto_color);
return 1;
+ case 'D':
+ load_ref_decorations(DECORATE_SHORT_REFS);
+ format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
+ return 1;
case 'g': /* reflog info */
switch(placeholder[1]) {
case 'd': /* reflog selector */
@@ -1393,9 +1376,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
* convert it back to chars
*/
padding = padding - len + local_sb.len;
- strbuf_grow(sb, padding);
- strbuf_setlen(sb, sb_len + padding);
- memset(sb->buf + sb_len, ' ', sb->len - sb_len);
+ strbuf_addchars(sb, ' ', padding);
memcpy(sb->buf + sb_len + offset, local_sb.buf,
local_sb.len);
}
@@ -1536,7 +1517,7 @@ static void pp_header(struct pretty_print_context *pp,
int parents_shown = 0;
for (;;) {
- const char *line = *msg_p;
+ const char *name, *line = *msg_p;
int linelen = get_one_line(*msg_p);
if (!linelen)
@@ -1571,14 +1552,14 @@ static void pp_header(struct pretty_print_context *pp,
* FULL shows both authors but not dates.
* FULLER shows both authors and dates.
*/
- if (starts_with(line, "author ")) {
+ if (skip_prefix(line, "author ", &name)) {
strbuf_grow(sb, linelen + 80);
- pp_user_info(pp, "Author", sb, line + 7, encoding);
+ pp_user_info(pp, "Author", sb, name, encoding);
}
- if (starts_with(line, "committer ") &&
+ if (skip_prefix(line, "committer ", &name) &&
(pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
strbuf_grow(sb, linelen + 80);
- pp_user_info(pp, "Commit", sb, line + 10, encoding);
+ pp_user_info(pp, "Commit", sb, name, encoding);
}
}
}
@@ -1670,10 +1651,8 @@ void pp_remainder(struct pretty_print_context *pp,
first = 0;
strbuf_grow(sb, linelen + indent + 20);
- if (indent) {
- memset(sb->buf + sb->len, ' ', indent);
- strbuf_setlen(sb, sb->len + indent);
- }
+ if (indent)
+ strbuf_addchars(sb, ' ', indent);
strbuf_add(sb, line, linelen);
strbuf_addch(sb, '\n');
}
diff --git a/prompt.c b/prompt.c
index d7bb17cb66..e5b4938efc 100644
--- a/prompt.c
+++ b/prompt.c
@@ -6,7 +6,7 @@
static char *do_askpass(const char *cmd, const char *prompt)
{
- struct child_process pass;
+ struct child_process pass = CHILD_PROCESS_INIT;
const char *args[3];
static struct strbuf buffer = STRBUF_INIT;
int err = 0;
@@ -15,7 +15,6 @@ static char *do_askpass(const char *cmd, const char *prompt)
args[1] = prompt;
args[2] = NULL;
- memset(&pass, 0, sizeof(pass));
pass.argv = args;
pass.out = -1;
diff --git a/reachable.c b/reachable.c
index 6f6835bf27..a647267ae9 100644
--- a/reachable.c
+++ b/reachable.c
@@ -8,6 +8,7 @@
#include "reachable.h"
#include "cache-tree.h"
#include "progress.h"
+#include "list-objects.h"
struct connectivity_progress {
struct progress *progress;
@@ -21,196 +22,134 @@ static void update_progress(struct connectivity_progress *cp)
display_progress(cp->progress, cp->count);
}
-static void process_blob(struct blob *blob,
- struct object_array *p,
- struct name_path *path,
- const char *name,
- struct connectivity_progress *cp)
+static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
- struct object *obj = &blob->object;
+ struct object *object = parse_object_or_die(sha1, path);
+ struct rev_info *revs = (struct rev_info *)cb_data;
- if (!blob)
- die("bad blob object");
- if (obj->flags & SEEN)
- return;
- obj->flags |= SEEN;
- update_progress(cp);
- /* Nothing to do, really .. The blob lookup was the important part */
+ add_pending_object(revs, object, "");
+
+ return 0;
}
-static void process_gitlink(const unsigned char *sha1,
- struct object_array *p,
- struct name_path *path,
- const char *name)
+/*
+ * The traversal will have already marked us as SEEN, so we
+ * only need to handle any progress reporting here.
+ */
+static void mark_object(struct object *obj, const struct name_path *path,
+ const char *name, void *data)
{
- /* I don't think we want to recurse into this, really. */
+ update_progress(data);
}
-static void process_tree(struct tree *tree,
- struct object_array *p,
- struct name_path *path,
- const char *name,
- struct connectivity_progress *cp)
+static void mark_commit(struct commit *c, void *data)
{
- struct object *obj = &tree->object;
- struct tree_desc desc;
- struct name_entry entry;
- struct name_path me;
-
- if (!tree)
- die("bad tree object");
- 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);
- me.up = path;
- me.elem = name;
- me.elem_len = strlen(name);
-
- init_tree_desc(&desc, tree->buffer, tree->size);
-
- while (tree_entry(&desc, &entry)) {
- if (S_ISDIR(entry.mode))
- 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, cp);
- }
- free_tree_buffer(tree);
+ mark_object(&c->object, NULL, NULL, data);
}
-static void process_tag(struct tag *tag, struct object_array *p,
- const char *name, struct connectivity_progress *cp)
+struct recent_data {
+ struct rev_info *revs;
+ unsigned long timestamp;
+};
+
+static void add_recent_object(const unsigned char *sha1,
+ unsigned long mtime,
+ struct recent_data *data)
{
- struct object *obj = &tag->object;
+ struct object *obj;
+ enum object_type type;
- if (obj->flags & SEEN)
+ if (mtime <= data->timestamp)
return;
- obj->flags |= SEEN;
- update_progress(cp);
- if (parse_tag(tag) < 0)
- die("bad tag object %s", sha1_to_hex(obj->sha1));
- if (tag->tagged)
- add_object(tag->tagged, p, NULL, name);
-}
-
-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, "", cp);
- update_progress(cp);
- }
-
- /* Then walk all the pending objects, recursively processing them too */
- for (i = 0; i < revs->pending.nr; i++) {
- struct object_array_entry *pending = revs->pending.objects + i;
- struct object *obj = pending->item;
- const char *name = pending->name;
- if (obj->type == OBJ_TAG) {
- process_tag((struct tag *) obj, &objects, name, cp);
- continue;
- }
- if (obj->type == OBJ_TREE) {
- process_tree((struct tree *)obj, &objects, NULL, name, cp);
- continue;
- }
- if (obj->type == OBJ_BLOB) {
- process_blob((struct blob *)obj, &objects, NULL, name, cp);
- continue;
- }
- die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
+ /*
+ * We do not want to call parse_object here, because
+ * inflating blobs and trees could be very expensive.
+ * However, we do need to know the correct type for
+ * later processing, and the revision machinery expects
+ * commits and tags to have been parsed.
+ */
+ type = sha1_object_info(sha1, NULL);
+ if (type < 0)
+ die("unable to get object info for %s", sha1_to_hex(sha1));
+
+ switch (type) {
+ case OBJ_TAG:
+ case OBJ_COMMIT:
+ obj = parse_object_or_die(sha1, NULL);
+ break;
+ case OBJ_TREE:
+ obj = (struct object *)lookup_tree(sha1);
+ break;
+ case OBJ_BLOB:
+ obj = (struct object *)lookup_blob(sha1);
+ break;
+ default:
+ die("unknown object type for %s: %s",
+ sha1_to_hex(sha1), typename(type));
}
-}
-static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
- const char *email, unsigned long timestamp, int tz,
- const char *message, void *cb_data)
-{
- struct object *object;
- struct rev_info *revs = (struct rev_info *)cb_data;
+ if (!obj)
+ die("unable to lookup %s", sha1_to_hex(sha1));
- object = parse_object(osha1);
- if (object)
- add_pending_object(revs, object, "");
- object = parse_object(nsha1);
- if (object)
- add_pending_object(revs, object, "");
- return 0;
+ add_pending_object(data->revs, obj, "");
}
-static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int add_recent_loose(const unsigned char *sha1,
+ const char *path, void *data)
{
- struct object *object = parse_object_or_die(sha1, path);
- struct rev_info *revs = (struct rev_info *)cb_data;
+ struct stat st;
+ struct object *obj = lookup_object(sha1);
- add_pending_object(revs, object, "");
+ if (obj && obj->flags & SEEN)
+ return 0;
- return 0;
-}
+ if (stat(path, &st) < 0) {
+ /*
+ * It's OK if an object went away during our iteration; this
+ * could be due to a simultaneous repack. But anything else
+ * we should abort, since we might then fail to mark objects
+ * which should not be pruned.
+ */
+ if (errno == ENOENT)
+ return 0;
+ return error("unable to stat %s: %s",
+ sha1_to_hex(sha1), strerror(errno));
+ }
-static int add_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
- for_each_reflog_ent(path, add_one_reflog_ent, cb_data);
+ add_recent_object(sha1, st.st_mtime, data);
return 0;
}
-static void add_one_tree(const unsigned char *sha1, struct rev_info *revs)
+static int add_recent_packed(const unsigned char *sha1,
+ struct packed_git *p, uint32_t pos,
+ void *data)
{
- struct tree *tree = lookup_tree(sha1);
- if (tree)
- add_pending_object(revs, &tree->object, "");
-}
+ struct object *obj = lookup_object(sha1);
-static void add_cache_tree(struct cache_tree *it, struct rev_info *revs)
-{
- int i;
-
- if (it->entry_count >= 0)
- add_one_tree(it->sha1, revs);
- for (i = 0; i < it->subtree_nr; i++)
- add_cache_tree(it->down[i]->cache_tree, revs);
+ if (obj && obj->flags & SEEN)
+ return 0;
+ add_recent_object(sha1, p->mtime, data);
+ return 0;
}
-static void add_cache_refs(struct rev_info *revs)
+int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
+ unsigned long timestamp)
{
- int i;
+ struct recent_data data;
+ int r;
- read_cache();
- for (i = 0; i < active_nr; i++) {
- /*
- * The index can contain blobs and GITLINKs, GITLINKs are hashes
- * that don't actually point to objects in the repository, it's
- * almost guaranteed that they are NOT blobs, so we don't call
- * lookup_blob() on them, to avoid populating the hash table
- * with invalid information
- */
- if (S_ISGITLINK(active_cache[i]->ce_mode))
- continue;
+ data.revs = revs;
+ data.timestamp = timestamp;
- lookup_blob(active_cache[i]->sha1);
- /*
- * We could add the blobs to the pending list, but quite
- * frankly, we don't care. Once we've looked them up, and
- * added them as objects, we've really done everything
- * there is to do for a blob
- */
- }
- if (active_cache_tree)
- add_cache_tree(active_cache_tree, revs);
+ r = for_each_loose_object(add_recent_loose, &data);
+ if (r)
+ return r;
+ return for_each_packed_object(add_recent_packed, &data);
}
void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
+ unsigned long mark_recent,
struct progress *progress)
{
struct connectivity_progress cp;
@@ -224,7 +163,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
revs->tree_objects = 1;
/* Add all refs from the index file */
- add_cache_refs(revs);
+ add_index_objects_to_pending(revs, 0);
/* Add all external refs */
for_each_ref(add_one_ref, revs);
@@ -234,7 +173,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
/* Add all reflog info */
if (mark_reflog)
- for_each_reflog(add_one_reflog, revs);
+ add_reflogs_to_pending(revs, 0);
cp.progress = progress;
cp.count = 0;
@@ -245,6 +184,16 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
*/
if (prepare_revision_walk(revs))
die("revision walk setup failed");
- walk_commit_list(revs, &cp);
+ traverse_commit_list(revs, mark_commit, mark_object, &cp);
+
+ if (mark_recent) {
+ revs->ignore_missing_links = 1;
+ if (add_unseen_recent_objects_to_traversal(revs, mark_recent))
+ die("unable to mark recent objects");
+ if (prepare_revision_walk(revs))
+ die("revision walk setup failed");
+ traverse_commit_list(revs, mark_commit, mark_object, &cp);
+ }
+
display_progress(cp.progress, cp.count);
}
diff --git a/reachable.h b/reachable.h
index 5d082adfec..d23efc36ec 100644
--- a/reachable.h
+++ b/reachable.h
@@ -2,6 +2,9 @@
#define REACHEABLE_H
struct progress;
-extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, struct progress *);
+extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
+ unsigned long timestamp);
+extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
+ unsigned long mark_recent, struct progress *);
#endif
diff --git a/read-cache.c b/read-cache.c
index 6f0057fe66..8f3e9eb314 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -5,6 +5,7 @@
*/
#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
+#include "lockfile.h"
#include "cache-tree.h"
#include "refs.h"
#include "dir.h"
@@ -1246,24 +1247,16 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
#define INDEX_FORMAT_DEFAULT 3
-static int index_format_config(const char *var, const char *value, void *cb)
-{
- unsigned int *version = cb;
- if (!strcmp(var, "index.version")) {
- *version = git_config_int(var, value);
- return 0;
- }
- return 1;
-}
-
static unsigned int get_index_format_default(void)
{
char *envversion = getenv("GIT_INDEX_VERSION");
char *endp;
+ int value;
unsigned int version = INDEX_FORMAT_DEFAULT;
if (!envversion) {
- git_config(index_format_config, &version);
+ if (!git_config_get_int("index.version", &value))
+ version = value;
if (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {
warning(_("index.version set, but the value is invalid.\n"
"Using version %i"), INDEX_FORMAT_DEFAULT);
@@ -1375,6 +1368,14 @@ static int read_index_extension(struct index_state *istate,
return 0;
}
+int hold_locked_index(struct lock_file *lk, int die_on_error)
+{
+ return hold_lock_file_for_update(lk, get_index_file(),
+ die_on_error
+ ? LOCK_DIE_ON_ERROR
+ : 0);
+}
+
int read_index(struct index_state *istate)
{
return read_index_from(istate, get_index_file());
@@ -1473,6 +1474,21 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
return ce;
}
+static void check_ce_order(struct cache_entry *ce, struct cache_entry *next_ce)
+{
+ int name_compare = strcmp(ce->name, next_ce->name);
+ if (0 < name_compare)
+ die("unordered stage entries in index");
+ if (!name_compare) {
+ if (!ce_stage(ce))
+ die("multiple stage entries for merged file '%s'",
+ ce->name);
+ if (ce_stage(ce) > ce_stage(next_ce))
+ die("unordered stage entries for '%s'",
+ ce->name);
+ }
+}
+
/* remember to discard_cache() before reading a different cache! */
int do_read_index(struct index_state *istate, const char *path, int must_exist)
{
@@ -1534,6 +1550,9 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
ce = create_from_disk(disk_ce, &consumed, previous_name);
set_index_entry(istate, i, ce);
+ if (i > 0)
+ check_ce_order(istate->cache[i - 1], ce);
+
src_offset += consumed;
}
strbuf_release(&previous_name_buf);
@@ -2031,16 +2050,10 @@ void set_alternate_index_output(const char *name)
static int commit_locked_index(struct lock_file *lk)
{
- if (alternate_index_output) {
- if (lk->fd >= 0 && close_lock_file(lk))
- return -1;
- if (rename(lk->filename, alternate_index_output))
- return -1;
- lk->filename[0] = 0;
- return 0;
- } else {
+ if (alternate_index_output)
+ return commit_lock_file_to(lk, alternate_index_output);
+ else
return commit_lock_file(lk);
- }
}
static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
@@ -2180,7 +2193,6 @@ int read_index_unmerged(struct index_state *istate)
if (add_index_entry(istate, new_ce, 0))
return error("%s: cannot drop to stage #0",
new_ce->name);
- i = index_name_pos(istate, new_ce->name, len);
}
return unmerged;
}
diff --git a/reflog-walk.c b/reflog-walk.c
index 9ce8b53ccc..222de762eb 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -48,7 +48,8 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
unsigned char sha1[20];
const char *name;
void *name_to_free;
- name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
+ name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
+ sha1, NULL);
if (name) {
for_each_reflog_ent(name, read_one_reflog, reflogs);
free(name_to_free);
@@ -133,7 +134,7 @@ struct reflog_walk_info {
struct commit_reflog *last_commit_reflog;
};
-void init_reflog_walk(struct reflog_walk_info** info)
+void init_reflog_walk(struct reflog_walk_info **info)
{
*info = xcalloc(1, sizeof(struct reflog_walk_info));
}
@@ -174,7 +175,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
if (*branch == '\0') {
unsigned char sha1[20];
free(branch);
- branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ branch = resolve_refdup("HEAD", 0, sha1, NULL);
if (!branch)
die ("No current branch");
diff --git a/reflog-walk.h b/reflog-walk.h
index 50265f51c5..a9bd60e32d 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -5,7 +5,7 @@
struct reflog_walk_info;
-extern void init_reflog_walk(struct reflog_walk_info** info);
+extern void init_reflog_walk(struct reflog_walk_info **info);
extern int add_reflog_for_walk(struct reflog_walk_info *info,
struct commit *commit, const char *name);
extern void fake_reflog_parent(struct reflog_walk_info *info,
diff --git a/refs.c b/refs.c
index 82e5b1b14f..0368ed461f 100644
--- a/refs.c
+++ b/refs.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "refs.h"
#include "object.h"
#include "tag.h"
@@ -25,6 +26,11 @@ static unsigned char refname_disposition[256] = {
};
/*
+ * Used as a flag to ref_transaction_delete when a loose ref is being
+ * pruned.
+ */
+#define REF_ISPRUNING 0x0100
+/*
* Try to read one refname component from the front of refname.
* Return the length of the component found, or -1 if the component is
* not legal. It is legal if it is something reasonable to have under
@@ -64,17 +70,10 @@ static int check_refname_component(const char *refname, int flags)
out:
if (cp == refname)
return 0; /* Component has zero length. */
- if (refname[0] == '.') {
- if (!(flags & REFNAME_DOT_COMPONENT))
- return -1; /* Component starts with '.'. */
- /*
- * Even if leading dots are allowed, don't allow "."
- * as a component (".." is prevented by a rule above).
- */
- if (refname[1] == '\0')
- return -1; /* Component equals ".". */
- }
- if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
+ if (refname[0] == '.')
+ return -1; /* Component starts with '.'. */
+ if (cp - refname >= LOCK_SUFFIX_LEN &&
+ !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
return -1; /* Refname ends with ".lock". */
return cp - refname;
}
@@ -188,8 +187,8 @@ struct ref_dir {
/*
* Bit values for ref_entry::flag. REF_ISSYMREF=0x01,
- * REF_ISPACKED=0x02, and REF_ISBROKEN=0x04 are public values; see
- * refs.h.
+ * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
+ * public values; see refs.h.
*/
/*
@@ -197,16 +196,16 @@ struct ref_dir {
* the correct peeled value for the reference, which might be
* null_sha1 if the reference is not a tag or if it is broken.
*/
-#define REF_KNOWS_PEELED 0x08
+#define REF_KNOWS_PEELED 0x10
/* ref_entry represents a directory of references */
-#define REF_DIR 0x10
+#define REF_DIR 0x20
/*
* Entry has not yet been read from disk (used only for REF_DIR
* entries representing loose references)
*/
-#define REF_INCOMPLETE 0x20
+#define REF_INCOMPLETE 0x40
/*
* A ref_entry represents either a reference or a "subdirectory" of
@@ -275,6 +274,39 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
return dir;
}
+/*
+ * Check if a refname is safe.
+ * For refs that start with "refs/" we consider it safe as long they do
+ * not try to resolve to outside of refs/.
+ *
+ * For all other refs we only consider them safe iff they only contain
+ * upper case characters and '_' (like "HEAD" AND "MERGE_HEAD", and not like
+ * "config").
+ */
+static int refname_is_safe(const char *refname)
+{
+ if (starts_with(refname, "refs/")) {
+ char *buf;
+ int result;
+
+ buf = xmalloc(strlen(refname) + 1);
+ /*
+ * Does the refname try to escape refs/?
+ * For example: refs/foo/../bar is safe but refs/foo/../../bar
+ * is not.
+ */
+ result = !normalize_path_copy(buf, refname + strlen("refs/"));
+ free(buf);
+ return result;
+ }
+ while (*refname) {
+ if (!isupper(*refname) && *refname != '_')
+ return 0;
+ refname++;
+ }
+ return 1;
+}
+
static struct ref_entry *create_ref_entry(const char *refname,
const unsigned char *sha1, int flag,
int check_name)
@@ -283,8 +315,10 @@ static struct ref_entry *create_ref_entry(const char *refname,
struct ref_entry *ref;
if (check_name &&
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die("Reference has invalid format: '%s'", refname);
+ if (!check_name && !refname_is_safe(refname))
+ die("Reference has invalid name: '%s'", refname);
len = strlen(refname) + 1;
ref = xmalloc(sizeof(struct ref_entry) + len);
hashcpy(ref->u.value.sha1, sha1);
@@ -779,60 +813,121 @@ static void prime_ref_dir(struct ref_dir *dir)
prime_ref_dir(get_ref_dir(entry));
}
}
-/*
- * Return true iff refname1 and refname2 conflict with each other.
- * Two reference names conflict if one of them exactly matches the
- * leading components of the other; e.g., "foo/bar" conflicts with
- * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
- * "foo/barbados".
- */
-static int names_conflict(const char *refname1, const char *refname2)
+
+static int entry_matches(struct ref_entry *entry, const struct string_list *list)
{
- for (; *refname1 && *refname1 == *refname2; refname1++, refname2++)
- ;
- return (*refname1 == '\0' && *refname2 == '/')
- || (*refname1 == '/' && *refname2 == '\0');
+ return list && string_list_has_string(list, entry->name);
}
-struct name_conflict_cb {
- const char *refname;
- const char *oldrefname;
- const char *conflicting_refname;
+struct nonmatching_ref_data {
+ const struct string_list *skip;
+ struct ref_entry *found;
};
-static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
+static int nonmatching_ref_fn(struct ref_entry *entry, void *vdata)
{
- struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
- if (data->oldrefname && !strcmp(data->oldrefname, entry->name))
+ struct nonmatching_ref_data *data = vdata;
+
+ if (entry_matches(entry, data->skip))
return 0;
- if (names_conflict(data->refname, entry->name)) {
- data->conflicting_refname = entry->name;
- return 1;
- }
- return 0;
+
+ data->found = entry;
+ return 1;
+}
+
+static void report_refname_conflict(struct ref_entry *entry,
+ const char *refname)
+{
+ error("'%s' exists; cannot create '%s'", entry->name, refname);
}
/*
* Return true iff a reference named refname could be created without
* conflicting with the name of an existing reference in dir. If
- * oldrefname is non-NULL, ignore potential conflicts with oldrefname
- * (e.g., because oldrefname is scheduled for deletion in the same
+ * skip is non-NULL, ignore potential conflicts with refs in skip
+ * (e.g., because they are scheduled for deletion in the same
* operation).
+ *
+ * Two reference names conflict if one of them exactly matches the
+ * leading components of the other; e.g., "foo/bar" conflicts with
+ * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
+ * "foo/barbados".
+ *
+ * skip must be sorted.
*/
-static int is_refname_available(const char *refname, const char *oldrefname,
+static int is_refname_available(const char *refname,
+ const struct string_list *skip,
struct ref_dir *dir)
{
- struct name_conflict_cb data;
- data.refname = refname;
- data.oldrefname = oldrefname;
- data.conflicting_refname = NULL;
+ const char *slash;
+ size_t len;
+ int pos;
+ char *dirname;
- sort_ref_dir(dir);
- if (do_for_each_entry_in_dir(dir, 0, name_conflict_fn, &data)) {
- error("'%s' exists; cannot create '%s'",
- data.conflicting_refname, refname);
+ for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+ /*
+ * We are still at a leading dir of the refname; we are
+ * looking for a conflict with a leaf entry.
+ *
+ * If we find one, we still must make sure it is
+ * not in "skip".
+ */
+ pos = search_ref_dir(dir, refname, slash - refname);
+ if (pos >= 0) {
+ struct ref_entry *entry = dir->entries[pos];
+ if (entry_matches(entry, skip))
+ return 1;
+ report_refname_conflict(entry, refname);
+ return 0;
+ }
+
+
+ /*
+ * Otherwise, we can try to continue our search with
+ * the next component; if we come up empty, we know
+ * there is nothing under this whole prefix.
+ */
+ pos = search_ref_dir(dir, refname, slash + 1 - refname);
+ if (pos < 0)
+ return 1;
+
+ dir = get_ref_dir(dir->entries[pos]);
+ }
+
+ /*
+ * We are at the leaf of our refname; we want to
+ * make sure there are no directories which match it.
+ */
+ len = strlen(refname);
+ dirname = xmallocz(len + 1);
+ sprintf(dirname, "%s/", refname);
+ pos = search_ref_dir(dir, dirname, len + 1);
+ free(dirname);
+
+ if (pos >= 0) {
+ /*
+ * We found a directory named "refname". It is a
+ * problem iff it contains any ref that is not
+ * in "skip".
+ */
+ struct ref_entry *entry = dir->entries[pos];
+ struct ref_dir *dir = get_ref_dir(entry);
+ struct nonmatching_ref_data data;
+
+ data.skip = skip;
+ sort_ref_dir(dir);
+ if (!do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data))
+ return 1;
+
+ report_refname_conflict(data.found, refname);
return 0;
}
+
+ /*
+ * There is no point in searching for another leaf
+ * node which matches it; such an entry would be the
+ * ref we are looking for, not a conflict.
+ */
return 1;
}
@@ -1051,7 +1146,13 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
refname = parse_ref_line(refline, sha1);
if (refname) {
- last = create_ref_entry(refname, sha1, REF_ISPACKED, 1);
+ int flag = REF_ISPACKED;
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ hashclr(sha1);
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
+ }
+ last = create_ref_entry(refname, sha1, flag, 0);
if (peeled == PEELED_FULLY ||
(peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
last->flag |= REF_KNOWS_PEELED;
@@ -1183,12 +1284,19 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
hashclr(sha1);
flag |= REF_ISBROKEN;
}
- } else if (read_ref_full(refname.buf, sha1, 1, &flag)) {
+ } else if (read_ref_full(refname.buf,
+ RESOLVE_REF_READING,
+ sha1, &flag)) {
hashclr(sha1);
flag |= REF_ISBROKEN;
}
+ if (check_refname_format(refname.buf,
+ REFNAME_ALLOW_ONELEVEL)) {
+ hashclr(sha1);
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
+ }
add_entry_to_dir(dir,
- create_ref_entry(refname.buf, sha1, flag, 1));
+ create_ref_entry(refname.buf, sha1, flag, 0));
}
strbuf_setlen(&refname, dirnamelen);
}
@@ -1307,10 +1415,10 @@ static struct ref_entry *get_packed_ref(const char *refname)
* A loose ref file doesn't exist; check for a packed ref. The
* options are forwarded from resolve_safe_unsafe().
*/
-static const char *handle_missing_loose_ref(const char *refname,
- unsigned char *sha1,
- int reading,
- int *flag)
+static int resolve_missing_loose_ref(const char *refname,
+ int resolve_flags,
+ unsigned char *sha1,
+ int *flags)
{
struct ref_entry *entry;
@@ -1321,35 +1429,51 @@ static const char *handle_missing_loose_ref(const char *refname,
entry = get_packed_ref(refname);
if (entry) {
hashcpy(sha1, entry->u.value.sha1);
- if (flag)
- *flag |= REF_ISPACKED;
- return refname;
+ if (flags)
+ *flags |= REF_ISPACKED;
+ return 0;
}
/* The reference is not a packed reference, either. */
- if (reading) {
- return NULL;
+ if (resolve_flags & RESOLVE_REF_READING) {
+ errno = ENOENT;
+ return -1;
} else {
hashclr(sha1);
- return refname;
+ return 0;
}
}
/* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
{
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
static char refname_buffer[256];
+ int bad_name = 0;
- if (flag)
- *flag = 0;
+ if (flags)
+ *flags = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- errno = EINVAL;
- return NULL;
- }
+ if (flags)
+ *flags |= REF_BAD_NAME;
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(refname)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /*
+ * dwim_ref() uses REF_ISBROKEN to distinguish between
+ * missing refs and refs that were present but invalid,
+ * to complain about the latter to stderr.
+ *
+ * We don't know whether the ref exists, so don't set
+ * REF_ISBROKEN yet.
+ */
+ bad_name = 1;
+ }
for (;;) {
char path[PATH_MAX];
struct stat st;
@@ -1374,11 +1498,17 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
*/
stat_ref:
if (lstat(path, &st) < 0) {
- if (errno == ENOENT)
- return handle_missing_loose_ref(refname, sha1,
- reading, flag);
- else
+ if (errno != ENOENT)
+ return NULL;
+ if (resolve_missing_loose_ref(refname, resolve_flags,
+ sha1, flags))
return NULL;
+ if (bad_name) {
+ hashclr(sha1);
+ if (flags)
+ *flags |= REF_ISBROKEN;
+ }
+ return refname;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
@@ -1396,8 +1526,12 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
!check_refname_format(buffer, 0)) {
strcpy(refname_buffer, buffer);
refname = refname_buffer;
- if (flag)
- *flag |= REF_ISSYMREF;
+ if (flags)
+ *flags |= REF_ISSYMREF;
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+ hashclr(sha1);
+ return refname;
+ }
continue;
}
}
@@ -1442,31 +1576,45 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
*/
if (get_sha1_hex(buffer, sha1) ||
(buffer[40] != '\0' && !isspace(buffer[40]))) {
- if (flag)
- *flag |= REF_ISBROKEN;
+ if (flags)
+ *flags |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
+ if (bad_name) {
+ hashclr(sha1);
+ if (flags)
+ *flags |= REF_ISBROKEN;
+ }
return refname;
}
- if (flag)
- *flag |= REF_ISSYMREF;
+ if (flags)
+ *flags |= REF_ISSYMREF;
buf = buffer + 4;
while (isspace(*buf))
buf++;
+ refname = strcpy(refname_buffer, buf);
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+ hashclr(sha1);
+ return refname;
+ }
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
- if (flag)
- *flag |= REF_ISBROKEN;
- errno = EINVAL;
- return NULL;
+ if (flags)
+ *flags |= REF_ISBROKEN;
+
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(buf)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ bad_name = 1;
}
- refname = strcpy(refname_buffer, buf);
}
}
-char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
{
- const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
+ const char *ret = resolve_ref_unsafe(ref, resolve_flags, sha1, flags);
return ret ? xstrdup(ret) : NULL;
}
@@ -1477,22 +1625,22 @@ struct ref_filter {
void *cb_data;
};
-int read_ref_full(const char *refname, unsigned char *sha1, int reading, int *flags)
+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
{
- if (resolve_ref_unsafe(refname, sha1, reading, flags))
+ if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
return 0;
return -1;
}
int read_ref(const char *refname, unsigned char *sha1)
{
- return read_ref_full(refname, sha1, 1, NULL);
+ return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
}
int ref_exists(const char *refname)
{
unsigned char sha1[20];
- return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
+ return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
}
static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
@@ -1605,7 +1753,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
return 0;
}
- if (read_ref_full(refname, base, 1, &flag))
+ if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
return -1;
/*
@@ -1646,7 +1794,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
+ resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
@@ -1771,7 +1919,7 @@ static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
return 0;
}
- if (!read_ref_full("HEAD", sha1, 1, &flag))
+ if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
return fn("HEAD", sha1, flag, cb_data);
return 0;
@@ -1851,7 +1999,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, sha1, 1, &flag))
+ if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
ret = fn(buf.buf, sha1, flag, cb_data);
strbuf_release(&buf);
@@ -1946,7 +2094,9 @@ int refname_match(const char *abbrev_name, const char *full_name)
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
- if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+ if (read_ref_full(lock->ref_name,
+ mustexist ? RESOLVE_REF_READING : 0,
+ lock->old_sha1, NULL)) {
int save_errno = errno;
error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
@@ -2019,7 +2169,8 @@ 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_unsafe(fullref, this_result, 1, &flag);
+ r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,
+ this_result, &flag);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -2048,7 +2199,8 @@ 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_unsafe(path, hash, 1, NULL);
+ ref = resolve_ref_unsafe(path, RESOLVE_REF_READING,
+ hash, NULL);
if (!ref)
continue;
if (reflog_exists(path))
@@ -2068,9 +2220,13 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
return logs_found;
}
-/* This function should make sure errno is meaningful on error */
+/*
+ * Locks a ref returning the lock on success and NULL on failure.
+ * On failure errno is set to something meaningful.
+ */
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
+ const struct string_list *skip,
int flags, int *type_p)
{
char *ref_file;
@@ -2079,13 +2235,23 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
int last_errno = 0;
int type, lflags;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
+ int resolve_flags = 0;
int missing = 0;
int attempts_remaining = 3;
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- refname = resolve_ref_unsafe(refname, lock->old_sha1, mustexist, &type);
+ if (mustexist)
+ resolve_flags |= RESOLVE_REF_READING;
+ if (flags & REF_DELETING) {
+ resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
+ if (flags & REF_NODEREF)
+ resolve_flags |= RESOLVE_REF_NO_RECURSE;
+ }
+
+ refname = resolve_ref_unsafe(refname, resolve_flags,
+ lock->old_sha1, &type);
if (!refname && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
@@ -2098,7 +2264,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
error("there are still refs under '%s'", orig_refname);
goto error_return;
}
- refname = resolve_ref_unsafe(orig_refname, lock->old_sha1, mustexist, &type);
+ refname = resolve_ref_unsafe(orig_refname, resolve_flags,
+ lock->old_sha1, &type);
}
if (type_p)
*type_p = type;
@@ -2115,7 +2282,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
* name is a proper prefix of our refname.
*/
if (missing &&
- !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
+ !is_refname_available(refname, skip, get_packed_refs(&ref_cache))) {
last_errno = ENOTDIR;
goto error_return;
}
@@ -2125,7 +2292,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
lflags = 0;
if (flags & REF_NODEREF) {
refname = orig_refname;
- lflags |= LOCK_NODEREF;
+ lflags |= LOCK_NO_DEREF;
}
lock->ref_name = xstrdup(refname);
lock->orig_ref_name = xstrdup(orig_refname);
@@ -2159,7 +2326,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
*/
goto retry;
else
- unable_to_lock_index_die(ref_file, errno);
+ unable_to_lock_die(ref_file, errno);
}
return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
@@ -2169,47 +2336,23 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
return NULL;
}
-struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1)
-{
- char refpath[PATH_MAX];
- if (check_refname_format(refname, 0))
- return NULL;
- strcpy(refpath, mkpath("refs/%s", refname));
- return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
-}
-
struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
{
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
- return NULL;
- return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
+ return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
}
/*
* Write an entry to the packed-refs file for the specified refname.
* If peeled is non-NULL, write it as the entry's peeled value.
*/
-static void write_packed_entry(int fd, char *refname, unsigned char *sha1,
+static void write_packed_entry(FILE *fh, char *refname, unsigned char *sha1,
unsigned char *peeled)
{
- char line[PATH_MAX + 100];
- int len;
-
- len = snprintf(line, sizeof(line), "%s %s\n",
- sha1_to_hex(sha1), refname);
- /* this should not happen but just being defensive */
- if (len > sizeof(line))
- die("too long a refname '%s'", refname);
- write_or_die(fd, line, len);
-
- if (peeled) {
- if (snprintf(line, sizeof(line), "^%s\n",
- sha1_to_hex(peeled)) != PEELED_LINE_LENGTH)
- die("internal error");
- write_or_die(fd, line, PEELED_LINE_LENGTH);
- }
+ fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
+ if (peeled)
+ fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
}
/*
@@ -2217,13 +2360,12 @@ static void write_packed_entry(int fd, char *refname, unsigned char *sha1,
*/
static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
{
- int *fd = cb_data;
enum peel_status peel_status = peel_entry(entry, 0);
if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
error("internal error: %s is not a valid packed reference!",
entry->name);
- write_packed_entry(*fd, entry->name, entry->u.value.sha1,
+ write_packed_entry(cb_data, entry->name, entry->u.value.sha1,
peel_status == PEEL_PEELED ?
entry->u.value.peeled : NULL);
return 0;
@@ -2259,15 +2401,19 @@ int commit_packed_refs(void)
get_packed_ref_cache(&ref_cache);
int error = 0;
int save_errno = 0;
+ FILE *out;
if (!packed_ref_cache->lock)
die("internal error: packed-refs not locked");
- write_or_die(packed_ref_cache->lock->fd,
- PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
+ out = fdopen_lock_file(packed_ref_cache->lock, "w");
+ if (!out)
+ die_errno("unable to fdopen packed-refs descriptor");
+
+ fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
- 0, write_packed_entry_fn,
- &packed_ref_cache->lock->fd);
+ 0, write_packed_entry_fn, out);
+
if (commit_lock_file(packed_ref_cache->lock)) {
save_errno = errno;
error = -1;
@@ -2387,14 +2533,25 @@ static void try_remove_empty_parents(char *name)
/* make sure nobody touched the ref, and unlink */
static void prune_ref(struct ref_to_prune *r)
{
- struct ref_lock *lock = lock_any_ref_for_update(r->name, r->sha1,
- 0, NULL);
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
- if (lock) {
- unlink_or_warn(git_path("%s", r->name));
- unlock_ref(lock);
- try_remove_empty_parents(r->name);
+ if (check_refname_format(r->name, 0))
+ return;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_delete(transaction, r->name, r->sha1,
+ REF_ISPRUNING, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
+ ref_transaction_free(transaction);
+ error("%s", err.buf);
+ strbuf_release(&err);
+ return;
}
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
+ try_remove_empty_parents(r->name);
}
static void prune_refs(struct ref_to_prune *r)
@@ -2450,7 +2607,7 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
unsigned char sha1[20];
int flags;
- if (read_ref_full(entry->name, sha1, 0, &flags))
+ if (read_ref_full(entry->name, 0, sha1, &flags))
/* We should at least have found the packed ref. */
die("Internal error");
if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
@@ -2489,6 +2646,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
struct string_list_item *ref_to_delete;
int i, ret, removed = 0;
+ assert(err);
+
/* Look for a packed ref */
for (i = 0; i < n; i++)
if (get_packed_ref(refnames[i]))
@@ -2499,13 +2658,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
return 0; /* no refname exists in packed refs */
if (lock_packed_refs(0)) {
- if (err) {
- unable_to_lock_message(git_path("packed-refs"), errno,
- err);
- return -1;
- }
- unable_to_lock_error(git_path("packed-refs"), errno);
- return error("cannot delete '%s' from packed refs", refnames[i]);
+ unable_to_lock_message(git_path("packed-refs"), errno, err);
+ return -1;
}
packed = get_packed_refs(&ref_cache);
@@ -2531,27 +2685,25 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
/* Write what remains */
ret = commit_packed_refs();
- if (ret && err)
+ if (ret)
strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
strerror(errno));
return ret;
}
-static int repack_without_ref(const char *refname)
+static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
{
- return repack_without_refs(&refname, 1, NULL);
-}
+ assert(err);
-static int delete_ref_loose(struct ref_lock *lock, int flag)
-{
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
- /* loose */
- int err, i = strlen(lock->lk->filename) - 5; /* .lock */
-
- lock->lk->filename[i] = 0;
- err = unlink_or_warn(lock->lk->filename);
- lock->lk->filename[i] = '.';
- if (err && errno != ENOENT)
+ /*
+ * loose. The loose file name is the same as the
+ * lockfile name, minus ".lock":
+ */
+ char *loose_filename = get_locked_file_path(lock->lk);
+ int res = unlink_or_msg(loose_filename, err);
+ free(loose_filename);
+ if (res)
return 1;
}
return 0;
@@ -2559,24 +2711,22 @@ static int delete_ref_loose(struct ref_lock *lock, int flag)
int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
{
- struct ref_lock *lock;
- int ret = 0, flag = 0;
-
- lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
- if (!lock)
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_delete(transaction, refname, sha1, delopt,
+ sha1 && !is_null_sha1(sha1), NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
+ error("%s", err.buf);
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
return 1;
- ret |= delete_ref_loose(lock, flag);
-
- /* removing the loose one could have resurrected an earlier
- * packed one. Also, if it was not loose we need to repack
- * without it.
- */
- ret |= repack_without_ref(lock->ref_name);
-
- unlink_or_warn(git_path("logs/%s", lock->ref_name));
- clear_loose_ref_cache(&ref_cache);
- unlock_ref(lock);
- return ret;
+ }
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
+ return 0;
}
/*
@@ -2633,6 +2783,21 @@ static int rename_tmp_log(const char *newrefname)
return 0;
}
+static int rename_ref_available(const char *oldname, const char *newname)
+{
+ struct string_list skip = STRING_LIST_INIT_NODUP;
+ int ret;
+
+ string_list_insert(&skip, oldname);
+ ret = is_refname_available(newname, &skip, get_packed_refs(&ref_cache))
+ && is_refname_available(newname, &skip, get_loose_refs(&ref_cache));
+ string_list_clear(&skip, 0);
+ return ret;
+}
+
+static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
+ const char *logmsg);
+
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
{
unsigned char sha1[20], orig_sha1[20];
@@ -2645,17 +2810,15 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldrefname);
- symref = resolve_ref_unsafe(oldrefname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
+ orig_sha1, &flag);
if (flag & REF_ISSYMREF)
return error("refname %s is a symbolic ref, renaming it is not supported",
oldrefname);
if (!symref)
return error("refname %s not found", oldrefname);
- if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
- return 1;
-
- if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
+ if (!rename_ref_available(oldrefname, newrefname))
return 1;
if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
@@ -2667,7 +2830,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
goto rollback;
}
- if (!read_ref_full(newrefname, sha1, 1, &flag) &&
+ if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
@@ -2685,7 +2848,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
logmoved = log;
- lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newrefname);
goto rollback;
@@ -2700,7 +2863,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
return 0;
rollback:
- lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for rollback", oldrefname);
goto rollbacklog;
@@ -2880,8 +3043,11 @@ int is_branch(const char *refname)
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
-/* This function must return a meaningful errno */
-int write_ref_sha1(struct ref_lock *lock,
+/*
+ * Write sha1 into the ref specified by the lock. Make sure that errno
+ * is sane on error.
+ */
+static int write_ref_sha1(struct ref_lock *lock,
const unsigned char *sha1, const char *logmsg)
{
static char term = '\n';
@@ -2914,7 +3080,7 @@ int write_ref_sha1(struct ref_lock *lock,
write_in_full(lock->lock_fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
int save_errno = errno;
- error("Couldn't write %s", lock->lk->filename);
+ error("Couldn't write %s", lock->lk->filename.buf);
unlock_ref(lock);
errno = save_errno;
return -1;
@@ -2942,7 +3108,8 @@ int write_ref_sha1(struct ref_lock *lock,
unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
- head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
+ head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+ head_sha1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -3105,7 +3272,7 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
return 1;
}
-int read_ref_at(const char *refname, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
unsigned char *sha1, char **msg,
unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
{
@@ -3123,8 +3290,12 @@ int read_ref_at(const char *refname, unsigned long at_time, int cnt,
for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
- if (!cb.reccnt)
- die("Log for %s is empty.", refname);
+ if (!cb.reccnt) {
+ if (flags & GET_SHA1_QUIETLY)
+ exit(128);
+ else
+ die("Log for %s is empty.", refname);
+ }
if (cb.found_it)
return 0;
@@ -3309,7 +3480,7 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
retval = do_for_each_reflog(name, fn, cb_data);
} else {
unsigned char sha1[20];
- if (read_ref_full(name->buf, sha1, 0, NULL))
+ if (read_ref_full(name->buf, 0, sha1, NULL))
retval = error("bad ref for %s", name->buf);
else
retval = fn(name->buf, sha1, 0, cb_data);
@@ -3333,43 +3504,6 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
return retval;
}
-static struct ref_lock *update_ref_lock(const char *refname,
- const unsigned char *oldval,
- int flags, int *type_p,
- enum action_on_err onerr)
-{
- struct ref_lock *lock;
- lock = lock_any_ref_for_update(refname, oldval, flags, type_p);
- if (!lock) {
- const char *str = "Cannot lock the ref '%s'.";
- switch (onerr) {
- case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
- case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
- case UPDATE_REFS_QUIET_ON_ERR: break;
- }
- }
- return lock;
-}
-
-static int update_ref_write(const char *action, const char *refname,
- const unsigned char *sha1, struct ref_lock *lock,
- struct strbuf *err, enum action_on_err onerr)
-{
- if (write_ref_sha1(lock, sha1, action) < 0) {
- const char *str = "Cannot update the ref '%s'.";
- if (err)
- strbuf_addf(err, str, refname);
-
- switch (onerr) {
- case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
- case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
- case UPDATE_REFS_QUIET_ON_ERR: break;
- }
- return 1;
- }
- return 0;
-}
-
/**
* Information needed for a single ref update. Set new_sha1 to the
* new value or to zero to delete the ref. To check the old value
@@ -3383,10 +3517,26 @@ struct ref_update {
int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
struct ref_lock *lock;
int type;
+ char *msg;
const char refname[FLEX_ARRAY];
};
/*
+ * Transaction states.
+ * OPEN: The transaction is in a valid state and can accept new updates.
+ * An OPEN transaction can be committed.
+ * CLOSED: A closed transaction is no longer active and no other operations
+ * than free can be used on it in this state.
+ * A transaction can either become closed by successfully committing
+ * an active transaction or if there is a failure while building
+ * the transaction thus rendering it failed/inactive.
+ */
+enum ref_transaction_state {
+ REF_TRANSACTION_OPEN = 0,
+ REF_TRANSACTION_CLOSED = 1
+};
+
+/*
* Data structure for holding a reference transaction, which can
* consist of checks and updates to multiple references, carried out
* as atomically as possible. This structure is opaque to callers.
@@ -3395,10 +3545,13 @@ struct ref_transaction {
struct ref_update **updates;
size_t alloc;
size_t nr;
+ enum ref_transaction_state state;
};
-struct ref_transaction *ref_transaction_begin(void)
+struct ref_transaction *ref_transaction_begin(struct strbuf *err)
{
+ assert(err);
+
return xcalloc(1, sizeof(struct ref_transaction));
}
@@ -3409,9 +3562,10 @@ void ref_transaction_free(struct ref_transaction *transaction)
if (!transaction)
return;
- for (i = 0; i < transaction->nr; i++)
+ for (i = 0; i < transaction->nr; i++) {
+ free(transaction->updates[i]->msg);
free(transaction->updates[i]);
-
+ }
free(transaction->updates);
free(transaction);
}
@@ -3432,61 +3586,129 @@ int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
+ assert(err);
+
+ if (transaction->state != REF_TRANSACTION_OPEN)
+ die("BUG: update called for transaction that is not open");
+
if (have_old && !old_sha1)
die("BUG: have_old is true but old_sha1 is NULL");
+ if (!is_null_sha1(new_sha1) &&
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ strbuf_addf(err, "refusing to update ref with bad name %s",
+ refname);
+ return -1;
+ }
+
update = add_update(transaction, refname);
hashcpy(update->new_sha1, new_sha1);
update->flags = flags;
update->have_old = have_old;
if (have_old)
hashcpy(update->old_sha1, old_sha1);
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
-void ref_transaction_create(struct ref_transaction *transaction,
- const char *refname,
- const unsigned char *new_sha1,
- int flags)
+int ref_transaction_create(struct ref_transaction *transaction,
+ const char *refname,
+ const unsigned char *new_sha1,
+ int flags, const char *msg,
+ struct strbuf *err)
{
- struct ref_update *update = add_update(transaction, refname);
+ struct ref_update *update;
+
+ assert(err);
+
+ if (transaction->state != REF_TRANSACTION_OPEN)
+ die("BUG: create called for transaction that is not open");
+
+ if (!new_sha1 || is_null_sha1(new_sha1))
+ die("BUG: create ref with null new_sha1");
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ strbuf_addf(err, "refusing to create ref with bad name %s",
+ refname);
+ return -1;
+ }
+
+ update = add_update(transaction, refname);
- assert(!is_null_sha1(new_sha1));
hashcpy(update->new_sha1, new_sha1);
hashclr(update->old_sha1);
update->flags = flags;
update->have_old = 1;
+ if (msg)
+ update->msg = xstrdup(msg);
+ return 0;
}
-void ref_transaction_delete(struct ref_transaction *transaction,
- const char *refname,
- const unsigned char *old_sha1,
- int flags, int have_old)
+int ref_transaction_delete(struct ref_transaction *transaction,
+ const char *refname,
+ const unsigned char *old_sha1,
+ int flags, int have_old, const char *msg,
+ struct strbuf *err)
{
- struct ref_update *update = add_update(transaction, refname);
+ struct ref_update *update;
+
+ assert(err);
+ if (transaction->state != REF_TRANSACTION_OPEN)
+ die("BUG: delete called for transaction that is not open");
+
+ if (have_old && !old_sha1)
+ die("BUG: have_old is true but old_sha1 is NULL");
+
+ update = add_update(transaction, refname);
update->flags = flags;
update->have_old = have_old;
if (have_old) {
assert(!is_null_sha1(old_sha1));
hashcpy(update->old_sha1, old_sha1);
}
+ if (msg)
+ update->msg = xstrdup(msg);
+ return 0;
}
int update_ref(const char *action, const char *refname,
const unsigned char *sha1, const unsigned char *oldval,
int flags, enum action_on_err onerr)
{
- struct ref_lock *lock;
- lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
- if (!lock)
+ struct ref_transaction *t;
+ struct strbuf err = STRBUF_INIT;
+
+ t = ref_transaction_begin(&err);
+ if (!t ||
+ ref_transaction_update(t, refname, sha1, oldval, flags,
+ !!oldval, action, &err) ||
+ ref_transaction_commit(t, &err)) {
+ const char *str = "update_ref failed for ref '%s': %s";
+
+ ref_transaction_free(t);
+ switch (onerr) {
+ case UPDATE_REFS_MSG_ON_ERR:
+ error(str, refname, err.buf);
+ break;
+ case UPDATE_REFS_DIE_ON_ERR:
+ die(str, refname, err.buf);
+ break;
+ case UPDATE_REFS_QUIET_ON_ERR:
+ break;
+ }
+ strbuf_release(&err);
return 1;
- return update_ref_write(action, refname, sha1, lock, NULL, onerr);
+ }
+ strbuf_release(&err);
+ ref_transaction_free(t);
+ return 0;
}
static int ref_update_compare(const void *r1, const void *r2)
@@ -3500,53 +3722,67 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
struct strbuf *err)
{
int i;
+
+ assert(err);
+
for (i = 1; i < n; i++)
if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
- const char *str =
- "Multiple updates for ref '%s' not allowed.";
- if (err)
- strbuf_addf(err, str, updates[i]->refname);
-
+ strbuf_addf(err,
+ "Multiple updates for ref '%s' not allowed.",
+ updates[i]->refname);
return 1;
}
return 0;
}
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, struct strbuf *err)
+ struct strbuf *err)
{
int ret = 0, delnum = 0, i;
const char **delnames;
int n = transaction->nr;
struct ref_update **updates = transaction->updates;
- if (!n)
+ assert(err);
+
+ if (transaction->state != REF_TRANSACTION_OPEN)
+ die("BUG: commit called for transaction that is not open");
+
+ if (!n) {
+ transaction->state = REF_TRANSACTION_CLOSED;
return 0;
+ }
/* Allocate work space */
delnames = xmalloc(sizeof(*delnames) * n);
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
- ret = ref_update_reject_duplicates(updates, n, err);
- if (ret)
+ if (ref_update_reject_duplicates(updates, n, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
+ }
/* Acquire all locks while verifying old values */
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
-
- update->lock = update_ref_lock(update->refname,
- (update->have_old ?
- update->old_sha1 : NULL),
- update->flags,
- &update->type,
- UPDATE_REFS_QUIET_ON_ERR);
+ int flags = update->flags;
+
+ if (is_null_sha1(update->new_sha1))
+ flags |= REF_DELETING;
+ update->lock = lock_ref_sha1_basic(update->refname,
+ (update->have_old ?
+ update->old_sha1 :
+ NULL),
+ NULL,
+ flags,
+ &update->type);
if (!update->lock) {
- if (err)
- strbuf_addf(err, "Cannot lock the ref '%s'.",
- update->refname);
- ret = 1;
+ ret = (errno == ENOTDIR)
+ ? TRANSACTION_NAME_CONFLICT
+ : TRANSACTION_GENERIC_ERROR;
+ strbuf_addf(err, "Cannot lock the ref '%s'.",
+ update->refname);
goto cleanup;
}
}
@@ -3556,14 +3792,15 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (!is_null_sha1(update->new_sha1)) {
- ret = update_ref_write(msg,
- update->refname,
- update->new_sha1,
- update->lock, err,
- UPDATE_REFS_QUIET_ON_ERR);
- update->lock = NULL; /* freed by update_ref_write */
- if (ret)
+ if (write_ref_sha1(update->lock, update->new_sha1,
+ update->msg)) {
+ update->lock = NULL; /* freed by write_ref_sha1 */
+ strbuf_addf(err, "Cannot update the ref '%s'.",
+ update->refname);
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
+ }
+ update->lock = NULL; /* freed by write_ref_sha1 */
}
}
@@ -3572,17 +3809,27 @@ int ref_transaction_commit(struct ref_transaction *transaction,
struct ref_update *update = updates[i];
if (update->lock) {
- delnames[delnum++] = update->lock->ref_name;
- ret |= delete_ref_loose(update->lock, update->type);
+ if (delete_ref_loose(update->lock, update->type, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ if (!(update->flags & REF_ISPRUNING))
+ delnames[delnum++] = update->lock->ref_name;
}
}
- ret |= repack_without_refs(delnames, delnum, err);
+ if (repack_without_refs(delnames, delnum, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);
cleanup:
+ transaction->state = REF_TRANSACTION_CLOSED;
+
for (i = 0; i < n; i++)
if (updates[i]->lock)
unlock_ref(updates[i]->lock);
diff --git a/refs.h b/refs.h
index ec46acdde7..2bc3556874 100644
--- a/refs.h
+++ b/refs.h
@@ -10,6 +10,38 @@ struct ref_lock {
int force_write;
};
+/*
+ * A ref_transaction represents a collection of ref updates
+ * that should succeed or fail together.
+ *
+ * Calling sequence
+ * ----------------
+ * - Allocate and initialize a `struct ref_transaction` by calling
+ * `ref_transaction_begin()`.
+ *
+ * - List intended ref updates by calling functions like
+ * `ref_transaction_update()` and `ref_transaction_create()`.
+ *
+ * - Call `ref_transaction_commit()` to execute the transaction.
+ * If this succeeds, the ref updates will have taken place and
+ * the transaction cannot be rolled back.
+ *
+ * - At any time call `ref_transaction_free()` to discard the
+ * transaction and free associated resources. In particular,
+ * this rolls back the transaction if it has not been
+ * successfully committed.
+ *
+ * Error handling
+ * --------------
+ *
+ * On error, transaction functions append a message about what
+ * went wrong to the 'err' argument. The message mentions what
+ * ref was being updated (if any) when the error occurred so it
+ * can be passed to 'die' or 'error' as-is.
+ *
+ * The message is appended to err without first clearing err.
+ * err will not be '\n' terminated.
+ */
struct ref_transaction;
/*
@@ -24,12 +56,20 @@ struct ref_transaction;
/*
* Reference cannot be resolved to an object name: dangling symbolic
- * reference (directly or indirectly), corrupt reference file, or
- * symbolic reference refers to ill-formatted reference name.
+ * reference (directly or indirectly), corrupt reference file,
+ * reference exists but name is bad, or symbolic reference refers to
+ * ill-formatted reference name.
*/
#define REF_ISBROKEN 0x04
/*
+ * Reference name is not well formed.
+ *
+ * See git-check-ref-format(1) for the definition of well formed ref names.
+ */
+#define REF_BAD_NAME 0x08
+
+/*
* The signature for the callback function for the for_each_*()
* functions below. The memory pointed to by the refname and sha1
* arguments is only guaranteed to be valid for the duration of a
@@ -77,7 +117,7 @@ static inline const char *has_glob_specials(const char *pattern)
extern int for_each_rawref(each_ref_fn, void *);
extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
-extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list* refnames);
+extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames);
/*
* Lock the packed-refs file for writing. Flags is passed to
@@ -141,14 +181,19 @@ extern int is_branch(const char *refname);
extern int peel_ref(const char *refname, unsigned char *sha1);
/*
- * Locks a "refs/" ref returning the lock on success and NULL on failure.
- * On failure errno is set to something meaningful.
+ * Flags controlling lock_any_ref_for_update(), ref_transaction_update(),
+ * ref_transaction_create(), etc.
+ * REF_NODEREF: act on the ref directly, instead of dereferencing
+ * symbolic references.
+ * REF_DELETING: tolerate broken refs
+ *
+ * Flags >= 0x100 are reserved for internal use.
*/
-extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1);
-
-/** Locks any ref (for 'HEAD' type refs). */
#define REF_NODEREF 0x01
-/* errno is set to something meaningful on failure */
+#define REF_DELETING 0x02
+/*
+ * This function sets errno to something meaningful on failure.
+ */
extern struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p);
@@ -162,16 +207,14 @@ extern int commit_ref(struct ref_lock *lock);
/** Release any lock taken but not written. **/
extern void unlock_ref(struct ref_lock *lock);
-/** Writes sha1 into the ref specified by the lock. **/
-extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
-
/*
* Setup reflog before using. Set errno to something meaningful on failure.
*/
int log_ref_setup(const char *refname, char *logfile, int bufsize);
/** Reads log for the value of ref during at_time. **/
-extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
+extern int read_ref_at(const char *refname, unsigned int flags,
+ unsigned long at_time, int cnt,
unsigned char *sha1, char **msg,
unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
@@ -194,7 +237,6 @@ extern int for_each_reflog(each_ref_fn, void *);
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
-#define REFNAME_DOT_COMPONENT 4
/*
* Return 0 iff refname has the correct format for a refname according
@@ -202,10 +244,7 @@ extern int for_each_reflog(each_ref_fn, void *);
* If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
* reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then
* allow a "*" wildcard character in place of one of the name
- * components. No leading or repeated slashes are accepted. If
- * REFNAME_DOT_COMPONENT is set in flags, then allow refname
- * components to start with "." (but not a whole component equal to
- * "." or "..").
+ * components. No leading or repeated slashes are accepted.
*/
extern int check_refname_format(const char *refname, int flags);
@@ -232,14 +271,14 @@ enum action_on_err {
* Begin a reference transaction. The reference transaction must
* be freed by calling ref_transaction_free().
*/
-struct ref_transaction *ref_transaction_begin(void);
+struct ref_transaction *ref_transaction_begin(struct strbuf *err);
/*
* The following functions add a reference check or update to a
* ref_transaction. In all of them, refname is the name of the
* reference to be affected. The functions make internal copies of
- * refname, so the caller retains ownership of the parameter. flags
- * can be REF_NODEREF; it is passed to update_ref_lock().
+ * refname and msg, so the caller retains ownership of these parameters.
+ * flags can be REF_NODEREF; it is passed to update_ref_lock().
*/
/*
@@ -250,13 +289,13 @@ struct ref_transaction *ref_transaction_begin(void);
* it must not have existed beforehand.
* Function returns 0 on success and non-zero on failure. A failure to update
* means that the transaction as a whole has failed and will need to be
- * rolled back. On failure the err buffer will be updated.
+ * rolled back.
*/
int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err);
/*
@@ -264,31 +303,42 @@ int ref_transaction_update(struct ref_transaction *transaction,
* that the reference should have after the update; it must not be the
* null SHA-1. It is verified that the reference does not exist
* already.
+ * Function returns 0 on success and non-zero on failure. A failure to create
+ * means that the transaction as a whole has failed and will need to be
+ * rolled back.
*/
-void ref_transaction_create(struct ref_transaction *transaction,
- const char *refname,
- const unsigned char *new_sha1,
- int flags);
+int ref_transaction_create(struct ref_transaction *transaction,
+ const char *refname,
+ const unsigned char *new_sha1,
+ int flags, const char *msg,
+ struct strbuf *err);
/*
* Add a reference deletion to transaction. If have_old is true, then
* old_sha1 holds the value that the reference should have had before
* the update (which must not be the null SHA-1).
+ * Function returns 0 on success and non-zero on failure. A failure to delete
+ * means that the transaction as a whole has failed and will need to be
+ * rolled back.
*/
-void ref_transaction_delete(struct ref_transaction *transaction,
- const char *refname,
- const unsigned char *old_sha1,
- int flags, int have_old);
+int ref_transaction_delete(struct ref_transaction *transaction,
+ const char *refname,
+ const unsigned char *old_sha1,
+ int flags, int have_old, const char *msg,
+ struct strbuf *err);
/*
* Commit all of the changes that have been queued in transaction, as
- * atomically as possible. Return a nonzero value if there is a
- * problem.
- * If err is non-NULL we will add an error string to it to explain why
- * the transaction failed. The string does not end in newline.
+ * atomically as possible.
+ *
+ * Returns 0 for success, or one of the below error codes for errors.
*/
+/* Naming conflict (for example, the ref names A and A/B conflict). */
+#define TRANSACTION_NAME_CONFLICT -1
+/* All other errors. */
+#define TRANSACTION_GENERIC_ERROR -2
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, struct strbuf *err);
+ struct strbuf *err);
/*
* Free an existing transaction and all associated data.
diff --git a/remote-curl.c b/remote-curl.c
index 558b9fecdf..dd63bc27ab 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -25,7 +25,8 @@ struct options {
update_shallow : 1,
followtags : 1,
dry_run : 1,
- thin : 1;
+ thin : 1,
+ push_cert : 1;
};
static struct options options;
static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -106,6 +107,14 @@ static int set_option(const char *name, const char *value)
else
return -1;
return 0;
+ } else if (!strcmp(name, "pushcert")) {
+ if (!strcmp(value, "true"))
+ options.push_cert = 1;
+ else if (!strcmp(value, "false"))
+ options.push_cert = 0;
+ else
+ return -1;
+ return 0;
} else {
return 1 /* unsupported */;
}
@@ -221,7 +230,7 @@ static int show_http_message(struct strbuf *type, struct strbuf *charset,
return 0;
}
-static struct discovery* discover_refs(const char *service, int for_push)
+static struct discovery *discover_refs(const char *service, int for_push)
{
struct strbuf exp = STRBUF_INIT;
struct strbuf type = STRBUF_INIT;
@@ -623,10 +632,9 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
const char *svc = rpc->service_name;
struct strbuf buf = STRBUF_INIT;
struct strbuf *preamble = rpc->stdin_preamble;
- struct child_process client;
+ struct child_process client = CHILD_PROCESS_INIT;
int err = 0;
- memset(&client, 0, sizeof(client));
client.in = -1;
client.out = -1;
client.git_cmd = 1;
@@ -873,6 +881,8 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
argv_array_push(&args, "--thin");
if (options.dry_run)
argv_array_push(&args, "--dry-run");
+ if (options.push_cert)
+ argv_array_push(&args, "--signed");
if (options.verbosity == 0)
argv_array_push(&args, "--quiet");
else if (options.verbosity > 1)
diff --git a/remote-testsvn.c b/remote-testsvn.c
index 686e07d317..48bf6eb93b 100644
--- a/remote-testsvn.c
+++ b/remote-testsvn.c
@@ -175,7 +175,7 @@ static int cmd_import(const char *line)
char *note_msg;
unsigned char head_sha1[20];
unsigned int startrev;
- struct child_process svndump_proc;
+ struct child_process svndump_proc = CHILD_PROCESS_INIT;
const char *command = "svnrdump";
if (read_ref(private_ref, head_sha1))
@@ -200,7 +200,6 @@ static int cmd_import(const char *line)
if(dumpin_fd < 0)
die_errno("Couldn't open svn dump file %s.", url);
} else {
- memset(&svndump_proc, 0, sizeof(struct child_process));
svndump_proc.out = -1;
argv_array_push(&svndump_proc.args, command);
argv_array_push(&svndump_proc.args, "dump");
diff --git a/remote.c b/remote.c
index 3d6c86a36f..f62421702f 100644
--- a/remote.c
+++ b/remote.c
@@ -42,6 +42,7 @@ struct rewrites {
static struct remote **remotes;
static int remotes_alloc;
static int remotes_nr;
+static struct hashmap remotes_hash;
static struct branch **branches;
static int branches_alloc;
@@ -136,26 +137,51 @@ static void add_url_alias(struct remote *remote, const char *url)
add_pushurl_alias(remote, url);
}
+struct remotes_hash_key {
+ const char *str;
+ int len;
+};
+
+static int remotes_hash_cmp(const struct remote *a, const struct remote *b, const struct remotes_hash_key *key)
+{
+ if (key)
+ return strncmp(a->name, key->str, key->len) || a->name[key->len];
+ else
+ return strcmp(a->name, b->name);
+}
+
+static inline void init_remotes_hash(void)
+{
+ if (!remotes_hash.cmpfn)
+ hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, 0);
+}
+
static struct remote *make_remote(const char *name, int len)
{
- struct remote *ret;
- int i;
+ struct remote *ret, *replaced;
+ struct remotes_hash_key lookup;
+ struct hashmap_entry lookup_entry;
- for (i = 0; i < remotes_nr; i++) {
- if (len ? (!strncmp(name, remotes[i]->name, len) &&
- !remotes[i]->name[len]) :
- !strcmp(name, remotes[i]->name))
- return remotes[i];
- }
+ if (!len)
+ len = strlen(name);
+
+ init_remotes_hash();
+ lookup.str = name;
+ lookup.len = len;
+ hashmap_entry_init(&lookup_entry, memhash(name, len));
+
+ if ((ret = hashmap_get(&remotes_hash, &lookup_entry, &lookup)) != NULL)
+ return ret;
ret = xcalloc(1, sizeof(struct remote));
ret->prune = -1; /* unspecified */
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
remotes[remotes_nr++] = ret;
- if (len)
- ret->name = xstrndup(name, len);
- else
- ret->name = xstrdup(name);
+ ret->name = xstrndup(name, len);
+
+ hashmap_entry_init(ret, lookup_entry.hash);
+ replaced = hashmap_put(&remotes_hash, ret);
+ assert(replaced == NULL); /* no previous entry overwritten */
return ret;
}
@@ -482,7 +508,7 @@ static void read_config(void)
return;
default_remote_name = "origin";
current_branch = NULL;
- head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
+ head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
skip_prefix(head_ref, "refs/heads/", &head_ref)) {
current_branch = make_branch(head_ref, 0);
@@ -717,13 +743,16 @@ struct remote *pushremote_get(const char *name)
int remote_is_configured(const char *name)
{
- int i;
+ struct remotes_hash_key lookup;
+ struct hashmap_entry lookup_entry;
read_config();
- for (i = 0; i < remotes_nr; i++)
- if (!strcmp(name, remotes[i]->name))
- return 1;
- return 0;
+ init_remotes_hash();
+ lookup.str = name;
+ lookup.len = strlen(name);
+ hashmap_entry_init(&lookup_entry, memhash(name, lookup.len));
+
+ return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL;
}
int for_each_remote(each_remote_fn fn, void *priv)
@@ -833,21 +862,14 @@ static int match_name_with_pattern(const char *key, const char *name,
ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
!memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
if (ret && value) {
+ struct strbuf sb = STRBUF_INIT;
const char *vstar = strchr(value, '*');
- size_t vlen;
- size_t vsuffixlen;
if (!vstar)
die("Value '%s' of pattern has no '*'", value);
- vlen = vstar - value;
- vsuffixlen = strlen(vstar + 1);
- *result = xmalloc(vlen + vsuffixlen +
- strlen(name) -
- klen - ksuffixlen + 1);
- strncpy(*result, value, vlen);
- strncpy(*result + vlen,
- name + klen, namelen - klen - ksuffixlen);
- strcpy(*result + vlen + namelen - klen - ksuffixlen,
- vstar + 1);
+ strbuf_add(&sb, value, vstar - value);
+ strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen);
+ strbuf_addstr(&sb, vstar + 1);
+ *result = strbuf_detach(&sb, NULL);
}
return ret;
}
@@ -1116,7 +1138,8 @@ static char *guess_ref(const char *name, struct ref *peer)
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
+ const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
+ sha1, NULL);
if (!r)
return NULL;
@@ -1177,7 +1200,9 @@ static int match_explicit(struct ref *src, struct ref *dst,
unsigned char sha1[20];
int flag;
- dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
+ dst_value = resolve_ref_unsafe(matched_src->name,
+ RESOLVE_REF_READING,
+ sha1, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
!starts_with(dst_value, "refs/heads/")))
@@ -1651,7 +1676,7 @@ static int ignore_symref_update(const char *refname)
unsigned char sha1[20];
int flag;
- if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
+ if (!resolve_ref_unsafe(refname, 0, sha1, &flag))
return 0; /* non-existing refs are OK */
return (flag & REF_ISSYMREF);
}
@@ -1893,7 +1918,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
init_revisions(&revs, NULL);
setup_revisions(rev_argc, rev_argv, &revs, NULL);
- prepare_revision_walk(&revs);
+ if (prepare_revision_walk(&revs))
+ die("revision walk setup failed");
/* ... and count the commits on each side. */
*num_ours = 0;
@@ -1920,7 +1946,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
int format_tracking_info(struct branch *branch, struct strbuf *sb)
{
int ours, theirs;
- const char *base;
+ char *base;
int upstream_is_gone = 0;
switch (stat_tracking_info(branch, &ours, &theirs)) {
@@ -1936,8 +1962,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
break;
}
- base = branch->merge[0]->dst;
- base = shorten_unambiguous_ref(base, 0);
+ base = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
if (upstream_is_gone) {
strbuf_addf(sb,
_("Your branch is based on '%s', but the upstream is gone.\n"),
@@ -1983,6 +2008,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
strbuf_addf(sb,
_(" (use \"git pull\" to merge the remote branch into yours)\n"));
}
+ free(base);
return 1;
}
diff --git a/remote.h b/remote.h
index 917d383a80..8b62efd2ad 100644
--- a/remote.h
+++ b/remote.h
@@ -2,6 +2,7 @@
#define REMOTE_H
#include "parse-options.h"
+#include "hashmap.h"
enum {
REMOTE_CONFIG,
@@ -10,6 +11,8 @@ enum {
};
struct remote {
+ struct hashmap_entry ent; /* must be first */
+
const char *name;
int origin;
diff --git a/rerere.c b/rerere.c
index d84b495895..1b0555f1a5 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "string-list.h"
#include "rerere.h"
#include "xdiff-interface.h"
@@ -573,15 +574,11 @@ static int do_plain_rerere(struct string_list *rr, int fd)
return write_rr(rr, fd);
}
-static int git_rerere_config(const char *var, const char *value, void *cb)
+static void git_rerere_config(void)
{
- if (!strcmp(var, "rerere.enabled"))
- rerere_enabled = git_config_bool(var, value);
- else if (!strcmp(var, "rerere.autoupdate"))
- rerere_autoupdate = git_config_bool(var, value);
- else
- return git_default_config(var, value, cb);
- return 0;
+ git_config_get_bool("rerere.enabled", &rerere_enabled);
+ git_config_get_bool("rerere.autoupdate", &rerere_autoupdate);
+ git_config(git_default_config, NULL);
}
static int is_rerere_enabled(void)
@@ -606,7 +603,7 @@ int setup_rerere(struct string_list *merge_rr, int flags)
{
int fd;
- git_config(git_rerere_config, NULL);
+ git_rerere_config();
if (!is_rerere_enabled())
return -1;
@@ -699,24 +696,6 @@ static void unlink_rr_item(const char *name)
rmdir(git_path("rr-cache/%s", name));
}
-struct rerere_gc_config_cb {
- int cutoff_noresolve;
- int cutoff_resolve;
-};
-
-static int git_rerere_gc_config(const char *var, const char *value, void *cb)
-{
- struct rerere_gc_config_cb *cf = cb;
-
- if (!strcmp(var, "gc.rerereresolved"))
- cf->cutoff_resolve = git_config_int(var, value);
- else if (!strcmp(var, "gc.rerereunresolved"))
- cf->cutoff_noresolve = git_config_int(var, value);
- else
- return git_default_config(var, value, cb);
- return 0;
-}
-
void rerere_gc(struct string_list *rr)
{
struct string_list to_remove = STRING_LIST_INIT_DUP;
@@ -724,9 +703,12 @@ void rerere_gc(struct string_list *rr)
struct dirent *e;
int i, cutoff;
time_t now = time(NULL), then;
- struct rerere_gc_config_cb cf = { 15, 60 };
+ int cutoff_noresolve = 15;
+ int cutoff_resolve = 60;
- git_config(git_rerere_gc_config, &cf);
+ git_config_get_int("gc.rerereresolved", &cutoff_resolve);
+ git_config_get_int("gc.rerereunresolved", &cutoff_noresolve);
+ git_config(git_default_config, NULL);
dir = opendir(git_path("rr-cache"));
if (!dir)
die_errno("unable to open rr-cache directory");
@@ -736,12 +718,12 @@ void rerere_gc(struct string_list *rr)
then = rerere_last_used_at(e->d_name);
if (then) {
- cutoff = cf.cutoff_resolve;
+ cutoff = cutoff_resolve;
} else {
then = rerere_created_at(e->d_name);
if (!then)
continue;
- cutoff = cf.cutoff_noresolve;
+ cutoff = cutoff_noresolve;
}
if (then < now - cutoff * 86400)
string_list_append(&to_remove, e->d_name);
diff --git a/rerere.h b/rerere.h
index 4aa06c9a51..2956c2edf2 100644
--- a/rerere.h
+++ b/rerere.h
@@ -24,6 +24,6 @@ extern void rerere_clear(struct string_list *);
extern void rerere_gc(struct string_list *);
#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
- "update the index with reused conflict resolution if possible")
+ N_("update the index with reused conflict resolution if possible"))
#endif
diff --git a/revision.c b/revision.c
index 615535c984..75dda928ea 100644
--- a/revision.c
+++ b/revision.c
@@ -17,6 +17,7 @@
#include "mailmap.h"
#include "commit-slab.h"
#include "dir.h"
+#include "cache-tree.h"
volatile show_early_output_fn_t show_early_output;
@@ -86,16 +87,6 @@ void show_object_with_name(FILE *out, struct object *obj,
fputc('\n', out);
}
-void add_object(struct object *obj,
- struct object_array *p,
- struct name_path *path,
- const char *name)
-{
- char *pn = path_name(path, name);
- add_object_array(obj, pn, p);
- free(pn);
-}
-
static void mark_blob_uninteresting(struct blob *blob)
{
if (!blob)
@@ -198,9 +189,10 @@ void mark_parents_uninteresting(struct commit *commit)
}
}
-static void add_pending_object_with_mode(struct rev_info *revs,
+static void add_pending_object_with_path(struct rev_info *revs,
struct object *obj,
- const char *name, unsigned mode)
+ const char *name, unsigned mode,
+ const char *path)
{
if (!obj)
return;
@@ -220,7 +212,14 @@ static void add_pending_object_with_mode(struct rev_info *revs,
if (st)
return;
}
- add_object_array_with_mode(obj, name, &revs->pending, mode);
+ add_object_array_with_path(obj, name, &revs->pending, mode, path);
+}
+
+static void add_pending_object_with_mode(struct rev_info *revs,
+ struct object *obj,
+ const char *name, unsigned mode)
+{
+ add_pending_object_with_path(revs, obj, name, mode, NULL);
}
void add_pending_object(struct rev_info *revs,
@@ -265,8 +264,12 @@ void add_pending_sha1(struct rev_info *revs, const char *name,
}
static struct commit *handle_commit(struct rev_info *revs,
- struct object *object, const char *name)
+ struct object_array_entry *entry)
{
+ struct object *object = entry->item;
+ const char *name = entry->name;
+ const char *path = entry->path;
+ unsigned int mode = entry->mode;
unsigned long flags = object->flags;
/*
@@ -285,6 +288,14 @@ static struct commit *handle_commit(struct rev_info *revs,
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
}
object->flags |= flags;
+ /*
+ * We'll handle the tagged object by looping or dropping
+ * through to the non-tag handlers below. Do not
+ * propagate data from the tag's pending entry.
+ */
+ name = "";
+ path = NULL;
+ mode = 0;
}
/*
@@ -300,7 +311,7 @@ static struct commit *handle_commit(struct rev_info *revs,
revs->limited = 1;
}
if (revs->show_source && !commit->util)
- commit->util = (void *) name;
+ commit->util = xstrdup(name);
return commit;
}
@@ -316,7 +327,7 @@ static struct commit *handle_commit(struct rev_info *revs,
mark_tree_contents_uninteresting(tree);
return NULL;
}
- add_pending_object(revs, object, "");
+ add_pending_object_with_path(revs, object, name, mode, path);
return NULL;
}
@@ -328,7 +339,7 @@ static struct commit *handle_commit(struct rev_info *revs,
return NULL;
if (flags & UNINTERESTING)
return NULL;
- add_pending_object(revs, object, "");
+ add_pending_object_with_path(revs, object, name, mode, path);
return NULL;
}
die("%s is unknown object", name);
@@ -473,7 +484,7 @@ static int rev_compare_tree(struct rev_info *revs,
* If we are simplifying by decoration, then the commit
* is worth showing if it has a tag pointing at it.
*/
- if (lookup_decoration(&name_decoration, &commit->object))
+ if (get_name_decoration(&commit->object))
return REV_TREE_DIFFERENT;
/*
* A commit that is not pointed by a tag is uninteresting
@@ -1275,7 +1286,7 @@ static int handle_one_reflog(const char *path, const unsigned char *sha1, int fl
return 0;
}
-static void handle_reflog(struct rev_info *revs, unsigned flags)
+void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
{
struct all_refs_cb cb;
cb.all_revs = revs;
@@ -1283,6 +1294,53 @@ static void handle_reflog(struct rev_info *revs, unsigned flags)
for_each_reflog(handle_one_reflog, &cb);
}
+static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
+ struct strbuf *path)
+{
+ size_t baselen = path->len;
+ int i;
+
+ if (it->entry_count >= 0) {
+ struct tree *tree = lookup_tree(it->sha1);
+ add_pending_object_with_path(revs, &tree->object, "",
+ 040000, path->buf);
+ }
+
+ for (i = 0; i < it->subtree_nr; i++) {
+ struct cache_tree_sub *sub = it->down[i];
+ strbuf_addf(path, "%s%s", baselen ? "/" : "", sub->name);
+ add_cache_tree(sub->cache_tree, revs, path);
+ strbuf_setlen(path, baselen);
+ }
+
+}
+
+void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
+{
+ int i;
+
+ read_cache();
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ struct blob *blob;
+
+ if (S_ISGITLINK(ce->ce_mode))
+ continue;
+
+ blob = lookup_blob(ce->sha1);
+ if (!blob)
+ die("unable to add index blob to traversal");
+ add_pending_object_with_path(revs, &blob->object, "",
+ ce->ce_mode, ce->name);
+ }
+
+ if (active_cache_tree) {
+ struct strbuf path = STRBUF_INIT;
+ add_cache_tree(active_cache_tree, revs, &path);
+ strbuf_release(&path);
+ }
+}
+
static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
{
unsigned char sha1[20];
@@ -1397,7 +1455,7 @@ static void prepare_show_merge(struct rev_info *revs)
continue;
if (ce_path_match(ce, &revs->prune_data, NULL)) {
prune_num++;
- prune = xrealloc(prune, sizeof(*prune) * prune_num);
+ REALLOC_ARRAY(prune, prune_num);
prune[prune_num-2] = ce->name;
prune[prune_num-1] = NULL;
}
@@ -1633,6 +1691,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
!strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
!strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
!strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
+ !strcmp(arg, "--indexed-objects") ||
starts_with(arg, "--exclude=") ||
starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
@@ -2061,7 +2120,9 @@ static int handle_revision_pseudo_opt(const char *submodule,
for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
clear_ref_exclusion(&revs->ref_excludes);
} else if (!strcmp(arg, "--reflog")) {
- handle_reflog(revs, *flags);
+ add_reflogs_to_pending(revs, *flags);
+ } else if (!strcmp(arg, "--indexed-objects")) {
+ add_index_objects_to_pending(revs, *flags);
} else if (!strcmp(arg, "--not")) {
*flags ^= UNINTERESTING | BOTTOM;
} else if (!strcmp(arg, "--no-walk")) {
@@ -2656,26 +2717,26 @@ void reset_revision_walk(void)
int prepare_revision_walk(struct rev_info *revs)
{
- int nr = revs->pending.nr;
- struct object_array_entry *e, *list;
+ int i;
+ struct object_array old_pending;
struct commit_list **next = &revs->commits;
- e = list = revs->pending.objects;
+ memcpy(&old_pending, &revs->pending, sizeof(old_pending));
revs->pending.nr = 0;
revs->pending.alloc = 0;
revs->pending.objects = NULL;
- while (--nr >= 0) {
- struct commit *commit = handle_commit(revs, e->item, e->name);
+ for (i = 0; i < old_pending.nr; i++) {
+ struct object_array_entry *e = old_pending.objects + i;
+ struct commit *commit = handle_commit(revs, e);
if (commit) {
if (!(commit->object.flags & SEEN)) {
commit->object.flags |= SEEN;
next = commit_list_append(commit, next);
}
}
- e++;
}
if (!revs->leak_pending)
- free(list);
+ object_array_clear(&old_pending);
/* Signal whether we need per-parent treesame decoration */
if (revs->simplify_merges ||
diff --git a/revision.h b/revision.h
index a6205307cf..9cb5adc4ea 100644
--- a/revision.h
+++ b/revision.h
@@ -264,11 +264,6 @@ char *path_name(const struct name_path *path, const char *name);
extern void show_object_with_name(FILE *, struct object *,
const struct name_path *, const char *);
-extern void add_object(struct object *obj,
- struct object_array *p,
- struct name_path *path,
- const char *name);
-
extern void add_pending_object(struct rev_info *revs,
struct object *obj, const char *name);
extern void add_pending_sha1(struct rev_info *revs,
@@ -276,6 +271,8 @@ extern void add_pending_sha1(struct rev_info *revs,
unsigned int flags);
extern void add_head_to_pending(struct rev_info *);
+extern void add_reflogs_to_pending(struct rev_info *, unsigned int flags);
+extern void add_index_objects_to_pending(struct rev_info *, unsigned int flags);
enum commit_action {
commit_ignore,
diff --git a/run-command.c b/run-command.c
index 35a3ebf07b..79a0a763ec 100644
--- a/run-command.c
+++ b/run-command.c
@@ -8,6 +8,13 @@
# define SHELL_PATH "/bin/sh"
#endif
+void child_process_init(struct child_process *child)
+{
+ memset(child, 0, sizeof(*child));
+ argv_array_init(&child->args);
+ argv_array_init(&child->env_array);
+}
+
struct child_to_clean {
pid_t pid;
struct child_to_clean *next;
@@ -281,6 +288,8 @@ int start_command(struct child_process *cmd)
if (!cmd->argv)
cmd->argv = cmd->args.argv;
+ if (!cmd->env)
+ cmd->env = cmd->env_array.argv;
/*
* In case of errors we must keep the promise to close FDs
@@ -332,6 +341,7 @@ fail_pipe:
error("cannot create %s pipe for %s: %s",
str, cmd->argv[0], strerror(failed_errno));
argv_array_clear(&cmd->args);
+ argv_array_clear(&cmd->env_array);
errno = failed_errno;
return -1;
}
@@ -518,6 +528,7 @@ fail_pipe:
else if (cmd->err)
close(cmd->err);
argv_array_clear(&cmd->args);
+ argv_array_clear(&cmd->env_array);
errno = failed_errno;
return -1;
}
@@ -544,6 +555,7 @@ int finish_command(struct child_process *cmd)
{
int ret = wait_or_whine(cmd->pid, cmd->argv[0]);
argv_array_clear(&cmd->args);
+ argv_array_clear(&cmd->env_array);
return ret;
}
@@ -555,31 +567,21 @@ int run_command(struct child_process *cmd)
return finish_command(cmd);
}
-static void prepare_run_command_v_opt(struct child_process *cmd,
- const char **argv,
- int opt)
-{
- memset(cmd, 0, sizeof(*cmd));
- cmd->argv = argv;
- cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
- cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
- cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
- cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
- cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0;
- cmd->clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;
-}
-
int run_command_v_opt(const char **argv, int opt)
{
- struct child_process cmd;
- prepare_run_command_v_opt(&cmd, argv, opt);
- return run_command(&cmd);
+ return run_command_v_opt_cd_env(argv, opt, NULL, NULL);
}
int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
{
- struct child_process cmd;
- prepare_run_command_v_opt(&cmd, argv, opt);
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ cmd.argv = argv;
+ cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
+ cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
+ cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+ cmd.silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
+ cmd.use_shell = opt & RUN_USING_SHELL ? 1 : 0;
+ cmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;
cmd.dir = dir;
cmd.env = env;
return run_command(&cmd);
@@ -624,6 +626,45 @@ static int async_die_is_recursing(void)
return ret != NULL;
}
+#else
+
+static struct {
+ void (**handlers)(void);
+ size_t nr;
+ size_t alloc;
+} git_atexit_hdlrs;
+
+static int git_atexit_installed;
+
+static void git_atexit_dispatch()
+{
+ size_t i;
+
+ for (i=git_atexit_hdlrs.nr ; i ; i--)
+ git_atexit_hdlrs.handlers[i-1]();
+}
+
+static void git_atexit_clear()
+{
+ free(git_atexit_hdlrs.handlers);
+ memset(&git_atexit_hdlrs, 0, sizeof(git_atexit_hdlrs));
+ git_atexit_installed = 0;
+}
+
+#undef atexit
+int git_atexit(void (*handler)(void))
+{
+ ALLOC_GROW(git_atexit_hdlrs.handlers, git_atexit_hdlrs.nr + 1, git_atexit_hdlrs.alloc);
+ git_atexit_hdlrs.handlers[git_atexit_hdlrs.nr++] = handler;
+ if (!git_atexit_installed) {
+ if (atexit(&git_atexit_dispatch))
+ return -1;
+ git_atexit_installed = 1;
+ }
+ return 0;
+}
+#define atexit git_atexit
+
#endif
int start_async(struct async *async)
@@ -682,6 +723,7 @@ int start_async(struct async *async)
close(fdin[1]);
if (need_out)
close(fdout[0]);
+ git_atexit_clear();
exit(!!async->proc(proc_in, proc_out, async->data));
}
@@ -763,14 +805,13 @@ char *find_hook(const char *name)
int run_hook_ve(const char *const *env, const char *name, va_list args)
{
- struct child_process hook;
+ struct child_process hook = CHILD_PROCESS_INIT;
const char *p;
p = find_hook(name);
if (!p)
return 0;
- memset(&hook, 0, sizeof(hook));
argv_array_push(&hook.args, p);
while ((p = va_arg(args, const char *)))
argv_array_push(&hook.args, p);
diff --git a/run-command.h b/run-command.h
index ea73de309b..2137315ee4 100644
--- a/run-command.h
+++ b/run-command.h
@@ -10,6 +10,7 @@
struct child_process {
const char **argv;
struct argv_array args;
+ struct argv_array env_array;
pid_t pid;
/*
* Using .in, .out, .err:
@@ -44,6 +45,9 @@ struct child_process {
unsigned clean_on_exit:1;
};
+#define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
+void child_process_init(struct child_process *);
+
int start_command(struct child_process *);
int finish_command(struct child_process *);
int run_command(struct child_process *);
diff --git a/send-pack.c b/send-pack.c
index 6129b0fd8e..949cb61aa0 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -11,6 +11,7 @@
#include "transport.h"
#include "version.h"
#include "sha1-array.h"
+#include "gpg-interface.h"
static int feed_object(const unsigned char *sha1, int fd, int negative)
{
@@ -47,7 +48,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
NULL,
NULL,
};
- struct child_process po;
+ struct child_process po = CHILD_PROCESS_INIT;
int i;
i = 4;
@@ -59,7 +60,6 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
argv[i++] = "-q";
if (args->progress)
argv[i++] = "--progress";
- memset(&po, 0, sizeof(po));
po.argv = argv;
po.in = -1;
po.out = args->stateless_rpc ? -1 : fd;
@@ -190,6 +190,94 @@ static void advertise_shallow_grafts_buf(struct strbuf *sb)
for_each_commit_graft(advertise_shallow_grafts_cb, sb);
}
+static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_args *args)
+{
+ if (!ref->peer_ref && !args->send_mirror)
+ return 0;
+
+ /* Check for statuses set by set_ref_status_for_push() */
+ switch (ref->status) {
+ case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
+ case REF_STATUS_REJECT_FETCH_FIRST:
+ case REF_STATUS_REJECT_NEEDS_FORCE:
+ case REF_STATUS_REJECT_STALE:
+ case REF_STATUS_REJECT_NODELETE:
+ case REF_STATUS_UPTODATE:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+/*
+ * the beginning of the next line, or the end of buffer.
+ *
+ * NEEDSWORK: perhaps move this to git-compat-util.h or somewhere and
+ * convert many similar uses found by "git grep -A4 memchr".
+ */
+static const char *next_line(const char *line, size_t len)
+{
+ const char *nl = memchr(line, '\n', len);
+ if (!nl)
+ return line + len; /* incomplete line */
+ return nl + 1;
+}
+
+static int generate_push_cert(struct strbuf *req_buf,
+ const struct ref *remote_refs,
+ struct send_pack_args *args,
+ const char *cap_string,
+ const char *push_cert_nonce)
+{
+ const struct ref *ref;
+ char *signing_key = xstrdup(get_signing_key());
+ const char *cp, *np;
+ struct strbuf cert = STRBUF_INIT;
+ int update_seen = 0;
+
+ strbuf_addf(&cert, "certificate version 0.1\n");
+ strbuf_addf(&cert, "pusher %s ", signing_key);
+ datestamp(&cert);
+ strbuf_addch(&cert, '\n');
+ if (args->url && *args->url) {
+ char *anon_url = transport_anonymize_url(args->url);
+ strbuf_addf(&cert, "pushee %s\n", anon_url);
+ free(anon_url);
+ }
+ if (push_cert_nonce[0])
+ strbuf_addf(&cert, "nonce %s\n", push_cert_nonce);
+ strbuf_addstr(&cert, "\n");
+
+ for (ref = remote_refs; ref; ref = ref->next) {
+ if (!ref_update_to_be_sent(ref, args))
+ continue;
+ update_seen = 1;
+ strbuf_addf(&cert, "%s %s %s\n",
+ sha1_to_hex(ref->old_sha1),
+ sha1_to_hex(ref->new_sha1),
+ ref->name);
+ }
+ if (!update_seen)
+ goto free_return;
+
+ if (sign_buffer(&cert, &cert, signing_key))
+ die(_("failed to sign the push certificate"));
+
+ packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string);
+ for (cp = cert.buf; cp < cert.buf + cert.len; cp = np) {
+ np = next_line(cp, cert.buf + cert.len - cp);
+ packet_buf_write(req_buf,
+ "%.*s", (int)(np - cp), cp);
+ }
+ packet_buf_write(req_buf, "push-cert-end\n");
+
+free_return:
+ free(signing_key);
+ strbuf_release(&cert);
+ return update_seen;
+}
+
int send_pack(struct send_pack_args *args,
int fd[], struct child_process *conn,
struct ref *remote_refs,
@@ -198,8 +286,9 @@ int send_pack(struct send_pack_args *args,
int in = fd[0];
int out = fd[1];
struct strbuf req_buf = STRBUF_INIT;
+ struct strbuf cap_buf = STRBUF_INIT;
struct ref *ref;
- int new_refs;
+ int need_pack_data = 0;
int allow_deleting_refs = 0;
int status_report = 0;
int use_sideband = 0;
@@ -208,6 +297,7 @@ int send_pack(struct send_pack_args *args,
unsigned cmds_sent = 0;
int ret;
struct async demux;
+ const char *push_cert_nonce = NULL;
/* Does the other end support the reporting? */
if (server_supports("report-status"))
@@ -224,6 +314,14 @@ int send_pack(struct send_pack_args *args,
agent_supported = 1;
if (server_supports("no-thin"))
args->use_thin_pack = 0;
+ if (args->push_cert) {
+ int len;
+
+ push_cert_nonce = server_feature_value("push-cert", &len);
+ if (!push_cert_nonce)
+ die(_("the receiving end does not support --signed push"));
+ push_cert_nonce = xmemdupz(push_cert_nonce, len);
+ }
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -231,64 +329,71 @@ int send_pack(struct send_pack_args *args,
return 0;
}
+ if (status_report)
+ strbuf_addstr(&cap_buf, " report-status");
+ if (use_sideband)
+ strbuf_addstr(&cap_buf, " side-band-64k");
+ if (quiet_supported && (args->quiet || !args->progress))
+ strbuf_addstr(&cap_buf, " quiet");
+ if (agent_supported)
+ strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
+
+ /*
+ * NEEDSWORK: why does delete-refs have to be so specific to
+ * send-pack machinery that set_ref_status_for_push() cannot
+ * set this bit for us???
+ */
+ for (ref = remote_refs; ref; ref = ref->next)
+ if (ref->deletion && !allow_deleting_refs)
+ ref->status = REF_STATUS_REJECT_NODELETE;
+
if (!args->dry_run)
advertise_shallow_grafts_buf(&req_buf);
+ if (!args->dry_run && args->push_cert)
+ cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
+ cap_buf.buf, push_cert_nonce);
+
/*
- * Finally, tell the other end!
+ * Clear the status for each ref and see if we need to send
+ * the pack data.
*/
- new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
- if (!ref->peer_ref && !args->send_mirror)
+ if (!ref_update_to_be_sent(ref, args))
continue;
- /* Check for statuses set by set_ref_status_for_push() */
- switch (ref->status) {
- case REF_STATUS_REJECT_NONFASTFORWARD:
- case REF_STATUS_REJECT_ALREADY_EXISTS:
- case REF_STATUS_REJECT_FETCH_FIRST:
- case REF_STATUS_REJECT_NEEDS_FORCE:
- case REF_STATUS_REJECT_STALE:
- case REF_STATUS_UPTODATE:
- continue;
- default:
- ; /* do nothing */
- }
+ if (!ref->deletion)
+ need_pack_data = 1;
- if (ref->deletion && !allow_deleting_refs) {
- ref->status = REF_STATUS_REJECT_NODELETE;
+ if (args->dry_run || !status_report)
+ ref->status = REF_STATUS_OK;
+ else
+ ref->status = REF_STATUS_EXPECTING_REPORT;
+ }
+
+ /*
+ * Finally, tell the other end!
+ */
+ for (ref = remote_refs; ref; ref = ref->next) {
+ char *old_hex, *new_hex;
+
+ if (args->dry_run || args->push_cert)
continue;
- }
- if (!ref->deletion)
- new_refs++;
+ if (!ref_update_to_be_sent(ref, args))
+ continue;
- if (args->dry_run) {
- ref->status = REF_STATUS_OK;
+ old_hex = sha1_to_hex(ref->old_sha1);
+ new_hex = sha1_to_hex(ref->new_sha1);
+ if (!cmds_sent) {
+ packet_buf_write(&req_buf,
+ "%s %s %s%c%s",
+ old_hex, new_hex, ref->name, 0,
+ cap_buf.buf);
+ cmds_sent = 1;
} else {
- char *old_hex = sha1_to_hex(ref->old_sha1);
- char *new_hex = sha1_to_hex(ref->new_sha1);
- int quiet = quiet_supported && (args->quiet || !args->progress);
-
- if (!cmds_sent && (status_report || use_sideband ||
- quiet || agent_supported)) {
- packet_buf_write(&req_buf,
- "%s %s %s%c%s%s%s%s%s",
- old_hex, new_hex, ref->name, 0,
- status_report ? " report-status" : "",
- use_sideband ? " side-band-64k" : "",
- quiet ? " quiet" : "",
- agent_supported ? " agent=" : "",
- agent_supported ? git_user_agent_sanitized() : ""
- );
- }
- else
- packet_buf_write(&req_buf, "%s %s %s",
- old_hex, new_hex, ref->name);
- ref->status = status_report ?
- REF_STATUS_EXPECTING_REPORT :
- REF_STATUS_OK;
- cmds_sent++;
+ packet_buf_write(&req_buf, "%s %s %s",
+ old_hex, new_hex, ref->name);
}
}
@@ -302,6 +407,7 @@ int send_pack(struct send_pack_args *args,
packet_flush(out);
}
strbuf_release(&req_buf);
+ strbuf_release(&cap_buf);
if (use_sideband && cmds_sent) {
memset(&demux, 0, sizeof(demux));
@@ -313,7 +419,7 @@ int send_pack(struct send_pack_args *args,
in = demux.out;
}
- if (new_refs && cmds_sent) {
+ if (need_pack_data && cmds_sent) {
if (pack_objects(out, remote_refs, extra_have, args) < 0) {
for (ref = remote_refs; ref; ref = ref->next)
ref->status = REF_STATUS_NONE;
diff --git a/send-pack.h b/send-pack.h
index 8e843924cf..5635457746 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -2,6 +2,7 @@
#define SEND_PACK_H
struct send_pack_args {
+ const char *url;
unsigned verbose:1,
quiet:1,
porcelain:1,
@@ -11,6 +12,7 @@ struct send_pack_args {
use_thin_pack:1,
use_ofs_delta:1,
dry_run:1,
+ push_cert:1,
stateless_rpc:1;
};
diff --git a/sequencer.c b/sequencer.c
index 3c060e0547..a03d4fa252 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "sequencer.h"
#include "dir.h"
#include "object.h"
@@ -237,23 +238,33 @@ static int error_dirty_index(struct replay_opts *opts)
static int fast_forward_to(const unsigned char *to, const unsigned char *from,
int unborn, struct replay_opts *opts)
{
- struct ref_lock *ref_lock;
+ struct ref_transaction *transaction;
struct strbuf sb = STRBUF_INIT;
- int ret;
+ struct strbuf err = STRBUF_INIT;
read_cache();
if (checkout_fast_forward(from, to, 1))
exit(128); /* the callee should have complained already */
- ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from,
- 0, NULL);
- if (!ref_lock)
- return error(_("Failed to lock HEAD during fast_forward_to"));
strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
- ret = write_ref_sha1(ref_lock, to, sb.buf);
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction ||
+ ref_transaction_update(transaction, "HEAD",
+ to, unborn ? null_sha1 : from,
+ 0, 1, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
+ ref_transaction_free(transaction);
+ error("%s", err.buf);
+ strbuf_release(&sb);
+ strbuf_release(&err);
+ return -1;
+ }
strbuf_release(&sb);
- return ret;
+ strbuf_release(&err);
+ ref_transaction_free(transaction);
+ return 0;
}
static int do_recursive_merge(struct commit *base, struct commit *next,
@@ -320,7 +331,7 @@ static int is_index_unchanged(void)
unsigned char head_sha1[20];
struct commit *head_commit;
- if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
return error(_("Could not resolve HEAD commit\n"));
head_commit = lookup_commit(head_sha1);
@@ -860,7 +871,7 @@ static int rollback_single_pick(void)
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))
+ if (read_ref_full("HEAD", 0, head_sha1, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_sha1(head_sha1))
return error(_("cannot abort from a branch yet to be born"));
diff --git a/server-info.c b/server-info.c
index 9ec744e9f2..31f4a749fb 100644
--- a/server-info.c
+++ b/server-info.c
@@ -4,45 +4,80 @@
#include "commit.h"
#include "tag.h"
-/* refs */
-static FILE *info_ref_fp;
+/*
+ * Create the file "path" by writing to a temporary file and renaming
+ * it into place. The contents of the file come from "generate", which
+ * should return non-zero if it encounters an error.
+ */
+static int update_info_file(char *path, int (*generate)(FILE *))
+{
+ char *tmp = mkpathdup("%s_XXXXXX", path);
+ int ret = -1;
+ int fd = -1;
+ FILE *fp = NULL;
+
+ safe_create_leading_directories(path);
+ fd = mkstemp(tmp);
+ if (fd < 0)
+ goto out;
+ fp = fdopen(fd, "w");
+ if (!fp)
+ goto out;
+ ret = generate(fp);
+ if (ret)
+ goto out;
+ if (fclose(fp))
+ goto out;
+ if (adjust_shared_perm(tmp) < 0)
+ goto out;
+ if (rename(tmp, path) < 0)
+ goto out;
+ ret = 0;
+
+out:
+ if (ret) {
+ error("unable to update %s: %s", path, strerror(errno));
+ if (fp)
+ fclose(fp);
+ else if (fd >= 0)
+ close(fd);
+ unlink(tmp);
+ }
+ free(tmp);
+ return ret;
+}
static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
+ FILE *fp = cb_data;
struct object *o = parse_object(sha1);
if (!o)
return -1;
- fprintf(info_ref_fp, "%s %s\n", sha1_to_hex(sha1), path);
+ if (fprintf(fp, "%s %s\n", sha1_to_hex(sha1), path) < 0)
+ return -1;
+
if (o->type == OBJ_TAG) {
o = deref_tag(o, path, 0);
if (o)
- fprintf(info_ref_fp, "%s %s^{}\n",
- sha1_to_hex(o->sha1), path);
+ if (fprintf(fp, "%s %s^{}\n",
+ sha1_to_hex(o->sha1), path) < 0)
+ return -1;
}
return 0;
}
+static int generate_info_refs(FILE *fp)
+{
+ return for_each_ref(add_info_ref, fp);
+}
+
static int update_info_refs(int force)
{
- char *path0 = git_pathdup("info/refs");
- int len = strlen(path0);
- char *path1 = xmalloc(len + 2);
-
- strcpy(path1, path0);
- strcpy(path1 + len, "+");
-
- safe_create_leading_directories(path0);
- info_ref_fp = fopen(path1, "w");
- if (!info_ref_fp)
- return error("unable to update %s", path1);
- for_each_ref(add_info_ref, NULL);
- fclose(info_ref_fp);
- adjust_shared_perm(path1);
- rename(path1, path0);
- free(path0);
- free(path1);
- return 0;
+ char *path = git_pathdup("info/refs");
+ int ret = update_info_file(path, generate_info_refs);
+ free(path);
+ return ret;
}
/* packs */
@@ -198,36 +233,36 @@ static void init_pack_info(const char *infofile, int force)
info[i]->new_num = i;
}
-static void write_pack_info_file(FILE *fp)
+static void free_pack_info(void)
{
int i;
for (i = 0; i < num_pack; i++)
- fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6);
- fputc('\n', fp);
+ free(info[i]);
+ free(info);
}
-static int update_info_packs(int force)
+static int write_pack_info_file(FILE *fp)
{
- char infofile[PATH_MAX];
- char name[PATH_MAX];
- int namelen;
- FILE *fp;
+ int i;
+ for (i = 0; i < num_pack; i++) {
+ if (fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6) < 0)
+ return -1;
+ }
+ if (fputc('\n', fp) == EOF)
+ return -1;
+ return 0;
+}
- namelen = sprintf(infofile, "%s/info/packs", get_object_directory());
- strcpy(name, infofile);
- strcpy(name + namelen, "+");
+static int update_info_packs(int force)
+{
+ char *infofile = mkpathdup("%s/info/packs", get_object_directory());
+ int ret;
init_pack_info(infofile, force);
-
- safe_create_leading_directories(name);
- fp = fopen(name, "w");
- if (!fp)
- return error("cannot open %s", name);
- write_pack_info_file(fp);
- fclose(fp);
- adjust_shared_perm(name);
- rename(name, infofile);
- return 0;
+ ret = update_info_file(infofile, write_pack_info_file);
+ free_pack_info();
+ free(infofile);
+ return ret;
}
/* public */
diff --git a/setup.c b/setup.c
index 0a22f8bd1d..979b13f0c6 100644
--- a/setup.c
+++ b/setup.c
@@ -387,7 +387,7 @@ const char *read_gitfile(const char *path)
}
static const char *setup_explicit_git_dir(const char *gitdirenv,
- char *cwd, int len,
+ struct strbuf *cwd,
int *nongit_ok)
{
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
@@ -434,16 +434,16 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
if (is_absolute_path(git_work_tree_cfg))
set_git_work_tree(git_work_tree_cfg);
else {
- char core_worktree[PATH_MAX];
+ char *core_worktree;
if (chdir(gitdirenv))
die_errno("Could not chdir to '%s'", gitdirenv);
if (chdir(git_work_tree_cfg))
die_errno("Could not chdir to '%s'", git_work_tree_cfg);
- if (!getcwd(core_worktree, PATH_MAX))
- die_errno("Could not get directory '%s'", git_work_tree_cfg);
- if (chdir(cwd))
+ core_worktree = xgetcwd();
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
set_git_work_tree(core_worktree);
+ free(core_worktree);
}
}
else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
@@ -459,21 +459,20 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
worktree = get_git_work_tree();
/* both get_git_work_tree() and cwd are already normalized */
- if (!strcmp(cwd, worktree)) { /* cwd == worktree */
+ if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
set_git_dir(gitdirenv);
free(gitfile);
return NULL;
}
- offset = dir_inside_of(cwd, worktree);
+ offset = dir_inside_of(cwd->buf, worktree);
if (offset >= 0) { /* cwd inside worktree? */
set_git_dir(real_path(gitdirenv));
if (chdir(worktree))
die_errno("Could not chdir to '%s'", worktree);
- cwd[len++] = '/';
- cwd[len] = '\0';
+ strbuf_addch(cwd, '/');
free(gitfile);
- return cwd + offset;
+ return cwd->buf + offset;
}
/* cwd outside worktree */
@@ -483,7 +482,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
}
static const char *setup_discovered_git_dir(const char *gitdir,
- char *cwd, int offset, int len,
+ struct strbuf *cwd, int offset,
int *nongit_ok)
{
if (check_repository_format_gently(gitdir, nongit_ok))
@@ -491,17 +490,17 @@ static const char *setup_discovered_git_dir(const char *gitdir,
/* --work-tree is set without --git-dir; use discovered one */
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
- if (offset != len && !is_absolute_path(gitdir))
+ if (offset != cwd->len && !is_absolute_path(gitdir))
gitdir = xstrdup(real_path(gitdir));
- if (chdir(cwd))
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
- return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+ return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
}
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
if (is_bare_repository_cfg > 0) {
- set_git_dir(offset == len ? gitdir : real_path(gitdir));
- if (chdir(cwd))
+ set_git_dir(offset == cwd->len ? gitdir : real_path(gitdir));
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
return NULL;
}
@@ -512,18 +511,18 @@ static const char *setup_discovered_git_dir(const char *gitdir,
set_git_dir(gitdir);
inside_git_dir = 0;
inside_work_tree = 1;
- if (offset == len)
+ if (offset == cwd->len)
return NULL;
/* Make "offset" point to past the '/', and add a '/' at the end */
offset++;
- cwd[len++] = '/';
- cwd[len] = 0;
- return cwd + offset;
+ strbuf_addch(cwd, '/');
+ return cwd->buf + offset;
}
/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
-static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongit_ok)
+static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
+ int *nongit_ok)
{
int root_len;
@@ -536,20 +535,20 @@ static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongi
if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
const char *gitdir;
- gitdir = offset == len ? "." : xmemdupz(cwd, offset);
- if (chdir(cwd))
+ gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
+ if (chdir(cwd->buf))
die_errno("Could not come back to cwd");
- return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
+ return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
}
inside_git_dir = 1;
inside_work_tree = 0;
- if (offset != len) {
- if (chdir(cwd))
+ if (offset != cwd->len) {
+ if (chdir(cwd->buf))
die_errno("Cannot come back to cwd");
- root_len = offset_1st_component(cwd);
- cwd[offset > root_len ? offset : root_len] = '\0';
- set_git_dir(cwd);
+ root_len = offset_1st_component(cwd->buf);
+ strbuf_setlen(cwd, offset > root_len ? offset : root_len);
+ set_git_dir(cwd->buf);
}
else
set_git_dir(".");
@@ -617,14 +616,23 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
{
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
- static char cwd[PATH_MAX + 1];
+ static struct strbuf cwd = STRBUF_INIT;
const char *gitdirenv, *ret;
char *gitfile;
- int len, offset, offset_parent, ceil_offset = -1;
+ int offset, offset_parent, ceil_offset = -1;
dev_t current_device = 0;
int one_filesystem = 1;
/*
+ * We may have read an incomplete configuration before
+ * setting-up the git directory. If so, clear the cache so
+ * that the next queries to the configuration reload complete
+ * configuration (including the per-repo config file that we
+ * ignored previously).
+ */
+ git_config_clear();
+
+ /*
* Let's assume that we are in a git repository.
* If it turns out later that we are somewhere else, the value will be
* updated accordingly.
@@ -632,9 +640,9 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
if (nongit_ok)
*nongit_ok = 0;
- if (!getcwd(cwd, sizeof(cwd) - 1))
+ if (strbuf_getcwd(&cwd))
die_errno("Unable to read current working directory");
- offset = len = strlen(cwd);
+ offset = cwd.len;
/*
* If GIT_DIR is set explicitly, we're not going
@@ -643,7 +651,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
*/
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
if (gitdirenv)
- return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
+ return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok);
if (env_ceiling_dirs) {
int empty_entry_found = 0;
@@ -651,11 +659,11 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
filter_string_list(&ceiling_dirs, 0,
canonicalize_ceiling_entry, &empty_entry_found);
- ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
+ ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs);
string_list_clear(&ceiling_dirs, 0);
}
- if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
+ if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf))
ceil_offset = 1;
/*
@@ -683,7 +691,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
if (gitdirenv) {
ret = setup_discovered_git_dir(gitdirenv,
- cwd, offset, len,
+ &cwd, offset,
nongit_ok);
free(gitfile);
return ret;
@@ -691,29 +699,31 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
free(gitfile);
if (is_git_directory("."))
- return setup_bare_git_dir(cwd, offset, len, nongit_ok);
+ return setup_bare_git_dir(&cwd, offset, nongit_ok);
offset_parent = offset;
- while (--offset_parent > ceil_offset && cwd[offset_parent] != '/');
+ while (--offset_parent > ceil_offset && cwd.buf[offset_parent] != '/');
if (offset_parent <= ceil_offset)
- return setup_nongit(cwd, nongit_ok);
+ return setup_nongit(cwd.buf, nongit_ok);
if (one_filesystem) {
- dev_t parent_device = get_device_or_die("..", cwd, offset);
+ dev_t parent_device = get_device_or_die("..", cwd.buf,
+ offset);
if (parent_device != current_device) {
if (nongit_ok) {
- if (chdir(cwd))
+ if (chdir(cwd.buf))
die_errno("Cannot come back to cwd");
*nongit_ok = 1;
return NULL;
}
- cwd[offset] = '\0';
+ strbuf_setlen(&cwd, offset);
die("Not a git repository (or any parent up to mount point %s)\n"
- "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).",
+ cwd.buf);
}
}
if (chdir("..")) {
- cwd[offset] = '\0';
- die_errno("Cannot change to '%s/..'", cwd);
+ strbuf_setlen(&cwd, offset);
+ die_errno("Cannot change to '%s/..'", cwd.buf);
}
offset = offset_parent;
}
diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c
index 6dd03a974a..2842a22d7f 100644
--- a/sh-i18n--envsubst.c
+++ b/sh-i18n--envsubst.c
@@ -208,11 +208,8 @@ string_list_append (string_list_ty *slp, const char *s)
/* Grow the list. */
if (slp->nitems >= slp->nitems_max)
{
- size_t nbytes;
-
slp->nitems_max = slp->nitems_max * 2 + 4;
- nbytes = slp->nitems_max * sizeof (slp->item[0]);
- slp->item = (const char **) xrealloc (slp->item, nbytes);
+ REALLOC_ARRAY(slp->item, slp->nitems_max);
}
/* Add the string to the end of the list. */
diff --git a/sha1-lookup.c b/sha1-lookup.c
index 2dd851598a..5f069214d9 100644
--- a/sha1-lookup.c
+++ b/sha1-lookup.c
@@ -84,8 +84,6 @@ int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
die("BUG: assertion failed in binary search");
}
}
- if (18 <= ofs)
- die("cannot happen -- lo and hi are identical");
}
do {
diff --git a/sha1_file.c b/sha1_file.c
index 3f70b1d86a..d7f1838c13 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -8,6 +8,7 @@
*/
#include "cache.h"
#include "string-list.h"
+#include "lockfile.h"
#include "delta.h"
#include "pack.h"
#include "blob.h"
@@ -350,7 +351,7 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
return;
}
- strbuf_addstr(&objdirbuf, absolute_path(get_object_directory()));
+ strbuf_add_absolute_path(&objdirbuf, get_object_directory());
normalize_path_copy(objdirbuf.buf, objdirbuf.buf);
alt_copy = xmemdupz(alt, len);
@@ -412,14 +413,18 @@ void add_to_alternates_file(const char *reference)
link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
}
-void foreach_alt_odb(alt_odb_fn fn, void *cb)
+int foreach_alt_odb(alt_odb_fn fn, void *cb)
{
struct alternate_object_database *ent;
+ int r = 0;
prepare_alt_odb();
- for (ent = alt_odb_list; ent; ent = ent->next)
- if (fn(ent, cb))
- return;
+ for (ent = alt_odb_list; ent; ent = ent->next) {
+ r = fn(ent, cb);
+ if (r)
+ break;
+ }
+ return r;
}
void prepare_alt_odb(void)
@@ -438,27 +443,53 @@ void prepare_alt_odb(void)
read_info_alternates(get_object_directory(), 0);
}
-static int has_loose_object_local(const unsigned char *sha1)
+static int freshen_file(const char *fn)
{
- return !access(sha1_file_name(sha1), F_OK);
+ struct utimbuf t;
+ t.actime = t.modtime = time(NULL);
+ return !utime(fn, &t);
}
-int has_loose_object_nonlocal(const unsigned char *sha1)
+static int check_and_freshen_file(const char *fn, int freshen)
+{
+ if (access(fn, F_OK))
+ return 0;
+ if (freshen && freshen_file(fn))
+ return 0;
+ return 1;
+}
+
+static int check_and_freshen_local(const unsigned char *sha1, int freshen)
+{
+ return check_and_freshen_file(sha1_file_name(sha1), freshen);
+}
+
+static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen)
{
struct alternate_object_database *alt;
prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt->next) {
fill_sha1_path(alt->name, sha1);
- if (!access(alt->base, F_OK))
+ if (check_and_freshen_file(alt->base, freshen))
return 1;
}
return 0;
}
+static int check_and_freshen(const unsigned char *sha1, int freshen)
+{
+ return check_and_freshen_local(sha1, freshen) ||
+ check_and_freshen_nonlocal(sha1, freshen);
+}
+
+int has_loose_object_nonlocal(const unsigned char *sha1)
+{
+ return check_and_freshen_nonlocal(sha1, 0);
+}
+
static int has_loose_object(const unsigned char *sha1)
{
- return has_loose_object_local(sha1) ||
- has_loose_object_nonlocal(sha1);
+ return check_and_freshen(sha1, 0);
}
static unsigned int pack_used_ctr;
@@ -663,10 +694,26 @@ void release_pack_memory(size_t need)
; /* nothing */
}
+static void mmap_limit_check(size_t length)
+{
+ static size_t limit = 0;
+ if (!limit) {
+ limit = git_env_ulong("GIT_MMAP_LIMIT", 0);
+ if (!limit)
+ limit = SIZE_MAX;
+ }
+ if (length > limit)
+ die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX,
+ (uintmax_t)length, (uintmax_t)limit);
+}
+
void *xmmap(void *start, size_t length,
int prot, int flags, int fd, off_t offset)
{
- void *ret = mmap(start, length, prot, flags, fd, offset);
+ void *ret;
+
+ mmap_limit_check(length);
+ ret = mmap(start, length, prot, flags, fd, offset);
if (ret == MAP_FAILED) {
if (!length)
return NULL;
@@ -1923,7 +1970,9 @@ static void *unpack_compressed_entry(struct packed_git *p,
git_zstream stream;
unsigned char *buffer, *in;
- buffer = xmallocz(size);
+ buffer = xmallocz_gently(size);
+ if (!buffer)
+ return NULL;
memset(&stream, 0, sizeof(stream));
stream.next_out = buffer;
stream.avail_out = size + 1;
@@ -2943,6 +2992,17 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
return move_temp_to_file(tmp_file, filename);
}
+static int freshen_loose_object(const unsigned char *sha1)
+{
+ return check_and_freshen(sha1, 1);
+}
+
+static int freshen_packed_object(const unsigned char *sha1)
+{
+ struct pack_entry e;
+ return find_pack_entry(sha1, &e) && freshen_file(e.p->pack_name);
+}
+
int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
unsigned char sha1[20];
@@ -2955,7 +3015,7 @@ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsign
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
if (returnsha1)
hashcpy(returnsha1, sha1);
- if (has_sha1_file(sha1))
+ if (freshen_loose_object(sha1) || freshen_packed_object(sha1))
return 0;
return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
}
@@ -3074,6 +3134,29 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
return ret;
}
+static int index_stream_convert_blob(unsigned char *sha1, int fd,
+ const char *path, unsigned flags)
+{
+ int ret;
+ const int write_object = flags & HASH_WRITE_OBJECT;
+ struct strbuf sbuf = STRBUF_INIT;
+
+ assert(path);
+ assert(would_convert_to_git_filter_fd(path));
+
+ convert_to_git_filter_fd(path, fd, &sbuf,
+ write_object ? safe_crlf : SAFE_CRLF_FALSE);
+
+ if (write_object)
+ ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
+ sha1);
+ else
+ ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
+ sha1);
+ strbuf_release(&sbuf);
+ return ret;
+}
+
static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
const char *path, unsigned flags)
{
@@ -3139,15 +3222,22 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st,
enum object_type type, const char *path, unsigned flags)
{
int ret;
- size_t size = xsize_t(st->st_size);
- if (!S_ISREG(st->st_mode))
+ /*
+ * Call xsize_t() only when needed to avoid potentially unnecessary
+ * die() for large files.
+ */
+ if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
+ ret = index_stream_convert_blob(sha1, fd, path, flags);
+ else if (!S_ISREG(st->st_mode))
ret = index_pipe(sha1, fd, type, path, flags);
- else if (size <= big_file_threshold || type != OBJ_BLOB ||
- (path && would_convert_to_git(path, NULL, 0, 0)))
- ret = index_core(sha1, fd, size, type, path, flags);
+ else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
+ (path && would_convert_to_git(path)))
+ ret = index_core(sha1, fd, xsize_t(st->st_size), type, path,
+ flags);
else
- ret = index_stream(sha1, fd, size, type, path, flags);
+ ret = index_stream(sha1, fd, xsize_t(st->st_size), type, path,
+ flags);
close(fd);
return ret;
}
@@ -3212,3 +3302,149 @@ void assert_sha1_type(const unsigned char *sha1, enum object_type expect)
die("%s is not a valid '%s' object", sha1_to_hex(sha1),
typename(expect));
}
+
+static int for_each_file_in_obj_subdir(int subdir_nr,
+ struct strbuf *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data)
+{
+ size_t baselen = path->len;
+ DIR *dir = opendir(path->buf);
+ struct dirent *de;
+ int r = 0;
+
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ return error("unable to open %s: %s", path->buf, strerror(errno));
+ }
+
+ while ((de = readdir(dir))) {
+ if (is_dot_or_dotdot(de->d_name))
+ continue;
+
+ strbuf_setlen(path, baselen);
+ strbuf_addf(path, "/%s", de->d_name);
+
+ if (strlen(de->d_name) == 38) {
+ char hex[41];
+ unsigned char sha1[20];
+
+ snprintf(hex, sizeof(hex), "%02x%s",
+ subdir_nr, de->d_name);
+ if (!get_sha1_hex(hex, sha1)) {
+ if (obj_cb) {
+ r = obj_cb(sha1, path->buf, data);
+ if (r)
+ break;
+ }
+ continue;
+ }
+ }
+
+ if (cruft_cb) {
+ r = cruft_cb(de->d_name, path->buf, data);
+ if (r)
+ break;
+ }
+ }
+ strbuf_setlen(path, baselen);
+
+ if (!r && subdir_cb)
+ r = subdir_cb(subdir_nr, path->buf, data);
+
+ closedir(dir);
+ return r;
+}
+
+int for_each_loose_file_in_objdir(const char *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+ size_t baselen;
+ int r = 0;
+ int i;
+
+ strbuf_addstr(&buf, path);
+ strbuf_addch(&buf, '/');
+ baselen = buf.len;
+
+ for (i = 0; i < 256; i++) {
+ strbuf_addf(&buf, "%02x", i);
+ r = for_each_file_in_obj_subdir(i, &buf, obj_cb, cruft_cb,
+ subdir_cb, data);
+ strbuf_setlen(&buf, baselen);
+ if (r)
+ break;
+ }
+
+ strbuf_release(&buf);
+ return r;
+}
+
+struct loose_alt_odb_data {
+ each_loose_object_fn *cb;
+ void *data;
+};
+
+static int loose_from_alt_odb(struct alternate_object_database *alt,
+ void *vdata)
+{
+ struct loose_alt_odb_data *data = vdata;
+ return for_each_loose_file_in_objdir(alt->base,
+ data->cb, NULL, NULL,
+ data->data);
+}
+
+int for_each_loose_object(each_loose_object_fn cb, void *data)
+{
+ struct loose_alt_odb_data alt;
+ int r;
+
+ r = for_each_loose_file_in_objdir(get_object_directory(),
+ cb, NULL, NULL, data);
+ if (r)
+ return r;
+
+ alt.cb = cb;
+ alt.data = data;
+ return foreach_alt_odb(loose_from_alt_odb, &alt);
+}
+
+static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
+{
+ uint32_t i;
+ int r = 0;
+
+ for (i = 0; i < p->num_objects; i++) {
+ const unsigned char *sha1 = nth_packed_object_sha1(p, i);
+
+ if (!sha1)
+ return error("unable to get sha1 of object %u in %s",
+ i, p->pack_name);
+
+ r = cb(sha1, p, i, data);
+ if (r)
+ break;
+ }
+ return r;
+}
+
+int for_each_packed_object(each_packed_object_fn cb, void *data)
+{
+ struct packed_git *p;
+ int r = 0;
+
+ prepare_packed_git();
+ for (p = packed_git; p; p = p->next) {
+ r = for_each_object_in_pack(p, cb, data);
+ if (r)
+ break;
+ }
+ return r;
+}
diff --git a/sha1_name.c b/sha1_name.c
index 63ee66fedd..5b004f513b 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -432,7 +432,8 @@ static inline int upstream_mark(const char *string, int len)
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
-static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
+static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
+ unsigned int flags)
{
static const char *warn_msg = "refname '%.*s' is ambiguous.";
static const char *object_name_msg = N_(
@@ -511,7 +512,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
if (!refs_found)
return -1;
- if (warn_ambiguous_refs &&
+ if (warn_ambiguous_refs && !(flags & GET_SHA1_QUIETLY) &&
(refs_found > 1 ||
!get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
warning(warn_msg, len, str);
@@ -545,7 +546,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
return -1;
}
}
- if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
+ if (read_ref_at(real_ref, flags, at_time, nth, sha1, NULL,
&co_time, &co_tz, &co_cnt)) {
if (!len) {
if (starts_with(real_ref, "refs/heads/")) {
@@ -557,11 +558,16 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
len = 4;
}
}
- if (at_time)
- warning("Log for '%.*s' only goes "
- "back to %s.", len, str,
- show_date(co_time, co_tz, DATE_RFC2822));
- else {
+ if (at_time) {
+ if (!(flags & GET_SHA1_QUIETLY)) {
+ warning("Log for '%.*s' only goes "
+ "back to %s.", len, str,
+ show_date(co_time, co_tz, DATE_RFC2822));
+ }
+ } else {
+ if (flags & GET_SHA1_QUIETLY) {
+ exit(128);
+ }
die("Log for '%.*s' only has %d entries.",
len, str, co_cnt);
}
@@ -801,7 +807,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
if (!ret)
return 0;
- ret = get_sha1_basic(name, len, sha1);
+ ret = get_sha1_basic(name, len, sha1, lookup_flags);
if (!ret)
return 0;
@@ -839,7 +845,7 @@ static int handle_one_ref(const char *path,
}
if (object->type != OBJ_COMMIT)
return 0;
- commit_list_insert_by_date((struct commit *)object, list);
+ commit_list_insert((struct commit *)object, list);
return 0;
}
@@ -1366,6 +1372,7 @@ static int get_sha1_with_context_1(const char *name,
if (!only_to_die && namelen > 2 && name[1] == '/') {
struct commit_list *list = NULL;
for_each_ref(handle_one_ref, &list);
+ commit_list_sort_by_date(&list);
return get_sha1_oneline(name + 2, sha1, list);
}
if (namelen < 3 ||
diff --git a/shallow.c b/shallow.c
index de07709e33..cdd0775146 100644
--- a/shallow.c
+++ b/shallow.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "commit.h"
#include "tag.h"
#include "pkt-line.h"
@@ -226,7 +227,6 @@ static void remove_temporary_shallow_on_signal(int signo)
const char *setup_temporary_shallow(const struct sha1_array *extra)
{
- static int installed_handler;
struct strbuf sb = STRBUF_INIT;
int fd;
@@ -237,10 +237,8 @@ const char *setup_temporary_shallow(const struct sha1_array *extra)
strbuf_addstr(&temporary_shallow, git_path("shallow_XXXXXX"));
fd = xmkstemp(temporary_shallow.buf);
- if (!installed_handler) {
- atexit(remove_temporary_shallow);
- sigchain_push_common(remove_temporary_shallow_on_signal);
- }
+ atexit(remove_temporary_shallow);
+ sigchain_push_common(remove_temporary_shallow_on_signal);
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
@@ -269,8 +267,8 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
if (write_shallow_commits(&sb, 0, extra)) {
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
- shallow_lock->filename);
- *alternate_shallow_file = shallow_lock->filename;
+ shallow_lock->filename.buf);
+ *alternate_shallow_file = shallow_lock->filename.buf;
} else
/*
* is_repository_shallow() sees empty string as "no
@@ -316,7 +314,7 @@ void prune_shallow(int show_only)
if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
- shallow_lock.filename);
+ shallow_lock.filename.buf);
commit_lock_file(&shallow_lock);
} else {
unlink(git_path("shallow"));
@@ -392,8 +390,7 @@ static uint32_t *paint_alloc(struct paint_info *info)
void *p;
if (!info->slab_count || info->free + size > info->end) {
info->slab_count++;
- info->slab = xrealloc(info->slab,
- info->slab_count * sizeof(*info->slab));
+ REALLOC_ARRAY(info->slab, info->slab_count);
info->free = xmalloc(COMMIT_SLAB_SIZE);
info->slab[info->slab_count - 1] = info->free;
info->end = info->free + COMMIT_SLAB_SIZE;
diff --git a/sigchain.c b/sigchain.c
index 1118b99e57..faa375d5d8 100644
--- a/sigchain.c
+++ b/sigchain.c
@@ -1,5 +1,5 @@
-#include "sigchain.h"
#include "cache.h"
+#include "sigchain.h"
#define SIGCHAIN_MAX_SIGNALS 32
diff --git a/strbuf.c b/strbuf.c
index 33018d847f..0346e74a47 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -204,6 +204,13 @@ void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len)
strbuf_setlen(sb, sb->len + len);
}
+void strbuf_addchars(struct strbuf *sb, int c, size_t n)
+{
+ strbuf_grow(sb, n);
+ memset(sb->buf + sb->len, c, n);
+ strbuf_setlen(sb, sb->len + n);
+}
+
void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
{
va_list ap;
@@ -406,6 +413,27 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
return -1;
}
+int strbuf_getcwd(struct strbuf *sb)
+{
+ size_t oldalloc = sb->alloc;
+ size_t guessed_len = 128;
+
+ for (;; guessed_len *= 2) {
+ strbuf_grow(sb, guessed_len);
+ if (getcwd(sb->buf, sb->alloc)) {
+ strbuf_setlen(sb, strlen(sb->buf));
+ return 0;
+ }
+ if (errno != ERANGE)
+ break;
+ }
+ if (oldalloc == 0)
+ strbuf_release(sb);
+ else
+ strbuf_reset(sb);
+ return -1;
+}
+
int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
{
int ch;
@@ -555,6 +583,31 @@ void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes)
}
}
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
+{
+ if (!*path)
+ die("The empty string is not a valid path");
+ if (!is_absolute_path(path)) {
+ struct stat cwd_stat, pwd_stat;
+ size_t orig_len = sb->len;
+ char *cwd = xgetcwd();
+ char *pwd = getenv("PWD");
+ if (pwd && strcmp(pwd, cwd) &&
+ !stat(cwd, &cwd_stat) &&
+ (cwd_stat.st_dev || cwd_stat.st_ino) &&
+ !stat(pwd, &pwd_stat) &&
+ pwd_stat.st_dev == cwd_stat.st_dev &&
+ pwd_stat.st_ino == cwd_stat.st_ino)
+ strbuf_addstr(sb, pwd);
+ else
+ strbuf_addstr(sb, cwd);
+ if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
+ strbuf_addch(sb, '/');
+ free(cwd);
+ }
+ strbuf_addstr(sb, path);
+}
+
int printf_ln(const char *fmt, ...)
{
int ret;
diff --git a/strbuf.h b/strbuf.h
index a7c0192e9e..652b6c432b 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -138,6 +138,7 @@ static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
strbuf_add(sb, sb2->buf, sb2->len);
}
extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
+extern void strbuf_addchars(struct strbuf *sb, int c, size_t n);
typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
@@ -174,6 +175,7 @@ extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
+extern int strbuf_getcwd(struct strbuf *sb);
extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
extern int strbuf_getline(struct strbuf *, FILE *, int);
@@ -189,6 +191,8 @@ extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
int reserved);
extern void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
+extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+
__attribute__((format (printf,1,2)))
extern int printf_ln(const char *fmt, ...);
__attribute__((format (printf,2,3)))
diff --git a/string-list.c b/string-list.c
index db38b62b46..c5aa0765e8 100644
--- a/string-list.c
+++ b/string-list.c
@@ -43,8 +43,7 @@ static int add_entry(int insert_at, struct string_list *list, const char *string
if (list->nr + 1 >= list->alloc) {
list->alloc += 32;
- list->items = xrealloc(list->items, list->alloc
- * sizeof(struct string_list_item));
+ REALLOC_ARRAY(list->items, list->alloc);
}
if (index < list->nr)
memmove(list->items + index + 1, list->items + index,
diff --git a/submodule.c b/submodule.c
index c3a61e70f9..0690dc50d0 100644
--- a/submodule.c
+++ b/submodule.c
@@ -433,13 +433,12 @@ static int submodule_needs_pushing(const char *path, const unsigned char sha1[20
return 0;
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL};
struct strbuf buf = STRBUF_INIT;
int needs_pushing = 0;
argv[1] = sha1_to_hex(sha1);
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.git_cmd = 1;
@@ -524,10 +523,9 @@ static int push_submodule(const char *path)
return 1;
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {"push", NULL};
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.git_cmd = 1;
@@ -569,12 +567,11 @@ static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) {
/* Even if the submodule is checked out and the commit is
* present, make sure it is reachable from a ref. */
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL};
struct strbuf buf = STRBUF_INIT;
argv[3] = sha1_to_hex(sha1);
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.git_cmd = 1;
@@ -695,7 +692,7 @@ int fetch_populated_submodules(const struct argv_array *options,
int quiet)
{
int i, result = 0;
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
struct argv_array argv = ARGV_ARRAY_INIT;
struct string_list_item *name_for_path;
const char *work_tree = get_git_work_tree();
@@ -711,7 +708,6 @@ int fetch_populated_submodules(const struct argv_array *options,
argv_array_push(&argv, "--recurse-submodules-default");
/* default value, "--submodule-prefix" and its value are added later */
- memset(&cp, 0, sizeof(cp));
cp.env = local_repo_env;
cp.git_cmd = 1;
cp.no_stdin = 1;
@@ -794,7 +790,7 @@ out:
unsigned is_submodule_modified(const char *path, int ignore_untracked)
{
ssize_t len;
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {
"status",
"--porcelain",
@@ -821,7 +817,6 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
if (ignore_untracked)
argv[2] = "-uno";
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.git_cmd = 1;
@@ -862,7 +857,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
int submodule_uses_gitfile(const char *path)
{
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {
"submodule",
"foreach",
@@ -883,7 +878,6 @@ int submodule_uses_gitfile(const char *path)
strbuf_release(&buf);
/* Now test that all nested submodules use a gitfile too */
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.git_cmd = 1;
@@ -901,7 +895,7 @@ int ok_to_remove_submodule(const char *path)
{
struct stat st;
ssize_t len;
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {
"status",
"--porcelain",
@@ -918,7 +912,6 @@ int ok_to_remove_submodule(const char *path)
if (!submodule_uses_gitfile(path))
return 0;
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.git_cmd = 1;
diff --git a/t/README b/t/README
index 52c77ae936..9952261299 100644
--- a/t/README
+++ b/t/README
@@ -82,6 +82,12 @@ appropriately before running "make".
numbers matching <pattern>. The number matched against is
simply the running count of the test within the file.
+-x::
+ Turn on shell tracing (i.e., `set -x`) during the tests
+ themselves. Implies `--verbose`. Note that this can cause
+ failures in some tests which redirect and test the
+ output of shell functions. Use with caution.
+
-d::
--debug::
This may help the person who is developing a new test.
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index 9e7d7962b0..d8e41f7ddd 100755
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -278,12 +278,10 @@ helper_test_timeout() {
'
}
-cat >askpass <<\EOF
-#!/bin/sh
+write_script askpass <<\EOF
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-gpg.sh b/t/lib-gpg.sh
index fd499e7c49..cd2baef383 100755
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -18,6 +18,7 @@ else
# No password given, to enable non-interactive operation.
cp -R "$TEST_DIRECTORY"/lib-gpg ./gpghome
chmod 0700 gpghome
+ chmod 0600 gpghome/*
GNUPGHOME="$(pwd)/gpghome"
export GNUPGHOME
test_set_prereq GPG
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index b384d79935..7713dd2609 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -68,6 +68,7 @@ LockFile accept.lock
PassEnv GIT_VALGRIND
PassEnv GIT_VALGRIND_OPTIONS
+PassEnv GNUPGHOME
Alias /dumb/ www/
Alias /auth/dumb/ www/auth/dumb/
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 981437b3a8..aeea50c633 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -27,4 +27,26 @@ test_expect_success !MINGW 'signals are propagated using shell convention' '
test_expect_code 143 git sigterm
'
+large_git () {
+ for i in $(test_seq 1 100)
+ do
+ git diff --cached --binary || return
+ done
+}
+
+test_expect_success 'create blob' '
+ test-genrandom foo 16384 >file &&
+ git add file
+'
+
+test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
+ OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 )
+ test "$OUT" -eq 141
+'
+
+test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
+ OUT=$( ((trap "" PIPE; large_git; echo $? 1>&3) | :) 3>&1 )
+ test "$OUT" -eq 141
+'
+
test_done
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 39e55a13c8..8dc6939b90 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -806,7 +806,7 @@ test_expect_success !MINGW 'quoting allows trailing whitespace' '
test_cmp err.expect err
'
-test_expect_success NOT_MINGW,NOT_CYGWIN 'correct handling of backslashes' '
+test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' '
rm -rf whitespace &&
mkdir whitespace &&
>"whitespace/trailing 1 " &&
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index f890c54d13..ca7d2a630a 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -153,17 +153,23 @@ test_expect_success 'filter shell-escaped filenames' '
:
'
-test_expect_success 'required filter success' '
- git config filter.required.smudge cat &&
- git config filter.required.clean cat &&
+test_expect_success 'required filter should filter data' '
+ git config filter.required.smudge ./rot13.sh &&
+ git config filter.required.clean ./rot13.sh &&
git config filter.required.required true &&
echo "*.r filter=required" >.gitattributes &&
- echo test >test.r &&
+ cat test.o >test.r &&
git add test.r &&
+
rm -f test.r &&
- git checkout -- test.r
+ git checkout -- test.r &&
+ cmp test.o test.r &&
+
+ ./rot13.sh <test.o >expected &&
+ git cat-file blob :test.r >actual &&
+ cmp expected actual
'
test_expect_success 'required filter smudge failure' '
@@ -190,6 +196,14 @@ test_expect_success 'required filter clean failure' '
test_must_fail git add test.fc
'
+test_expect_success 'filtering large input to small output should use little memory' '
+ git config filter.devnull.clean "cat >/dev/null" &&
+ git config filter.devnull.required true &&
+ for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
+ echo "30MB filter=devnull" >.gitattributes &&
+ GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
+'
+
test_expect_success EXPENSIVE 'filter large file' '
git config filter.largefile.smudge cat &&
git config filter.largefile.clean cat &&
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
index 4807b0f015..c5203e232c 100755
--- a/t/t0026-eol-config.sh
+++ b/t/t0026-eol-config.sh
@@ -80,4 +80,24 @@ test_expect_success 'autocrlf=true overrides unset eol' '
test -z "$onediff" && test -z "$twodiff"
'
+test_expect_success NATIVE_CRLF 'eol native is crlf' '
+
+ rm -rf native_eol && mkdir native_eol &&
+ (
+ cd native_eol &&
+ printf "*.txt text\n" >.gitattributes &&
+ printf "one\r\ntwo\r\nthree\r\n" >filedos.txt &&
+ printf "one\ntwo\nthree\n" >fileunix.txt &&
+ git init &&
+ git config core.autocrlf false &&
+ git config core.eol native &&
+ git add filedos.txt fileunix.txt &&
+ git commit -m "first" &&
+ rm file*.txt &&
+ git reset --hard HEAD &&
+ has_cr filedos.txt &&
+ has_cr fileunix.txt
+ )
+'
+
test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index 72dd3e8bb4..2a4a6c1226 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -10,30 +10,26 @@ then
test_done
fi
-
-compare_files()
-{
- od -c <"$1" >"$1".expect &&
- od -c <"$2" >"$2".actual &&
+compare_files () {
+ tr '\015\000' QN <"$1" >"$1".expect &&
+ tr '\015\000' QN <"$2" >"$2".actual &&
test_cmp "$1".expect "$2".actual &&
rm "$1".expect "$2".actual
}
-compare_ws_file()
-{
+compare_ws_file () {
pfx=$1
exp=$2.expect
act=$pfx.actual.$3
- od -c <"$2" >"$exp" &&
- od -c <"$3" >"$act" &&
+ tr '\015\000' QN <"$2" >"$exp" &&
+ tr '\015\000' QN <"$3" >"$act" &&
test_cmp $exp $act &&
rm $exp $act
}
-create_gitattributes()
-{
- txtbin=$1
- case "$txtbin" in
+create_gitattributes () {
+ attr=$1
+ case "$attr" in
auto)
echo "*.txt text=auto" >.gitattributes
;;
@@ -43,35 +39,43 @@ create_gitattributes()
-text)
echo "*.txt -text" >.gitattributes
;;
- *)
+ crlf)
+ echo "*.txt eol=crlf" >.gitattributes
+ ;;
+ lf)
+ echo "*.txt eol=lf" >.gitattributes
+ ;;
+ "")
echo >.gitattributes
;;
+ *)
+ echo >&2 invalid attribute: $attr
+ exit 1
+ ;;
esac
}
-create_file_in_repo()
-{
+create_file_in_repo () {
crlf=$1
- txtbin=$2
- create_gitattributes "$txtbin" &&
+ attr=$2
+ create_gitattributes "$attr" &&
for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
do
- pfx=crlf_${crlf}_attr_${txtbin}_$f.txt &&
+ pfx=crlf_${crlf}_attr_${attr}_$f.txt &&
cp $f $pfx && git -c core.autocrlf=$crlf add $pfx
done &&
git commit -m "core.autocrlf $crlf"
}
-check_files_in_repo()
-{
+check_files_in_repo () {
crlf=$1
- txtbin=$2
+ attr=$2
lfname=$3
crlfname=$4
lfmixcrlf=$5
lfmixcr=$6
crlfnul=$7
- pfx=crlf_${crlf}_attr_${txtbin}_ &&
+ pfx=crlf_${crlf}_attr_${attr}_ &&
compare_files $lfname ${pfx}LF.txt &&
compare_files $crlfname ${pfx}CRLF.txt &&
compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt &&
@@ -80,19 +84,18 @@ check_files_in_repo()
}
-check_files_in_ws()
-{
+check_files_in_ws () {
eol=$1
crlf=$2
- txtbin=$3
+ attr=$3
lfname=$4
crlfname=$5
lfmixcrlf=$6
lfmixcr=$7
crlfnul=$8
- create_gitattributes $txtbin &&
+ create_gitattributes $attr &&
git config core.autocrlf $crlf &&
- pfx=eol_${eol}_crlf_${crlf}_attr_${txtbin}_ &&
+ pfx=eol_${eol}_crlf_${crlf}_attr_${attr}_ &&
src=crlf_false_attr__ &&
for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
do
@@ -104,42 +107,24 @@ check_files_in_ws()
fi
done
-
- test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF" "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=LF" "
compare_ws_file $pfx $lfname ${src}LF.txt
"
- test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF" "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=CRLF" "
compare_ws_file $pfx $crlfname ${src}CRLF.txt
"
- test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_mix_LF" "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=CRLF_mix_LF" "
compare_ws_file $pfx $lfmixcrlf ${src}CRLF_mix_LF.txt
"
- test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF_mix_CR" "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=LF_mix_CR" "
compare_ws_file $pfx $lfmixcr ${src}LF_mix_CR.txt
"
- test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_nul" "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=CRLF_nul" "
compare_ws_file $pfx $crlfnul ${src}CRLF_nul.txt
"
}
#######
-(
- type od >/dev/null &&
- printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul &&
- cat >expect <<-EOF &&
- 0000000 l i n e 1 \0 \r \n l i n e 2 \r \n l
- 0000020 i n e 3
- 0000024
-EOF
- od -c CRLF_nul | sed -e "s/[ ][ ]*/ /g" -e "s/ *$//" >actual
- test_cmp expect actual &&
- rm expect actual
-) || {
- skip_all="od not found or od -c not usable"
- exit 0
- test_done
-}
-
test_expect_success 'setup master' '
echo >.gitattributes &&
git checkout -b master &&
@@ -150,9 +135,10 @@ test_expect_success 'setup master' '
printf "line1\r\nline2\nline3" >CRLF_mix_LF &&
printf "line1\nline2\rline3" >LF_mix_CR &&
printf "line1\r\nline2\rline3" >CRLF_mix_CR &&
+ printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul &&
printf "line1Q\nline2\nline3" | q_to_nul >LF_nul
'
-# CRLF_nul had been created above
+
test_expect_success 'create files' '
create_file_in_repo false "" &&
@@ -201,7 +187,8 @@ test_expect_success 'commit -text' '
################################################################################
# Check how files in the repo are changed when they are checked out
# How to read the table below:
-# - check_files_in_ws will check multiple files, see below
+# - check_files_in_ws will check multiple files with a combination of settings
+# and attributes (core.autocrlf=input is forbidden with core.eol=crlf)
# - parameter $1 : core.eol lf | crlf
# - parameter $2 : core.autocrlf false | true | input
# - parameter $3 : text in .gitattributs "" (empty) | auto | text | -text
@@ -211,55 +198,88 @@ test_expect_success 'commit -text' '
# - parameter $7 : reference for a file with LF and CR in the repo (does somebody uses this ?)
# - parameter $8 : reference for a file with CRLF and a NUL (should be handled as binary when auto)
-check_files_in_ws lf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws lf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws lf input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
-check_files_in_ws lf false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws lf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
-check_files_in_ws lf input "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
-check_files_in_ws lf false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws lf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
-check_files_in_ws lf input "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
-check_files_in_ws lf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws lf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws lf input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
-###########
-#core.autocrlf=input is forbidden with core.eol=crlf
-check_files_in_ws crlf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws crlf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
-check_files_in_ws crlf false "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
-check_files_in_ws crlf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
-
-check_files_in_ws crlf false "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
-check_files_in_ws crlf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
-
-check_files_in_ws crlf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws crlf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
+# What we have in the repo:
+# ----------------- EOL in repo ----------------
+# LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+# settings with checkout:
+# core. core. .gitattr
+# eol acrlf
+# ----------------------------------------------
+# What we want to have in the working tree:
if test_have_prereq MINGW
then
-check_files_in_ws "" false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws "" true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws "" false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws "" true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
-check_files_in_ws "" false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws "" true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
-check_files_in_ws "" false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws "" true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-
-check_files_in_ws native false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws native true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws native false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws native true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
-check_files_in_ws native false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws native true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
-check_files_in_ws native false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
-check_files_in_ws native true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+MIX_CRLF_LF=CRLF
+MIX_LF_CR=CRLF_mix_CR
+NL=CRLF
+else
+MIX_CRLF_LF=CRLF_mix_LF
+MIX_LF_CR=LF_mix_CR
+NL=LF
fi
+export CRLF_MIX_LF_CR MIX NL
+
+check_files_in_ws lf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws lf input "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf false "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws lf true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws lf input "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+
+check_files_in_ws crlf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf false "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws crlf false "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws crlf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws crlf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf false "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws crlf true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+
+check_files_in_ws "" false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "auto" $NL CRLF $MIX_CRLF_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws "" input "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "text" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR CRLF_nul
+check_files_in_ws "" true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws "" input "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" input "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws "" true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws "" input "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+
+check_files_in_ws native false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native false "auto" $NL CRLF $MIX_CRLF_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws native false "text" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR CRLF_nul
+check_files_in_ws native true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws native false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native false "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "lf" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native false "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws native true "crlf" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
test_done
diff --git a/t/t0064-sha1-array.sh b/t/t0064-sha1-array.sh
new file mode 100755
index 0000000000..50b31ffe75
--- /dev/null
+++ b/t/t0064-sha1-array.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='basic tests for the SHA1 array implementation'
+. ./test-lib.sh
+
+echo20 () {
+ prefix="${1:+$1 }"
+ shift
+ while test $# -gt 0
+ do
+ echo "$prefix$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1"
+ shift
+ done
+}
+
+test_expect_success 'ordered enumeration' '
+ echo20 "" 44 55 88 aa >expect &&
+ {
+ echo20 append 88 44 aa 55 &&
+ echo for_each_unique
+ } | test-sha1-array >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ordered enumeration with duplicate suppression' '
+ echo20 "" 44 55 88 aa >expect &&
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 append 88 44 aa 55 &&
+ echo for_each_unique
+ } | test-sha1-array >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'lookup' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -eq 1
+'
+
+test_expect_success 'lookup non-existing entry' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 33
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -lt 0
+'
+
+test_expect_success 'lookup with duplicates' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -ge 2 &&
+ test "$n" -le 3
+'
+
+test_expect_success 'lookup non-existing entry with duplicates' '
+ {
+ echo20 append 88 44 aa 55 &&
+ echo20 append 88 44 aa 55 &&
+ echo20 lookup 66
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -lt 0
+'
+
+test_expect_success 'lookup with almost duplicate values' '
+ {
+ echo "append 5555555555555555555555555555555555555555" &&
+ echo "append 555555555555555555555555555555555555555f" &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -eq 0
+'
+
+test_expect_success 'lookup with single duplicate value' '
+ {
+ echo20 append 55 55 &&
+ echo20 lookup 55
+ } | test-sha1-array >actual &&
+ n=$(cat actual) &&
+ test "$n" -ge 0 &&
+ test "$n" -le 1
+'
+
+test_done
diff --git a/t/t0081-line-buffer.sh b/t/t0081-line-buffer.sh
index 25dba008f3..ce92e6acad 100755
--- a/t/t0081-line-buffer.sh
+++ b/t/t0081-line-buffer.sh
@@ -29,7 +29,7 @@ test_expect_success '0-length read, send along greeting' '
test_cmp expect actual
'
-test_expect_success NOT_MINGW 'read from file descriptor' '
+test_expect_success !MINGW 'read from file descriptor' '
rm -f input &&
echo hello >expect &&
echo hello >input &&
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 6c33e28ee8..158cf4f03b 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -8,7 +8,7 @@ cache-tree extension.
. ./test-lib.sh
cmp_cache_tree () {
- test-dump-cache-tree >actual &&
+ test-dump-cache-tree | sed -e '/#(ref)/d' >actual &&
sed "s/$_x40/SHA/" <actual >filtered &&
test_cmp "$1" filtered
}
@@ -16,15 +16,40 @@ cmp_cache_tree () {
# 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 () {
- printf "SHA (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >expect &&
+generate_expected_cache_tree_rec () {
+ dir="$1${1:+/}" &&
+ parent="$2" &&
+ # ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
+ # We want to count only foo because it's the only direct child
+ subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
+ subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 {++c} END {print c}') &&
+ entries=$(git ls-files|wc -l) &&
+ printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
+ for subtree in $subtrees
+ do
+ cd "$subtree"
+ generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1
+ cd ..
+ done &&
+ dir=$parent
+}
+
+generate_expected_cache_tree () {
+ (
+ generate_expected_cache_tree_rec
+ )
+}
+
+test_cache_tree () {
+ generate_expected_cache_tree >expect &&
cmp_cache_tree expect
}
test_invalid_cache_tree () {
- echo "invalid (0 subtrees)" >expect &&
- printf "SHA #(ref) (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >>expect &&
- cmp_cache_tree expect
+ printf "invalid %s ()\n" "" "$@" >expect &&
+ test-dump-cache-tree |
+ sed -n -e "s/[0-9]* subtrees//" -e '/#(ref)/d' -e '/^invalid /p' >actual &&
+ test_cmp expect actual
}
test_no_cache_tree () {
@@ -32,26 +57,59 @@ test_no_cache_tree () {
cmp_cache_tree expect
}
-test_expect_failure 'initial commit has cache-tree' '
+test_expect_success 'initial commit has cache-tree' '
test_commit foo &&
- test_shallow_cache_tree
+ test_cache_tree
'
test_expect_success 'read-tree HEAD establishes cache-tree' '
git read-tree HEAD &&
- test_shallow_cache_tree
+ test_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 &&
+ echo "I changed this file" >foo &&
git add foo &&
test_invalid_cache_tree
'
+test_expect_success 'git-add in subdir invalidates cache-tree' '
+ test_when_finished "git reset --hard; git read-tree HEAD" &&
+ mkdir dirx &&
+ echo "I changed this file" >dirx/foo &&
+ git add dirx/foo &&
+ test_invalid_cache_tree
+'
+
+cat >before <<\EOF
+SHA (3 entries, 2 subtrees)
+SHA dir1/ (1 entries, 0 subtrees)
+SHA dir2/ (1 entries, 0 subtrees)
+EOF
+
+cat >expect <<\EOF
+invalid (2 subtrees)
+invalid dir1/ (0 subtrees)
+SHA dir2/ (1 entries, 0 subtrees)
+EOF
+
+test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
+ git tag no-children &&
+ test_when_finished "git reset --hard no-children; git read-tree HEAD" &&
+ mkdir dir1 dir2 &&
+ test_commit dir1/a &&
+ test_commit dir2/b &&
+ echo "I changed this file" >dir1/a &&
+ cmp_cache_tree before &&
+ echo "I changed this file" >dir1/a &&
+ git add dir1/a &&
+ cmp_cache_tree expect
+'
+
test_expect_success 'update-index invalidates cache-tree' '
test_when_finished "git reset --hard; git read-tree HEAD" &&
- echo "I changed this file" > foo &&
+ echo "I changed this file" >foo &&
git update-index --add foo &&
test_invalid_cache_tree
'
@@ -59,7 +117,7 @@ test_expect_success 'update-index invalidates cache-tree' '
test_expect_success 'write-tree establishes cache-tree' '
test-scrap-cache-tree &&
git write-tree &&
- test_shallow_cache_tree
+ test_cache_tree
'
test_expect_success 'test-scrap-cache-tree works' '
@@ -70,24 +128,94 @@ test_expect_success 'test-scrap-cache-tree works' '
test_expect_success 'second commit has cache-tree' '
test_commit bar &&
- test_shallow_cache_tree
+ test_cache_tree
+'
+
+test_expect_success 'commit --interactive gives cache-tree on partial commit' '
+ cat <<-\EOT >foo.c &&
+ int foo()
+ {
+ return 42;
+ }
+ int bar()
+ {
+ return 42;
+ }
+ EOT
+ git add foo.c &&
+ test_invalid_cache_tree &&
+ git commit -m "add a file" &&
+ test_cache_tree &&
+ cat <<-\EOT >foo.c &&
+ int foo()
+ {
+ return 43;
+ }
+ int bar()
+ {
+ return 44;
+ }
+ EOT
+ (echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
+ git commit --interactive -m foo &&
+ test_cache_tree
+'
+
+test_expect_success 'commit in child dir has cache-tree' '
+ mkdir dir &&
+ >dir/child.t &&
+ git add dir/child.t &&
+ git commit -m dir/child.t &&
+ test_cache_tree
'
test_expect_success 'reset --hard gives cache-tree' '
test-scrap-cache-tree &&
git reset --hard &&
- test_shallow_cache_tree
+ test_cache_tree
'
test_expect_success 'reset --hard without index gives cache-tree' '
rm -f .git/index &&
git reset --hard &&
- test_shallow_cache_tree
+ test_cache_tree
'
-test_expect_failure 'checkout gives cache-tree' '
+test_expect_success 'checkout gives cache-tree' '
+ git tag current &&
git checkout HEAD^ &&
- test_shallow_cache_tree
+ test_cache_tree
+'
+
+test_expect_success 'checkout -b gives cache-tree' '
+ git checkout current &&
+ git checkout -b prev HEAD^ &&
+ test_cache_tree
+'
+
+test_expect_success 'checkout -B gives cache-tree' '
+ git checkout current &&
+ git checkout -B prev HEAD^ &&
+ test_cache_tree
+'
+
+test_expect_success 'partial commit gives cache-tree' '
+ git checkout -b partial no-children &&
+ test_commit one &&
+ test_commit two &&
+ echo "some change" >one.t &&
+ git add one.t &&
+ echo "some other change" >two.t &&
+ git commit two.t -m partial &&
+ test_cache_tree
+'
+
+test_expect_success 'no phantom error when switching trees' '
+ mkdir newdir &&
+ >newdir/one &&
+ git add newdir/one &&
+ git checkout 2>errors &&
+ ! test -s errors
'
test_done
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
index 62c0d25af4..2edb4f2de5 100755
--- a/t/t1020-subdirectory.sh
+++ b/t/t1020-subdirectory.sh
@@ -118,7 +118,7 @@ test_expect_success 'alias expansion' '
)
'
-test_expect_success NOT_MINGW '!alias expansion' '
+test_expect_success !MINGW '!alias expansion' '
pwd >expect &&
(
git config alias.test-alias-directory !pwd &&
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index aea493646e..f5a9119290 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -13,7 +13,7 @@ test_expect_success setup '
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 &&
- GIT_ALLOC_LIMIT=1500 &&
+ GIT_ALLOC_LIMIT=1500k &&
export GIT_ALLOC_LIMIT
'
@@ -112,6 +112,20 @@ test_expect_success 'diff --raw' '
git diff --raw HEAD^
'
+test_expect_success 'diff --stat' '
+ git diff --stat HEAD^ HEAD
+'
+
+test_expect_success 'diff' '
+ git diff HEAD^ HEAD >actual &&
+ grep "Binary files.*differ" actual
+'
+
+test_expect_success 'diff --cached' '
+ git diff --cached HEAD^ >actual &&
+ grep "Binary files.*differ" actual
+'
+
test_expect_success 'hash-object' '
git hash-object large1
'
@@ -163,4 +177,10 @@ test_expect_success 'zip achiving, deflate' '
git archive --format=zip HEAD >/dev/null
'
+test_expect_success 'fsck' '
+ test_must_fail git fsck 2>err &&
+ n=$(grep "error: attempting to allocate .* over limit" err | wc -l) &&
+ test "$n" -gt 1
+'
+
test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 46f6ae2571..938fc8bfd7 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -824,14 +824,14 @@ cat >expect <<\EOF
trailingtilde = foo~
EOF
-test_expect_success NOT_MINGW 'set --path' '
+test_expect_success !MINGW 'set --path' '
rm -f .git/config &&
git config --path path.home "~/" &&
git config --path path.normal "/dev/null" &&
git config --path path.trailingtilde "foo~" &&
test_cmp expect .git/config'
-if test_have_prereq NOT_MINGW && test "${HOME+set}"
+if test_have_prereq !MINGW && test "${HOME+set}"
then
test_set_prereq HOMEVAR
fi
@@ -854,7 +854,7 @@ cat >expect <<\EOF
foo~
EOF
-test_expect_success NOT_MINGW 'get --path copes with unset $HOME' '
+test_expect_success !MINGW 'get --path copes with unset $HOME' '
(
unset HOME;
test_must_fail git config --get --path path.home \
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
index 79045abb51..f5422f1d33 100755
--- a/t/t1304-default-acl.sh
+++ b/t/t1304-default-acl.sh
@@ -26,7 +26,7 @@ test_expect_success 'checking for a working acl setup' '
if test -z "$LOGNAME"
then
- LOGNAME=$USER
+ LOGNAME="${USER:-$(id -u -n)}"
fi
check_perms_and_acl () {
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
new file mode 100755
index 0000000000..91235b76ba
--- /dev/null
+++ b/t/t1308-config-set.sh
@@ -0,0 +1,221 @@
+#!/bin/sh
+
+test_description='Test git config-set API in different settings'
+
+. ./test-lib.sh
+
+# 'check_config get_* section.key value' verifies that the entry for
+# section.key is 'value'
+check_config () {
+ if test "$1" = expect_code
+ then
+ expect_code="$2" && shift && shift
+ else
+ expect_code=0
+ fi &&
+ op=$1 key=$2 && shift && shift &&
+ if test $# != 0
+ then
+ printf "%s\n" "$@"
+ fi >expect &&
+ test_expect_code $expect_code test-config "$op" "$key" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup default config' '
+ cat >.git/config <<-\EOF
+ [case]
+ penguin = very blue
+ Movie = BadPhysics
+ UPPERCASE = true
+ MixedCase = true
+ my =
+ foo
+ baz = sam
+ [Cores]
+ WhatEver = Second
+ baz = bar
+ [cores]
+ baz = bat
+ [CORES]
+ baz = ball
+ [my "Foo bAr"]
+ hi = mixed-case
+ [my "FOO BAR"]
+ hi = upper-case
+ [my "foo bar"]
+ hi = lower-case
+ [case]
+ baz = bat
+ baz = hask
+ [lamb]
+ chop = 65
+ head = none
+ [goat]
+ legs = 4
+ head = true
+ skin = false
+ nose = 1
+ horns
+ EOF
+'
+
+test_expect_success 'get value for a simple key' '
+ check_config get_value case.penguin "very blue"
+'
+
+test_expect_success 'get value for a key with value as an empty string' '
+ check_config get_value case.my ""
+'
+
+test_expect_success 'get value for a key with value as NULL' '
+ check_config get_value case.foo "(NULL)"
+'
+
+test_expect_success 'upper case key' '
+ check_config get_value case.UPPERCASE "true" &&
+ check_config get_value case.uppercase "true"
+'
+
+test_expect_success 'mixed case key' '
+ check_config get_value case.MixedCase "true" &&
+ check_config get_value case.MIXEDCASE "true" &&
+ check_config get_value case.mixedcase "true"
+'
+
+test_expect_success 'key and value with mixed case' '
+ check_config get_value case.Movie "BadPhysics"
+'
+
+test_expect_success 'key with case sensitive subsection' '
+ check_config get_value "my.Foo bAr.hi" "mixed-case" &&
+ check_config get_value "my.FOO BAR.hi" "upper-case" &&
+ check_config get_value "my.foo bar.hi" "lower-case"
+'
+
+test_expect_success 'key with case insensitive section header' '
+ check_config get_value cores.baz "ball" &&
+ check_config get_value Cores.baz "ball" &&
+ check_config get_value CORES.baz "ball" &&
+ check_config get_value coreS.baz "ball"
+'
+
+test_expect_success 'key with case insensitive section header & variable' '
+ check_config get_value CORES.BAZ "ball" &&
+ check_config get_value cores.baz "ball" &&
+ check_config get_value cores.BaZ "ball" &&
+ check_config get_value cOreS.bAz "ball"
+'
+
+test_expect_success 'find value with misspelled key' '
+ check_config expect_code 1 get_value "my.fOo Bar.hi" "Value not found for \"my.fOo Bar.hi\""
+'
+
+test_expect_success 'find value with the highest priority' '
+ check_config get_value case.baz "hask"
+'
+
+test_expect_success 'find integer value for a key' '
+ check_config get_int lamb.chop 65
+'
+
+test_expect_success 'find string value for a key' '
+ check_config get_string case.baz hask &&
+ check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\""
+'
+
+test_expect_success 'check line error when NULL string is queried' '
+ test_expect_code 128 test-config get_string case.foo 2>result &&
+ test_i18ngrep "fatal: .*case\.foo.*\.git/config.*line 7" result
+'
+
+test_expect_success 'find integer if value is non parse-able' '
+ check_config expect_code 128 get_int lamb.head
+'
+
+test_expect_success 'find bool value for the entered key' '
+ check_config get_bool goat.head 1 &&
+ check_config get_bool goat.skin 0 &&
+ check_config get_bool goat.nose 1 &&
+ check_config get_bool goat.horns 1 &&
+ check_config get_bool goat.legs 1
+'
+
+test_expect_success 'find multiple values' '
+ check_config get_value_multi case.baz sam bat hask
+'
+
+test_expect_success 'find value from a configset' '
+ cat >config2 <<-\EOF &&
+ [case]
+ baz = lama
+ [my]
+ new = silk
+ [case]
+ baz = ball
+ EOF
+ echo silk >expect &&
+ test-config configset_get_value my.new config2 .git/config >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'find value with highest priority from a configset' '
+ echo hask >expect &&
+ test-config configset_get_value case.baz config2 .git/config >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'find value_list for a key from a configset' '
+ cat >except <<-\EOF &&
+ sam
+ bat
+ hask
+ lama
+ ball
+ EOF
+ test-config configset_get_value case.baz config2 .git/config >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'proper error on non-existent files' '
+ echo "Error (-1) reading configuration file non-existent-file." >expect &&
+ test_expect_code 2 test-config configset_get_value foo.bar non-existent-file 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success POSIXPERM,SANITY 'proper error on non-accessible files' '
+ chmod -r .git/config &&
+ test_when_finished "chmod +r .git/config" &&
+ echo "Error (-1) reading configuration file .git/config." >expect &&
+ test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'proper error on error in default config files' '
+ cp .git/config .git/config.old &&
+ test_when_finished "mv .git/config.old .git/config" &&
+ echo "[" >>.git/config &&
+ echo "fatal: bad config file line 34 in .git/config" >expect &&
+ test_expect_code 128 test-config get_value foo.bar 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'proper error on error in custom config files' '
+ echo "[" >>syntax-error &&
+ echo "fatal: bad config file line 1 in syntax-error" >expect &&
+ test_expect_code 128 test-config configset_get_value foo.bar syntax-error 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check line errors for malformed values' '
+ mv .git/config .git/config.old &&
+ test_when_finished "mv .git/config.old .git/config" &&
+ cat >.git/config <<-\EOF &&
+ [alias]
+ br
+ EOF
+ test_expect_code 128 git br 2>result &&
+ test_i18ngrep "fatal: .*alias\.br.*\.git/config.*line 2" result
+'
+
+test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 0218e96366..7b4707b776 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -110,6 +110,32 @@ test_expect_success "delete symref without dereference when the referred ref is
cp -f .git/HEAD.orig .git/HEAD
git update-ref -d $m
+test_expect_success 'update-ref -d is not confused by self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ test_must_fail git update-ref -d refs/heads/self &&
+ test_path_is_file .git/refs/heads/self
+'
+
+test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+ git symbolic-ref refs/heads/self refs/heads/self &&
+ test_when_finished "rm -f .git/refs/heads/self" &&
+ test_path_is_file .git/refs/heads/self &&
+ git update-ref --no-deref -d refs/heads/self &&
+ test_path_is_missing .git/refs/heads/self
+'
+
+test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+ >.git/refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/bad" &&
+ git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
+ test_when_finished "rm -f .git/refs/heads/ref-to-bad" &&
+ test_path_is_file .git/refs/heads/ref-to-bad &&
+ git update-ref --no-deref -d refs/heads/ref-to-bad &&
+ test_path_is_missing .git/refs/heads/ref-to-bad
+'
+
test_expect_success '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
@@ -374,12 +400,6 @@ test_expect_success 'stdin fails create with no ref' '
grep "fatal: create: missing <ref>" err
'
-test_expect_success 'stdin fails create with bad ref name' '
- echo "create ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails create with no new value' '
echo "create $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -398,12 +418,6 @@ test_expect_success 'stdin fails update with no ref' '
grep "fatal: update: missing <ref>" err
'
-test_expect_success 'stdin fails update with bad ref name' '
- echo "update ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails update with no new value' '
echo "update $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -422,12 +436,6 @@ test_expect_success 'stdin fails delete with no ref' '
grep "fatal: delete: missing <ref>" err
'
-test_expect_success 'stdin fails delete with bad ref name' '
- echo "delete ~a $m" >stdin &&
- test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin fails delete with too many arguments' '
echo "delete $a $m $m" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -700,12 +708,6 @@ test_expect_success 'stdin -z fails create with no ref' '
grep "fatal: create: missing <ref>" err
'
-test_expect_success 'stdin -z fails create with bad ref name' '
- printf $F "create ~a " "$m" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a " err
-'
-
test_expect_success 'stdin -z fails create with no new value' '
printf $F "create $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
@@ -730,12 +732,6 @@ test_expect_success 'stdin -z fails update with too few args' '
grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
'
-test_expect_success 'stdin -z fails update with bad ref name' '
- printf $F "update ~a" "$m" "" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin -z emits warning with empty new value' '
git update-ref $a $m &&
printf $F "update $a" "" "" >stdin &&
@@ -768,12 +764,6 @@ test_expect_success 'stdin -z fails delete with no ref' '
grep "fatal: delete: missing <ref>" err
'
-test_expect_success 'stdin -z fails delete with bad ref name' '
- printf $F "delete ~a" "$m" >stdin &&
- test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: invalid ref format: ~a" err
-'
-
test_expect_success 'stdin -z fails delete with no old value' '
printf $F "delete $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index 4bc7141226..e5dc62e9ef 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -7,7 +7,7 @@ test_description='Test git check-ref-format'
valid_ref() {
prereq=
case $1 in
- [A-Z]*)
+ [A-Z!]*)
prereq=$1
shift
esac
@@ -19,7 +19,7 @@ valid_ref() {
invalid_ref() {
prereq=
case $1 in
- [A-Z]*)
+ [A-Z!]*)
prereq=$1
shift
esac
@@ -30,17 +30,17 @@ invalid_ref() {
}
invalid_ref ''
-invalid_ref NOT_MINGW '/'
-invalid_ref NOT_MINGW '/' --allow-onelevel
-invalid_ref NOT_MINGW '/' --normalize
-invalid_ref NOT_MINGW '/' '--allow-onelevel --normalize'
+invalid_ref !MINGW '/'
+invalid_ref !MINGW '/' --allow-onelevel
+invalid_ref !MINGW '/' --normalize
+invalid_ref !MINGW '/' '--allow-onelevel --normalize'
valid_ref 'foo/bar/baz'
valid_ref 'foo/bar/baz' --normalize
invalid_ref 'refs///heads/foo'
valid_ref 'refs///heads/foo' --normalize
invalid_ref 'heads/foo/'
-invalid_ref NOT_MINGW '/heads/foo'
-valid_ref NOT_MINGW '/heads/foo' --normalize
+invalid_ref !MINGW '/heads/foo'
+valid_ref !MINGW '/heads/foo' --normalize
invalid_ref '///heads/foo'
valid_ref '///heads/foo' --normalize
invalid_ref './foo'
@@ -120,14 +120,14 @@ invalid_ref "$ref" --refspec-pattern
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='/foo'
-invalid_ref NOT_MINGW "$ref"
-invalid_ref NOT_MINGW "$ref" --allow-onelevel
-invalid_ref NOT_MINGW "$ref" --refspec-pattern
-invalid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel'
-invalid_ref NOT_MINGW "$ref" --normalize
-valid_ref NOT_MINGW "$ref" '--allow-onelevel --normalize'
-invalid_ref NOT_MINGW "$ref" '--refspec-pattern --normalize'
-valid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel --normalize'
+invalid_ref !MINGW "$ref"
+invalid_ref !MINGW "$ref" --allow-onelevel
+invalid_ref !MINGW "$ref" --refspec-pattern
+invalid_ref !MINGW "$ref" '--refspec-pattern --allow-onelevel'
+invalid_ref !MINGW "$ref" --normalize
+valid_ref !MINGW "$ref" '--allow-onelevel --normalize'
+invalid_ref !MINGW "$ref" '--refspec-pattern --normalize'
+valid_ref !MINGW "$ref" '--refspec-pattern --allow-onelevel --normalize'
test_expect_success "check-ref-format --branch @{-1}" '
T=$(git write-tree) &&
@@ -162,7 +162,7 @@ test_expect_success 'check-ref-format --branch from subdir' '
valid_ref_normalized() {
prereq=
case $1 in
- [A-Z]*)
+ [A-Z!]*)
prereq=$1
shift
esac
@@ -174,7 +174,7 @@ valid_ref_normalized() {
invalid_ref_normalized() {
prereq=
case $1 in
- [A-Z]*)
+ [A-Z!]*)
prereq=$1
shift
esac
@@ -185,10 +185,10 @@ invalid_ref_normalized() {
valid_ref_normalized 'heads/foo' 'heads/foo'
valid_ref_normalized 'refs///heads/foo' 'refs/heads/foo'
-valid_ref_normalized NOT_MINGW '/heads/foo' 'heads/foo'
+valid_ref_normalized !MINGW '/heads/foo' 'heads/foo'
valid_ref_normalized '///heads/foo' 'heads/foo'
invalid_ref_normalized 'foo'
-invalid_ref_normalized NOT_MINGW '/foo'
+invalid_ref_normalized !MINGW '/foo'
invalid_ref_normalized 'heads/foo/../bar'
invalid_ref_normalized 'heads/./foo'
invalid_ref_normalized 'heads\foo'
diff --git a/t/t1413-reflog-detach.sh b/t/t1413-reflog-detach.sh
new file mode 100755
index 0000000000..c730600d8a
--- /dev/null
+++ b/t/t1413-reflog-detach.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='Test reflog interaction with detached HEAD'
+. ./test-lib.sh
+
+reset_state () {
+ git checkout master &&
+ cp saved_reflog .git/logs/HEAD
+}
+
+test_expect_success setup '
+ test_tick &&
+ git commit --allow-empty -m initial &&
+ git branch side &&
+ test_tick &&
+ git commit --allow-empty -m second &&
+ cat .git/logs/HEAD >saved_reflog
+'
+
+test_expect_success baseline '
+ reset_state &&
+ git rev-parse master master^ >expect &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'switch to branch' '
+ reset_state &&
+ git rev-parse side master master^ >expect &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to other' '
+ reset_state &&
+ git rev-parse master side master master^ >expect &&
+ git checkout side &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'detach to self' '
+ reset_state &&
+ git rev-parse master master master^ >expect &&
+ git checkout master^0 &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to self' '
+ reset_state &&
+ git rev-parse master master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout master &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'attach to other' '
+ reset_state &&
+ git rev-parse side master master master^ >expect &&
+ git checkout master^0 &&
+ git checkout side &&
+ git log -g --format=%H >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
new file mode 100755
index 0000000000..468e85621a
--- /dev/null
+++ b/t/t1430-bad-ref-name.sh
@@ -0,0 +1,207 @@
+#!/bin/sh
+
+test_description='Test handling of ref names that check-ref-format rejects'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit one &&
+ test_commit two
+'
+
+test_expect_success 'fast-import: fail on invalid branch name ".badbranchname"' '
+ test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+ cat >input <<-INPUT_END &&
+ commit .badbranchname
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ corrupt
+ COMMIT
+
+ from refs/heads/master
+
+ INPUT_END
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"' '
+ test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+ cat >input <<-INPUT_END &&
+ commit bad[branch]name
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ corrupt
+ COMMIT
+
+ from refs/heads/master
+
+ INPUT_END
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'git branch shows badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch >output &&
+ grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -d can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -d broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D can delete badly named ref' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -D broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -D cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git branch -D ../../my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'branch -D cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git branch -D "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
+test_expect_success 'git branch cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git branch broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'branch -m cannot rename to a bad ref name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_might_fail git branch -D goodref &&
+ git branch goodref &&
+ test_must_fail git branch -m goodref broken...ref &&
+ test_cmp_rev master goodref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'branch -m can rename from a bad ref name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch -m broken...ref renamed &&
+ test_cmp_rev master renamed &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'push cannot create a badly named ref' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_failure 'push --mirror can delete badly named ref' '
+ top=$(pwd) &&
+ git init src &&
+ git init dest &&
+
+ (
+ cd src &&
+ test_commit one
+ ) &&
+ (
+ cd dest &&
+ test_commit two &&
+ git checkout --detach &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref
+ ) &&
+ git -C src push --mirror "file://$top/dest" &&
+ git -C dest branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'rev-parse skips symref pointing to broken name' '
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git branch shadow one &&
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ git symbolic-ref refs/tags/shadow refs/heads/broken...ref &&
+
+ git rev-parse --verify one >expect &&
+ git rev-parse --verify shadow >actual 2>err &&
+ test_cmp expect actual &&
+ test_i18ngrep "ignoring.*refs/tags/shadow" err
+'
+
+test_expect_success 'update-ref --no-deref -d can delete reference to broken name' '
+ git symbolic-ref refs/heads/badname refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/badname" &&
+ test_path_is_file .git/refs/heads/badname &&
+ git update-ref --no-deref -d refs/heads/badname &&
+ test_path_is_missing .git/refs/heads/badname
+'
+
+test_expect_success 'update-ref -d can delete broken name' '
+ cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+ test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+ git update-ref -d refs/heads/broken...ref &&
+ git branch >output &&
+ ! grep -e "broken\.\.\.ref" output
+'
+
+test_expect_success 'update-ref -d cannot delete non-ref in .git dir' '
+ echo precious >.git/my-private-file &&
+ echo precious >expect &&
+ test_must_fail git update-ref -d my-private-file &&
+ test_cmp expect .git/my-private-file
+'
+
+test_expect_success 'update-ref -d cannot delete absolute path' '
+ git branch -f extra &&
+ test_must_fail git update-ref -d "$(pwd)/.git/refs/heads/extra" &&
+ test_cmp_rev HEAD extra
+'
+
+test_expect_success 'update-ref --stdin fails create with bad ref name' '
+ echo "create ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails update with bad ref name' '
+ echo "update ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin fails delete with bad ref name' '
+ echo "delete ~a refs/heads/master" >stdin &&
+ test_must_fail git update-ref --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails create with bad ref name' '
+ printf "%s\0" "create ~a " refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a " err
+'
+
+test_expect_success 'update-ref --stdin -z fails update with bad ref name' '
+ printf "%s\0" "update ~a" refs/heads/master "" >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'update-ref --stdin -z fails delete with bad ref name' '
+ printf "%s\0" "delete ~a" refs/heads/master >stdin &&
+ test_must_fail git update-ref -z --stdin <stdin 2>err &&
+ grep "fatal: invalid ref format: ~a" err
+'
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index b52397afd3..019fddd4e8 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -214,6 +214,44 @@ test_expect_success 'tag pointing to something else than its type' '
test_must_fail git fsck --tags
'
+test_expect_success 'tag with incorrect tag name & missing tagger' '
+ sha=$(git rev-parse HEAD) &&
+ cat >wrong-tag <<-EOF &&
+ object $sha
+ type commit
+ tag wrong name format
+
+ This is an invalid tag.
+ EOF
+
+ tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ test_when_finished "remove_object $tag" &&
+ echo $tag >.git/refs/tags/wrong &&
+ test_when_finished "git update-ref -d refs/tags/wrong" &&
+ git fsck --tags 2>out &&
+ grep "invalid .tag. name" out &&
+ grep "expected .tagger. line" out
+'
+
+test_expect_success 'tag with bad tagger' '
+ sha=$(git rev-parse HEAD) &&
+ cat >wrong-tag <<-EOF &&
+ object $sha
+ type commit
+ tag not-quite-wrong
+ tagger Bad Tagger Name
+
+ This is an invalid tag.
+ EOF
+
+ tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
+ test_when_finished "remove_object $tag" &&
+ echo $tag >.git/refs/tags/wrong &&
+ test_when_finished "git update-ref -d refs/tags/wrong" &&
+ test_must_fail git fsck --tags 2>out &&
+ grep "error in tag .*: invalid author/committer" out
+'
+
test_expect_success 'cleaned up' '
git fsck >actual 2>&1 &&
test_cmp empty actual
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index 922423e7d0..ebe7c3b87c 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -19,7 +19,7 @@ sed -e 's/^|//' >expect <<\END_EXPECT
| -d, --data[=...] short and long option with an optional argument
|
|Argument hints
-| -b <arg> short option required argument
+| -B <arg> short option required argument
| --bar2 <arg> long option required argument
| -e, --fuz <with-space>
| short and long option required argument
@@ -51,7 +51,7 @@ sed -e 's/^|//' >optionspec <<\EOF
|d,data? short and long option with an optional argument
|
| Argument hints
-|b=arg short option required argument
+|B=arg short option required argument
|bar2=arg long option required argument
|e,fuz=with-space short and long option required argument
|s?some short option optional argument
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index 813cc1b3e2..823fe1d799 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -72,15 +72,42 @@ test_expect_success 'fails with any bad rev or many good revs' '
test_expect_success 'fails silently when using -q' '
test_must_fail git rev-parse --verify --quiet 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse -q --verify foo 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse --verify -q HEAD bar 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse --quiet --verify baz HEAD 2>error &&
- test -z "$(cat error)" &&
+ test_must_be_empty error &&
test_must_fail git rev-parse -q --verify $HASH2 HEAD 2>error &&
- test -z "$(cat error)"
+ test_must_be_empty error
+'
+
+test_expect_success 'fails silently when using -q with deleted reflogs' '
+ ref=$(git rev-parse HEAD) &&
+ : >.git/logs/refs/test &&
+ git update-ref -m "message for refs/test" refs/test "$ref" &&
+ git reflog delete --updateref --rewrite refs/test@{0} &&
+ test_must_fail git rev-parse -q --verify refs/test@{0} >error 2>&1 &&
+ test_must_be_empty error
+'
+
+test_expect_success 'fails silently when using -q with not enough reflogs' '
+ ref=$(git rev-parse HEAD) &&
+ : >.git/logs/refs/test2 &&
+ git update-ref -m "message for refs/test2" refs/test2 "$ref" &&
+ test_must_fail git rev-parse -q --verify refs/test2@{999} >error 2>&1 &&
+ test_must_be_empty error
+'
+
+test_expect_success 'succeeds silently with -q and reflogs that do not go far back enough in time' '
+ ref=$(git rev-parse HEAD) &&
+ : >.git/logs/refs/test3 &&
+ git update-ref -m "message for refs/test3" refs/test3 "$ref" &&
+ git rev-parse -q --verify refs/test3@{1.year.ago} >actual 2>error &&
+ test_must_be_empty error &&
+ echo "$ref" >expect &&
+ test_cmp expect actual
'
test_expect_success 'no stdout output on error' '
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ac31b711f2..432921b6b8 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -285,6 +285,15 @@ test_expect_success 'deleting a dangling symref' '
test_i18ncmp expect actual
'
+test_expect_success 'deleting a self-referential symref' '
+ git symbolic-ref refs/heads/self-reference refs/heads/self-reference &&
+ test_path_is_file .git/refs/heads/self-reference &&
+ echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
+ git branch -d self-reference >actual &&
+ test_path_is_missing .git/refs/heads/self-reference &&
+ test_i18ncmp expect actual
+'
+
test_expect_success 'renaming a symref is not allowed' '
git symbolic-ref refs/heads/master2 refs/heads/master &&
test_must_fail git branch -m master2 master3 &&
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
index 141b0611ea..912a6635a8 100755
--- a/t/t3201-branch-contains.sh
+++ b/t/t3201-branch-contains.sh
@@ -130,4 +130,33 @@ test_expect_success 'implicit --list conflicts with modification options' '
'
+# We want to set up a case where the walk for the tracking info
+# of one branch crosses the tip of another branch (and make sure
+# that the latter walk does not mess up our flag to see if it was
+# merged).
+#
+# Here "topic" tracks "master" with one extra commit, and "zzz" points to the
+# same tip as master The name "zzz" must come alphabetically after "topic"
+# as we process them in that order.
+test_expect_success 'branch --merged with --verbose' '
+ git branch --track topic master &&
+ git branch zzz topic &&
+ git checkout topic &&
+ test_commit foo &&
+ git branch --merged topic >actual &&
+ cat >expect <<-\EOF &&
+ master
+ * topic
+ zzz
+ EOF
+ test_cmp expect actual &&
+ git branch --verbose --merged topic >actual &&
+ cat >expect <<-\EOF &&
+ master c77a0a9 second on master
+ * topic 2c939f4 [ahead 1] foo
+ zzz c77a0a9 second on master
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 3a017bf437..aa9eb3a3e5 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -11,7 +11,9 @@ semantic is still the same.
'
. ./test-lib.sh
-echo '[core] logallrefupdates = true' >>.git/config
+test_expect_success 'enable reflogs' '
+ git config core.logallrefupdates true
+'
test_expect_success \
'prepare a trivial repository' \
@@ -158,4 +160,31 @@ test_expect_success 'pack ref directly below refs/' '
test_path_is_missing .git/refs/top
'
+test_expect_success 'disable reflogs' '
+ git config core.logallrefupdates false &&
+ rm -rf .git/logs
+'
+
+test_expect_success 'create packed foo/bar/baz branch' '
+ git branch foo/bar/baz &&
+ git pack-refs --all --prune &&
+ test_path_is_missing .git/refs/heads/foo/bar/baz &&
+ test_path_is_missing .git/logs/refs/heads/foo/bar/baz
+'
+
+test_expect_success 'notice d/f conflict with existing directory' '
+ test_must_fail git branch foo &&
+ test_must_fail git branch foo/bar
+'
+
+test_expect_success 'existing directory reports concrete ref' '
+ test_must_fail git branch foo 2>stderr &&
+ grep refs/heads/foo/bar/baz stderr
+'
+
+test_expect_success 'notice d/f conflict with existing ref' '
+ test_must_fail git branch foo/bar/baz/extra &&
+ test_must_fail git branch foo/bar/baz/lots/of/extra/components
+'
+
test_done
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index 8d44e04354..7217c5e222 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -7,8 +7,6 @@ test_description='Test commit notes index (expensive!)'
. ./test-lib.sh
-test -n "$GIT_NOTES_TIMING_TESTS" && test_set_prereq EXPENSIVE
-
create_repo () {
number_of_commits=$1
nr=0
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
index 9292b499f3..217dd79b2e 100755
--- a/t/t3419-rebase-patch-id.sh
+++ b/t/t3419-rebase-patch-id.sh
@@ -4,8 +4,6 @@ test_description='git rebase - test patch id computation'
. ./test-lib.sh
-test -n "$GIT_PATCHID_TIMING_TESTS" && test_set_prereq EXPENSIVE
-
count () {
i=0
while test $i -lt $1
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 55c8a2f576..a392f3d1d6 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -54,7 +54,7 @@ test_expect_success setup '
git add yours &&
git commit -s -m "Second on side" &&
- if test_have_prereq NOT_MINGW
+ if test_have_prereq !MINGW
then
# the second one on the side branch is ISO-8859-1
git config i18n.commitencoding ISO8859-1 &&
@@ -122,7 +122,7 @@ test_expect_success 'rebase (U/L)' '
check_encoding 2
'
-test_expect_success NOT_MINGW 'rebase (L/L)' '
+test_expect_success !MINGW 'rebase (L/L)' '
# In this test we want ISO-8859-1 encoded commits as the result
git config i18n.commitencoding ISO8859-1 &&
git config i18n.logoutputencoding ISO8859-1 &&
@@ -134,7 +134,7 @@ test_expect_success NOT_MINGW 'rebase (L/L)' '
check_encoding 2 8859
'
-test_expect_success NOT_MINGW 'rebase (L/U)' '
+test_expect_success !MINGW 'rebase (L/U)' '
# This is pathological -- use UTF-8 as intermediate form
# to get ISO-8859-1 results.
git config i18n.commitencoding ISO8859-1 &&
@@ -162,7 +162,7 @@ test_expect_success 'cherry-pick(U/U)' '
check_encoding 3
'
-test_expect_success NOT_MINGW 'cherry-pick(L/L)' '
+test_expect_success !MINGW 'cherry-pick(L/L)' '
# Both the commitencoding and logoutputencoding is set to ISO-8859-1
git config i18n.commitencoding ISO8859-1 &&
@@ -192,7 +192,7 @@ test_expect_success 'cherry-pick(U/L)' '
check_encoding 3
'
-test_expect_success NOT_MINGW 'cherry-pick(L/U)' '
+test_expect_success !MINGW 'cherry-pick(L/U)' '
# Again, the commitencoding is set to ISO-8859-1 but
# logoutputencoding is set to UTF-8.
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 5b79b216e2..1e29962fad 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -685,4 +685,46 @@ test_expect_success 'handle stash specification with spaces' '
grep pig file
'
+test_expect_success 'setup stash with index and worktree changes' '
+ git stash clear &&
+ git reset --hard &&
+ echo index >file &&
+ git add file &&
+ echo working >file &&
+ git stash
+'
+
+test_expect_success 'stash list implies --first-parent -m' '
+ cat >expect <<-\EOF &&
+ stash@{0}: WIP on master: b27a2bc subdir
+
+ diff --git a/file b/file
+ index 257cc56..d26b33d 100644
+ --- a/file
+ +++ b/file
+ @@ -1 +1 @@
+ -foo
+ +working
+ EOF
+ git stash list -p >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'stash list --cc shows combined diff' '
+ cat >expect <<-\EOF &&
+ stash@{0}: WIP on master: b27a2bc subdir
+
+ diff --cc file
+ index 257cc56,9015a7a..d26b33d
+ --- a/file
+ +++ b/file
+ @@@ -1,1 -1,1 +1,1 @@@
+ - foo
+ -index
+ ++working
+ EOF
+ git stash list -p --cc >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index cd0454356a..741e0803c1 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -79,7 +79,7 @@ test_expect_success 'non-integer config parsing' '
test_expect_success 'negative integer config parsing' '
git config diff.context -1 &&
test_must_fail git diff 2>output &&
- test_i18ngrep "bad config file" output
+ test_i18ngrep "bad config variable" output
'
test_expect_success '-U0 is valid, so is diff.context=0' '
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 565c020c45..7600a3e3e8 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -93,7 +93,7 @@ test_expect_success 'output from user-defined format is re-wrapped' '
test_cmp expect log.predictable
'
-test_expect_success NOT_MINGW 'shortlog wrapping' '
+test_expect_success !MINGW 'shortlog wrapping' '
cat >expect <<\EOF &&
A U Thor (5):
Test
@@ -114,7 +114,7 @@ EOF
test_cmp expect out
'
-test_expect_success NOT_MINGW 'shortlog from non-git directory' '
+test_expect_success !MINGW 'shortlog from non-git directory' '
git log HEAD >log &&
GIT_DIR=non-existing git shortlog -w <log >out &&
test_cmp expect out
@@ -159,7 +159,7 @@ $DSCHO (2):
EOF
-test_expect_success NOT_MINGW 'shortlog encoding' '
+test_expect_success !MINGW 'shortlog encoding' '
git reset --hard "$commit" &&
git config --unset i18n.commitencoding &&
echo 2 > a1 &&
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 349c531989..7398605e7b 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -431,6 +431,21 @@ EOF
test_cmp expected actual
'
+test_expect_success 'strbuf_utf8_replace() not producing NUL' '
+ git log --color --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)%C(auto)%d" |
+ test_decode_color |
+ nul_to_q >actual &&
+ ! grep Q actual
+'
+
+# ISO strict date format
+test_expect_success 'ISO and ISO-strict date formats display the same values' '
+ git log --format=%ai%n%ci |
+ sed -e "s/ /T/; s/ //; s/..\$/:&/" >expected &&
+ git log --format=%aI%n%cI >actual &&
+ test_cmp expected actual
+'
+
# get new digests (with no abbreviations)
head1=$(git rev-parse --verify HEAD~0) &&
head2=$(git rev-parse --verify HEAD~1) &&
@@ -450,4 +465,15 @@ EOF
test_cmp expected actual1
'
+test_expect_success 'clean log decoration' '
+ git log --no-walk --tags --pretty="%H %D" --decorate=full >actual &&
+ cat >expected <<EOF &&
+$head1 tag: refs/tags/tag2
+$head2 tag: refs/tags/message-one
+$old_head1 tag: refs/tags/message-two
+EOF
+ sort actual >actual1 &&
+ test_cmp expected actual1
+'
+
test_done
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index 9110404e55..e585fe6129 100755
--- a/t/t4210-log-i18n.sh
+++ b/t/t4210-log-i18n.sh
@@ -34,7 +34,7 @@ test_expect_success 'log --grep searches in log output encoding (utf8)' '
test_cmp expect actual
'
-test_expect_success NOT_MINGW 'log --grep searches in log output encoding (latin1)' '
+test_expect_success !MINGW 'log --grep searches in log output encoding (latin1)' '
cat >expect <<-\EOF &&
latin1
utf8
@@ -43,7 +43,7 @@ test_expect_success NOT_MINGW 'log --grep searches in log output encoding (latin
test_cmp expect actual
'
-test_expect_success NOT_MINGW 'log --grep does not find non-reencoded values (utf8)' '
+test_expect_success !MINGW 'log --grep does not find non-reencoded values (utf8)' '
>expect &&
git log --encoding=utf8 --format=%s --grep=$latin1_e >actual &&
test_cmp expect actual
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 7b8babd89b..d01bbdc968 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -305,4 +305,18 @@ test_expect_success GZIP 'remote tar.gz can be disabled' '
>remote.tar.gz
'
+test_expect_success 'archive and :(glob)' '
+ git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
+ cat >expect <<EOF &&
+a/
+a/bin/
+a/bin/sh
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'catch non-matching pathspec' '
+ test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
+'
+
test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 4bbb718751..61bc8da560 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -243,4 +243,23 @@ test_expect_success 'running index-pack in the object store' '
test -f .git/objects/pack/pack-${pack1}.idx
'
+test_expect_success 'index-pack --strict warns upon missing tagger in tag' '
+ sha=$(git rev-parse HEAD) &&
+ cat >wrong-tag <<EOF &&
+object $sha
+type commit
+tag guten tag
+
+This is an invalid tag.
+EOF
+
+ tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
+ pack1=$(echo $tag $sha | git pack-objects tag-test) &&
+ echo remove tag object &&
+ thirtyeight=${tag#??} &&
+ rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&
+ git index-pack --strict tag-test-${pack1}.pack 2>err &&
+ grep "^error:.* expected .tagger. line" err
+'
+
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 01c6a3fc1d..e32e46dee1 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -13,8 +13,8 @@ add_blob() {
before=$(git count-objects | sed "s/ .*//") &&
BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
+ verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
test-chmtime =+0 $BLOB_FILE
}
@@ -35,9 +35,9 @@ test_expect_success 'prune stale packs' '
: > .git/objects/tmp_2.pack &&
test-chmtime =-86501 .git/objects/tmp_1.pack &&
git prune --expire 1.day &&
- test -f $orig_pack &&
- test -f .git/objects/tmp_2.pack &&
- ! test -f .git/objects/tmp_1.pack
+ test_path_is_file $orig_pack &&
+ test_path_is_file .git/objects/tmp_2.pack &&
+ test_path_is_missing .git/objects/tmp_1.pack
'
@@ -45,12 +45,12 @@ test_expect_success 'prune --expire' '
add_blob &&
git prune --expire=1.hour.ago &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
+ verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
test-chmtime =-86500 $BLOB_FILE &&
git prune --expire 1.day &&
- test $before = $(git count-objects | sed "s/ .*//") &&
- ! test -f $BLOB_FILE
+ verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_missing $BLOB_FILE
'
@@ -59,12 +59,12 @@ test_expect_success 'gc: implicit prune --expire' '
add_blob &&
test-chmtime =-$((2*$week-30)) $BLOB_FILE &&
git gc &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
+ verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
git gc &&
- test $before = $(git count-objects | sed "s/ .*//") &&
- ! test -f $BLOB_FILE
+ verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_missing $BLOB_FILE
'
@@ -110,7 +110,7 @@ test_expect_success 'prune: do not prune detached HEAD with no reflog' '
git commit --allow-empty -m "detached commit" &&
# verify that there is no reflogs
# (should be removed and disabled by previous test)
- test ! -e .git/logs &&
+ test_path_is_missing .git/logs &&
git prune -n >prune_actual &&
: >prune_expected &&
test_cmp prune_actual prune_expected
@@ -144,8 +144,8 @@ test_expect_success 'gc --no-prune' '
test-chmtime =-$((5001*$day)) $BLOB_FILE &&
git config gc.pruneExpire 2.days.ago &&
git gc --no-prune &&
- test 1 = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE
+ verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE
'
@@ -153,10 +153,10 @@ test_expect_success 'gc respects gc.pruneExpire' '
git config gc.pruneExpire 5002.days.ago &&
git gc &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git config gc.pruneExpire 5000.days.ago &&
git gc &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -165,9 +165,9 @@ test_expect_success 'gc --prune=<date>' '
add_blob &&
test-chmtime =-$((5001*$day)) $BLOB_FILE &&
git gc --prune=5002.days.ago &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git gc --prune=5000.days.ago &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -175,9 +175,9 @@ test_expect_success 'gc --prune=never' '
add_blob &&
git gc --prune=never &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git gc --prune=now &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -186,10 +186,10 @@ test_expect_success 'gc respects gc.pruneExpire=never' '
git config gc.pruneExpire never &&
add_blob &&
git gc &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git config gc.pruneExpire now &&
git gc &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -197,9 +197,9 @@ test_expect_success 'prune --expire=never' '
add_blob &&
git prune --expire=never &&
- test -f $BLOB_FILE &&
+ test_path_is_file $BLOB_FILE &&
git prune &&
- test ! -f $BLOB_FILE
+ test_path_is_missing $BLOB_FILE
'
@@ -209,11 +209,11 @@ test_expect_success 'gc: prune old objects after local clone' '
git clone --no-hardlinks . aclone &&
(
cd aclone &&
- test 1 = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
+ verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_file $BLOB_FILE &&
git gc --prune &&
- test 0 = $(git count-objects | sed "s/ .*//") &&
- ! test -f $BLOB_FILE
+ verbose test 0 = $(git count-objects | sed "s/ .*//") &&
+ test_path_is_missing $BLOB_FILE
)
'
@@ -250,7 +250,7 @@ test_expect_success 'prune .git/shallow' '
grep $SHA1 .git/shallow &&
grep $SHA1 out &&
git prune &&
- ! test -f .git/shallow
+ test_path_is_missing .git/shallow
'
test_done
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 0580258c91..6003490192 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -170,4 +170,13 @@ test_expect_success JGIT 'jgit can read our bitmaps' '
)
'
+test_expect_success 'splitting packs does not generate bogus bitmaps' '
+ test-genrandom foo $((1024 * 1024)) >rand &&
+ git add rand &&
+ git commit -m "commit with big file" &&
+ git -c pack.packSizeLimit=500k repack -adb &&
+ git init --bare no-bitmaps.git &&
+ git -C no-bitmaps.git fetch .. HEAD
+'
+
test_done
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 17bcb0b040..7f278d8ce9 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -135,4 +135,17 @@ test_expect_success 'send-pack stderr contains hook messages' '
test_cmp expect actual
'
+test_expect_success 'pre-receive hook that forgets to read its input' '
+ write_script victim.git/hooks/pre-receive <<-\EOF &&
+ exit 0
+ EOF
+ rm -f victim.git/hooks/update victim.git/hooks/post-update &&
+
+ for v in $(test_seq 100 999)
+ do
+ git branch branch_$v master || return
+ done &&
+ git push ./victim.git "+refs/heads/*:refs/heads/*"
+'
+
test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 67e0ab3462..7c8a769a90 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1277,4 +1277,17 @@ EOF
git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
'
+test_expect_success 'pushing a tag pushes the tagged object' '
+ rm -rf dst.git &&
+ blob=$(echo unreferenced | git hash-object -w --stdin) &&
+ git tag -m foo tag-of-blob $blob &&
+ git init --bare dst.git &&
+ git push dst.git tag-of-blob &&
+ # the receiving index-pack should have noticed
+ # any problems, but we double check
+ echo unreferenced >expect &&
+ git --git-dir=dst.git cat-file blob tag-of-blob >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
new file mode 100755
index 0000000000..ecb8d446a5
--- /dev/null
+++ b/t/t5534-push-signed.sh
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+test_description='signed push'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+prepare_dst () {
+ rm -fr dst &&
+ test_create_repo dst &&
+
+ git push dst master:noop master:ff master:noff
+}
+
+test_expect_success setup '
+ # master, ff and noff branches pointing at the same commit
+ test_tick &&
+ git commit --allow-empty -m initial &&
+
+ git checkout -b noop &&
+ git checkout -b ff &&
+ git checkout -b noff &&
+
+ # noop stays the same, ff advances, noff rewrites
+ test_tick &&
+ git commit --allow-empty --amend -m rewritten &&
+ git checkout ff &&
+
+ test_tick &&
+ git commit --allow-empty -m second
+'
+
+test_expect_success 'unsigned push does not send push certificate' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi
+ EOF
+
+ git push dst noop ff +noff &&
+ ! test -f dst/push-cert
+'
+
+test_expect_success 'talking with a receiver without push certificate support' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi
+ EOF
+
+ git push dst noop ff +noff &&
+ ! test -f dst/push-cert
+'
+
+test_expect_success 'push --signed fails with a receiver without push certificate support' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ test_must_fail git push --signed dst noop ff +noff 2>err &&
+ test_i18ngrep "the receiving end does not support" err
+'
+
+test_expect_success GPG 'no certificate for a signed push with no update' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi
+ EOF
+ git push dst noop &&
+ ! test -f dst/push-cert
+'
+
+test_expect_success GPG 'signed push sends push certificate' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=C O Mitter <committer@example.com>
+ KEY=13B6F51ECDDE430D
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) >expect &&
+
+ grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert &&
+ grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
+test_expect_success GPG 'fail without key and heed user.signingkey' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ unset GIT_COMMITTER_EMAIL &&
+ git config user.email hasnokey@nowhere.com &&
+ test_must_fail git push --signed dst noop ff +noff &&
+ git config user.signingkey committer@example.com &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=C O Mitter <committer@example.com>
+ KEY=13B6F51ECDDE430D
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) >expect &&
+
+ grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert &&
+ grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
+test_done
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index db1998873c..d2c681ebfd 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -12,6 +12,7 @@ if test -n "$NO_CURL"; then
fi
ROOT_PATH="$PWD"
+. "$TEST_DIRECTORY"/lib-gpg.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
start_httpd
@@ -338,5 +339,45 @@ test_expect_success CMDLINE_LIMIT 'push 2000 tags over http' '
run_with_limited_cmdline git push --mirror
'
+test_expect_success GPG 'push with post-receive to inspect certificate' '
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ mkdir -p hooks &&
+ write_script hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+ EOF
+
+ git config receive.certnonceseed sekrit &&
+ git config receive.certnonceslop 30
+ ) &&
+ cd "$ROOT_PATH/test_repo_clone" &&
+ test_commit cert-test &&
+ git push --signed "$HTTPD_URL/smart/test_repo.git" &&
+ (
+ cd "$HTTPD_DOCUMENT_ROOT_PATH" &&
+ cat <<-\EOF &&
+ SIGNER=C O Mitter <committer@example.com>
+ KEY=13B6F51ECDDE430D
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" push-cert
+ ) >expect &&
+ test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH/push-cert-status"
+'
+
stop_httpd
test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 5e67035be8..e4f10c0f68 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -318,7 +318,7 @@ test_expect_success 'clone myhost:src uses ssh' '
expect_ssh myhost src
'
-test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' '
+test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' '
cp -R src "foo:bar" &&
git clone "foo:bar" foobar &&
expect_ssh none
@@ -339,7 +339,7 @@ test_clone_url () {
expect_ssh "$2" "$3"
}
-test_expect_success NOT_MINGW 'clone c:temp is ssl' '
+test_expect_success !MINGW 'clone c:temp is ssl' '
test_clone_url c:temp c temp
'
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 3794e4ceaf..2602086303 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -73,4 +73,27 @@ test_expect_success 'symleft flag bit is propagated down from tag' '
test_cmp expect actual
'
+test_expect_success 'rev-list can show index objects' '
+ # Of the blobs and trees in the index, note:
+ #
+ # - we do not show two/three, because it is the
+ # same blob as "one", and we show objects only once
+ #
+ # - we do show the tree "two", because it has a valid cache tree
+ # from the last commit
+ #
+ # - we do not show the root tree; since we updated the index, it
+ # does not have a valid cache tree
+ #
+ cat >expect <<-\EOF
+ 8e4020bb5a8d8c873b25de15933e75cc0fc275df one
+ d9d3a7417b9605cfd88ee6306b28dadc29e6ab08 only-in-index
+ 9200b628cf9dc883a85a7abc8d6e6730baee589c two
+ EOF
+ echo only-in-index >only-in-index &&
+ git add only-in-index &&
+ git rev-list --objects --indexed-objects >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
index a953f1b55c..6464a16a19 100755
--- a/t/t6031-merge-recursive.sh
+++ b/t/t6031-merge-recursive.sh
@@ -13,6 +13,7 @@ test_expect_success 'mode change in one branch: keep changed version' '
git commit -m a &&
git checkout -b b1 master &&
test_chmod +x file1 &&
+ git add file1 &&
git commit -m b1 &&
git checkout a1 &&
git merge-recursive master -- a1 b1 &&
diff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh
index d9c2d386dd..85c10b0940 100755
--- a/t/t6038-merge-text-auto.sh
+++ b/t/t6038-merge-text-auto.sh
@@ -72,6 +72,10 @@ test_expect_success 'Merge after setting text=auto' '
same line
EOF
+ if test_have_prereq NATIVE_CRLF; then
+ append_cr <expected >expected.temp &&
+ mv expected.temp expected
+ fi &&
git config merge.renormalize true &&
git rm -fr . &&
rm -f .gitattributes &&
@@ -86,6 +90,10 @@ test_expect_success 'Merge addition of text=auto' '
same line
EOF
+ if test_have_prereq NATIVE_CRLF; then
+ append_cr <expected >expected.temp &&
+ mv expected.temp expected
+ fi &&
git config merge.renormalize true &&
git rm -fr . &&
rm -f .gitattributes &&
@@ -95,16 +103,19 @@ test_expect_success 'Merge addition of text=auto' '
'
test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
- q_to_cr <<-\EOF >expected &&
- <<<<<<<
- first line
- same line
- =======
- first lineQ
- same lineQ
- >>>>>>>
- EOF
-
+ echo "<<<<<<<" >expected &&
+ if test_have_prereq NATIVE_CRLF; then
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected &&
+ echo ======= | append_cr >>expected
+ else
+ echo first line >>expected &&
+ echo same line >>expected &&
+ echo ======= >>expected
+ fi &&
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected &&
+ echo ">>>>>>>" >>expected &&
git config merge.renormalize false &&
rm -f .gitattributes &&
git reset --hard a &&
@@ -114,16 +125,19 @@ test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
'
test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
- q_to_cr <<-\EOF >expected &&
- <<<<<<<
- first lineQ
- same lineQ
- =======
- first line
- same line
- >>>>>>>
- EOF
-
+ echo "<<<<<<<" >expected &&
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected &&
+ if test_have_prereq NATIVE_CRLF; then
+ echo ======= | append_cr >>expected &&
+ echo first line | append_cr >>expected &&
+ echo same line | append_cr >>expected
+ else
+ echo ======= >>expected &&
+ echo first line >>expected &&
+ echo same line >>expected
+ fi &&
+ echo ">>>>>>>" >>expected &&
git config merge.renormalize false &&
rm -f .gitattributes &&
git reset --hard b &&
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
new file mode 100755
index 0000000000..157f3f91db
--- /dev/null
+++ b/t/t6501-freshen-objects.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+#
+# This test covers the handling of objects which might have old
+# mtimes in the filesystem (because they were used previously)
+# and are just now becoming referenced again.
+#
+# We're going to do two things that are a little bit "fake" to
+# help make our simulation easier:
+#
+# 1. We'll turn off reflogs. You can still run into
+# problems with reflogs on, but your objects
+# don't get pruned until both the reflog expiration
+# has passed on their references, _and_ they are out
+# of prune's expiration period. Dropping reflogs
+# means we only have to deal with one variable in our tests,
+# but the results generalize.
+#
+# 2. We'll use a temporary index file to create our
+# works-in-progress. Most workflows would mention
+# referenced objects in the index, which prune takes
+# into account. However, many operations don't. For
+# example, a partial commit with "git commit foo"
+# will use a temporary index. Or they may not need
+# an index at all (e.g., creating a new commit
+# to refer to an existing tree).
+
+test_description='check pruning of dependent objects'
+. ./test-lib.sh
+
+# We care about reachability, so we do not want to use
+# the normal test_commit, which creates extra tags.
+add () {
+ echo "$1" >"$1" &&
+ git add "$1"
+}
+commit () {
+ test_tick &&
+ add "$1" &&
+ git commit -m "$1"
+}
+
+maybe_repack () {
+ if test -n "$repack"; then
+ git repack -ad
+ fi
+}
+
+for repack in '' true; do
+ title=${repack:+repack}
+ title=${title:-loose}
+
+ test_expect_success "make repo completely empty ($title)" '
+ rm -rf .git &&
+ git init
+ '
+
+ test_expect_success "disable reflogs ($title)" '
+ git config core.logallrefupdates false &&
+ rm -rf .git/logs
+ '
+
+ test_expect_success "setup basic history ($title)" '
+ commit base
+ '
+
+ test_expect_success "create and abandon some objects ($title)" '
+ git checkout -b experiment &&
+ commit abandon &&
+ maybe_repack &&
+ git checkout master &&
+ git branch -D experiment
+ '
+
+ test_expect_success "simulate time passing ($title)" '
+ find .git/objects -type f |
+ xargs test-chmtime -v -86400
+ '
+
+ test_expect_success "start writing new commit with old blob ($title)" '
+ tree=$(
+ GIT_INDEX_FILE=index.tmp &&
+ export GIT_INDEX_FILE &&
+ git read-tree HEAD &&
+ add unrelated &&
+ add abandon &&
+ git write-tree
+ )
+ '
+
+ test_expect_success "simultaneous gc ($title)" '
+ git gc --prune=12.hours.ago
+ '
+
+ test_expect_success "finish writing out commit ($title)" '
+ commit=$(echo foo | git commit-tree -p HEAD $tree) &&
+ git update-ref HEAD $commit
+ '
+
+ # "abandon" blob should have been rescued by reference from new tree
+ test_expect_success "repository passes fsck ($title)" '
+ git fsck
+ '
+
+ test_expect_success "abandon objects again ($title)" '
+ git reset --hard HEAD^ &&
+ find .git/objects -type f |
+ xargs test-chmtime -v -86400
+ '
+
+ test_expect_success "start writing new commit with same tree ($title)" '
+ tree=$(
+ GIT_INDEX_FILE=index.tmp &&
+ export GIT_INDEX_FILE &&
+ git read-tree HEAD &&
+ add abandon &&
+ add unrelated &&
+ git write-tree
+ )
+ '
+
+ test_expect_success "simultaneous gc ($title)" '
+ git gc --prune=12.hours.ago
+ '
+
+ # tree should have been refreshed by write-tree
+ test_expect_success "finish writing out commit ($title)" '
+ commit=$(echo foo | git commit-tree -p HEAD $tree) &&
+ git update-ref HEAD $commit
+ '
+done
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 54d78079e8..69f11bd40d 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -350,10 +350,11 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
'
test_expect_success 'git mv moves a submodule with gitfile' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
(
cd mod &&
git mv ../sub/ .
@@ -372,11 +373,12 @@ test_expect_success 'git mv moves a submodule with gitfile' '
'
test_expect_success 'mv does not complain when no .gitmodules file is found' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git rm .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
git mv sub mod/sub 2>actual.err &&
! test -s actual.err &&
! test -e sub &&
@@ -390,11 +392,12 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' '
'
test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules foo.bar true &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
+ mkdir mod &&
test_must_fail git mv sub mod/sub 2>actual.err &&
test -s actual.err &&
test -e sub &&
@@ -413,13 +416,14 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
'
test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules --remove-section submodule.sub &&
git add .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
+ mkdir mod &&
git mv sub mod/sub 2>actual.err &&
test_i18ncmp expect.err actual.err &&
! test -e sub &&
@@ -433,9 +437,10 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule
'
test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
- rm -rf mod/sub &&
+ rm -rf mod &&
git reset --hard &&
git submodule update &&
+ mkdir mod &&
git mv -n sub mod/sub 2>actual.err &&
test -f sub/.git &&
git diff-index --exit-code HEAD &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 0366653088..796e9f79ea 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1460,7 +1460,7 @@ test_expect_success 'invalid sort parameter in configuratoin' '
'
run_with_limited_stack () {
- (ulimit -s 64 && "$@")
+ (ulimit -s 128 && "$@")
}
test_lazy_prereq ULIMIT 'run_with_limited_stack true'
@@ -1469,7 +1469,7 @@ test_lazy_prereq ULIMIT 'run_with_limited_stack true'
test_expect_success ULIMIT '--contains works in a deep repo' '
>expect &&
i=1 &&
- while test $i -lt 4000
+ while test $i -lt 8000
do
echo "commit refs/heads/master
committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
new file mode 100755
index 0000000000..1efb88051a
--- /dev/null
+++ b/t/t7513-interpret-trailers.sh
@@ -0,0 +1,863 @@
+#!/bin/sh
+#
+# Copyright (c) 2013, 2014 Christian Couder
+#
+
+test_description='git interpret-trailers'
+
+. ./test-lib.sh
+
+# When we want one trailing space at the end of each line, let's use sed
+# to make sure that these spaces are not removed by any automatic tool.
+
+test_expect_success 'setup' '
+ : >empty &&
+ cat >basic_message <<-\EOF &&
+ subject
+
+ body
+ EOF
+ cat >complex_message_body <<-\EOF &&
+ my subject
+
+ my body which is long
+ and contains some special
+ chars like : = ? !
+
+ EOF
+ sed -e "s/ Z\$/ /" >complex_message_trailers <<-\EOF &&
+ Fixes: Z
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ cat >basic_patch <<-\EOF
+ ---
+ foo.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+ diff --git a/foo.txt b/foo.txt
+ index 0353767..1d91aa1 100644
+ --- a/foo.txt
+ +++ b/foo.txt
+ @@ -1,3 +1,3 @@
+
+ -bar
+ +baz
+
+ --
+ 1.9.rc0.11.ga562ddc
+
+ EOF
+'
+
+test_expect_success 'without config' '
+ sed -e "s/ Z\$/ /" >expected <<-\EOF &&
+
+ ack: Peff
+ Reviewed-by: Z
+ Acked-by: Johan
+ EOF
+ git interpret-trailers --trailer "ack = Peff" --trailer "Reviewed-by" \
+ --trailer "Acked-by: Johan" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'without config in another order' '
+ sed -e "s/ Z\$/ /" >expected <<-\EOF &&
+
+ Acked-by: Johan
+ Reviewed-by: Z
+ ack: Peff
+ EOF
+ git interpret-trailers --trailer "Acked-by: Johan" --trailer "Reviewed-by" \
+ --trailer "ack = Peff" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--trim-empty without config' '
+ cat >expected <<-\EOF &&
+
+ ack: Peff
+ Acked-by: Johan
+ EOF
+ git interpret-trailers --trim-empty --trailer ack=Peff \
+ --trailer "Reviewed-by" --trailer "Acked-by: Johan" \
+ --trailer "sob:" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config option on the command line' '
+ cat >expected <<-\EOF &&
+
+ Acked-by: Johan
+ Reviewed-by: Peff
+ EOF
+ echo "Acked-by: Johan" |
+ git -c "trailer.Acked-by.ifexists=addifdifferent" interpret-trailers \
+ --trailer "Reviewed-by: Peff" --trailer "Acked-by: Johan" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup' '
+ git config trailer.ack.key "Acked-by: " &&
+ cat >expected <<-\EOF &&
+
+ Acked-by: Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack = Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by = Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by :Peff" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and ":=" as separators' '
+ git config trailer.separators ":=" &&
+ git config trailer.ack.key "Acked-by= " &&
+ cat >expected <<-\EOF &&
+
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack = Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by= Peff" empty >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by : Peff" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and "%" as separators' '
+ git config trailer.separators "%" &&
+ cat >expected <<-\EOF &&
+
+ bug% 42
+ count% 10
+ bug% 422
+ EOF
+ git interpret-trailers --trim-empty --trailer "bug = 42" \
+ --trailer count%10 --trailer "test: stuff" \
+ --trailer "bug % 422" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with "%" as separators and a message with trailers' '
+ cat >special_message <<-\EOF &&
+ Special Message
+
+ bug% 42
+ count% 10
+ bug% 422
+ EOF
+ cat >expected <<-\EOF &&
+ Special Message
+
+ bug% 42
+ count% 10
+ bug% 422
+ count% 100
+ EOF
+ git interpret-trailers --trailer count%100 \
+ special_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and ":=#" as separators' '
+ git config trailer.separators ":=#" &&
+ git config trailer.bug.key "Bug #" &&
+ cat >expected <<-\EOF &&
+
+ Bug #42
+ EOF
+ git interpret-trailers --trim-empty --trailer "bug = 42" empty >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit basic message' '
+ cat basic_message >expected &&
+ echo >>expected &&
+ git interpret-trailers <basic_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with basic patch' '
+ cat basic_message >input &&
+ cat basic_patch >>input &&
+ cat basic_message >expected &&
+ echo >>expected &&
+ cat basic_patch >>expected &&
+ git interpret-trailers <input >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message as argument' '
+ cat complex_message_body complex_message_trailers >complex_message &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with 2 files arguments' '
+ cat basic_message >>expected &&
+ echo >>expected &&
+ cat basic_patch >>expected &&
+ git interpret-trailers complex_message input >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with message that has comments' '
+ cat basic_message >>message_with_comments &&
+ sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
+ # comment
+
+ # other comment
+ Cc: Z
+ # yet another comment
+ Reviewed-by: Johan
+ Reviewed-by: Z
+ # last comment
+
+ EOF
+ cat basic_patch >>message_with_comments &&
+ cat basic_message >expected &&
+ cat >>expected <<-\EOF &&
+ # comment
+
+ Reviewed-by: Johan
+ Cc: Peff
+ EOF
+ cat basic_patch >>expected &&
+ git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message and trailer args' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Acked-by= Peff
+ Bug #42
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with complex patch, args and --trim-empty' '
+ cat complex_message >complex_patch &&
+ cat basic_patch >>complex_patch &&
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Acked-by= Peff
+ Bug #42
+ EOF
+ cat basic_patch >>expected &&
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "bug: 42" <complex_patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before"' '
+ git config trailer.bug.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = after"' '
+ git config trailer.ack.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = end"' '
+ git config trailer.review.key "Reviewed-by" &&
+ git config trailer.review.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Reviewed-by: Junio
+ Reviewed-by: Johannes
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Reviewed-by: Junio" --trailer "Reviewed-by: Johannes" \
+ complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = start"' '
+ git config trailer.review.key "Reviewed-by" &&
+ git config trailer.review.where "start" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Reviewed-by: Johannes
+ Reviewed-by: Junio
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Reviewed-by: Junio" --trailer "Reviewed-by: Johannes" \
+ complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" for a token in the middle of the message' '
+ git config trailer.review.key "Reviewed-by:" &&
+ git config trailer.review.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by:Johan
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "bug: 42" \
+ --trailer "review: Johan" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" and --trim-empty' '
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Bug #46
+ Bug #42
+ Acked-by= Peff
+ Reviewed-by:Johan
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "bug: 42" --trailer "review: Johan" \
+ --trailer "Bug: 46" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" --trailer "ack: Peff" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'default "ifExists" is now "addIfDifferent"' '
+ git config trailer.ifexists "addIfDifferent" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" --trailer "ack: Peff" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
+ git config trailer.ack.ifExists "addIfDifferent" &&
+ git config trailer.ack.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
+ git config trailer.ack.ifExists "addIfDifferent" &&
+ git config trailer.ack.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Peff
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end"' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.ack.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Tested-by: Jakub
+ Acked-by= Junio
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "ack: Junio" --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = after"' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.ack.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Tested-by: Jakub
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "ack: Junio" --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = add" with "where = end"' '
+ git config trailer.ack.ifExists "add" &&
+ git config trailer.ack.where "end" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Acked-by= Peff
+ Acked-by= Peff
+ Tested-by: Jakub
+ Acked-by= Junio
+ Tested-by: Johannes
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "bug: 42" --trailer "Tested-by: Johannes" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = add" with "where = after"' '
+ git config trailer.ack.ifExists "add" &&
+ git config trailer.ack.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = replace"' '
+ git config trailer.fix.key "Fixes: " &&
+ git config trailer.fix.ifExists "replace" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Fixes: 22
+ EOF
+ git interpret-trailers --trailer "review:" \
+ --trailer "fix=53" --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = replace" with "where = after"' '
+ git config trailer.fix.where "after" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: 22
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" \
+ --trailer "fix=53" --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = doNothing"' '
+ git config trailer.fix.ifExists "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'the default is "ifMissing = add"' '
+ git config trailer.cc.key "Cc: " &&
+ git config trailer.cc.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Cc: Linus
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'when default "ifMissing" is "doNothing"' '
+ git config trailer.ifmissing "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual &&
+ git config trailer.ifmissing "add"
+'
+
+test_expect_success 'using "ifMissing = add" with "where = end"' '
+ git config trailer.cc.key "Cc: " &&
+ git config trailer.cc.where "end" &&
+ git config trailer.cc.ifMissing "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Cc: Linus
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "cc=Linus" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = add" with "where = before"' '
+ git config trailer.cc.key "Cc: " &&
+ git config trailer.cc.where "before" &&
+ git config trailer.cc.ifMissing "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Cc: Linus
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "cc=Linus" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = doNothing"' '
+ git config trailer.cc.ifMissing "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'default "where" is now "after"' '
+ git config trailer.where "after" &&
+ git config --unset trailer.ack.where &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by:
+ Signed-off-by: Z
+ Tested-by: Jakub
+ Tested-by: Johannes
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "Tested-by: Jakub" --trailer "ack: Junio" \
+ --trailer "bug: 42" --trailer "Tested-by: Johannes" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with simple command' '
+ git config trailer.sign.key "Signed-off-by: " &&
+ git config trailer.sign.where "after" &&
+ git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with command using commiter information' '
+ git config trailer.sign.ifExists "addIfDifferent" &&
+ git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: C O Mitter <committer@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with command using author information' '
+ git config trailer.sign.key "Signed-off-by: " &&
+ git config trailer.sign.where "after" &&
+ git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup a commit' '
+ echo "Content of the first commit." > a.txt &&
+ git add a.txt &&
+ git commit -m "Add file a.txt"
+'
+
+test_expect_success 'with command using $ARG' '
+ git config trailer.fix.ifExists "replace" &&
+ git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+ FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+ Fixes: $FIXED
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with failing command using $ARG' '
+ git config trailer.fix.ifExists "replace" &&
+ git config trailer.fix.command "false \$ARG" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by:
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with empty tokens' '
+ git config --unset trailer.fix.command &&
+ cat >expected <<-EOF &&
+
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer ":" --trailer ":test" >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'with command but no key' '
+ git config --unset trailer.sign.key &&
+ cat >expected <<-EOF &&
+
+ sign: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'with no command and no key' '
+ git config --unset trailer.review.key &&
+ cat >expected <<-EOF &&
+
+ review: Junio
+ sign: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:Junio" >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 05d9db090d..7eeb207b32 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -14,512 +14,527 @@ Testing basic merge tool invocation'
# running mergetool
test_expect_success 'setup' '
- git config rerere.enabled true &&
- echo master >file1 &&
- echo master spaced >"spaced name" &&
- echo master file11 >file11 &&
- echo master file12 >file12 &&
- echo master file13 >file13 &&
- echo master file14 >file14 &&
- mkdir subdir &&
- echo master sub >subdir/file3 &&
- test_create_repo submod &&
- (
- cd submod &&
- : >foo &&
- git add foo &&
- git commit -m "Add foo"
- ) &&
- git submodule add git://example.com/submod submod &&
- git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod &&
- git commit -m "add initial versions" &&
-
- git checkout -b branch1 master &&
- git submodule update -N &&
- echo branch1 change >file1 &&
- echo branch1 newfile >file2 &&
- echo branch1 spaced >"spaced name" &&
- echo branch1 both added >both &&
- echo branch1 change file11 >file11 &&
- echo branch1 change file13 >file13 &&
- echo branch1 sub >subdir/file3 &&
- (
- cd submod &&
- echo branch1 submodule >bar &&
- git add bar &&
- git commit -m "Add bar on branch1" &&
- git checkout -b submod-branch1
- ) &&
- git add file1 "spaced name" file11 file13 file2 subdir/file3 submod &&
- git add both &&
- git rm file12 &&
- git commit -m "branch1 changes" &&
-
- git checkout -b stash1 master &&
- echo stash1 change file11 >file11 &&
- git add file11 &&
- git commit -m "stash1 changes" &&
-
- git checkout -b stash2 master &&
- echo stash2 change file11 >file11 &&
- git add file11 &&
- git commit -m "stash2 changes" &&
-
- git checkout master &&
- git submodule update -N &&
- echo master updated >file1 &&
- echo master new >file2 &&
- echo master updated spaced >"spaced name" &&
- echo master both added >both &&
- echo master updated file12 >file12 &&
- echo master updated file14 >file14 &&
- echo master new sub >subdir/file3 &&
- (
- cd submod &&
- echo master submodule >bar &&
- git add bar &&
- git commit -m "Add bar on master" &&
- git checkout -b submod-master
- ) &&
- git add file1 "spaced name" file12 file14 file2 subdir/file3 submod &&
- git add both &&
- git rm file11 &&
- git commit -m "master updates" &&
-
- git config merge.tool mytool &&
- git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
- git config mergetool.mytool.trustExitCode true &&
- git config mergetool.mybase.cmd "cat \"\$BASE\" >\"\$MERGED\"" &&
- git config mergetool.mybase.trustExitCode true
+ test_config rerere.enabled true &&
+ echo master >file1 &&
+ echo master spaced >"spaced name" &&
+ echo master file11 >file11 &&
+ echo master file12 >file12 &&
+ echo master file13 >file13 &&
+ echo master file14 >file14 &&
+ mkdir subdir &&
+ echo master sub >subdir/file3 &&
+ test_create_repo submod &&
+ (
+ cd submod &&
+ : >foo &&
+ git add foo &&
+ git commit -m "Add foo"
+ ) &&
+ git submodule add git://example.com/submod submod &&
+ git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod &&
+ git commit -m "add initial versions" &&
+
+ git checkout -b branch1 master &&
+ git submodule update -N &&
+ echo branch1 change >file1 &&
+ echo branch1 newfile >file2 &&
+ echo branch1 spaced >"spaced name" &&
+ echo branch1 both added >both &&
+ echo branch1 change file11 >file11 &&
+ echo branch1 change file13 >file13 &&
+ echo branch1 sub >subdir/file3 &&
+ (
+ cd submod &&
+ echo branch1 submodule >bar &&
+ git add bar &&
+ git commit -m "Add bar on branch1" &&
+ git checkout -b submod-branch1
+ ) &&
+ git add file1 "spaced name" file11 file13 file2 subdir/file3 submod &&
+ git add both &&
+ git rm file12 &&
+ git commit -m "branch1 changes" &&
+
+ git checkout -b stash1 master &&
+ echo stash1 change file11 >file11 &&
+ git add file11 &&
+ git commit -m "stash1 changes" &&
+
+ git checkout -b stash2 master &&
+ echo stash2 change file11 >file11 &&
+ git add file11 &&
+ git commit -m "stash2 changes" &&
+
+ git checkout master &&
+ git submodule update -N &&
+ echo master updated >file1 &&
+ echo master new >file2 &&
+ echo master updated spaced >"spaced name" &&
+ echo master both added >both &&
+ echo master updated file12 >file12 &&
+ echo master updated file14 >file14 &&
+ echo master new sub >subdir/file3 &&
+ (
+ cd submod &&
+ echo master submodule >bar &&
+ git add bar &&
+ git commit -m "Add bar on master" &&
+ git checkout -b submod-master
+ ) &&
+ git add file1 "spaced name" file12 file14 file2 subdir/file3 submod &&
+ git add both &&
+ git rm file11 &&
+ git commit -m "master updates" &&
+
+ git config merge.tool mytool &&
+ git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ git config mergetool.mytool.trustExitCode true &&
+ git config mergetool.mybase.cmd "cat \"\$BASE\" >\"\$MERGED\"" &&
+ git config mergetool.mybase.trustExitCode true
'
test_expect_success 'custom mergetool' '
- git checkout -b test1 branch1 &&
- git submodule update -N &&
- test_must_fail git merge master >/dev/null 2>&1 &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool file1 file1 ) &&
- ( yes "" | git mergetool file2 "spaced name" >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
- ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
- test "$(cat file1)" = "master updated" &&
- test "$(cat file2)" = "master new" &&
- test "$(cat subdir/file3)" = "master new sub" &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
- git commit -m "branch1 resolved with mergetool"
+ git checkout -b test1 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file1 file1 ) &&
+ ( yes "" | git mergetool file2 "spaced name" >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
+ test "$(cat file1)" = "master updated" &&
+ test "$(cat file2)" = "master new" &&
+ test "$(cat subdir/file3)" = "master new sub" &&
+ test "$(cat submod/bar)" = "branch1 submodule" &&
+ git commit -m "branch1 resolved with mergetool"
'
test_expect_success 'mergetool crlf' '
- git config core.autocrlf true &&
- git checkout -b test2 branch1 &&
- test_must_fail git merge master >/dev/null 2>&1 &&
- ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool "spaced name" >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
- ( yes "r" | git mergetool submod >/dev/null 2>&1 ) &&
- test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
- test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
- test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
- git commit -m "branch1 resolved with mergetool - autocrlf" &&
- git config core.autocrlf false &&
- git reset --hard
+ test_config core.autocrlf true &&
+ git checkout -b test2 branch1 &&
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool "spaced name" >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod >/dev/null 2>&1 ) &&
+ test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
+ test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
+ test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git commit -m "branch1 resolved with mergetool - autocrlf" &&
+ test_config core.autocrlf false &&
+ git reset --hard
'
test_expect_success 'mergetool in subdir' '
- git checkout -b test3 branch1 &&
- git submodule update -N &&
- (
- cd subdir &&
- test_must_fail git merge master >/dev/null 2>&1 &&
- ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
- test "$(cat file3)" = "master new sub"
- )
+ git checkout -b test3 branch1 &&
+ git submodule update -N &&
+ (
+ cd subdir &&
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+ test "$(cat file3)" = "master new sub"
+ )
'
test_expect_success 'mergetool on file in parent dir' '
- (
- cd subdir &&
- ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool ../both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
- ( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) &&
- test "$(cat ../file1)" = "master updated" &&
- test "$(cat ../file2)" = "master new" &&
- test "$(cat ../submod/bar)" = "branch1 submodule" &&
- git commit -m "branch1 resolved with mergetool - subdir"
- )
+ (
+ cd subdir &&
+ ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool ../both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ test "$(cat ../submod/bar)" = "branch1 submodule" &&
+ git commit -m "branch1 resolved with mergetool - subdir"
+ )
'
test_expect_success 'mergetool skips autoresolved' '
- git checkout -b test4 branch1 &&
- git submodule update -N &&
- test_must_fail git merge master &&
- test -n "$(git ls-files -u)" &&
- ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
- ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git reset --hard
+ git checkout -b test4 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git reset --hard
'
test_expect_success 'mergetool merges all from subdir' '
- (
- cd subdir &&
- git config rerere.enabled false &&
- test_must_fail git merge master &&
- ( yes "r" | git mergetool ../submod ) &&
- ( yes "d" "d" | git mergetool --no-prompt ) &&
- test "$(cat ../file1)" = "master updated" &&
- test "$(cat ../file2)" = "master new" &&
- test "$(cat file3)" = "master new sub" &&
- ( cd .. && git submodule update -N ) &&
- test "$(cat ../submod/bar)" = "master submodule" &&
- git commit -m "branch2 resolved by mergetool from subdir"
- )
+ (
+ cd subdir &&
+ test_config rerere.enabled false &&
+ test_must_fail git merge master &&
+ ( yes "r" | git mergetool ../submod ) &&
+ ( yes "d" "d" | git mergetool --no-prompt ) &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ test "$(cat file3)" = "master new sub" &&
+ ( cd .. && git submodule update -N ) &&
+ test "$(cat ../submod/bar)" = "master submodule" &&
+ git commit -m "branch2 resolved by mergetool from subdir"
+ )
'
test_expect_success 'mergetool skips resolved paths when rerere is active' '
- git config rerere.enabled true &&
- rm -rf .git/rr-cache &&
- git checkout -b test5 branch1
- git submodule update -N &&
- test_must_fail git merge master >/dev/null 2>&1 &&
- ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&
- ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
- git submodule update -N &&
- output="$(yes "n" | git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git reset --hard
+ test_config rerere.enabled true &&
+ rm -rf .git/rr-cache &&
+ git checkout -b test5 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&
+ ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
+ git submodule update -N &&
+ output="$(yes "n" | git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git reset --hard
'
test_expect_success 'conflicted stash sets up rerere' '
- git config rerere.enabled true &&
- git checkout stash1 &&
- echo "Conflicting stash content" >file11 &&
- git stash &&
-
- git checkout --detach stash2 &&
- test_must_fail git stash apply &&
-
- test -n "$(git ls-files -u)" &&
- conflicts="$(git rerere remaining)" &&
- test "$conflicts" = "file11" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" != "No files need merging" &&
-
- git commit -am "save the stash resolution" &&
-
- git reset --hard stash2 &&
- test_must_fail git stash apply &&
-
- test -n "$(git ls-files -u)" &&
- conflicts="$(git rerere remaining)" &&
- test -z "$conflicts" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging"
+ test_config rerere.enabled true &&
+ git checkout stash1 &&
+ echo "Conflicting stash content" >file11 &&
+ git stash &&
+
+ git checkout --detach stash2 &&
+ test_must_fail git stash apply &&
+
+ test -n "$(git ls-files -u)" &&
+ conflicts="$(git rerere remaining)" &&
+ test "$conflicts" = "file11" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" != "No files need merging" &&
+
+ git commit -am "save the stash resolution" &&
+
+ git reset --hard stash2 &&
+ test_must_fail git stash apply &&
+
+ test -n "$(git ls-files -u)" &&
+ conflicts="$(git rerere remaining)" &&
+ test -z "$conflicts" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging"
'
test_expect_success 'mergetool takes partial path' '
- git reset --hard
- git config rerere.enabled false &&
- git checkout -b test12 branch1 &&
- git submodule update -N &&
- test_must_fail git merge master &&
-
- #should not need these lines
- #( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
- #( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
- #( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
- #( yes "" | git mergetool file1 file2 >/dev/null 2>&1 ) &&
-
- ( yes "" | git mergetool subdir ) &&
-
- test "$(cat subdir/file3)" = "master new sub" &&
- git reset --hard
+ git reset --hard &&
+ test_config rerere.enabled false &&
+ git checkout -b test12 branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge master &&
+
+ ( yes "" | git mergetool subdir ) &&
+
+ test "$(cat subdir/file3)" = "master new sub" &&
+ git reset --hard
'
test_expect_success 'deleted vs modified submodule' '
- git checkout -b test6 branch1 &&
- git submodule update -N &&
- mv submod submod-movedaside &&
- git rm --cached submod &&
- git commit -m "Submodule deleted from branch" &&
- git checkout -b test6.a test6 &&
- test_must_fail git merge master &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "r" | git mergetool submod ) &&
- rmdir submod && mv submod-movedaside submod &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by keeping module" &&
-
- mv submod submod-movedaside &&
- git checkout -b test6.b test6 &&
- git submodule update -N &&
- test_must_fail git merge master &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "l" | git mergetool submod ) &&
- test ! -e submod &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by deleting module" &&
-
- mv submod-movedaside submod &&
- git checkout -b test6.c master &&
- git submodule update -N &&
- test_must_fail git merge test6 &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "r" | git mergetool submod ) &&
- test ! -e submod &&
- test -d submod.orig &&
- git submodule update -N &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by deleting module" &&
- mv submod.orig submod &&
-
- git checkout -b test6.d master &&
- git submodule update -N &&
- test_must_fail git merge test6 &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "l" | git mergetool submod ) &&
- test "$(cat submod/bar)" = "master submodule" &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by keeping module" &&
- git reset --hard HEAD
+ git checkout -b test6 branch1 &&
+ git submodule update -N &&
+ mv submod submod-movedaside &&
+ git rm --cached submod &&
+ git commit -m "Submodule deleted from branch" &&
+ git checkout -b test6.a test6 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ rmdir submod && mv submod-movedaside submod &&
+ test "$(cat submod/bar)" = "branch1 submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module" &&
+
+ mv submod submod-movedaside &&
+ git checkout -b test6.b test6 &&
+ git submodule update -N &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ test ! -e submod &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by deleting module" &&
+
+ mv submod-movedaside submod &&
+ git checkout -b test6.c master &&
+ git submodule update -N &&
+ test_must_fail git merge test6 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ test ! -e submod &&
+ test -d submod.orig &&
+ git submodule update -N &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by deleting module" &&
+ mv submod.orig submod &&
+
+ git checkout -b test6.d master &&
+ git submodule update -N &&
+ test_must_fail git merge test6 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module" &&
+ git reset --hard HEAD
'
test_expect_success 'file vs modified submodule' '
- git checkout -b test7 branch1 &&
- git submodule update -N &&
- mv submod submod-movedaside &&
- git rm --cached submod &&
- echo not a submodule >submod &&
- git add submod &&
- git commit -m "Submodule path becomes file" &&
- git checkout -b test7.a branch1 &&
- test_must_fail git merge master &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "r" | git mergetool submod ) &&
- rmdir submod && mv submod-movedaside submod &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by keeping module" &&
-
- mv submod submod-movedaside &&
- git checkout -b test7.b test7 &&
- test_must_fail git merge master &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "l" | git mergetool submod ) &&
- git submodule update -N &&
- test "$(cat submod)" = "not a submodule" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by keeping file" &&
-
- git checkout -b test7.c master &&
- rmdir submod && mv submod-movedaside submod &&
- test ! -e submod.orig &&
- git submodule update -N &&
- test_must_fail git merge test7 &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "r" | git mergetool submod ) &&
- test -d submod.orig &&
- git submodule update -N &&
- test "$(cat submod)" = "not a submodule" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by keeping file" &&
-
- git checkout -b test7.d master &&
- rmdir submod && mv submod.orig submod &&
- git submodule update -N &&
- test_must_fail git merge test7 &&
- test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
- ( yes "" | git mergetool both>/dev/null 2>&1 ) &&
- ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
- ( yes "l" | git mergetool submod ) &&
- test "$(cat submod/bar)" = "master submodule" &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
- output="$(git mergetool --no-prompt)" &&
- test "$output" = "No files need merging" &&
- git commit -m "Merge resolved by keeping module"
+ git checkout -b test7 branch1 &&
+ git submodule update -N &&
+ mv submod submod-movedaside &&
+ git rm --cached submod &&
+ echo not a submodule >submod &&
+ git add submod &&
+ git commit -m "Submodule path becomes file" &&
+ git checkout -b test7.a branch1 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ rmdir submod && mv submod-movedaside submod &&
+ test "$(cat submod/bar)" = "branch1 submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module" &&
+
+ mv submod submod-movedaside &&
+ git checkout -b test7.b test7 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ git submodule update -N &&
+ test "$(cat submod)" = "not a submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping file" &&
+
+ git checkout -b test7.c master &&
+ rmdir submod && mv submod-movedaside submod &&
+ test ! -e submod.orig &&
+ git submodule update -N &&
+ test_must_fail git merge test7 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "r" | git mergetool submod ) &&
+ test -d submod.orig &&
+ git submodule update -N &&
+ test "$(cat submod)" = "not a submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping file" &&
+
+ git checkout -b test7.d master &&
+ rmdir submod && mv submod.orig submod &&
+ git submodule update -N &&
+ test_must_fail git merge test7 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool both>/dev/null 2>&1 ) &&
+ ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) &&
+ ( yes "l" | git mergetool submod ) &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git commit -m "Merge resolved by keeping module"
'
test_expect_success 'submodule in subdirectory' '
- git checkout -b test10 branch1 &&
- git submodule update -N &&
- (
- cd subdir &&
- test_create_repo subdir_module &&
+ git checkout -b test10 branch1 &&
+ git submodule update -N &&
+ (
+ cd subdir &&
+ test_create_repo subdir_module &&
+ (
+ cd subdir_module &&
+ : >file15 &&
+ git add file15 &&
+ git commit -m "add initial versions"
+ )
+ ) &&
+ git submodule add git://example.com/subsubmodule subdir/subdir_module &&
+ git add subdir/subdir_module &&
+ git commit -m "add submodule in subdirectory" &&
+
+ git checkout -b test10.a test10 &&
+ git submodule update -N &&
(
- cd subdir_module &&
- : >file15 &&
- git add file15 &&
- git commit -m "add initial versions"
- )
- ) &&
- git submodule add git://example.com/subsubmodule subdir/subdir_module &&
- git add subdir/subdir_module &&
- git commit -m "add submodule in subdirectory" &&
-
- git checkout -b test10.a test10 &&
- git submodule update -N &&
- (
- cd subdir/subdir_module &&
- git checkout -b super10.a &&
- echo test10.a >file15 &&
- git add file15 &&
- git commit -m "on branch 10.a"
- ) &&
- git add subdir/subdir_module &&
- git commit -m "change submodule in subdirectory on test10.a" &&
-
- git checkout -b test10.b test10 &&
- git submodule update -N &&
- (
cd subdir/subdir_module &&
- git checkout -b super10.b &&
- echo test10.b >file15 &&
- git add file15 &&
- git commit -m "on branch 10.b"
- ) &&
- git add subdir/subdir_module &&
- git commit -m "change submodule in subdirectory on test10.b" &&
-
- test_must_fail git merge test10.a >/dev/null 2>&1 &&
- (
- cd subdir &&
- ( yes "l" | git mergetool subdir_module )
- ) &&
- test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
- git submodule update -N &&
- test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
- git reset --hard &&
- git submodule update -N &&
-
- test_must_fail git merge test10.a >/dev/null 2>&1 &&
- ( yes "r" | git mergetool subdir/subdir_module ) &&
- test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
- git submodule update -N &&
- test "$(cat subdir/subdir_module/file15)" = "test10.a" &&
- git commit -m "branch1 resolved with mergetool" &&
- rm -rf subdir/subdir_module
+ git checkout -b super10.a &&
+ echo test10.a >file15 &&
+ git add file15 &&
+ git commit -m "on branch 10.a"
+ ) &&
+ git add subdir/subdir_module &&
+ git commit -m "change submodule in subdirectory on test10.a" &&
+
+ git checkout -b test10.b test10 &&
+ git submodule update -N &&
+ (
+ cd subdir/subdir_module &&
+ git checkout -b super10.b &&
+ echo test10.b >file15 &&
+ git add file15 &&
+ git commit -m "on branch 10.b"
+ ) &&
+ git add subdir/subdir_module &&
+ git commit -m "change submodule in subdirectory on test10.b" &&
+
+ test_must_fail git merge test10.a >/dev/null 2>&1 &&
+ (
+ cd subdir &&
+ ( yes "l" | git mergetool subdir_module )
+ ) &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+ git submodule update -N &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+ git reset --hard &&
+ git submodule update -N &&
+
+ test_must_fail git merge test10.a >/dev/null 2>&1 &&
+ ( yes "r" | git mergetool subdir/subdir_module ) &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+ git submodule update -N &&
+ test "$(cat subdir/subdir_module/file15)" = "test10.a" &&
+ git commit -m "branch1 resolved with mergetool" &&
+ rm -rf subdir/subdir_module
'
test_expect_success 'directory vs modified submodule' '
- git checkout -b test11 branch1 &&
- mv submod submod-movedaside &&
- git rm --cached submod &&
- mkdir submod &&
- echo not a submodule >submod/file16 &&
- git add submod/file16 &&
- git commit -m "Submodule path becomes directory" &&
-
- test_must_fail git merge master &&
- test -n "$(git ls-files -u)" &&
- ( yes "l" | git mergetool submod ) &&
- test "$(cat submod/file16)" = "not a submodule" &&
- rm -rf submod.orig &&
-
- git reset --hard >/dev/null 2>&1 &&
- test_must_fail git merge master &&
- test -n "$(git ls-files -u)" &&
- test ! -e submod.orig &&
- ( yes "r" | git mergetool submod ) &&
- test -d submod.orig &&
- test "$(cat submod.orig/file16)" = "not a submodule" &&
- rm -r submod.orig &&
- mv submod-movedaside/.git submod &&
- ( cd submod && git clean -f && git reset --hard ) &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
- git reset --hard >/dev/null 2>&1 && rm -rf submod-movedaside &&
-
- git checkout -b test11.c master &&
- git submodule update -N &&
- test_must_fail git merge test11 &&
- test -n "$(git ls-files -u)" &&
- ( yes "l" | git mergetool submod ) &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
-
- git reset --hard >/dev/null 2>&1 &&
- git submodule update -N &&
- test_must_fail git merge test11 &&
- test -n "$(git ls-files -u)" &&
- test ! -e submod.orig &&
- ( yes "r" | git mergetool submod ) &&
- test "$(cat submod/file16)" = "not a submodule" &&
-
- git reset --hard master >/dev/null 2>&1 &&
- ( cd submod && git clean -f && git reset --hard ) &&
- git submodule update -N
+ git checkout -b test11 branch1 &&
+ mv submod submod-movedaside &&
+ git rm --cached submod &&
+ mkdir submod &&
+ echo not a submodule >submod/file16 &&
+ git add submod/file16 &&
+ git commit -m "Submodule path becomes directory" &&
+
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "l" | git mergetool submod ) &&
+ test "$(cat submod/file16)" = "not a submodule" &&
+ rm -rf submod.orig &&
+
+ git reset --hard >/dev/null 2>&1 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ test ! -e submod.orig &&
+ ( yes "r" | git mergetool submod ) &&
+ test -d submod.orig &&
+ test "$(cat submod.orig/file16)" = "not a submodule" &&
+ rm -r submod.orig &&
+ mv submod-movedaside/.git submod &&
+ ( cd submod && git clean -f && git reset --hard ) &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+ git reset --hard >/dev/null 2>&1 && rm -rf submod-movedaside &&
+
+ git checkout -b test11.c master &&
+ git submodule update -N &&
+ test_must_fail git merge test11 &&
+ test -n "$(git ls-files -u)" &&
+ ( yes "l" | git mergetool submod ) &&
+ git submodule update -N &&
+ test "$(cat submod/bar)" = "master submodule" &&
+
+ git reset --hard >/dev/null 2>&1 &&
+ git submodule update -N &&
+ test_must_fail git merge test11 &&
+ test -n "$(git ls-files -u)" &&
+ test ! -e submod.orig &&
+ ( yes "r" | git mergetool submod ) &&
+ test "$(cat submod/file16)" = "not a submodule" &&
+
+ git reset --hard master >/dev/null 2>&1 &&
+ ( cd submod && git clean -f && git reset --hard ) &&
+ git submodule update -N
'
test_expect_success 'file with no base' '
- git checkout -b test13 branch1 &&
- test_must_fail git merge master &&
- git mergetool --no-prompt --tool mybase -- both &&
- >expected &&
- test_cmp both expected &&
- git reset --hard master >/dev/null 2>&1
+ git checkout -b test13 branch1 &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool mybase -- both &&
+ >expected &&
+ test_cmp both expected &&
+ git reset --hard master >/dev/null 2>&1
'
test_expect_success 'custom commands override built-ins' '
- git checkout -b test14 branch1 &&
- git config mergetool.defaults.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
- git config mergetool.defaults.trustExitCode true &&
- test_must_fail git merge master &&
- git mergetool --no-prompt --tool defaults -- both &&
- echo master both added >expected &&
- test_cmp both expected &&
- git config --unset mergetool.defaults.cmd &&
- git config --unset mergetool.defaults.trustExitCode &&
- git reset --hard master >/dev/null 2>&1
+ git checkout -b test14 branch1 &&
+ test_config mergetool.defaults.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ test_config mergetool.defaults.trustExitCode true &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool defaults -- both &&
+ echo master both added >expected &&
+ test_cmp both expected &&
+ git reset --hard master >/dev/null 2>&1
+'
+
+test_expect_success 'filenames seen by tools start with ./' '
+ git checkout -b test15 branch1 &&
+ test_config mergetool.writeToTemp false &&
+ test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
+ test_config mergetool.myecho.trustExitCode true &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool myecho -- both >actual &&
+ grep ^\./both_LOCAL_ actual >/dev/null &&
+ git reset --hard master >/dev/null 2>&1
+'
+
+test_expect_success 'temporary filenames are used with mergetool.writeToTemp' '
+ git checkout -b test16 branch1 &&
+ test_config mergetool.writeToTemp true &&
+ test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
+ test_config mergetool.myecho.trustExitCode true &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt --tool myecho -- both >actual &&
+ test_must_fail grep ^\./both_LOCAL_ actual >/dev/null &&
+ grep /both_LOCAL_ actual >/dev/null &&
+ git reset --hard master >/dev/null 2>&1
'
test_done
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index b8d4cdea8c..aad8a9c64d 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -109,4 +109,17 @@ test_expect_success 'do not bother loosening old objects' '
test_must_fail git cat-file -p $obj2
'
+test_expect_success 'keep packed objects found only in index' '
+ echo my-unique-content >file &&
+ git add file &&
+ git commit -m "make it reachable" &&
+ git gc &&
+ git reset HEAD^ &&
+ git reflog expire --expire=now --all &&
+ git add file &&
+ test-chmtime =-86400 .git/objects/pack/* &&
+ git gc --prune=1.hour.ago &&
+ git cat-file blob :file
+'
+
test_done
diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh
index a6e73d0635..847d098c09 100755
--- a/t/t8005-blame-i18n.sh
+++ b/t/t8005-blame-i18n.sh
@@ -33,7 +33,7 @@ author $SJIS_NAME
summary $SJIS_MSG
EOF
-test_expect_success NOT_MINGW \
+test_expect_success !MINGW \
'blame respects i18n.commitencoding' '
git blame --incremental file | \
egrep "^(author|summary) " > actual &&
@@ -49,7 +49,7 @@ author $EUC_JAPAN_NAME
summary $EUC_JAPAN_MSG
EOF
-test_expect_success NOT_MINGW \
+test_expect_success !MINGW \
'blame respects i18n.logoutputencoding' '
git config i18n.logoutputencoding eucJP &&
git blame --incremental file | \
@@ -66,7 +66,7 @@ author $UTF8_NAME
summary $UTF8_MSG
EOF
-test_expect_success NOT_MINGW \
+test_expect_success !MINGW \
'blame respects --encoding=UTF-8' '
git blame --incremental --encoding=UTF-8 file | \
egrep "^(author|summary) " > actual &&
@@ -82,7 +82,7 @@ author $UTF8_NAME
summary $UTF8_MSG
EOF
-test_expect_success NOT_MINGW \
+test_expect_success !MINGW \
'blame respects --encoding=none' '
git blame --incremental --encoding=none file | \
egrep "^(author|summary) " > actual &&
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
index ff19695e77..f16f3234a1 100755
--- a/t/t9119-git-svn-info.sh
+++ b/t/t9119-git-svn-info.sh
@@ -74,6 +74,36 @@ test_expect_success 'info .' "
test_cmp_info expected.info-dot actual.info-dot
"
+test_expect_success 'info $(pwd)' '
+ (cd svnwc; svn info "$(pwd)") >expected.info-pwd &&
+ (cd gitwc; git svn info "$(pwd)") >actual.info-pwd &&
+ grep -v ^Path: <expected.info-pwd >expected.info-np &&
+ grep -v ^Path: <actual.info-pwd >actual.info-np &&
+ test_cmp_info expected.info-np actual.info-np &&
+ test "$(sed -ne \"/^Path:/ s!/svnwc!!\" <expected.info-pwd)" = \
+ "$(sed -ne \"/^Path:/ s!/gitwc!!\" <actual.info-pwd)"
+ '
+
+test_expect_success 'info $(pwd)/../___wc' '
+ (cd svnwc; svn info "$(pwd)/../svnwc") >expected.info-pwd &&
+ (cd gitwc; git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
+ grep -v ^Path: <expected.info-pwd >expected.info-np &&
+ grep -v ^Path: <actual.info-pwd >actual.info-np &&
+ test_cmp_info expected.info-np actual.info-np &&
+ test "$(sed -ne \"/^Path:/ s!/svnwc!!\" <expected.info-pwd)" = \
+ "$(sed -ne \"/^Path:/ s!/gitwc!!\" <actual.info-pwd)"
+ '
+
+test_expect_success 'info $(pwd)/../___wc//file' '
+ (cd svnwc; svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
+ (cd gitwc; git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
+ grep -v ^Path: <expected.info-pwd >expected.info-np &&
+ grep -v ^Path: <actual.info-pwd >actual.info-np &&
+ test_cmp_info expected.info-np actual.info-np &&
+ test "$(sed -ne \"/^Path:/ s!/svnwc!!\" <expected.info-pwd)" = \
+ "$(sed -ne \"/^Path:/ s!/gitwc!!\" <actual.info-pwd)"
+ '
+
test_expect_success 'info --url .' '
test "$(cd gitwc; git svn info --url .)" = "$quoted_svnrepo"
'
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index d400442a4a..37c2d633f0 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -347,36 +347,6 @@ test_expect_success 'B: fail on invalid blob sha1' '
rm -f .git/objects/pack_* .git/objects/index_*
cat >input <<INPUT_END
-commit .badbranchname
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
-
-from refs/heads/master
-
-INPUT_END
-test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
- test_must_fail git fast-import <input
-'
-rm -f .git/objects/pack_* .git/objects/index_*
-
-cat >input <<INPUT_END
-commit bad[branch]name
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
-
-from refs/heads/master
-
-INPUT_END
-test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
- test_must_fail git fast-import <input
-'
-rm -f .git/objects/pack_* .git/objects/index_*
-
-cat >input <<INPUT_END
commit TEMP_TAG
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
@@ -2336,7 +2306,7 @@ test_expect_success 'R: cat-blob-fd must be a nonnegative integer' '
test_must_fail git fast-import --cat-blob-fd=-1 </dev/null
'
-test_expect_success NOT_MINGW 'R: print old blob' '
+test_expect_success !MINGW 'R: print old blob' '
blob=$(echo "yes it can" | git hash-object -w --stdin) &&
cat >expect <<-EOF &&
${blob} blob 11
@@ -2348,7 +2318,7 @@ test_expect_success NOT_MINGW 'R: print old blob' '
test_cmp expect actual
'
-test_expect_success NOT_MINGW 'R: in-stream cat-blob-fd not respected' '
+test_expect_success !MINGW 'R: in-stream cat-blob-fd not respected' '
echo hello >greeting &&
blob=$(git hash-object -w greeting) &&
cat >expect <<-EOF &&
@@ -2369,7 +2339,7 @@ test_expect_success NOT_MINGW 'R: in-stream cat-blob-fd not respected' '
test_cmp expect actual.1
'
-test_expect_success NOT_MINGW 'R: print new blob' '
+test_expect_success !MINGW 'R: print new blob' '
blob=$(echo "yep yep yep" | git hash-object --stdin) &&
cat >expect <<-EOF &&
${blob} blob 12
@@ -2387,7 +2357,7 @@ test_expect_success NOT_MINGW 'R: print new blob' '
test_cmp expect actual
'
-test_expect_success NOT_MINGW 'R: print new blob by sha1' '
+test_expect_success !MINGW 'R: print new blob by sha1' '
blob=$(echo "a new blob named by sha1" | git hash-object --stdin) &&
cat >expect <<-EOF &&
${blob} blob 25
@@ -2687,7 +2657,7 @@ test_expect_success 'R: verify created pack' '
test_expect_success \
'R: verify written objects' \
'git --git-dir=R/.git cat-file blob big-file:big1 >actual &&
- test_cmp expect actual &&
+ test_cmp_bin expect actual &&
a=$(git --git-dir=R/.git rev-parse big-file:big1) &&
b=$(git --git-dir=R/.git rev-parse big-file:big2) &&
test $a = $b'
@@ -2866,7 +2836,7 @@ test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
#
# notemodify, mark in commit-ish
#
-test_expect_success 'S: notemodify with garbarge after mark commit-ish must fail' '
+test_expect_success 'S: notemodify with garbage after mark commit-ish must fail' '
test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
commit refs/heads/Snotes
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh
new file mode 100755
index 0000000000..897dc50907
--- /dev/null
+++ b/t/t9351-fast-export-anonymize.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+test_description='basic tests for fast-export --anonymize'
+. ./test-lib.sh
+
+test_expect_success 'setup simple repo' '
+ test_commit base &&
+ test_commit foo &&
+ git checkout -b other HEAD^ &&
+ mkdir subdir &&
+ test_commit subdir/bar &&
+ test_commit subdir/xyzzy &&
+ git tag -m "annotated tag" mytag
+'
+
+test_expect_success 'export anonymized stream' '
+ git fast-export --anonymize --all >stream
+'
+
+# this also covers commit messages
+test_expect_success 'stream omits path names' '
+ ! grep base stream &&
+ ! grep foo stream &&
+ ! grep subdir stream &&
+ ! grep bar stream &&
+ ! grep xyzzy stream
+'
+
+test_expect_success 'stream allows master as refname' '
+ grep master stream
+'
+
+test_expect_success 'stream omits other refnames' '
+ ! grep other stream &&
+ ! grep mytag stream
+'
+
+test_expect_success 'stream omits identities' '
+ ! grep "$GIT_COMMITTER_NAME" stream &&
+ ! grep "$GIT_COMMITTER_EMAIL" stream &&
+ ! grep "$GIT_AUTHOR_NAME" stream &&
+ ! grep "$GIT_AUTHOR_EMAIL" stream
+'
+
+test_expect_success 'stream omits tag message' '
+ ! grep "annotated tag" stream
+'
+
+# NOTE: we chdir to the new, anonymized repository
+# after this. All further tests should assume this.
+test_expect_success 'import stream to new repository' '
+ git init new &&
+ cd new &&
+ git fast-import <../stream
+'
+
+test_expect_success 'result has two branches' '
+ git for-each-ref --format="%(refname)" refs/heads >branches &&
+ test_line_count = 2 branches &&
+ other_branch=$(grep -v refs/heads/master branches)
+'
+
+test_expect_success 'repo has original shape and timestamps' '
+ shape () {
+ git log --format="%m %ct" --left-right --boundary "$@"
+ } &&
+ (cd .. && shape master...other) >expect &&
+ shape master...$other_branch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'root tree has original shape' '
+ # the output entries are not necessarily in the same
+ # order, but we know at least that we will have one tree
+ # and one blob, so just check the sorted order
+ cat >expect <<-\EOF &&
+ blob
+ tree
+ EOF
+ git ls-tree $other_branch >root &&
+ cut -d" " -f2 <root | sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'paths in subdir ended up in one tree' '
+ cat >expect <<-\EOF &&
+ blob
+ blob
+ EOF
+ tree=$(grep tree root | cut -f2) &&
+ git ls-tree $other_branch:$tree >tree &&
+ cut -d" " -f2 <tree >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'tag points to branch tip' '
+ git rev-parse $other_branch >expect &&
+ git for-each-ref --format="%(*objectname)" | grep . >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'idents are shared' '
+ git log --all --format="%an <%ae>" >authors &&
+ sort -u authors >unique &&
+ test_line_count = 1 unique &&
+ git log --all --format="%cn <%ce>" >committers &&
+ sort -u committers >unique &&
+ test_line_count = 1 unique &&
+ ! test_cmp authors committers
+'
+
+test_done
diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh
index 23a827fa77..897b3c3034 100755
--- a/t/t9809-git-p4-client-view.sh
+++ b/t/t9809-git-p4-client-view.sh
@@ -365,7 +365,7 @@ test_expect_success 'wildcard files submit back to p4, client-spec case' '
(
cd "$git" &&
echo git-wild-hash >dir1/git-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
echo git-wild-star >dir1/git-wild\*star
fi &&
@@ -379,7 +379,7 @@ test_expect_success 'wildcard files submit back to p4, client-spec case' '
(
cd "$cli" &&
test_path_is_file dir1/git-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
test_path_is_file dir1/git-wild\*star
fi &&
diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh
index c7472cbf54..0206771fbb 100755
--- a/t/t9812-git-p4-wildcards.sh
+++ b/t/t9812-git-p4-wildcards.sh
@@ -14,7 +14,7 @@ test_expect_success 'add p4 files with wildcards in the names' '
printf "file2\nhas\nsome\nrandom\ntext\n" >file2 &&
p4 add file2 &&
echo file-wild-hash >file-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
echo file-wild-star >file-wild\*star
fi &&
@@ -31,7 +31,7 @@ test_expect_success 'wildcard files git p4 clone' '
(
cd "$git" &&
test -f file-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
test -f file-wild\*star
fi &&
@@ -46,7 +46,7 @@ test_expect_success 'wildcard files submit back to p4, add' '
(
cd "$git" &&
echo git-wild-hash >git-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
echo git-wild-star >git-wild\*star
fi &&
@@ -60,7 +60,7 @@ test_expect_success 'wildcard files submit back to p4, add' '
(
cd "$cli" &&
test_path_is_file git-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
test_path_is_file git-wild\*star
fi &&
@@ -75,7 +75,7 @@ test_expect_success 'wildcard files submit back to p4, modify' '
(
cd "$git" &&
echo new-line >>git-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
echo new-line >>git-wild\*star
fi &&
@@ -89,7 +89,7 @@ test_expect_success 'wildcard files submit back to p4, modify' '
(
cd "$cli" &&
test_line_count = 2 git-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
test_line_count = 2 git-wild\*star
fi &&
@@ -152,7 +152,7 @@ test_expect_success 'wildcard files submit back to p4, delete' '
(
cd "$cli" &&
test_path_is_missing git-wild#hash &&
- if test_have_prereq NOT_MINGW NOT_CYGWIN
+ if test_have_prereq !MINGW,!CYGWIN
then
test_path_is_missing git-wild\*star
fi &&
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index 1243d96092..4cff6a760f 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -417,7 +417,7 @@ test_expect_success 'cleanup chmod after submit cancel' '
! p4 fstat -T action text &&
test_path_is_file text+x &&
! p4 fstat -T action text+x &&
- if test_have_prereq NOT_CYGWIN
+ if test_have_prereq !CYGWIN
then
stat --format=%A text | egrep ^-r-- &&
stat --format=%A text+x | egrep ^-r-x
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index dafd6ad21a..0d93e33de4 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -413,7 +413,7 @@ test_external () {
# test_run_, but keep its stdout on our stdout even in
# non-verbose mode.
"$@" 2>&4
- if [ "$?" = 0 ]
+ if test "$?" = 0
then
if test $test_external_has_tap -eq 0; then
test_ok_ "$descr"
@@ -440,11 +440,12 @@ test_external_without_stderr () {
tmp=${TMPDIR:-/tmp}
stderr="$tmp/git-external-stderr.$$.tmp"
test_external "$@" 4> "$stderr"
- [ -f "$stderr" ] || error "Internal error: $stderr disappeared."
+ test -f "$stderr" || error "Internal error: $stderr disappeared."
descr="no stderr: $1"
shift
say >&3 "# expecting no stderr from previous command"
- if [ ! -s "$stderr" ]; then
+ if test ! -s "$stderr"
+ then
rm "$stderr"
if test $test_external_has_tap -eq 0; then
@@ -454,8 +455,9 @@ test_external_without_stderr () {
test_success=$(($test_success + 1))
fi
else
- if [ "$verbose" = t ]; then
- output=`echo; echo "# Stderr is:"; cat "$stderr"`
+ if test "$verbose" = t
+ then
+ output=$(echo; echo "# Stderr is:"; cat "$stderr")
else
output=
fi
@@ -474,7 +476,7 @@ test_external_without_stderr () {
# The commands test the existence or non-existence of $1. $2 can be
# given to provide a more precise diagnosis.
test_path_is_file () {
- if ! [ -f "$1" ]
+ if ! test -f "$1"
then
echo "File $1 doesn't exist. $*"
false
@@ -482,7 +484,7 @@ test_path_is_file () {
}
test_path_is_dir () {
- if ! [ -d "$1" ]
+ if ! test -d "$1"
then
echo "Directory $1 doesn't exist. $*"
false
@@ -501,11 +503,12 @@ test_dir_is_empty () {
}
test_path_is_missing () {
- if [ -e "$1" ]
+ if test -e "$1"
then
echo "Path exists:"
ls -ld "$1"
- if [ $# -ge 1 ]; then
+ if test $# -ge 1
+ then
echo "$*"
fi
false
@@ -634,6 +637,15 @@ test_cmp_bin() {
cmp "$@"
}
+# Call any command "$@" but be more verbose about its
+# failure. This is handy for commands like "test" which do
+# not output anything when they fail.
+verbose () {
+ "$@" && return 0
+ echo >&2 "command failed: $(git rev-parse --sq-quote "$@")"
+ return 1
+}
+
# Check if the file expected to be empty is indeed empty, and barfs
# otherwise.
@@ -657,9 +669,12 @@ test_cmp_rev () {
# similar to GNU seq(1), but the latter might not be available
# everywhere (and does not do letters). It may be used like:
#
-# for i in `test_seq 100`; do
-# for j in `test_seq 10 20`; do
-# for k in `test_seq a z`; do
+# for i in $(test_seq 100)
+# do
+# for j in $(test_seq 10 20)
+# do
+# for k in $(test_seq a z)
+# do
# echo $i-$j-$k
# done
# done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index b1bc65bfb5..cf19339cce 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -233,6 +233,10 @@ do
--root=*)
root=$(expr "z$1" : 'z[^=]*=\(.*\)')
shift ;;
+ -x)
+ trace=t
+ verbose=t
+ shift ;;
*)
echo "error: unknown test option '$1'" >&2; exit 1 ;;
esac
@@ -517,10 +521,39 @@ maybe_setup_valgrind () {
fi
}
+# This is a separate function because some tests use
+# "return" to end a test_expect_success block early
+# (and we want to make sure we run any cleanup like
+# "set +x").
+test_eval_inner_ () {
+ # Do not add anything extra (including LF) after '$*'
+ eval "
+ test \"$trace\" = t && set -x
+ $*"
+}
+
test_eval_ () {
- # This is a separate function because some tests use
- # "return" to end a test_expect_success block early.
- eval </dev/null >&3 2>&4 "$*"
+ # We run this block with stderr redirected to avoid extra cruft
+ # during a "-x" trace. Once in "set -x" mode, we cannot prevent
+ # the shell from printing the "set +x" to turn it off (nor the saving
+ # of $? before that). But we can make sure that the output goes to
+ # /dev/null.
+ #
+ # The test itself is run with stderr put back to &4 (so either to
+ # /dev/null, or to the original stderr if --verbose was used).
+ {
+ test_eval_inner_ "$@" </dev/null >&3 2>&4
+ test_eval_ret_=$?
+ if test "$trace" = t
+ then
+ set +x
+ if test "$test_eval_ret_" != 0
+ then
+ say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
+ fi
+ fi
+ } 2>/dev/null
+ return $test_eval_ret_
}
test_run_ () {
@@ -531,7 +564,8 @@ test_run_ () {
eval_ret=$?
teardown_malloc_check
- if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
+ if test -z "$immediate" || test $eval_ret = 0 ||
+ test -n "$expecting_failure" && test "$test_cleanup" != ":"
then
setup_malloc_check
test_eval_ "$test_cleanup"
@@ -813,7 +847,8 @@ rm -fr "$TRASH_DIRECTORY" || {
}
HOME="$TRASH_DIRECTORY"
-export HOME
+GNUPGHOME="$HOME/gnupg-home-not-used"
+export HOME GNUPGHOME
if test -z "$TEST_NO_CREATE_REPO"
then
@@ -870,7 +905,7 @@ case $(uname -s) in
# backslashes in pathspec are converted to '/'
# exec does not inherit the PID
test_set_prereq MINGW
- test_set_prereq NOT_CYGWIN
+ test_set_prereq NATIVE_CRLF
test_set_prereq SED_STRIPS_CR
test_set_prereq GREP_STRIPS_CR
GIT_TEST_CMP=mingw_test_cmp
@@ -878,7 +913,6 @@ case $(uname -s) in
*CYGWIN*)
test_set_prereq POSIXPERM
test_set_prereq EXECKEEPSPID
- test_set_prereq NOT_MINGW
test_set_prereq CYGWIN
test_set_prereq SED_STRIPS_CR
test_set_prereq GREP_STRIPS_CR
@@ -887,8 +921,6 @@ case $(uname -s) in
test_set_prereq POSIXPERM
test_set_prereq BSLASHPSPEC
test_set_prereq EXECKEEPSPID
- test_set_prereq NOT_MINGW
- test_set_prereq NOT_CYGWIN
;;
esac
diff --git a/tag.c b/tag.c
index 82d841bf2d..5b0ac62ed8 100644
--- a/tag.c
+++ b/tag.c
@@ -4,9 +4,6 @@
#include "tree.h"
#include "blob.h"
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
-
const char *tag_type = "tag";
struct object *deref_tag(struct object *o, const char *warn, int warnlen)
@@ -143,20 +140,3 @@ int parse_tag(struct tag *item)
free(data);
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;
- size_t len = 0;
- while (len < size && !starts_with(buf + len, PGP_SIGNATURE) &&
- !starts_with(buf + len, PGP_MESSAGE)) {
- eol = memchr(buf + len, '\n', size - len);
- len += eol ? eol - (buf + len) + 1 : size - len;
- }
- return len;
-}
diff --git a/tag.h b/tag.h
index bc8a1e40f0..f4580aea42 100644
--- a/tag.h
+++ b/tag.h
@@ -17,6 +17,5 @@ extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long si
extern int parse_tag(struct tag *item);
extern struct object *deref_tag(struct object *, const char *, int);
extern struct object *deref_tag_noverify(struct object *);
-extern size_t parse_signature(const char *buf, unsigned long size);
#endif /* TAG_H */
diff --git a/templates/hooks--pre-push.sample b/templates/hooks--pre-push.sample
index 1f3bcebfd7..69e3c67b00 100755
--- a/templates/hooks--pre-push.sample
+++ b/templates/hooks--pre-push.sample
@@ -45,7 +45,7 @@ do
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
if [ -n "$commit" ]
then
- echo "Found WIP commit in $local_ref, not pushing"
+ echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
fi
fi
diff --git a/test-config.c b/test-config.c
new file mode 100644
index 0000000000..6a77552210
--- /dev/null
+++ b/test-config.c
@@ -0,0 +1,152 @@
+#include "cache.h"
+#include "string-list.h"
+
+/*
+ * This program exposes the C API of the configuration mechanism
+ * as a set of simple commands in order to facilitate testing.
+ *
+ * Reads stdin and prints result of command to stdout:
+ *
+ * get_value -> prints the value with highest priority for the entered key
+ *
+ * get_value_multi -> prints all values for the entered key in increasing order
+ * of priority
+ *
+ * get_int -> print integer value for the entered key or die
+ *
+ * get_bool -> print bool value for the entered key or die
+ *
+ * get_string -> print string value for the entered key or die
+ *
+ * configset_get_value -> returns value with the highest priority for the entered key
+ * from a config_set constructed from files entered as arguments.
+ *
+ * configset_get_value_multi -> returns value_list for the entered key sorted in
+ * ascending order of priority from a config_set
+ * constructed from files entered as arguments.
+ *
+ * Examples:
+ *
+ * To print the value with highest priority for key "foo.bAr Baz.rock":
+ * test-config get_value "foo.bAr Baz.rock"
+ *
+ */
+
+
+int main(int argc, char **argv)
+{
+ int i, val;
+ const char *v;
+ const struct string_list *strptr;
+ struct config_set cs;
+ git_configset_init(&cs);
+
+ if (argc < 2) {
+ fprintf(stderr, "Please, provide a command name on the command-line\n");
+ goto exit1;
+ } else if (argc == 3 && !strcmp(argv[1], "get_value")) {
+ if (!git_config_get_value(argv[2], &v)) {
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
+ strptr = git_config_get_value_multi(argv[2]);
+ if (strptr) {
+ for (i = 0; i < strptr->nr; i++) {
+ v = strptr->items[i].string;
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ }
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_int")) {
+ if (!git_config_get_int(argv[2], &val)) {
+ printf("%d\n", val);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_bool")) {
+ if (!git_config_get_bool(argv[2], &val)) {
+ printf("%d\n", val);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (argc == 3 && !strcmp(argv[1], "get_string")) {
+ if (!git_config_get_string_const(argv[2], &v)) {
+ printf("%s\n", v);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (!strcmp(argv[1], "configset_get_value")) {
+ for (i = 3; i < argc; i++) {
+ int err;
+ if ((err = git_configset_add_file(&cs, argv[i]))) {
+ fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+ goto exit2;
+ }
+ }
+ if (!git_configset_get_value(&cs, argv[2], &v)) {
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ } else if (!strcmp(argv[1], "configset_get_value_multi")) {
+ for (i = 3; i < argc; i++) {
+ int err;
+ if ((err = git_configset_add_file(&cs, argv[i]))) {
+ fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
+ goto exit2;
+ }
+ }
+ strptr = git_configset_get_value_multi(&cs, argv[2]);
+ if (strptr) {
+ for (i = 0; i < strptr->nr; i++) {
+ v = strptr->items[i].string;
+ if (!v)
+ printf("(NULL)\n");
+ else
+ printf("%s\n", v);
+ }
+ goto exit0;
+ } else {
+ printf("Value not found for \"%s\"\n", argv[2]);
+ goto exit1;
+ }
+ }
+
+ die("%s: Please check the syntax and the function name", argv[0]);
+
+exit0:
+ git_configset_clear(&cs);
+ return 0;
+
+exit1:
+ git_configset_clear(&cs);
+ return 1;
+
+exit2:
+ git_configset_clear(&cs);
+ return 2;
+}
diff --git a/test-date.c b/test-date.c
index 10afaabbfa..94a6997a8f 100644
--- a/test-date.c
+++ b/test-date.c
@@ -19,19 +19,21 @@ static void show_dates(char **argv, struct timeval *now)
static void parse_dates(char **argv, struct timeval *now)
{
+ struct strbuf result = STRBUF_INIT;
+
for (; *argv; argv++) {
- char result[100];
unsigned long t;
int tz;
- result[0] = 0;
- parse_date(*argv, result, sizeof(result));
- if (sscanf(result, "%lu %d", &t, &tz) == 2)
+ strbuf_reset(&result);
+ parse_date(*argv, &result);
+ if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
printf("%s -> %s\n",
*argv, show_date(t, tz, DATE_ISO8601));
else
printf("%s -> bad\n", *argv);
}
+ strbuf_release(&result);
}
static void parse_approxidate(char **argv, struct timeval *now)
diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c
index 330ba4f4dd..54c0872fcb 100644
--- a/test-dump-cache-tree.c
+++ b/test-dump-cache-tree.c
@@ -26,16 +26,16 @@ static int dump_cache_tree(struct cache_tree *it,
return 0;
if (it->entry_count < 0) {
+ /* invalid */
dump_one(it, pfx, "");
dump_one(ref, pfx, "#(ref) ");
- if (it->subtree_nr != ref->subtree_nr)
- errs = 1;
}
else {
dump_one(it, pfx, "");
if (hashcmp(it->sha1, ref->sha1) ||
ref->entry_count != it->entry_count ||
ref->subtree_nr != it->subtree_nr) {
+ /* claims to be valid but is lying */
dump_one(ref, pfx, "#(ref) ");
errs = 1;
}
diff --git a/test-regex.c b/test-regex.c
index b5bfd54139..0dc598ecdc 100644
--- a/test-regex.c
+++ b/test-regex.c
@@ -1,4 +1,4 @@
-#include <git-compat-util.h>
+#include "git-compat-util.h"
int main(int argc, char **argv)
{
diff --git a/test-run-command.c b/test-run-command.c
index 37918e15f5..89c7de2c60 100644
--- a/test-run-command.c
+++ b/test-run-command.c
@@ -15,9 +15,7 @@
int main(int argc, char **argv)
{
- struct child_process proc;
-
- memset(&proc, 0, sizeof(proc));
+ struct child_process proc = CHILD_PROCESS_INIT;
if (argc < 3)
return 1;
diff --git a/test-scrap-cache-tree.c b/test-scrap-cache-tree.c
index 9ebcbca9d2..6efee31a48 100644
--- a/test-scrap-cache-tree.c
+++ b/test-scrap-cache-tree.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "tree.h"
#include "cache-tree.h"
diff --git a/test-sha1-array.c b/test-sha1-array.c
new file mode 100644
index 0000000000..ddc491eff9
--- /dev/null
+++ b/test-sha1-array.c
@@ -0,0 +1,34 @@
+#include "cache.h"
+#include "sha1-array.h"
+
+static void print_sha1(const unsigned char sha1[20], void *data)
+{
+ puts(sha1_to_hex(sha1));
+}
+
+int main(int argc, char **argv)
+{
+ struct sha1_array array = SHA1_ARRAY_INIT;
+ struct strbuf line = STRBUF_INIT;
+
+ while (strbuf_getline(&line, stdin, '\n') != EOF) {
+ const char *arg;
+ unsigned char sha1[20];
+
+ if (skip_prefix(line.buf, "append ", &arg)) {
+ if (get_sha1_hex(arg, sha1))
+ die("not a hexadecimal SHA1: %s", arg);
+ sha1_array_append(&array, sha1);
+ } else if (skip_prefix(line.buf, "lookup ", &arg)) {
+ if (get_sha1_hex(arg, sha1))
+ die("not a hexadecimal SHA1: %s", arg);
+ printf("%d\n", sha1_array_lookup(&array, sha1));
+ } else if (!strcmp(line.buf, "clear"))
+ sha1_array_clear(&array);
+ else if (!strcmp(line.buf, "for_each_unique"))
+ sha1_array_for_each_unique(&array, print_sha1, NULL);
+ else
+ die("unknown command: %s", line.buf);
+ }
+ return 0;
+}
diff --git a/test-sigchain.c b/test-sigchain.c
index 42db234e87..e499fce60f 100644
--- a/test-sigchain.c
+++ b/test-sigchain.c
@@ -1,5 +1,5 @@
-#include "sigchain.h"
#include "cache.h"
+#include "sigchain.h"
#define X(f) \
static void f(int sig) { \
diff --git a/test-subprocess.c b/test-subprocess.c
index 93525eb7be..56881a0324 100644
--- a/test-subprocess.c
+++ b/test-subprocess.c
@@ -3,7 +3,7 @@
int main(int argc, char **argv)
{
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
int nogit = 0;
setup_git_directory_gently(&nogit);
@@ -13,7 +13,6 @@ int main(int argc, char **argv)
setup_work_tree();
argv++;
}
- memset(&cp, 0, sizeof(cp));
cp.git_cmd = 1;
cp.argv = (const char **)argv + 1;
return run_command(&cp);
diff --git a/thread-utils.h b/thread-utils.h
index 6fb98c333c..d9a769d190 100644
--- a/thread-utils.h
+++ b/thread-utils.h
@@ -7,5 +7,9 @@
extern int online_cpus(void);
extern int init_recursive_mutex(pthread_mutex_t*);
+#else
+
+#define online_cpus() 1
+
#endif
#endif /* THREAD_COMPAT_H */
diff --git a/trace.c b/trace.c
index e583dc63bb..4778608941 100644
--- a/trace.c
+++ b/trace.c
@@ -216,7 +216,7 @@ void trace_argv_printf(const char **argv, const char *format, ...)
va_end(ap);
}
-void trace_strbuf(const char *key, const struct strbuf *data)
+void trace_strbuf(struct trace_key *key, const struct strbuf *data)
{
trace_strbuf_fl(NULL, 0, key, data);
}
@@ -298,13 +298,12 @@ void trace_repo_setup(const char *prefix)
{
static struct trace_key key = TRACE_KEY_INIT(SETUP);
const char *git_work_tree;
- char cwd[PATH_MAX];
+ char *cwd;
if (!trace_want(&key))
return;
- if (!getcwd(cwd, PATH_MAX))
- die("Unable to get current working directory");
+ cwd = xgetcwd();
if (!(git_work_tree = get_git_work_tree()))
git_work_tree = "(null)";
@@ -316,6 +315,8 @@ void trace_repo_setup(const char *prefix)
trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
+
+ free(cwd);
}
int trace_want(struct trace_key *key)
@@ -384,7 +385,7 @@ static inline uint64_t gettimeofday_nanos(void)
* Returns nanoseconds since the epoch (01/01/1970), for performance tracing
* (i.e. favoring high precision over wall clock time accuracy).
*/
-inline uint64_t getnanotime(void)
+uint64_t getnanotime(void)
{
static uint64_t offset;
if (offset > 1) {
diff --git a/trailer.c b/trailer.c
new file mode 100644
index 0000000000..8514566564
--- /dev/null
+++ b/trailer.c
@@ -0,0 +1,851 @@
+#include "cache.h"
+#include "string-list.h"
+#include "run-command.h"
+#include "string-list.h"
+#include "trailer.h"
+/*
+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
+ */
+
+enum action_where { WHERE_END, WHERE_AFTER, WHERE_BEFORE, WHERE_START };
+enum action_if_exists { EXISTS_ADD_IF_DIFFERENT_NEIGHBOR, EXISTS_ADD_IF_DIFFERENT,
+ EXISTS_ADD, EXISTS_REPLACE, EXISTS_DO_NOTHING };
+enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };
+
+struct conf_info {
+ char *name;
+ char *key;
+ char *command;
+ enum action_where where;
+ enum action_if_exists if_exists;
+ enum action_if_missing if_missing;
+};
+
+static struct conf_info default_conf_info;
+
+struct trailer_item {
+ struct trailer_item *previous;
+ struct trailer_item *next;
+ const char *token;
+ const char *value;
+ struct conf_info conf;
+};
+
+static struct trailer_item *first_conf_item;
+
+static char *separators = ":";
+
+#define TRAILER_ARG_STRING "$ARG"
+
+static int after_or_end(enum action_where where)
+{
+ return (where == WHERE_AFTER) || (where == WHERE_END);
+}
+
+/*
+ * Return the length of the string not including any final
+ * punctuation. E.g., the input "Signed-off-by:" would return
+ * 13, stripping the trailing punctuation but retaining
+ * internal punctuation.
+ */
+static size_t token_len_without_separator(const char *token, size_t len)
+{
+ while (len > 0 && !isalnum(token[len - 1]))
+ len--;
+ return len;
+}
+
+static int same_token(struct trailer_item *a, struct trailer_item *b)
+{
+ size_t a_len = token_len_without_separator(a->token, strlen(a->token));
+ size_t b_len = token_len_without_separator(b->token, strlen(b->token));
+ size_t min_len = (a_len > b_len) ? b_len : a_len;
+
+ return !strncasecmp(a->token, b->token, min_len);
+}
+
+static int same_value(struct trailer_item *a, struct trailer_item *b)
+{
+ return !strcasecmp(a->value, b->value);
+}
+
+static int same_trailer(struct trailer_item *a, struct trailer_item *b)
+{
+ return same_token(a, b) && same_value(a, b);
+}
+
+static inline int contains_only_spaces(const char *str)
+{
+ const char *s = str;
+ while (*s && isspace(*s))
+ s++;
+ return !*s;
+}
+
+static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
+{
+ const char *ptr = strstr(sb->buf, a);
+ if (ptr)
+ strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
+}
+
+static void free_trailer_item(struct trailer_item *item)
+{
+ free(item->conf.name);
+ free(item->conf.key);
+ free(item->conf.command);
+ free((char *)item->token);
+ free((char *)item->value);
+ free(item);
+}
+
+static char last_non_space_char(const char *s)
+{
+ int i;
+ for (i = strlen(s) - 1; i >= 0; i--)
+ if (!isspace(s[i]))
+ return s[i];
+ return '\0';
+}
+
+static void print_tok_val(const char *tok, const char *val)
+{
+ char c = last_non_space_char(tok);
+ if (!c)
+ return;
+ if (strchr(separators, c))
+ printf("%s%s\n", tok, val);
+ else
+ printf("%s%c %s\n", tok, separators[0], val);
+}
+
+static void print_all(struct trailer_item *first, int trim_empty)
+{
+ struct trailer_item *item;
+ for (item = first; item; item = item->next) {
+ if (!trim_empty || strlen(item->value) > 0)
+ print_tok_val(item->token, item->value);
+ }
+}
+
+static void update_last(struct trailer_item **last)
+{
+ if (*last)
+ while ((*last)->next != NULL)
+ *last = (*last)->next;
+}
+
+static void update_first(struct trailer_item **first)
+{
+ if (*first)
+ while ((*first)->previous != NULL)
+ *first = (*first)->previous;
+}
+
+static void add_arg_to_input_list(struct trailer_item *on_tok,
+ struct trailer_item *arg_tok,
+ struct trailer_item **first,
+ struct trailer_item **last)
+{
+ if (after_or_end(arg_tok->conf.where)) {
+ arg_tok->next = on_tok->next;
+ on_tok->next = arg_tok;
+ arg_tok->previous = on_tok;
+ if (arg_tok->next)
+ arg_tok->next->previous = arg_tok;
+ update_last(last);
+ } else {
+ arg_tok->previous = on_tok->previous;
+ on_tok->previous = arg_tok;
+ arg_tok->next = on_tok;
+ if (arg_tok->previous)
+ arg_tok->previous->next = arg_tok;
+ update_first(first);
+ }
+}
+
+static int check_if_different(struct trailer_item *in_tok,
+ struct trailer_item *arg_tok,
+ int check_all)
+{
+ enum action_where where = arg_tok->conf.where;
+ do {
+ if (!in_tok)
+ return 1;
+ if (same_trailer(in_tok, arg_tok))
+ return 0;
+ /*
+ * if we want to add a trailer after another one,
+ * we have to check those before this one
+ */
+ in_tok = after_or_end(where) ? in_tok->previous : in_tok->next;
+ } while (check_all);
+ return 1;
+}
+
+static void remove_from_list(struct trailer_item *item,
+ struct trailer_item **first,
+ struct trailer_item **last)
+{
+ struct trailer_item *next = item->next;
+ struct trailer_item *previous = item->previous;
+
+ if (next) {
+ item->next->previous = previous;
+ item->next = NULL;
+ } else if (last)
+ *last = previous;
+
+ if (previous) {
+ item->previous->next = next;
+ item->previous = NULL;
+ } else if (first)
+ *first = next;
+}
+
+static struct trailer_item *remove_first(struct trailer_item **first)
+{
+ struct trailer_item *item = *first;
+ *first = item->next;
+ if (item->next) {
+ item->next->previous = NULL;
+ item->next = NULL;
+ }
+ return item;
+}
+
+static int read_from_command(struct child_process *cp, struct strbuf *buf)
+{
+ if (run_command(cp))
+ return error("running trailer command '%s' failed", cp->argv[0]);
+ if (strbuf_read(buf, cp->out, 1024) < 1)
+ return error("reading from trailer command '%s' failed", cp->argv[0]);
+ strbuf_trim(buf);
+ return 0;
+}
+
+static const char *apply_command(const char *command, const char *arg)
+{
+ struct strbuf cmd = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ struct child_process cp;
+ const char *argv[] = {NULL, NULL};
+ const char *result;
+
+ strbuf_addstr(&cmd, command);
+ if (arg)
+ strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+
+ argv[0] = cmd.buf;
+ memset(&cp, 0, sizeof(cp));
+ cp.argv = argv;
+ cp.env = local_repo_env;
+ cp.no_stdin = 1;
+ cp.out = -1;
+ cp.use_shell = 1;
+
+ if (read_from_command(&cp, &buf)) {
+ strbuf_release(&buf);
+ result = xstrdup("");
+ } else
+ result = strbuf_detach(&buf, NULL);
+
+ strbuf_release(&cmd);
+ return result;
+}
+
+static void apply_item_command(struct trailer_item *in_tok, struct trailer_item *arg_tok)
+{
+ if (arg_tok->conf.command) {
+ const char *arg;
+ if (arg_tok->value && arg_tok->value[0]) {
+ arg = arg_tok->value;
+ } else {
+ if (in_tok && in_tok->value)
+ arg = xstrdup(in_tok->value);
+ else
+ arg = xstrdup("");
+ }
+ arg_tok->value = apply_command(arg_tok->conf.command, arg);
+ free((char *)arg);
+ }
+}
+
+static void apply_arg_if_exists(struct trailer_item *in_tok,
+ struct trailer_item *arg_tok,
+ struct trailer_item *on_tok,
+ struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last)
+{
+ switch (arg_tok->conf.if_exists) {
+ case EXISTS_DO_NOTHING:
+ free_trailer_item(arg_tok);
+ break;
+ case EXISTS_REPLACE:
+ apply_item_command(in_tok, arg_tok);
+ add_arg_to_input_list(on_tok, arg_tok,
+ in_tok_first, in_tok_last);
+ remove_from_list(in_tok, in_tok_first, in_tok_last);
+ free_trailer_item(in_tok);
+ break;
+ case EXISTS_ADD:
+ apply_item_command(in_tok, arg_tok);
+ add_arg_to_input_list(on_tok, arg_tok,
+ in_tok_first, in_tok_last);
+ break;
+ case EXISTS_ADD_IF_DIFFERENT:
+ apply_item_command(in_tok, arg_tok);
+ if (check_if_different(in_tok, arg_tok, 1))
+ add_arg_to_input_list(on_tok, arg_tok,
+ in_tok_first, in_tok_last);
+ else
+ free_trailer_item(arg_tok);
+ break;
+ case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
+ apply_item_command(in_tok, arg_tok);
+ if (check_if_different(on_tok, arg_tok, 0))
+ add_arg_to_input_list(on_tok, arg_tok,
+ in_tok_first, in_tok_last);
+ else
+ free_trailer_item(arg_tok);
+ break;
+ }
+}
+
+static void apply_arg_if_missing(struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last,
+ struct trailer_item *arg_tok)
+{
+ struct trailer_item **in_tok;
+ enum action_where where;
+
+ switch (arg_tok->conf.if_missing) {
+ case MISSING_DO_NOTHING:
+ free_trailer_item(arg_tok);
+ break;
+ case MISSING_ADD:
+ where = arg_tok->conf.where;
+ in_tok = after_or_end(where) ? in_tok_last : in_tok_first;
+ apply_item_command(NULL, arg_tok);
+ if (*in_tok) {
+ add_arg_to_input_list(*in_tok, arg_tok,
+ in_tok_first, in_tok_last);
+ } else {
+ *in_tok_first = arg_tok;
+ *in_tok_last = arg_tok;
+ }
+ break;
+ }
+}
+
+static int find_same_and_apply_arg(struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last,
+ struct trailer_item *arg_tok)
+{
+ struct trailer_item *in_tok;
+ struct trailer_item *on_tok;
+ struct trailer_item *following_tok;
+
+ enum action_where where = arg_tok->conf.where;
+ int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
+ int backwards = after_or_end(where);
+ struct trailer_item *start_tok = backwards ? *in_tok_last : *in_tok_first;
+
+ for (in_tok = start_tok; in_tok; in_tok = following_tok) {
+ following_tok = backwards ? in_tok->previous : in_tok->next;
+ if (!same_token(in_tok, arg_tok))
+ continue;
+ on_tok = middle ? in_tok : start_tok;
+ apply_arg_if_exists(in_tok, arg_tok, on_tok,
+ in_tok_first, in_tok_last);
+ return 1;
+ }
+ return 0;
+}
+
+static void process_trailers_lists(struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last,
+ struct trailer_item **arg_tok_first)
+{
+ struct trailer_item *arg_tok;
+ struct trailer_item *next_arg;
+
+ if (!*arg_tok_first)
+ return;
+
+ for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) {
+ int applied = 0;
+
+ next_arg = arg_tok->next;
+ remove_from_list(arg_tok, arg_tok_first, NULL);
+
+ applied = find_same_and_apply_arg(in_tok_first,
+ in_tok_last,
+ arg_tok);
+
+ if (!applied)
+ apply_arg_if_missing(in_tok_first,
+ in_tok_last,
+ arg_tok);
+ }
+}
+
+static int set_where(struct conf_info *item, const char *value)
+{
+ if (!strcasecmp("after", value))
+ item->where = WHERE_AFTER;
+ else if (!strcasecmp("before", value))
+ item->where = WHERE_BEFORE;
+ else if (!strcasecmp("end", value))
+ item->where = WHERE_END;
+ else if (!strcasecmp("start", value))
+ item->where = WHERE_START;
+ else
+ return -1;
+ return 0;
+}
+
+static int set_if_exists(struct conf_info *item, const char *value)
+{
+ if (!strcasecmp("addIfDifferent", value))
+ item->if_exists = EXISTS_ADD_IF_DIFFERENT;
+ else if (!strcasecmp("addIfDifferentNeighbor", value))
+ item->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
+ else if (!strcasecmp("add", value))
+ item->if_exists = EXISTS_ADD;
+ else if (!strcasecmp("replace", value))
+ item->if_exists = EXISTS_REPLACE;
+ else if (!strcasecmp("doNothing", value))
+ item->if_exists = EXISTS_DO_NOTHING;
+ else
+ return -1;
+ return 0;
+}
+
+static int set_if_missing(struct conf_info *item, const char *value)
+{
+ if (!strcasecmp("doNothing", value))
+ item->if_missing = MISSING_DO_NOTHING;
+ else if (!strcasecmp("add", value))
+ item->if_missing = MISSING_ADD;
+ else
+ return -1;
+ return 0;
+}
+
+static void duplicate_conf(struct conf_info *dst, struct conf_info *src)
+{
+ *dst = *src;
+ if (src->name)
+ dst->name = xstrdup(src->name);
+ if (src->key)
+ dst->key = xstrdup(src->key);
+ if (src->command)
+ dst->command = xstrdup(src->command);
+}
+
+static struct trailer_item *get_conf_item(const char *name)
+{
+ struct trailer_item *item;
+ struct trailer_item *previous;
+
+ /* Look up item with same name */
+ for (previous = NULL, item = first_conf_item;
+ item;
+ previous = item, item = item->next) {
+ if (!strcasecmp(item->conf.name, name))
+ return item;
+ }
+
+ /* Item does not already exists, create it */
+ item = xcalloc(sizeof(struct trailer_item), 1);
+ duplicate_conf(&item->conf, &default_conf_info);
+ item->conf.name = xstrdup(name);
+
+ if (!previous)
+ first_conf_item = item;
+ else {
+ previous->next = item;
+ item->previous = previous;
+ }
+
+ return item;
+}
+
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
+ TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+
+static struct {
+ const char *name;
+ enum trailer_info_type type;
+} trailer_config_items[] = {
+ { "key", TRAILER_KEY },
+ { "command", TRAILER_COMMAND },
+ { "where", TRAILER_WHERE },
+ { "ifexists", TRAILER_IF_EXISTS },
+ { "ifmissing", TRAILER_IF_MISSING }
+};
+
+static int git_trailer_default_config(const char *conf_key, const char *value, void *cb)
+{
+ const char *trailer_item, *variable_name;
+
+ if (!skip_prefix(conf_key, "trailer.", &trailer_item))
+ return 0;
+
+ variable_name = strrchr(trailer_item, '.');
+ if (!variable_name) {
+ if (!strcmp(trailer_item, "where")) {
+ if (set_where(&default_conf_info, value) < 0)
+ warning(_("unknown value '%s' for key '%s'"),
+ value, conf_key);
+ } else if (!strcmp(trailer_item, "ifexists")) {
+ if (set_if_exists(&default_conf_info, value) < 0)
+ warning(_("unknown value '%s' for key '%s'"),
+ value, conf_key);
+ } else if (!strcmp(trailer_item, "ifmissing")) {
+ if (set_if_missing(&default_conf_info, value) < 0)
+ warning(_("unknown value '%s' for key '%s'"),
+ value, conf_key);
+ } else if (!strcmp(trailer_item, "separators")) {
+ separators = xstrdup(value);
+ }
+ }
+ return 0;
+}
+
+static int git_trailer_config(const char *conf_key, const char *value, void *cb)
+{
+ const char *trailer_item, *variable_name;
+ struct trailer_item *item;
+ struct conf_info *conf;
+ char *name = NULL;
+ enum trailer_info_type type;
+ int i;
+
+ if (!skip_prefix(conf_key, "trailer.", &trailer_item))
+ return 0;
+
+ variable_name = strrchr(trailer_item, '.');
+ if (!variable_name)
+ return 0;
+
+ variable_name++;
+ for (i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {
+ if (strcmp(trailer_config_items[i].name, variable_name))
+ continue;
+ name = xstrndup(trailer_item, variable_name - trailer_item - 1);
+ type = trailer_config_items[i].type;
+ break;
+ }
+
+ if (!name)
+ return 0;
+
+ item = get_conf_item(name);
+ conf = &item->conf;
+ free(name);
+
+ switch (type) {
+ case TRAILER_KEY:
+ if (conf->key)
+ warning(_("more than one %s"), conf_key);
+ conf->key = xstrdup(value);
+ break;
+ case TRAILER_COMMAND:
+ if (conf->command)
+ warning(_("more than one %s"), conf_key);
+ conf->command = xstrdup(value);
+ break;
+ case TRAILER_WHERE:
+ if (set_where(conf, value))
+ warning(_("unknown value '%s' for key '%s'"), value, conf_key);
+ break;
+ case TRAILER_IF_EXISTS:
+ if (set_if_exists(conf, value))
+ warning(_("unknown value '%s' for key '%s'"), value, conf_key);
+ break;
+ case TRAILER_IF_MISSING:
+ if (set_if_missing(conf, value))
+ warning(_("unknown value '%s' for key '%s'"), value, conf_key);
+ break;
+ default:
+ die("internal bug in trailer.c");
+ }
+ return 0;
+}
+
+static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trailer)
+{
+ size_t len;
+ struct strbuf seps = STRBUF_INIT;
+ strbuf_addstr(&seps, separators);
+ strbuf_addch(&seps, '=');
+ len = strcspn(trailer, seps.buf);
+ strbuf_release(&seps);
+ if (len == 0)
+ return error(_("empty trailer token in trailer '%s'"), trailer);
+ if (len < strlen(trailer)) {
+ strbuf_add(tok, trailer, len);
+ strbuf_trim(tok);
+ strbuf_addstr(val, trailer + len + 1);
+ strbuf_trim(val);
+ } else {
+ strbuf_addstr(tok, trailer);
+ strbuf_trim(tok);
+ }
+ return 0;
+}
+
+static const char *token_from_item(struct trailer_item *item, char *tok)
+{
+ if (item->conf.key)
+ return item->conf.key;
+ if (tok)
+ return tok;
+ return item->conf.name;
+}
+
+static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,
+ char *tok, char *val)
+{
+ struct trailer_item *new = xcalloc(sizeof(*new), 1);
+ new->value = val ? val : xstrdup("");
+
+ if (conf_item) {
+ duplicate_conf(&new->conf, &conf_item->conf);
+ new->token = xstrdup(token_from_item(conf_item, tok));
+ free(tok);
+ } else {
+ duplicate_conf(&new->conf, &default_conf_info);
+ new->token = tok;
+ }
+
+ return new;
+}
+
+static int token_matches_item(const char *tok, struct trailer_item *item, int tok_len)
+{
+ if (!strncasecmp(tok, item->conf.name, tok_len))
+ return 1;
+ return item->conf.key ? !strncasecmp(tok, item->conf.key, tok_len) : 0;
+}
+
+static struct trailer_item *create_trailer_item(const char *string)
+{
+ struct strbuf tok = STRBUF_INIT;
+ struct strbuf val = STRBUF_INIT;
+ struct trailer_item *item;
+ int tok_len;
+
+ if (parse_trailer(&tok, &val, string))
+ return NULL;
+
+ tok_len = token_len_without_separator(tok.buf, tok.len);
+
+ /* Lookup if the token matches something in the config */
+ for (item = first_conf_item; item; item = item->next) {
+ if (token_matches_item(tok.buf, item, tok_len))
+ return new_trailer_item(item,
+ strbuf_detach(&tok, NULL),
+ strbuf_detach(&val, NULL));
+ }
+
+ return new_trailer_item(NULL,
+ strbuf_detach(&tok, NULL),
+ strbuf_detach(&val, NULL));
+}
+
+static void add_trailer_item(struct trailer_item **first,
+ struct trailer_item **last,
+ struct trailer_item *new)
+{
+ if (!new)
+ return;
+ if (!*last) {
+ *first = new;
+ *last = new;
+ } else {
+ (*last)->next = new;
+ new->previous = *last;
+ *last = new;
+ }
+}
+
+static struct trailer_item *process_command_line_args(struct string_list *trailers)
+{
+ struct trailer_item *arg_tok_first = NULL;
+ struct trailer_item *arg_tok_last = NULL;
+ struct string_list_item *tr;
+ struct trailer_item *item;
+
+ /* Add a trailer item for each configured trailer with a command */
+ for (item = first_conf_item; item; item = item->next) {
+ if (item->conf.command) {
+ struct trailer_item *new = new_trailer_item(item, NULL, NULL);
+ add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+ }
+ }
+
+ /* Add a trailer item for each trailer on the command line */
+ for_each_string_list_item(tr, trailers) {
+ struct trailer_item *new = create_trailer_item(tr->string);
+ add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+ }
+
+ return arg_tok_first;
+}
+
+static struct strbuf **read_input_file(const char *file)
+{
+ struct strbuf **lines;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (file) {
+ if (strbuf_read_file(&sb, file, 0) < 0)
+ die_errno(_("could not read input file '%s'"), file);
+ } else {
+ if (strbuf_read(&sb, fileno(stdin), 0) < 0)
+ die_errno(_("could not read from stdin"));
+ }
+
+ lines = strbuf_split(&sb, '\n');
+
+ strbuf_release(&sb);
+
+ return lines;
+}
+
+/*
+ * Return the (0 based) index of the start of the patch or the line
+ * count if there is no patch in the message.
+ */
+static int find_patch_start(struct strbuf **lines, int count)
+{
+ int i;
+
+ /* Get the start of the patch part if any */
+ for (i = 0; i < count; i++) {
+ if (starts_with(lines[i]->buf, "---"))
+ return i;
+ }
+
+ return count;
+}
+
+/*
+ * Return the (0 based) index of the first trailer line or count if
+ * there are no trailers. Trailers are searched only in the lines from
+ * index (count - 1) down to index 0.
+ */
+static int find_trailer_start(struct strbuf **lines, int count)
+{
+ int start, only_spaces = 1;
+
+ /*
+ * Get the start of the trailers by looking starting from the end
+ * for a line with only spaces before lines with one separator.
+ */
+ for (start = count - 1; start >= 0; start--) {
+ if (lines[start]->buf[0] == comment_line_char)
+ continue;
+ if (contains_only_spaces(lines[start]->buf)) {
+ if (only_spaces)
+ continue;
+ return start + 1;
+ }
+ if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
+ if (only_spaces)
+ only_spaces = 0;
+ continue;
+ }
+ return count;
+ }
+
+ return only_spaces ? count : 0;
+}
+
+static int has_blank_line_before(struct strbuf **lines, int start)
+{
+ for (;start >= 0; start--) {
+ if (lines[start]->buf[0] == comment_line_char)
+ continue;
+ return contains_only_spaces(lines[start]->buf);
+ }
+ return 0;
+}
+
+static void print_lines(struct strbuf **lines, int start, int end)
+{
+ int i;
+ for (i = start; lines[i] && i < end; i++)
+ printf("%s", lines[i]->buf);
+}
+
+static int process_input_file(struct strbuf **lines,
+ struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last)
+{
+ int count = 0;
+ int patch_start, trailer_start, i;
+
+ /* Get the line count */
+ while (lines[count])
+ count++;
+
+ patch_start = find_patch_start(lines, count);
+ trailer_start = find_trailer_start(lines, patch_start);
+
+ /* Print lines before the trailers as is */
+ print_lines(lines, 0, trailer_start);
+
+ if (!has_blank_line_before(lines, trailer_start - 1))
+ printf("\n");
+
+ /* Parse trailer lines */
+ for (i = trailer_start; i < patch_start; i++) {
+ struct trailer_item *new = create_trailer_item(lines[i]->buf);
+ add_trailer_item(in_tok_first, in_tok_last, new);
+ }
+
+ return patch_start;
+}
+
+static void free_all(struct trailer_item **first)
+{
+ while (*first) {
+ struct trailer_item *item = remove_first(first);
+ free_trailer_item(item);
+ }
+}
+
+void process_trailers(const char *file, int trim_empty, struct string_list *trailers)
+{
+ struct trailer_item *in_tok_first = NULL;
+ struct trailer_item *in_tok_last = NULL;
+ struct trailer_item *arg_tok_first;
+ struct strbuf **lines;
+ int patch_start;
+
+ /* Default config must be setup first */
+ git_config(git_trailer_default_config, NULL);
+ git_config(git_trailer_config, NULL);
+
+ lines = read_input_file(file);
+
+ /* Print the lines before the trailers */
+ patch_start = process_input_file(lines, &in_tok_first, &in_tok_last);
+
+ arg_tok_first = process_command_line_args(trailers);
+
+ process_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);
+
+ print_all(in_tok_first, trim_empty);
+
+ free_all(&in_tok_first);
+
+ /* Print the lines after the trailers as is */
+ print_lines(lines, patch_start, INT_MAX);
+
+ strbuf_list_free(lines);
+}
diff --git a/trailer.h b/trailer.h
new file mode 100644
index 0000000000..8eb25d565e
--- /dev/null
+++ b/trailer.h
@@ -0,0 +1,6 @@
+#ifndef TRAILER_H
+#define TRAILER_H
+
+void process_trailers(const char *file, int trim_empty, struct string_list *trailers);
+
+#endif /* TRAILER_H */
diff --git a/transport-helper.c b/transport-helper.c
index 3d8fe7d801..6cd9dd1f9f 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -108,17 +108,12 @@ static struct child_process *get_helper(struct transport *transport)
int refspec_alloc = 0;
int duped;
int code;
- char git_dir_buf[sizeof(GIT_DIR_ENVIRONMENT) + PATH_MAX + 1];
- const char *helper_env[] = {
- git_dir_buf,
- NULL
- };
-
if (data->helper)
return data->helper;
- helper = xcalloc(1, sizeof(*helper));
+ helper = xmalloc(sizeof(*helper));
+ child_process_init(helper);
helper->in = -1;
helper->out = -1;
helper->err = 0;
@@ -128,8 +123,8 @@ static struct child_process *get_helper(struct transport *transport)
helper->git_cmd = 0;
helper->silent_exec_failure = 1;
- snprintf(git_dir_buf, sizeof(git_dir_buf), "%s=%s", GIT_DIR_ENVIRONMENT, get_git_dir());
- helper->env = helper_env;
+ argv_array_pushf(&helper->env_array, "%s=%s", GIT_DIR_ENVIRONMENT,
+ get_git_dir());
code = start_command(helper);
if (code < 0 && errno == ENOENT)
@@ -259,7 +254,8 @@ static const char *unsupported_options[] = {
static const char *boolean_options[] = {
TRANS_OPT_THIN,
TRANS_OPT_KEEP,
- TRANS_OPT_FOLLOWTAGS
+ TRANS_OPT_FOLLOWTAGS,
+ TRANS_OPT_PUSH_CERT
};
static int set_helper_option(struct transport *transport,
@@ -395,7 +391,7 @@ static int get_importer(struct transport *transport, struct child_process *fasti
struct child_process *helper = get_helper(transport);
struct helper_data *data = transport->data;
int cat_blob_fd, code;
- memset(fastimport, 0, sizeof(*fastimport));
+ child_process_init(fastimport);
fastimport->in = helper->out;
argv_array_push(&fastimport->args, "fast-import");
argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
@@ -835,6 +831,9 @@ static int push_refs_with_push(struct transport *transport,
if (flags & TRANSPORT_PUSH_DRY_RUN) {
if (set_helper_option(transport, "dry-run", "true") != 0)
die("helper %s does not support dry-run", data->name);
+ } else if (flags & TRANSPORT_PUSH_CERT) {
+ if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
+ die("helper %s does not support --signed", data->name);
}
strbuf_addch(&buf, '\n');
@@ -859,6 +858,9 @@ static int push_refs_with_export(struct transport *transport,
if (flags & TRANSPORT_PUSH_DRY_RUN) {
if (set_helper_option(transport, "dry-run", "true") != 0)
die("helper %s does not support dry-run", data->name);
+ } else if (flags & TRANSPORT_PUSH_CERT) {
+ if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
+ die("helper %s does not support dry-run", data->name);
}
if (flags & TRANSPORT_PUSH_FORCE) {
@@ -889,7 +891,10 @@ static int push_refs_with_export(struct transport *transport,
int flag;
/* Follow symbolic refs (mainly for HEAD). */
- name = resolve_ref_unsafe(ref->peer_ref->name, sha1, 1, &flag);
+ name = resolve_ref_unsafe(
+ ref->peer_ref->name,
+ RESOLVE_REF_READING,
+ sha1, &flag);
if (!name || !(flag & REF_ISSYMREF))
name = ref->peer_ref->name;
diff --git a/transport.c b/transport.c
index 662421bb5e..70d38e4c4b 100644
--- a/transport.c
+++ b/transport.c
@@ -168,7 +168,8 @@ 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_unsafe(localname, sha, 1, &flag);
+ tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
+ sha, &flag);
if (tmp && flag & REF_ISSYMREF &&
starts_with(tmp, "refs/heads/"))
localname = tmp;
@@ -201,7 +202,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
{
struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
struct ref dummy = {NULL}, *tail = &dummy;
- struct child_process rsync;
+ struct child_process rsync = CHILD_PROCESS_INIT;
const char *args[5];
int temp_dir_len;
@@ -218,7 +219,6 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
strbuf_addstr(&buf, rsync_url(transport->url));
strbuf_addstr(&buf, "/refs");
- memset(&rsync, 0, sizeof(rsync));
rsync.argv = args;
rsync.stdout_to_stderr = 1;
args[0] = "rsync";
@@ -263,9 +263,8 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
static int fetch_objs_via_rsync(struct transport *transport,
int nr_objs, struct ref **to_fetch)
{
- struct child_process rsync;
+ struct child_process rsync = CHILD_PROCESS_INIT;
- memset(&rsync, 0, sizeof(rsync));
rsync.stdout_to_stderr = 1;
argv_array_push(&rsync.args, "rsync");
argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r");
@@ -327,7 +326,7 @@ static int rsync_transport_push(struct transport *transport,
{
struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
int result = 0, i;
- struct child_process rsync;
+ struct child_process rsync = CHILD_PROCESS_INIT;
const char *args[10];
if (flags & TRANSPORT_PUSH_MIRROR)
@@ -338,7 +337,6 @@ static int rsync_transport_push(struct transport *transport,
strbuf_addstr(&buf, rsync_url(transport->url));
strbuf_addch(&buf, '/');
- memset(&rsync, 0, sizeof(rsync));
rsync.argv = args;
rsync.stdout_to_stderr = 1;
i = 0;
@@ -480,6 +478,9 @@ static int set_git_option(struct git_transport_options *opts,
die("transport: invalid depth option '%s'", value);
}
return 0;
+ } else if (!strcmp(name, TRANS_OPT_PUSH_CERT)) {
+ opts->push_cert = !!value;
+ return 0;
}
return 1;
}
@@ -743,7 +744,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
unsigned char head_sha1[20];
char *head;
- head = resolve_refdup("HEAD", head_sha1, 1, NULL);
+ head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
@@ -774,6 +775,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
*reject_reasons |= REJECT_NEEDS_FORCE;
}
}
+ free(head);
}
void transport_verify_remote_names(int nr_heads, const char **heads)
@@ -823,6 +825,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
args.progress = transport->progress;
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
+ args.push_cert = !!(flags & TRANSPORT_PUSH_CERT);
+ args.url = transport->url;
ret = send_pack(&args, data->fd, data->conn, remote_refs,
&data->extra_have);
@@ -1056,7 +1060,7 @@ static int run_pre_push_hook(struct transport *transport,
{
int ret = 0, x;
struct ref *r;
- struct child_process proc;
+ struct child_process proc = CHILD_PROCESS_INIT;
struct strbuf buf;
const char *argv[4];
@@ -1067,7 +1071,6 @@ static int run_pre_push_hook(struct transport *transport,
argv[2] = transport->url;
argv[3] = NULL;
- memset(&proc, 0, sizeof(proc));
proc.argv = argv;
proc.in = -1;
diff --git a/transport.h b/transport.h
index 02ea248db1..3e0091eaab 100644
--- a/transport.h
+++ b/transport.h
@@ -12,6 +12,7 @@ struct git_transport_options {
unsigned check_self_contained_and_connected : 1;
unsigned self_contained_and_connected : 1;
unsigned update_shallow : 1;
+ unsigned push_cert : 1;
int depth;
const char *uploadpack;
const char *receivepack;
@@ -123,6 +124,7 @@ struct transport {
#define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
#define TRANSPORT_PUSH_NO_HOOK 512
#define TRANSPORT_PUSH_FOLLOW_TAGS 1024
+#define TRANSPORT_PUSH_CERT 2048
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
@@ -156,6 +158,9 @@ struct transport *transport_get(struct remote *, const char *);
/* Accept refs that may update .git/shallow without --depth */
#define TRANS_OPT_UPDATE_SHALLOW "updateshallow"
+/* Send push certificates */
+#define TRANS_OPT_PUSH_CERT "pushcert"
+
/**
* Returns 0 if the option was used, non-zero otherwise. Prints a
* message to stderr if the option is not used.
diff --git a/unix-socket.c b/unix-socket.c
index 91bd6b89d4..19ed48be99 100644
--- a/unix-socket.c
+++ b/unix-socket.c
@@ -18,12 +18,12 @@ static int chdir_len(const char *orig, int len)
}
struct unix_sockaddr_context {
- char orig_dir[PATH_MAX];
+ char *orig_dir;
};
static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx)
{
- if (!ctx->orig_dir[0])
+ if (!ctx->orig_dir)
return;
/*
* If we fail, we can't just return an error, since we have
@@ -32,6 +32,7 @@ static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx)
*/
if (chdir(ctx->orig_dir) < 0)
die("unable to restore original working directory");
+ free(ctx->orig_dir);
}
static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path,
@@ -39,10 +40,11 @@ static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path,
{
int size = strlen(path) + 1;
- ctx->orig_dir[0] = '\0';
+ ctx->orig_dir = NULL;
if (size > sizeof(sa->sun_path)) {
const char *slash = find_last_dir_sep(path);
const char *dir;
+ struct strbuf cwd = STRBUF_INIT;
if (!slash) {
errno = ENAMETOOLONG;
@@ -56,11 +58,9 @@ static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path,
errno = ENAMETOOLONG;
return -1;
}
-
- if (!getcwd(ctx->orig_dir, sizeof(ctx->orig_dir))) {
- errno = ENAMETOOLONG;
+ if (strbuf_getcwd(&cwd))
return -1;
- }
+ ctx->orig_dir = strbuf_detach(&cwd, NULL);
if (chdir_len(dir, slash - dir) < 0)
return -1;
}
diff --git a/upload-pack.c b/upload-pack.c
index 433211a238..ac9ac1592d 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -80,7 +80,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
static void create_pack_file(void)
{
- struct child_process pack_objects;
+ struct child_process pack_objects = CHILD_PROCESS_INIT;
char data[8193], progress[128];
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
@@ -108,7 +108,6 @@ static void create_pack_file(void)
argv[arg++] = "--include-tag";
argv[arg++] = NULL;
- memset(&pack_objects, 0, sizeof(pack_objects));
pack_objects.in = -1;
pack_objects.out = -1;
pack_objects.err = -1;
@@ -450,7 +449,7 @@ static void check_non_tip(void)
static const char *argv[] = {
"rev-list", "--stdin", NULL,
};
- static struct child_process cmd;
+ static struct child_process cmd = CHILD_PROCESS_INIT;
struct object *o;
char namebuf[42]; /* ^ + SHA-1 + LF */
int i;
@@ -745,7 +744,7 @@ static int find_symref(const char *refname, const unsigned char *sha1, int flag,
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, unused, 0, &flag);
+ symref_target = resolve_ref_unsafe(refname, 0, unused, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, refname);
diff --git a/urlmatch.c b/urlmatch.c
index 3d4c54b5cd..618d216491 100644
--- a/urlmatch.c
+++ b/urlmatch.c
@@ -43,11 +43,11 @@ static int append_normalized_escapes(struct strbuf *buf,
from_len--;
if (ch == '%') {
if (from_len < 2 ||
- !isxdigit((unsigned char)from[0]) ||
- !isxdigit((unsigned char)from[1]))
+ !isxdigit(from[0]) ||
+ !isxdigit(from[1]))
return 0;
- ch = hexval_table[(unsigned char)*from++] << 4;
- ch |= hexval_table[(unsigned char)*from++];
+ ch = hexval(*from++) << 4;
+ ch |= hexval(*from++);
from_len -= 2;
was_esc = 1;
}
diff --git a/utf8.c b/utf8.c
index b30790d043..4541777949 100644
--- a/utf8.c
+++ b/utf8.c
@@ -239,13 +239,6 @@ int is_utf8(const char *text)
return 1;
}
-static void strbuf_addchars(struct strbuf *sb, int c, size_t n)
-{
- strbuf_grow(sb, n);
- memset(sb->buf + sb->len, c, n);
- strbuf_setlen(sb, sb->len + n);
-}
-
static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
int indent, int indent2)
{
@@ -382,6 +375,9 @@ void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
dst += n;
}
+ if (src >= end)
+ break;
+
old = src;
n = utf8_width((const char**)&src, NULL);
if (!src) /* broken utf-8, do nothing */
diff --git a/varint.c b/varint.c
index 4ed7729496..409c4977a1 100644
--- a/varint.c
+++ b/varint.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
#include "varint.h"
uintmax_t decode_varint(const unsigned char **bufp)
diff --git a/varint.h b/varint.h
index 0321195796..c1c44d9a6b 100644
--- a/varint.h
+++ b/varint.h
@@ -1,8 +1,6 @@
#ifndef VARINT_H
#define VARINT_H
-#include "git-compat-util.h"
-
extern int encode_varint(uintmax_t, unsigned char *);
extern uintmax_t decode_varint(const unsigned char **);
diff --git a/walker.c b/walker.c
index 014826464e..f149371e71 100644
--- a/walker.c
+++ b/walker.c
@@ -205,7 +205,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag,
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
if (commit) {
commit->object.flags |= COMPLETE;
- commit_list_insert_by_date(commit, &complete);
+ commit_list_insert(commit, &complete);
}
return 0;
}
@@ -228,8 +228,8 @@ int walker_targets_stdin(char ***target, const char ***write_ref)
if (targets >= targets_alloc) {
targets_alloc = targets_alloc ? targets_alloc * 2 : 64;
- *target = xrealloc(*target, targets_alloc * sizeof(**target));
- *write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref));
+ REALLOC_ARRAY(*target, targets_alloc);
+ REALLOC_ARRAY(*write_ref, targets_alloc);
}
(*target)[targets] = xstrdup(tg_one);
(*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
@@ -251,64 +251,75 @@ void walker_targets_free(int targets, char **target, const char **write_ref)
int walker_fetch(struct walker *walker, int targets, char **target,
const char **write_ref, const char *write_ref_log_details)
{
- struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
+ struct strbuf refname = STRBUF_INIT;
+ struct strbuf err = STRBUF_INIT;
+ struct ref_transaction *transaction = NULL;
unsigned char *sha1 = xmalloc(targets * 20);
- const char *msg;
- char *to_free = NULL;
- int ret;
- int i;
+ char *msg = NULL;
+ int i, ret = -1;
save_commit_buffer = 0;
- for (i = 0; i < targets; i++) {
- if (!write_ref || !write_ref[i])
- continue;
-
- lock[i] = lock_ref_sha1(write_ref[i], NULL);
- if (!lock[i]) {
- error("Can't lock ref %s", write_ref[i]);
- goto unlock_and_fail;
+ if (write_ref) {
+ transaction = ref_transaction_begin(&err);
+ if (!transaction) {
+ error("%s", err.buf);
+ goto done;
}
}
- if (!walker->get_recover)
+ if (!walker->get_recover) {
for_each_ref(mark_complete, NULL);
+ commit_list_sort_by_date(&complete);
+ }
for (i = 0; i < targets; i++) {
if (interpret_target(walker, target[i], &sha1[20 * i])) {
error("Could not interpret response from server '%s' as something to pull", target[i]);
- goto unlock_and_fail;
+ goto done;
}
if (process(walker, lookup_unknown_object(&sha1[20 * i])))
- goto unlock_and_fail;
+ goto done;
}
if (loop(walker))
- goto unlock_and_fail;
-
- if (write_ref_log_details)
- msg = to_free = xstrfmt("fetch from %s", write_ref_log_details);
- else
- msg = "fetch (unknown)";
+ goto done;
+ if (!write_ref) {
+ ret = 0;
+ goto done;
+ }
+ if (write_ref_log_details) {
+ msg = xstrfmt("fetch from %s", write_ref_log_details);
+ } else {
+ msg = NULL;
+ }
for (i = 0; i < targets; i++) {
- if (!write_ref || !write_ref[i])
+ if (!write_ref[i])
continue;
- ret = write_ref_sha1(lock[i], &sha1[20 * i], msg);
- lock[i] = NULL;
- if (ret)
- goto unlock_and_fail;
+ strbuf_reset(&refname);
+ strbuf_addf(&refname, "refs/%s", write_ref[i]);
+ if (ref_transaction_update(transaction, refname.buf,
+ &sha1[20 * i], NULL, 0, 0,
+ msg ? msg : "fetch (unknown)",
+ &err)) {
+ error("%s", err.buf);
+ goto done;
+ }
+ }
+ if (ref_transaction_commit(transaction, &err)) {
+ error("%s", err.buf);
+ goto done;
}
- free(to_free);
-
- return 0;
-unlock_and_fail:
- for (i = 0; i < targets; i++)
- if (lock[i])
- unlock_ref(lock[i]);
- free(to_free);
+ ret = 0;
- return -1;
+done:
+ ref_transaction_free(transaction);
+ free(msg);
+ free(sha1);
+ strbuf_release(&err);
+ strbuf_release(&refname);
+ return ret;
}
void walker_free(struct walker *walker)
diff --git a/wrapper.c b/wrapper.c
index bc1bfb8600..007ec0d8ea 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -9,16 +9,24 @@ static void do_nothing(size_t size)
static void (*try_to_free_routine)(size_t size) = do_nothing;
-static void memory_limit_check(size_t size)
+static int memory_limit_check(size_t size, int gentle)
{
- static int limit = -1;
- if (limit == -1) {
- const char *env = getenv("GIT_ALLOC_LIMIT");
- limit = env ? atoi(env) * 1024 : 0;
+ static size_t limit = 0;
+ if (!limit) {
+ limit = git_env_ulong("GIT_ALLOC_LIMIT", 0);
+ if (!limit)
+ limit = SIZE_MAX;
}
- if (limit && size > limit)
- die("attempting to allocate %"PRIuMAX" over limit %d",
- (intmax_t)size, limit);
+ if (size > limit) {
+ if (gentle) {
+ error("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX,
+ (uintmax_t)size, (uintmax_t)limit);
+ return -1;
+ } else
+ die("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX,
+ (uintmax_t)size, (uintmax_t)limit);
+ }
+ return 0;
}
try_to_free_t set_try_to_free_routine(try_to_free_t routine)
@@ -42,11 +50,12 @@ char *xstrdup(const char *str)
return ret;
}
-void *xmalloc(size_t size)
+static void *do_xmalloc(size_t size, int gentle)
{
void *ret;
- memory_limit_check(size);
+ if (memory_limit_check(size, gentle))
+ return NULL;
ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
@@ -55,9 +64,16 @@ void *xmalloc(size_t size)
ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
- if (!ret)
- die("Out of memory, malloc failed (tried to allocate %lu bytes)",
- (unsigned long)size);
+ if (!ret) {
+ if (!gentle)
+ die("Out of memory, malloc failed (tried to allocate %lu bytes)",
+ (unsigned long)size);
+ else {
+ error("Out of memory, malloc failed (tried to allocate %lu bytes)",
+ (unsigned long)size);
+ return NULL;
+ }
+ }
}
#ifdef XMALLOC_POISON
memset(ret, 0xA5, size);
@@ -65,16 +81,37 @@ void *xmalloc(size_t size)
return ret;
}
-void *xmallocz(size_t size)
+void *xmalloc(size_t size)
+{
+ return do_xmalloc(size, 0);
+}
+
+static void *do_xmallocz(size_t size, int gentle)
{
void *ret;
- if (unsigned_add_overflows(size, 1))
- die("Data too large to fit into virtual memory space.");
- ret = xmalloc(size + 1);
- ((char*)ret)[size] = 0;
+ if (unsigned_add_overflows(size, 1)) {
+ if (gentle) {
+ error("Data too large to fit into virtual memory space.");
+ return NULL;
+ } else
+ die("Data too large to fit into virtual memory space.");
+ }
+ ret = do_xmalloc(size + 1, gentle);
+ if (ret)
+ ((char*)ret)[size] = 0;
return ret;
}
+void *xmallocz(size_t size)
+{
+ return do_xmallocz(size, 0);
+}
+
+void *xmallocz_gently(size_t size)
+{
+ return do_xmallocz(size, 1);
+}
+
/*
* xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
* "data" to the allocated memory, zero terminates the allocated memory,
@@ -96,7 +133,7 @@ void *xrealloc(void *ptr, size_t size)
{
void *ret;
- memory_limit_check(size);
+ memory_limit_check(size, 0);
ret = realloc(ptr, size);
if (!ret && !size)
ret = realloc(ptr, 1);
@@ -115,7 +152,7 @@ void *xcalloc(size_t nmemb, size_t size)
{
void *ret;
- memory_limit_check(size * nmemb);
+ memory_limit_check(size * nmemb, 0);
ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
@@ -429,17 +466,29 @@ int xmkstemp_mode(char *template, int mode)
static int warn_if_unremovable(const char *op, const char *file, int rc)
{
- if (rc < 0) {
- int err = errno;
- if (ENOENT != err) {
- warning("unable to %s %s: %s",
- op, file, strerror(errno));
- errno = err;
- }
- }
+ int err;
+ if (!rc || errno == ENOENT)
+ return 0;
+ err = errno;
+ warning("unable to %s %s: %s", op, file, strerror(errno));
+ errno = err;
return rc;
}
+int unlink_or_msg(const char *file, struct strbuf *err)
+{
+ int rc = unlink(file);
+
+ assert(err);
+
+ if (!rc || errno == ENOENT)
+ return 0;
+
+ strbuf_addf(err, "unable to unlink %s: %s",
+ file, strerror(errno));
+ return -1;
+}
+
int unlink_or_warn(const char *file)
{
return warn_if_unremovable("unlink", file, unlink(file));
@@ -493,3 +542,11 @@ struct passwd *xgetpwuid_self(void)
errno ? strerror(errno) : _("no such user"));
return pw;
}
+
+char *xgetcwd(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ if (strbuf_getcwd(&sb))
+ die_errno(_("unable to get current working directory"));
+ return strbuf_detach(&sb, NULL);
+}
diff --git a/write_or_die.c b/write_or_die.c
index b50f99a936..e7afe7a295 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -49,6 +49,21 @@ void maybe_flush_or_die(FILE *f, const char *desc)
}
}
+void fprintf_or_die(FILE *f, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vfprintf(f, fmt, ap);
+ va_end(ap);
+
+ if (ret < 0) {
+ check_pipe(errno);
+ die_errno("write error");
+ }
+}
+
void fsync_or_die(int fd, const char *msg)
{
if (fsync(fd) < 0) {
diff --git a/wt-status.c b/wt-status.c
index 27da5296be..cdbc8d798a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -128,7 +128,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;
- s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ s->branch = resolve_refdup("HEAD", 0, sha1, NULL);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
@@ -725,15 +725,15 @@ static void wt_status_print_changed(struct wt_status *s)
static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
{
- struct child_process sm_summary;
- struct argv_array env = ARGV_ARRAY_INIT;
+ struct child_process sm_summary = CHILD_PROCESS_INIT;
struct argv_array argv = ARGV_ARRAY_INIT;
struct strbuf cmd_stdout = STRBUF_INIT;
struct strbuf summary = STRBUF_INIT;
char *summary_content;
size_t len;
- argv_array_pushf(&env, "GIT_INDEX_FILE=%s", s->index_file);
+ argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s",
+ s->index_file);
argv_array_push(&argv, "submodule");
argv_array_push(&argv, "summary");
@@ -744,16 +744,13 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
if (!uncommitted)
argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
- memset(&sm_summary, 0, sizeof(sm_summary));
sm_summary.argv = argv.argv;
- sm_summary.env = env.argv;
sm_summary.git_cmd = 1;
sm_summary.no_stdin = 1;
fflush(s->fp);
sm_summary.out = -1;
run_command(&sm_summary);
- argv_array_clear(&env);
argv_array_clear(&argv);
len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);