summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes2
-rw-r--r--.mailmap1
-rw-r--r--.travis.yml2
-rw-r--r--Documentation/RelNotes/2.10.0.txt556
-rw-r--r--Documentation/RelNotes/2.9.3.txt102
-rw-r--r--Documentation/config.txt125
-rw-r--r--Documentation/diff-options.txt3
-rw-r--r--Documentation/git-am.txt3
-rw-r--r--Documentation/git-fast-import.txt2
-rw-r--r--Documentation/git-fetch.txt51
-rw-r--r--Documentation/git-fsck.txt9
-rw-r--r--Documentation/git-log.txt4
-rw-r--r--Documentation/git-mailsplit.txt7
-rw-r--r--Documentation/git-pack-objects.txt4
-rw-r--r--Documentation/git-push.txt13
-rw-r--r--Documentation/git-repack.txt19
-rw-r--r--Documentation/git-submodule.txt11
-rw-r--r--Documentation/git-svn.txt3
-rw-r--r--Documentation/git-upload-pack.txt16
-rw-r--r--Documentation/git-worktree.txt49
-rw-r--r--Documentation/git.txt8
-rw-r--r--Documentation/gitattributes.txt22
-rw-r--r--Documentation/githooks.txt18
-rw-r--r--Documentation/gitmodules.txt5
-rw-r--r--Documentation/pretty-formats.txt10
-rw-r--r--Documentation/rev-list-options.txt44
-rw-r--r--Documentation/technical/api-hashmap.txt5
-rw-r--r--Documentation/technical/pack-protocol.txt12
-rw-r--r--Documentation/technical/protocol-capabilities.txt9
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile70
l---------RelNotes2
-rw-r--r--advice.c23
-rw-r--r--archive-tar.c77
-rw-r--r--archive.c2
-rw-r--r--bisect.c63
-rw-r--r--builtin/am.c103
-rw-r--r--builtin/apply.c1438
-rw-r--r--builtin/blame.c41
-rw-r--r--builtin/branch.c30
-rw-r--r--builtin/cat-file.c4
-rw-r--r--builtin/checkout.c11
-rw-r--r--builtin/clone.c16
-rw-r--r--builtin/commit.c17
-rw-r--r--builtin/config.c3
-rw-r--r--builtin/fast-export.c10
-rw-r--r--builtin/fetch.c205
-rw-r--r--builtin/fmt-merge-msg.c2
-rw-r--r--builtin/fsck.c84
-rw-r--r--builtin/index-pack.c33
-rw-r--r--builtin/init-db.c17
-rw-r--r--builtin/interpret-trailers.c2
-rw-r--r--builtin/log.c114
-rw-r--r--builtin/ls-files.c3
-rw-r--r--builtin/mailsplit.c18
-rw-r--r--builtin/merge-recursive.c20
-rw-r--r--builtin/merge.c118
-rw-r--r--builtin/notes.c29
-rw-r--r--builtin/pack-objects.c88
-rw-r--r--builtin/pull.c5
-rw-r--r--builtin/push.c21
-rw-r--r--builtin/receive-pack.c192
-rw-r--r--builtin/remote.c29
-rw-r--r--builtin/repack.c12
-rw-r--r--builtin/reset.c6
-rw-r--r--builtin/rev-list.c17
-rw-r--r--builtin/rev-parse.c2
-rw-r--r--builtin/revert.c13
-rw-r--r--builtin/rm.c5
-rw-r--r--builtin/shortlog.c21
-rw-r--r--builtin/submodule--helper.c136
-rw-r--r--builtin/unpack-objects.c4
-rw-r--r--builtin/update-index.c4
-rw-r--r--builtin/worktree.c85
-rw-r--r--cache-tree.c14
-rw-r--r--cache.h62
-rw-r--r--color.c35
-rw-r--r--color.h15
-rw-r--r--combine-diff.c16
-rw-r--r--commit-slab.h6
-rw-r--r--commit.c10
-rw-r--r--commit.h10
-rw-r--r--common-main.c41
-rw-r--r--compat/mingw.h5
-rw-r--r--compat/nedmalloc/nedmalloc.c17
-rw-r--r--config.c243
-rw-r--r--config.mak.uname4
-rw-r--r--configure.ac5
-rw-r--r--connect.c15
-rw-r--r--connected.c71
-rw-r--r--connected.h37
-rw-r--r--contrib/coccinelle/README2
-rw-r--r--contrib/coccinelle/object_id.cocci95
-rw-r--r--contrib/completion/git-completion.bash151
-rwxr-xr-xcontrib/fast-import/import-tars.perl31
-rw-r--r--contrib/git-jump/README6
-rwxr-xr-xcontrib/git-jump/git-jump8
-rw-r--r--contrib/persistent-https/Makefile6
-rwxr-xr-xcontrib/subtree/git-subtree.sh678
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh32
-rw-r--r--convert.c42
-rw-r--r--convert.h3
-rw-r--r--credential-cache--daemon.c2
-rw-r--r--credential-cache.c2
-rw-r--r--credential-store.c2
-rw-r--r--daemon.c24
-rw-r--r--date.c8
-rw-r--r--diff.c108
-rw-r--r--diffcore-break.c4
-rw-r--r--diffcore-rename.c18
-rw-r--r--diffcore.h4
-rw-r--r--dir-iterator.c202
-rw-r--r--dir-iterator.h87
-rw-r--r--dir.c2
-rw-r--r--dir.h23
-rw-r--r--fast-import.c70
-rw-r--r--fetch-pack.c19
-rw-r--r--fsck.c113
-rw-r--r--fsck.h7
-rwxr-xr-xgit-bisect.sh8
-rw-r--r--git-compat-util.h19
-rwxr-xr-xgit-difftool.perl86
-rwxr-xr-xgit-merge-octopus.sh23
-rwxr-xr-xgit-p4.py2
-rw-r--r--git-rebase--interactive.sh292
-rwxr-xr-xgit-rebase.sh5
-rwxr-xr-xgit-send-email.perl40
-rw-r--r--git-sh-i18n.sh18
-rw-r--r--git-sh-setup.sh71
-rwxr-xr-xgit-stash.sh1
-rwxr-xr-xgit-submodule.sh69
-rwxr-xr-xgit-svn.perl9
-rw-r--r--git.c37
-rwxr-xr-xgitweb/gitweb.perl2
-rw-r--r--gpg-interface.c93
-rw-r--r--graph.c30
-rw-r--r--grep.c30
-rw-r--r--grep.h1
-rw-r--r--hex.c5
-rw-r--r--http-backend.c5
-rw-r--r--http-fetch.c6
-rw-r--r--http-push.c12
-rw-r--r--http-walker.c55
-rw-r--r--http.c142
-rw-r--r--http.h2
-rw-r--r--ident.c5
-rw-r--r--imap-send.c9
-rw-r--r--iterator.h81
-rw-r--r--line-log.c46
-rw-r--r--list.h166
-rw-r--r--log-tree.c75
-rw-r--r--merge-recursive.c784
-rw-r--r--merge-recursive.h8
-rw-r--r--mru.c50
-rw-r--r--mru.h45
-rw-r--r--notes-merge.c52
-rw-r--r--notes.c2
-rw-r--r--pack-check.c23
-rw-r--r--pack-write.c1
-rw-r--r--pack.h1
-rw-r--r--pager.c32
-rw-r--r--parse-options-cb.c31
-rw-r--r--parse-options.h2
-rw-r--r--path.c2
-rw-r--r--pathspec.c2
-rw-r--r--pathspec.h4
-rw-r--r--perl/Git/SVN.pm33
-rw-r--r--po/is.po46
-rw-r--r--pretty.c33
-rw-r--r--read-cache.c5
-rw-r--r--refs.c98
-rw-r--r--refs.h141
-rw-r--r--refs/files-backend.c1573
-rw-r--r--refs/iterator.c384
-rw-r--r--refs/refs-internal.h321
-rw-r--r--remote-curl.c5
-rw-r--r--remote-testsvn.c3
-rw-r--r--remote.c9
-rw-r--r--remote.h1
-rw-r--r--revision.c12
-rw-r--r--run-command.c152
-rw-r--r--run-command.h31
-rw-r--r--send-pack.c62
-rw-r--r--send-pack.h3
-rw-r--r--sequencer.c22
-rw-r--r--setup.c16
-rw-r--r--sh-i18n--envsubst.c2
-rw-r--r--sha1_file.c72
-rw-r--r--shallow.c2
-rw-r--r--shell.c13
-rw-r--r--shortlog.h1
-rw-r--r--show-index.c4
-rw-r--r--sideband.c136
-rw-r--r--sideband.h2
-rw-r--r--strbuf.c7
-rw-r--r--strbuf.h6
-rw-r--r--submodule-config.c54
-rw-r--r--submodule-config.h2
-rw-r--r--submodule.c6
-rw-r--r--t/helper/test-chmtime.c2
-rw-r--r--t/helper/test-config.c38
-rw-r--r--t/helper/test-ctype.c2
-rw-r--r--t/helper/test-date.c14
-rw-r--r--t/helper/test-delta.c2
-rw-r--r--t/helper/test-dump-cache-tree.c2
-rw-r--r--t/helper/test-dump-split-index.c2
-rw-r--r--t/helper/test-dump-untracked-cache.c2
-rw-r--r--t/helper/test-fake-ssh.c2
-rw-r--r--t/helper/test-genrandom.c2
-rw-r--r--t/helper/test-hashmap.c2
-rw-r--r--t/helper/test-index-version.c2
-rw-r--r--t/helper/test-line-buffer.c2
-rw-r--r--t/helper/test-match-trees.c2
-rw-r--r--t/helper/test-mergesort.c2
-rw-r--r--t/helper/test-mktemp.c2
-rw-r--r--t/helper/test-parse-options.c4
-rw-r--r--t/helper/test-path-utils.c4
-rw-r--r--t/helper/test-prio-queue.c2
-rw-r--r--t/helper/test-read-cache.c2
-rw-r--r--t/helper/test-regex.c2
-rw-r--r--t/helper/test-revision-walking.c2
-rw-r--r--t/helper/test-run-command.c8
-rw-r--r--t/helper/test-scrap-cache-tree.c2
-rw-r--r--t/helper/test-sha1-array.c2
-rw-r--r--t/helper/test-sha1.c2
-rw-r--r--t/helper/test-sigchain.c2
-rw-r--r--t/helper/test-string-list.c2
-rw-r--r--t/helper/test-submodule-config.c10
-rw-r--r--t/helper/test-subprocess.c2
-rw-r--r--t/helper/test-svn-fe.c4
-rw-r--r--t/helper/test-urlmatch-normalization.c2
-rw-r--r--t/helper/test-wildmatch.c2
-rw-r--r--t/lib-git-svn.sh91
-rw-r--r--t/lib-httpd.sh9
-rw-r--r--t/lib-httpd/apache.conf4
-rw-r--r--t/lib-rebase.sh1
-rw-r--r--t/perf/README12
-rwxr-xr-xt/perf/p4211-line-log.sh6
-rwxr-xr-xt/perf/p5303-many-packs.sh87
-rw-r--r--t/perf/perf-lib.sh5
-rwxr-xr-xt/t0000-basic.sh2
-rwxr-xr-xt/t0006-date.sh3
-rwxr-xr-xt/t0008-ignores.sh4
-rwxr-xr-xt/t0021-conversion.sh11
-rwxr-xr-xt/t0025-crlf-auto.sh4
-rwxr-xr-xt/t0027-auto-crlf.sh29
-rwxr-xr-xt/t1011-read-tree-sparse-checkout.sh12
-rwxr-xr-xt/t1050-large.sh7
-rwxr-xr-xt/t1100-commit-tree-options.sh2
-rwxr-xr-xt/t1300-repo-config.sh8
-rwxr-xr-xt/t1307-config-blob.sh5
-rwxr-xr-xt/t1308-config-set.sh39
-rwxr-xr-xt/t1400-update-ref.sh43
-rwxr-xr-xt/t1404-update-ref-df-conflicts.sh107
-rwxr-xr-xt/t1404-update-ref-errors.sh407
-rwxr-xr-xt/t1430-bad-ref-name.sh2
-rwxr-xr-xt/t1450-fsck.sh22
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh2
-rwxr-xr-xt/t1700-split-index.sh24
-rwxr-xr-xt/t2010-checkout-ambiguous.sh2
-rwxr-xr-xt/t2018-checkout-branch.sh2
-rwxr-xr-xt/t2025-worktree-add.sh16
-rwxr-xr-xt/t2028-worktree-move.sh62
-rwxr-xr-xt/t2203-add-intent.sh31
-rwxr-xr-xt/t3102-ls-tree-wildcards.sh8
-rwxr-xr-xt/t3200-branch.sh15
-rwxr-xr-xt/t3201-branch-contains.sh2
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh8
-rwxr-xr-xt/t3320-notes-merge-worktrees.sh4
-rwxr-xr-xt/t3400-rebase.sh4
-rwxr-xr-xt/t3404-rebase-interactive.sh26
-rwxr-xr-xt/t3415-rebase-autosquash.sh33
-rwxr-xr-xt/t3700-add.sh65
-rwxr-xr-xt/t4010-diff-pathspec.sh2
-rwxr-xr-xt/t4014-format-patch.sh81
-rwxr-xr-xt/t4018-diff-funcname.sh1
-rw-r--r--t/t4018/css-brace-in-col-15
-rw-r--r--t/t4018/css-colon-eol4
-rw-r--r--t/t4018/css-colon-selector5
-rw-r--r--t/t4018/css-common4
-rw-r--r--t/t4018/css-long-selector-list6
-rw-r--r--t/t4018/css-prop-sans-indent5
-rw-r--r--t/t4018/css-short-selector-list4
-rw-r--r--t/t4018/css-trailing-space5
-rwxr-xr-xt/t4026-color.sh9
-rwxr-xr-xt/t4033-diff-patience.sh8
-rwxr-xr-xt/t4034-diff-words.sh1
-rw-r--r--t/t4034/css/expect16
-rw-r--r--t/t4034/css/post10
-rw-r--r--t/t4034/css/pre10
-rwxr-xr-xt/t4054-diff-bogus-tree.sh10
-rwxr-xr-xt/t4130-apply-criss-cross-rename.sh10
-rwxr-xr-xt/t4150-am.sh20
-rwxr-xr-xt/t4153-am-resume-override-opts.sh2
-rwxr-xr-xt/t4201-shortlog.sh6
-rwxr-xr-xt/t4202-log.sh46
-rwxr-xr-xt/t4205-log-pretty-formats.sh398
-rwxr-xr-xt/t4207-log-decoration-colors.sh2
-rwxr-xr-xt/t4208-log-magic-pathspec.sh4
-rwxr-xr-xt/t4211-line-log.sh7
-rwxr-xr-xt/t5000-tar-tree.sh74
-rw-r--r--t/t5000/19f9c8273ec45a8938e6999cb59b3ff66739902abin0 -> 2048 bytes
-rw-r--r--t/t5000/huge-and-future.tarbin0 -> 2048 bytes
-rwxr-xr-xt/t5100-mailinfo.sh31
-rw-r--r--t/t5100/0001mboxrd4
-rw-r--r--t/t5100/0002mboxrd5
-rw-r--r--t/t5100/sample.mboxrd19
-rwxr-xr-xt/t5504-fetch-receive-strict.sh4
-rwxr-xr-xt/t5505-remote.sh2
-rwxr-xr-xt/t5510-fetch.sh32
-rwxr-xr-xt/t5520-pull.sh50
-rwxr-xr-xt/t5523-push-upstream.sh12
-rwxr-xr-xt/t5533-push-cas.sh38
-rwxr-xr-xt/t5536-fetch-conflicts.sh4
-rwxr-xr-xt/t5541-http-push-smart.sh21
-rwxr-xr-xt/t5544-pack-objects-hook.sh62
-rwxr-xr-xt/t5545-push-options.sh103
-rwxr-xr-xt/t5614-clone-submodules.sh52
-rwxr-xr-xt/t6030-bisect-porcelain.sh26
-rwxr-xr-xt/t6038-merge-text-auto.sh74
-rwxr-xr-xt/t6301-for-each-ref-errors.sh10
-rwxr-xr-xt/t7004-tag.sh9
-rwxr-xr-xt/t7006-pager.sh13
-rwxr-xr-xt/t7011-skip-worktree-reading.sh12
-rwxr-xr-xt/t7012-skip-worktree-writing.sh10
-rwxr-xr-xt/t7060-wtstatus.sh4
-rwxr-xr-xt/t7063-status-untracked-cache.sh25
-rwxr-xr-xt/t7102-reset.sh4
-rwxr-xr-xt/t7201-co.sh2
-rwxr-xr-xt/t7400-submodule-basic.sh2
-rwxr-xr-xt/t7403-submodule-sync.sh4
-rwxr-xr-xt/t7406-submodule-update.sh80
-rwxr-xr-xt/t7411-submodule-config.sh11
-rwxr-xr-xt/t7508-status.sh6
-rwxr-xr-xt/t7510-signed-commit.sh7
-rwxr-xr-xt/t7512-status-help.sh1
-rwxr-xr-xt/t7607-merge-overwrite.sh4
-rwxr-xr-xt/t7609-merge-co-error-msgs.sh10
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh28
-rwxr-xr-xt/t7800-difftool.sh14
-rwxr-xr-xt/t8003-blame-corner-cases.sh79
-rwxr-xr-xt/t9003-help-autocorrect.sh4
-rwxr-xr-xt/t9100-git-svn-basic.sh23
-rwxr-xr-xt/t9115-git-svn-dcommit-funky-renames.sh7
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh2
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh2
-rwxr-xr-xt/t9142-git-svn-shallow-clone.sh2
-rwxr-xr-xt/t9158-git-svn-mergeinfo.sh2
-rwxr-xr-xt/t9160-git-svn-preserve-empty-dirs.sh1
-rwxr-xr-xt/t9300-fast-import.sh25
-rwxr-xr-xt/t9302-fast-import-unpack-limit.sh105
-rwxr-xr-xt/t9801-git-p4-branch.sh4
-rw-r--r--t/test-lib-functions.sh14
-rw-r--r--t/test-lib.sh7
-rw-r--r--templates/hooks--pre-receive.sample24
-rw-r--r--trace.c55
-rw-r--r--trailer.c2
-rw-r--r--transport-helper.c2
-rw-r--r--transport.c30
-rw-r--r--transport.h7
-rw-r--r--tree-diff.c4
-rw-r--r--unpack-trees.c18
-rw-r--r--upload-pack.c92
-rw-r--r--userdiff.c12
-rw-r--r--walker.c10
-rw-r--r--walker.h3
-rw-r--r--worktree.c99
-rw-r--r--worktree.h21
-rw-r--r--wrapper.c52
-rw-r--r--write_or_die.c23
-rw-r--r--wt-status.c16
-rw-r--r--xdiff/xpatience.c2
-rw-r--r--xdiff/xutils.c6
373 files changed, 11591 insertions, 4482 deletions
diff --git a/.gitattributes b/.gitattributes
index 5e98806..320e33c 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,3 @@
* whitespace=!indent,trail,space
-*.[ch] whitespace=indent,trail,space
+*.[ch] whitespace=indent,trail,space diff=cpp
*.sh whitespace=indent,trail,space
diff --git a/.mailmap b/.mailmap
index a714e69..9441a54 100644
--- a/.mailmap
+++ b/.mailmap
@@ -33,6 +33,7 @@ Cheng Renquan <crquan@gmail.com>
Chris Shoemaker <c.shoemaker@cox.net>
Chris Wright <chrisw@sous-sol.org> <chrisw@osdl.org>
Cord Seele <cowose@gmail.com> <cowose@googlemail.com>
+Christian Couder <chriscool@tuxfamily.org> <christian.couder@gmail.com>
Christian Stimming <stimming@tuhh.de> <chs@ckiste.goetheallee>
Csaba Henk <csaba@gluster.com> <csaba@lowlife.hu>
Dan Johnson <computerdruid@gmail.com>
diff --git a/.travis.yml b/.travis.yml
index c2b76f9..477c3d2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,6 +19,7 @@ addons:
packages:
- language-pack-is
- git-svn
+ - apache2
env:
global:
@@ -31,6 +32,7 @@ env:
- DEFAULT_TEST_TARGET=prove
- GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
- GIT_TEST_OPTS="--verbose --tee"
+ - GIT_TEST_HTTPD=true
- GIT_TEST_CLONE_2GB=YesPlease
# t9810 occasionally fails on Travis CI OS X
# t9816 occasionally fails with "TAP out of sequence errors" on Travis CI OS X
diff --git a/Documentation/RelNotes/2.10.0.txt b/Documentation/RelNotes/2.10.0.txt
new file mode 100644
index 0000000..d9ac291
--- /dev/null
+++ b/Documentation/RelNotes/2.10.0.txt
@@ -0,0 +1,556 @@
+Git 2.10 Release Notes
+======================
+
+Backward compatibility notes
+----------------------------
+
+Updates since v2.9
+------------------
+
+UI, Workflows & Features
+
+ * "git pull --rebase --verify-signature" learned to warn the user
+ that "--verify-signature" is a no-op when rebasing.
+
+ * An upstream project can make a recommendation to shallowly clone
+ some submodules in the .gitmodules file it ships.
+
+ * "git worktree add" learned that '-' can be used as a short-hand for
+ "@{-1}", the previous branch.
+
+ * Update the funcname definition to support css files.
+
+ * The completion script (in contrib/) learned to complete "git
+ status" options.
+
+ * Messages that are generated by auto gc during "git push" on the
+ receiving end are now passed back to the sending end in such a way
+ that they are shown with "remote: " prefix to avoid confusing the
+ users.
+
+ * "git add -i/-p" learned to honor diff.compactionHeuristic
+ experimental knob, so that the user can work on the same hunk split
+ as "git diff" output.
+
+ * "upload-pack" allows a custom "git pack-objects" replacement when
+ responding to "fetch/clone" via the uploadpack.packObjectsHook.
+ (merge b738396 jk/upload-pack-hook later to maint).
+
+ * Teach format-patch and mailsplit (hence "am") how a line that
+ happens to begin with "From " in the e-mail message is quoted with
+ ">", so that these lines can be restored to their original shape.
+ (merge d9925d1 ew/mboxrd-format-am later to maint).
+
+ * "git repack" learned the "--keep-unreachable" option, which sends
+ loose unreachable objects to a pack instead of leaving them loose.
+ This helps heuristics based on the number of loose objects
+ (e.g. "gc --auto").
+ (merge e26a8c4 jk/repack-keep-unreachable later to maint).
+
+ * "log --graph --format=" learned that "%>|(N)" specifies the width
+ relative to the terminal's left edge, not relative to the area to
+ draw text that is to the right of the ancestry-graph section. It
+ also now accepts negative N that means the column limit is relative
+ to the right border.
+
+ * A careless invocation of "git send-email directory/" after editing
+ 0001-change.patch with an editor often ends up sending both
+ 0001-change.patch and its backup file, 0001-change.patch~, causing
+ embarrassment and a minor confusion. Detect such an input and
+ offer to skip the backup files when sending the patches out.
+ (merge 531220b jc/send-email-skip-backup later to maint).
+
+ * "git submodule update" that drives many "git clone" could
+ eventually hit flaky servers/network conditions on one of the
+ submodules; the command learned to retry the attempt.
+
+ * The output coloring scheme learned two new attributes, italic and
+ strike, in addition to existing bold, reverse, etc.
+
+ * "git log" learns log.showSignature configuration variable, and a
+ command line option "--no-show-signature" to countermand it.
+ (merge fce04c3 mj/log-show-signature-conf later to maint).
+
+ * A couple of "git svn" updates.
+
+ * More markings of messages for i18n, with updates to various tests
+ to pass GETTEXT_POISON tests.
+
+ * "git archive" learned to handle files that are larger than 8GB and
+ commits far in the future than expressible by the traditional US-TAR
+ format.
+ (merge 5caeeb8 jk/big-and-future-archive-tar later to maint).
+
+ * A new configuration variable core.sshCommand has been added to
+ specify what value for GIT_SSH_COMMAND to use per repository.
+
+ * "git worktree prune" protected worktrees that are marked as
+ "locked" by creating a file in a known location. "git worktree"
+ command learned a dedicated command pair to create and remove such
+ a file, so that the users do not have to do this with editor.
+
+ * A handful of "git svn" updates.
+
+ * "git push" learned to accept and pass extra options to the
+ receiving end so that hooks can read and react to them.
+
+ * "git status" learned to suggest "merge --abort" during a conflicted
+ merge, just like it already suggests "rebase --abort" during a
+ conflicted rebase.
+
+ * "git jump" script (in contrib/) has been updated a bit.
+ (merge a91e692 jk/git-jump later to maint).
+
+ * "git push" and "git clone" learned to give better progress meters
+ to the end user who is waiting on the terminal.
+
+ * An entry "git log --decorate" for the tip of the current branch is
+ shown as "HEAD -> name" (where "name" is the name of the branch);
+ paint the arrow in the same color as "HEAD", not in the color for
+ commits.
+
+ * "git format-patch" learned format.from configuration variable to
+ specify the default settings for its "--from" option.
+
+ * "git am -3" calls "git merge-recursive" when it needs to fall back
+ to a three-way merge; this call has been turned into an internal
+ subroutine call instead of spawning a separate subprocess.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "git fast-import" learned the same performance trick to avoid
+ creating too small a packfile as "git fetch" and "git push" have,
+ using *.unpackLimit configuration.
+
+ * When "git daemon" is run without --[init-]timeout specified, a
+ connection from a client that silently goes offline can hang around
+ for a long time, wasting resources. The socket-level KEEPALIVE has
+ been enabled to allow the OS to notice such failed connections.
+
+ * "git upload-pack" command has been updated to use the parse-options
+ API.
+
+ * The "git apply" standalone program is being libified; this is the
+ first step to move many state variables into a structure that can
+ be explicitly (re)initialized to make the machinery callable more
+ than once.
+
+ * HTTP transport gained an option to produce more detailed debugging
+ trace.
+ (merge 73e57aa ep/http-curl-trace later to maint).
+
+ * Instead of taking advantage of a struct string_list that is
+ allocated with all NULs happens to be STRING_LIST_INIT_NODUP kind,
+ initialize them explicitly as such, to document their behaviour
+ better.
+ (merge 2721ce2 jk/string-list-static-init later to maint).
+
+ * HTTPd tests learned to show the server error log to help diagnosing
+ a failing tests.
+ (merge 44f243d nd/test-lib-httpd-show-error-log-in-verbose later to maint).
+
+ * The ownership rule for the piece of memory that hold references to
+ be fetched in "git fetch" was screwy, which has been cleaned up.
+
+ * "git bisect" makes an internal call to "git diff-tree" when
+ bisection finds the culprit, but this call did not initialize the
+ data structure to pass to the diff-tree API correctly.
+
+ * Further preparatory clean-up for "worktree" feature continues.
+ (merge 0409e0b nd/worktree-cleanup-post-head-protection later to maint).
+
+ * Formats of the various data (and how to validate them) where we use
+ GPG signature have been documented.
+
+ * A new run-command API function pipe_command() is introduced to
+ sanely feed data to the standard input while capturing data from
+ the standard output and the standard error of an external process,
+ which is cumbersome to hand-roll correctly without deadlocking.
+
+ The codepath to sign data in a prepared buffer with GPG has been
+ updated to use this API to read from the status-fd to check for
+ errors (instead of relying on GPG's exit status).
+ (merge efee955 jk/gpg-interface-cleanup later to maint).
+
+ * Allow t/perf framework to use the features from the most recent
+ version of Git even when testing an older installed version.
+
+ * The commands in the "log/diff" family have had an FILE* pointer in the
+ data structure they pass around for a long time, but some codepaths
+ used to always write to the standard output. As a preparatory step
+ to make "git format-patch" available to the internal callers, these
+ codepaths have been updated to consistently write into that FILE*
+ instead.
+
+ * Conversion from unsigned char sha1[20] to struct object_id
+ continues.
+
+ * Improve the look of the way "git fetch" reports what happened to
+ each ref that was fetched.
+
+ * The .c/.h sources are marked as such in our .gitattributes file so
+ that "git diff -W" and friends would work better.
+
+ * Code clean-up to avoid using a variable string that compilers may
+ feel untrustable as printf-style format given to write_file()
+ helper function.
+
+ * "git p4" used a location outside $GIT_DIR/refs/ to place its
+ temporary branches, which has been moved to refs/git-p4-tmp/.
+
+ * Existing autoconf generated test for the need to link with pthread
+ library did not check all the functions from pthread libraries;
+ recent FreeBSD has some functions in libc but not others, and we
+ mistakenly thought linking with libc is enough when it is not.
+
+ * When "git fsck" reports a broken link (e.g. a tree object contains
+ a blob that does not exist), both containing object and the object
+ that is referred to were reported with their 40-hex object names.
+ The command learned the "--name-objects" option to show the path to
+ the containing object from existing refs (e.g. "HEAD~24^2:file.txt").
+
+ * Allow http daemon tests in Travis CI tests.
+
+ * Makefile assumed that -lrt is always available on platforms that
+ want to use clock_gettime() and CLOCK_MONOTONIC, which is not a
+ case for recent Mac OS X. The necessary symbols are often found in
+ libc on many modern systems and having -lrt on the command line, as
+ long as the library exists, had no effect, but when the platform
+ removes librt.a that is a different matter--having -lrt will break
+ the linkage.
+
+ This change could be seen as a regression for those who do need to
+ specify -lrt, as they now specifically ask for NEEDS_LIBRT when
+ building. Hopefully they are in the minority these days.
+
+ * Further preparatory work on the refs API before the pluggable
+ backend series can land.
+
+ * Error handling in the codepaths that updates refs has been
+ improved.
+
+ * The API to iterate over all the refs (i.e. for_each_ref(), etc.)
+ has been revamped.
+
+ * A few tests that specifically target "git rebase -i" have been
+ added.
+
+ * Dumb http transport on the client side has been optimized.
+ (merge ecba195 ew/http-walker later to maint).
+
+ * Users of the parse_options_concat() API function need to allocate
+ extra slots in advance and fill them with OPT_END() when they want
+ to decide the set of supported options dynamically, which makes the
+ code error-prone and hard to read. This has been corrected by tweaking
+ the API to allocate and return a new copy of "struct option" array.
+
+ * "git fetch" exchanges batched have/ack messages between the sender
+ and the receiver, initially doubling every time and then falling
+ back to enlarge the window size linearly. The "smart http"
+ transport, being an half-duplex protocol, outgrows the preset limit
+ too quickly and becomes inefficient when interacting with a large
+ repository. The internal mechanism learned to grow the window size
+ more aggressively when working with the "smart http" transport.
+
+ * Tests for "git svn" have been taught to reuse the lib-httpd test
+ infrastructure when testing the subversion integration that
+ interacts with subversion repositories served over the http://
+ protocol.
+ (merge a8a5d25 ew/git-svn-http-tests later to maint).
+
+ * "git pack-objects" has a few options that tell it not to pack
+ objects found in certain packfiles, which require it to scan .idx
+ files of all available packs. The codepaths involved in these
+ operations have been optimized for a common case of not having any
+ non-local pack and/or any .kept pack.
+
+ * The t3700 test about "add --chmod=-x" have been made a bit more
+ robust and generally cleaned up.
+ (merge 766cdc4 ib/t3700-add-chmod-x-updates later to maint).
+
+ * The build procedure learned PAGER_ENV knob that lists what default
+ environment variable settings to export for popular pagers. This
+ mechanism is used to tweak the default settings to MORE on FreeBSD.
+ (merge 995bc22 ew/build-time-pager-tweaks later to maint).
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.9
+----------------
+
+Unless otherwise noted, all the fixes since v2.8 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * The commands in `git log` family take %C(auto) in a custom format
+ string. This unconditionally turned the color on, ignoring
+ --no-color or with --color=auto when the output is not connected to
+ a tty; this was corrected to make the format truly behave as
+ "auto".
+
+ * "git rev-list --count" whose walk-length is limited with "-n"
+ option did not work well with the counting optimized to look at the
+ bitmap index.
+
+ * "git show -W" (extend hunks to cover the entire function, delimited
+ by lines that match the "funcname" pattern) used to show the entire
+ file when a change added an entire function at the end of the file,
+ which has been fixed.
+
+ * The documentation set has been updated so that literal commands,
+ configuration variables and environment variables are consistently
+ typeset in fixed-width font and bold in manpages.
+
+ * "git svn propset" subcommand that was added in 2.3 days is
+ documented now.
+
+ * The documentation tries to consistently spell "GPG"; when
+ referring to the specific program name, "gpg" is used.
+
+ * "git reflog" stopped upon seeing an entry that denotes a branch
+ creation event (aka "unborn"), which made it appear as if the
+ reflog was truncated.
+
+ * The git-prompt scriptlet (in contrib/) was not friendly with those
+ who uses "set -u", which has been fixed.
+
+ * compat/regex code did not cleanly compile.
+
+ * A codepath that used alloca(3) to place an unbounded amount of data
+ on the stack has been updated to avoid doing so.
+
+ * "git update-index --add --chmod=+x file" may be usable as an escape
+ hatch, but not a friendly thing to force for people who do need to
+ use it regularly. "git add --chmod=+x file" can be used instead.
+
+ * Build improvements for gnome-keyring (in contrib/)
+
+ * "git status" used to say "working directory" when it meant "working
+ tree".
+
+ * Comments about misbehaving FreeBSD shells have been clarified with
+ the version number (9.x and before are broken, newer ones are OK).
+
+ * "git cherry-pick A" worked on an unborn branch, but "git
+ cherry-pick A..B" didn't.
+
+ * Fix an unintended regression in v2.9 that breaks "clone --depth"
+ that recurses down to submodules by forcing the submodules to also
+ be cloned shallowly, which many server instances that host upstream
+ of the submodules are not prepared for.
+
+ * Fix unnecessarily waste in the idiomatic use of ': ${VAR=default}'
+ to set the default value, without enclosing it in double quotes.
+
+ * Some platform-specific code had non-ANSI strict declarations of C
+ functions that do not take any parameters, which has been
+ corrected.
+
+ * The internal code used to show local timezone offset is not
+ prepared to handle timestamps beyond year 2100, and gave a
+ bogus offset value to the caller. Use a more benign looking
+ +0000 instead and let "git log" going in such a case, instead
+ of aborting.
+
+ * One among four invocations of readlink(1) in our test suite has
+ been rewritten so that the test can run on systems without the
+ command (others are in valgrind test framework and t9802).
+
+ * t/perf needs /usr/bin/time with GNU extension; the invocation of it
+ is updated to "gtime" on Darwin.
+
+ * A bug, which caused "git p4" while running under verbose mode to
+ report paths that are omitted due to branch prefix incorrectly, has
+ been fixed; the command said "Ignoring file outside of prefix" for
+ paths that are _inside_.
+
+ * The top level documentation "git help git" still pointed at the
+ documentation set hosted at now-defunct google-code repository.
+ Update it to point to https://git.github.io/htmldocs/git.html
+ instead.
+
+ * A helper function that takes the contents of a commit object and
+ finds its subject line did not ignore leading blank lines, as is
+ commonly done by other codepaths. Make it ignore leading blank
+ lines to match.
+
+ * For a long time, we carried an in-code comment that said our
+ colored output would work only when we use fprintf/fputs on
+ Windows, which no longer is the case for the past few years.
+
+ * "gc.autoPackLimit" when set to 1 should not trigger a repacking
+ when there is only one pack, but the code counted poorly and did
+ so.
+
+ * Add a test to specify the desired behaviour that currently is not
+ available in "git rebase -Xsubtree=...".
+
+ * More mark-up updates to typeset strings that are expected to
+ literally typed by the end user in fixed-width font.
+
+ * "git commit --amend --allow-empty-message -S" for a commit without
+ any message body could have misidentified where the header of the
+ commit object ends.
+
+ * "git rebase -i --autostash" did not restore the auto-stashed change
+ when the operation was aborted.
+
+ * Git does not know what the contents in the index should be for a
+ path added with "git add -N" yet, so "git grep --cached" should not
+ show hits (or show lack of hits, with -L) in such a path, but that
+ logic does not apply to "git grep", i.e. searching in the working
+ tree files. But we did so by mistake, which has been corrected.
+
+ * "git blame -M" missed a single line that was moved within the file.
+
+ * Fix recently introduced codepaths that are involved in parallel
+ submodule operations, which gave up on reading too early, and
+ could have wasted CPU while attempting to write under a corner
+ case condition.
+
+ * "git grep -i" has been taught to fold case in non-ascii locales
+ correctly.
+
+ * A test that unconditionally used "mktemp" learned that the command
+ is not necessarily available everywhere.
+
+ * There are certain house-keeping tasks that need to be performed at
+ the very beginning of any Git program, and programs that are not
+ built-in commands had to do them exactly the same way as "git"
+ potty does. It was easy to make mistakes in one-off standalone
+ programs (like test helpers). A common "main()" function that
+ calls cmd_main() of individual program has been introduced to
+ make it harder to make mistakes.
+ (merge de61ceb jk/common-main later to maint).
+
+ * The test framework learned a new helper test_match_signal to
+ check an exit code from getting killed by an expected signal.
+
+ * General code clean-up around a helper function to write a
+ single-liner to a file.
+ (merge 7eb6e10 jk/write-file later to maint).
+
+ * One part of "git am" had an oddball helper function that called
+ stuff from outside "his" as opposed to calling what we have "ours",
+ which was not gender-neutral and also inconsistent with the rest of
+ the system where outside stuff is usuall called "theirs" in
+ contrast to "ours".
+
+ * "git blame file" allowed the lineage of lines in the uncommitted,
+ unadded contents of "file" to be inspected, but it refused when
+ "file" did not appear in the current commit. When "file" was
+ created by renaming an existing file (but the change has not been
+ committed), this restriction was unnecessarily tight.
+
+ * "git add -N dir/file && git write-tree" produced an incorrect tree
+ when there are other paths in the same directory that sorts after
+ "file".
+
+ * "git fetch http://user:pass@host/repo..." scrubbed the userinfo
+ part, but "git push" didn't.
+
+ * "git merge" with renormalization did not work well with
+ merge-recursive, due to "safer crlf" conversion kicking in when it
+ shouldn't.
+ (merge 1335d76 jc/renormalize-merge-kill-safer-crlf later to maint).
+
+ * The use of strbuf in "git rm" to build filename to remove was a bit
+ suboptimal, which has been fixed.
+
+ * An age old bug that caused "git diff --ignore-space-at-eol"
+ misbehave has been fixed.
+
+ * "git notes merge" had a code to see if a path exists (and fails if
+ it does) and then open the path for writing (when it doesn't).
+ Replace it with open with O_EXCL.
+
+ * "git pack-objects" and "git index-pack" mostly operate with off_t
+ when talking about the offset of objects in a packfile, but there
+ were a handful of places that used "unsigned long" to hold that
+ value, leading to an unintended truncation.
+
+ * Recent update to "git daemon" tries to enable the socket-level
+ KEEPALIVE, but when it is spawned via inetd, the standard input
+ file descriptor may not necessarily be connected to a socket.
+ Suppress an ENOTSOCK error from setsockopt().
+
+ * Recent FreeBSD stopped making perl available at /usr/bin/perl;
+ switch the default the built-in path to /usr/local/bin/perl on not
+ too ancient FreeBSD releases.
+
+ * "git commit --help" said "--no-verify" is only about skipping the
+ pre-commit hook, and failed to say that it also skipped the
+ commit-msg hook.
+
+ * "git merge" in Git v2.9 was taught to forbid merging an unrelated
+ lines of history by default, but that is exactly the kind of thing
+ the "--rejoin" mode of "git subtree" (in contrib/) wants to do.
+ "git subtree" has been taught to use the "--allow-unrelated-histories"
+ option to override the default.
+
+ * The build procedure for "git persistent-https" helper (in contrib/)
+ has been updated so that it can be built with more recent versions
+ of Go.
+
+ * There is an optimization used in "git diff $treeA $treeB" to borrow
+ an already checked-out copy in the working tree when it is known to
+ be the same as the blob being compared, expecting that open/mmap of
+ such a file is faster than reading it from the object store, which
+ involves inflating and applying delta. This however kicked in even
+ when the checked-out copy needs to go through the convert-to-git
+ conversion (including the clean filter), which defeats the whole
+ point of the optimization. The optimization has been disabled when
+ the conversion is necessary.
+
+ * "git -c grep.patternType=extended log --basic-regexp" misbehaved
+ because the internal API to access the grep machinery was not
+ designed well.
+
+ * Windows port was failing some tests in t4130, due to the lack of
+ inum in the returned values by its lstat(2) emulation.
+
+ * The reflog output format is documented better, and a new format
+ --date=unix to report the seconds-since-epoch (without timezone)
+ has been added.
+ (merge 442f6fd jk/reflog-date later to maint).
+
+ * "git difftool <paths>..." started in a subdirectory failed to
+ interpret the paths relative to that directory, which has been
+ fixed.
+ (merge 32b8c58 jk/difftool-in-subdir later to maint).
+
+ * The characters in the label shown for tags/refs for commits in
+ "gitweb" output are now properly escaped for proper HTML output.
+
+ * FreeBSD can lie when asked mtime of a directory, which made the
+ untracked cache code to fall back to a slow-path, which in turn
+ caused tests in t7063 to fail because it wanted to verify the
+ behaviour of the fast-path.
+
+ * Squelch compiler warnings for netmalloc (in compat/) library.
+
+ * A small memory leak in the command line parsing of "git blame"
+ has been plugged.
+
+ * The API documentation for hashmap was unclear if hashmap_entry
+ can be safely discarded without any other consideration. State
+ that it is safe to do so.
+
+ * Not-so-recent rewrite of "git am" that started making internal
+ calls into the commit machinery had an unintended regression, in
+ that no matter how many seconds it took to apply many patches, the
+ resulting committer timestamp for the resulting commits were all
+ the same.
+ (merge 4d9c7e6 jk/reset-ident-time-per-commit later to maint).
+
+ * "git push --force-with-lease" already had enough logic to allow
+ ensuring that such a push results in creation of a ref (i.e. the
+ receiving end did not have another push from sideways that would be
+ discarded by our force-pushing), but didn't expose this possibility
+ to the users. It does so now.
+ (merge 9eed4f3 jk/push-force-with-lease-creation later to maint).
+
+ * Other minor clean-ups and documentation updates
diff --git a/Documentation/RelNotes/2.9.3.txt b/Documentation/RelNotes/2.9.3.txt
index 28003a5..d19f144 100644
--- a/Documentation/RelNotes/2.9.3.txt
+++ b/Documentation/RelNotes/2.9.3.txt
@@ -55,4 +55,106 @@ Fixes since v2.9.2
* A test that unconditionally used "mktemp" learned that the command
is not necessarily available everywhere.
+ * "git blame file" allowed the lineage of lines in the uncommitted,
+ unadded contents of "file" to be inspected, but it refused when
+ "file" did not appear in the current commit. When "file" was
+ created by renaming an existing file (but the change has not been
+ committed), this restriction was unnecessarily tight.
+
+ * "git add -N dir/file && git write-tree" produced an incorrect tree
+ when there are other paths in the same directory that sorts after
+ "file".
+
+ * "git fetch http://user:pass@host/repo..." scrubbed the userinfo
+ part, but "git push" didn't.
+
+ * An age old bug that caused "git diff --ignore-space-at-eol"
+ misbehave has been fixed.
+
+ * "git notes merge" had a code to see if a path exists (and fails if
+ it does) and then open the path for writing (when it doesn't).
+ Replace it with open with O_EXCL.
+
+ * "git pack-objects" and "git index-pack" mostly operate with off_t
+ when talking about the offset of objects in a packfile, but there
+ were a handful of places that used "unsigned long" to hold that
+ value, leading to an unintended truncation.
+
+ * Recent update to "git daemon" tries to enable the socket-level
+ KEEPALIVE, but when it is spawned via inetd, the standard input
+ file descriptor may not necessarily be connected to a socket.
+ Suppress an ENOTSOCK error from setsockopt().
+
+ * Recent FreeBSD stopped making perl available at /usr/bin/perl;
+ switch the default the built-in path to /usr/local/bin/perl on not
+ too ancient FreeBSD releases.
+
+ * "git status" learned to suggest "merge --abort" during a conflicted
+ merge, just like it already suggests "rebase --abort" during a
+ conflicted rebase.
+
+ * The .c/.h sources are marked as such in our .gitattributes file so
+ that "git diff -W" and friends would work better.
+
+ * Existing autoconf generated test for the need to link with pthread
+ library did not check all the functions from pthread libraries;
+ recent FreeBSD has some functions in libc but not others, and we
+ mistakenly thought linking with libc is enough when it is not.
+
+ * Allow http daemon tests in Travis CI tests.
+
+ * Users of the parse_options_concat() API function need to allocate
+ extra slots in advance and fill them with OPT_END() when they want
+ to decide the set of supported options dynamically, which makes the
+ code error-prone and hard to read. This has been corrected by tweaking
+ the API to allocate and return a new copy of "struct option" array.
+
+ * The use of strbuf in "git rm" to build filename to remove was a bit
+ suboptimal, which has been fixed.
+
+ * "git commit --help" said "--no-verify" is only about skipping the
+ pre-commit hook, and failed to say that it also skipped the
+ commit-msg hook.
+
+ * "git merge" in Git v2.9 was taught to forbid merging an unrelated
+ lines of history by default, but that is exactly the kind of thing
+ the "--rejoin" mode of "git subtree" (in contrib/) wants to do.
+ "git subtree" has been taught to use the "--allow-unrelated-histories"
+ option to override the default.
+
+ * The build procedure for "git persistent-https" helper (in contrib/)
+ has been updated so that it can be built with more recent versions
+ of Go.
+
+ * There is an optimization used in "git diff $treeA $treeB" to borrow
+ an already checked-out copy in the working tree when it is known to
+ be the same as the blob being compared, expecting that open/mmap of
+ such a file is faster than reading it from the object store, which
+ involves inflating and applying delta. This however kicked in even
+ when the checked-out copy needs to go through the convert-to-git
+ conversion (including the clean filter), which defeats the whole
+ point of the optimization. The optimization has been disabled when
+ the conversion is necessary.
+
+ * "git -c grep.patternType=extended log --basic-regexp" misbehaved
+ because the internal API to access the grep machinery was not
+ designed well.
+
+ * Windows port was failing some tests in t4130, due to the lack of
+ inum in the returned values by its lstat(2) emulation.
+
+ * The characters in the label shown for tags/refs for commits in
+ "gitweb" output are now properly escaped for proper HTML output.
+
+ * FreeBSD can lie when asked mtime of a directory, which made the
+ untracked cache code to fall back to a slow-path, which in turn
+ caused tests in t7063 to fail because it wanted to verify the
+ behaviour of the fast-path.
+
+ * Squelch compiler warnings for netmalloc (in compat/) library.
+
+ * The API documentation for hashmap was unclear if hashmap_entry
+ can be safely discarded without any other consideration. State
+ that it is safe to do so.
+
Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 6ad3eb6..0bcb679 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -150,27 +150,34 @@ integer::
1024", "by 1024x1024", etc.
color::
- The value for a variables that takes a color is a list of
- colors (at most two) and attributes (at most one), separated
- by spaces. The colors accepted are `normal`, `black`,
- `red`, `green`, `yellow`, `blue`, `magenta`, `cyan` and
- `white`; the attributes are `bold`, `dim`, `ul`, `blink` and
- `reverse`. The first color given is the foreground; the
- second is the background. The position of the attribute, if
- any, doesn't matter. Attributes may be turned off specifically
- by prefixing them with `no` (e.g., `noreverse`, `noul`, etc).
-+
-Colors (foreground and background) may also be given as numbers between
-0 and 255; these use ANSI 256-color mode (but note that not all
-terminals may support this). If your terminal supports it, you may also
-specify 24-bit RGB values as hex, like `#ff0ab3`.
-+
-The attributes are meant to be reset at the beginning of each item
-in the colored output, so setting color.decorate.branch to `black`
-will paint that branch name in a plain `black`, even if the previous
-thing on the same output line (e.g. opening parenthesis before the
-list of branch names in `log --decorate` output) is set to be
-painted with `bold` or some other attribute.
+ The value for a variable that takes a color is a list of
+ colors (at most two, one for foreground and one for background)
+ and attributes (as many as you want), separated by spaces.
++
+The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
+`blue`, `magenta`, `cyan` and `white`. The first color given is the
+foreground; the second is the background.
++
+Colors may also be given as numbers between 0 and 255; these use ANSI
+256-color mode (but note that not all terminals may support this). If
+your terminal supports it, you may also specify 24-bit RGB values as
+hex, like `#ff0ab3`.
++
+The accepted attributes are `bold`, `dim`, `ul`, `blink`, `reverse`,
+`italic`, and `strike` (for crossed-out or "strikethrough" letters).
+The position of any attributes with respect to the colors
+(before, after, or in between), doesn't matter. Specific attributes may
+be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
+`no-ul`, etc).
++
+For git's pre-defined color slots, the attributes are meant to be reset
+at the beginning of each item in the colored output. So setting
+`color.decorate.branch` to `black` will paint that branch name in a
+plain `black`, even if the previous thing on the same output line (e.g.
+opening parenthesis before the list of branch names in `log --decorate`
+output) is set to be painted with `bold` or some other attribute.
+However, custom log formats may do more complicated and layered
+coloring, and the negated forms may be useful there.
pathname::
A variable that takes a pathname value can be given a
@@ -405,13 +412,11 @@ file with mixed line endings would be reported by the `core.safecrlf`
mechanism.
core.autocrlf::
- Setting this variable to "true" is almost the same as setting
- the `text` attribute to "auto" on all files except that text
- files are not guaranteed to be normalized: files that contain
- `CRLF` in the repository will not be touched. Use this
- setting if you want to have `CRLF` line endings in your
- working directory even though the repository does not have
- normalized line endings. This variable can be set to 'input',
+ Setting this variable to "true" is the same as setting
+ the `text` attribute to "auto" on all files and core.eol to "crlf".
+ Set to true if you want to have `CRLF` line endings in your
+ working directory and the repository has LF line endings.
+ This variable can be set to 'input',
in which case no output conversion is performed.
core.symlinks::
@@ -443,6 +448,13 @@ specify that no proxy be used for a given domain pattern.
This is useful for excluding servers inside a firewall from
proxy use, while defaulting to a common proxy for external domains.
+core.sshCommand::
+ If this variable is set, `git fetch` and `git push` will
+ use the specified command instead of `ssh` when they need to
+ connect to a remote system. The command is in the same form as
+ the `GIT_SSH_COMMAND` environment variable and is overridden
+ when the environment variable is set.
+
core.ignoreStat::
If true, Git will avoid using lstat() calls to detect if files have
changed by setting the "assume-unchanged" bit for those tracked files
@@ -1189,6 +1201,15 @@ difftool.<tool>.cmd::
difftool.prompt::
Prompt before each invocation of the diff tool.
+fastimport.unpackLimit::
+ If the number of objects imported by linkgit:git-fast-import[1]
+ is below this limit, then the objects will be unpacked into
+ loose object files. However if the number of imported objects
+ equals or exceeds this limit then the pack will be stored as a
+ pack. Storing the pack from a fast-import can make the import
+ operation complete faster, especially on slow filesystems. If
+ not set, the value of `transfer.unpackLimit` is used instead.
+
fetch.recurseSubmodules::
This option can be either set to a boolean value or to 'on-demand'.
Setting it to a boolean changes the behavior of fetch and pull to
@@ -1220,6 +1241,11 @@ fetch.prune::
If true, fetch will automatically behave as if the `--prune`
option was given on the command line. See also `remote.<name>.prune`.
+fetch.output::
+ Control how ref update status is printed. Valid values are
+ `full` and `compact`. Default value is `full`. See section
+ OUTPUT in linkgit:git-fetch[1] for detail.
+
format.attach::
Enable multipart/mixed attachments as the default for
'format-patch'. The value can also be a double quoted string
@@ -1227,6 +1253,16 @@ format.attach::
value as the boundary. See the --attach option in
linkgit:git-format-patch[1].
+format.from::
+ Provides the default value for the `--from` option to format-patch.
+ Accepts a boolean value, or a name and email address. If false,
+ format-patch defaults to `--no-from`, using commit authors directly in
+ the "From:" field of patch mails. If true, format-patch defaults to
+ `--from`, using your committer identity in the "From:" field of patch
+ mails and including a "From:" field in the body of the patch mail if
+ different. If set to a non-boolean value, format-patch uses that
+ value instead of your committer identity. Defaults to false.
+
format.numbered::
A boolean which can enable or disable sequence numbers in patch
subjects. It defaults to "auto" which enables it only if there
@@ -2401,8 +2437,13 @@ rebase.instructionFormat
receive.advertiseAtomic::
By default, git-receive-pack will advertise the atomic push
- capability to its clients. If you don't want to this capability
- to be advertised, set this variable to false.
+ capability to its clients. If you don't want to advertise this
+ capability, set this variable to false.
+
+receive.advertisePushOptions::
+ By default, git-receive-pack will advertise the push options
+ capability to its clients. If you don't want to advertise this
+ capability, set this variable to false.
receive.autogc::
By default, git-receive-pack will run "git-gc --auto" after
@@ -2457,6 +2498,15 @@ receive.fsck.skipList::
can be safely ignored such as invalid committer email addresses.
Note: corrupt objects cannot be skipped with this setting.
+receive.keepAlive::
+ After receiving the pack from the client, `receive-pack` may
+ produce no output (if `--quiet` was specified) while processing
+ the pack, causing some networks to drop the TCP connection.
+ With this option set, if `receive-pack` does not transmit
+ any data in this phase for `receive.keepAlive` seconds, it will
+ send a short keepalive packet. The default is 5 seconds; set
+ to 0 to disable keepalives entirely.
+
receive.unpackLimit::
If the number of objects received in a push is below this
limit then the objects will be unpacked into loose object
@@ -2883,6 +2933,21 @@ uploadpack.keepAlive::
`uploadpack.keepAlive` seconds. Setting this option to 0
disables keepalive packets entirely. The default is 5 seconds.
+uploadpack.packObjectsHook::
+ If this option is set, when `upload-pack` would run
+ `git pack-objects` to create a packfile for a client, it will
+ run this shell command instead. The `pack-objects` command and
+ arguments it _would_ have run (including the `git pack-objects`
+ at the beginning) are appended to the shell command. The stdin
+ and stdout of the hook are treated as if `pack-objects` itself
+ was run. I.e., `upload-pack` will feed input intended for
+ `pack-objects` to the hook, and expects a completed packfile on
+ stdout.
++
+Note that this configuration variable is ignored if it is seen in the
+repository-level config (this is a safety measure against fetching from
+untrusted repositories).
+
url.<base>.insteadOf::
Any URL that starts with this value will be rewritten to
start, instead, with <base>. In cases where some site serves a
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index d9ae681..705a873 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -419,6 +419,9 @@ ifndef::git-format-patch[]
paths are selected if there is any file that matches
other criteria in the comparison; if there is no file
that matches other criteria, nothing is selected.
++
+Also, these upper-case letters can be downcased to exclude. E.g.
+`--diff-filter=ad` excludes added and deleted paths.
-S<string>::
Look for differences that change the number of occurrences of
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 8dd9e4f..12879e4 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -116,7 +116,8 @@ default. You can use `--no-utf8` to override this.
By default the command will try to detect the patch format
automatically. This option allows the user to bypass the automatic
detection and specify the patch format that the patch(es) should be
- interpreted as. Valid formats are mbox, stgit, stgit-series and hg.
+ interpreted as. Valid formats are mbox, mboxrd,
+ stgit, stgit-series and hg.
-i::
--interactive::
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index c105f21..2b76265 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -136,6 +136,8 @@ Performance and Compression Tuning
Maximum size of each output packfile.
The default is unlimited.
+fastimport.unpackLimit::
+ See linkgit:git-config[1]
Performance
-----------
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index efe56e0..9e42169 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -99,6 +99,57 @@ The latter use of the `remote.<repository>.fetch` values can be
overridden by giving the `--refmap=<refspec>` parameter(s) on the
command line.
+OUTPUT
+------
+
+The output of "git fetch" depends on the transport method used; this
+section describes the output when fetching over the Git protocol
+(either locally or via ssh) and Smart HTTP protocol.
+
+The status of the fetch is output in tabular form, with each line
+representing the status of a single ref. Each line is of the form:
+
+-------------------------------
+ <flag> <summary> <from> -> <to> [<reason>]
+-------------------------------
+
+The status of up-to-date refs is shown only if the --verbose option is
+used.
+
+In compact output mode, specified with configuration variable
+fetch.output, if either entire `<from>` or `<to>` is found in the
+other string, it will be substituted with `*` in the other string. For
+example, `master -> origin/master` becomes `master -> origin/*`.
+
+flag::
+ A single character indicating the status of the ref:
+(space);; for a successfully fetched fast-forward;
+`+`;; for a successful forced update;
+`-`;; for a successfully pruned ref;
+`t`;; for a successful tag update;
+`*`;; for a successfully fetched new ref;
+`!`;; for a ref that was rejected or failed to update; and
+`=`;; for a ref that was up to date and did not need fetching.
+
+summary::
+ For a successfully fetched ref, the summary shows the old and new
+ values of the ref in a form suitable for using as an argument to
+ `git log` (this is `<old>..<new>` in most cases, and
+ `<old>...<new>` for forced non-fast-forward updates).
+
+from::
+ The name of the remote ref being fetched from, minus its
+ `refs/<type>/` prefix. In the case of deletion, the name of
+ the remote ref is "(none)".
+
+to::
+ The name of the local ref being updated, minus its
+ `refs/<type>/` prefix.
+
+reason::
+ A human-readable explanation. In the case of successfully fetched
+ refs, no explanation is needed. For a failed ref, the reason for
+ failure is described.
EXAMPLES
--------
diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt
index 7fc68eb..b9f060e 100644
--- a/Documentation/git-fsck.txt
+++ b/Documentation/git-fsck.txt
@@ -11,7 +11,8 @@ SYNOPSIS
[verse]
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
[--[no-]full] [--strict] [--verbose] [--lost-found]
- [--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]
+ [--[no-]dangling] [--[no-]progress] [--connectivity-only]
+ [--[no-]name-objects] [<object>*]
DESCRIPTION
-----------
@@ -82,6 +83,12 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs
a blob, the contents are written into the file, rather than
its object name.
+--name-objects::
+ When displaying names of reachable objects, in addition to the
+ SHA-1 also display a name that describes *how* they are reachable,
+ compatible with linkgit:git-rev-parse[1], e.g.
+ `HEAD@{1234567890}~25^2:src/`.
+
--[no-]progress::
Progress status is reported on the standard error stream by
default when it is attached to a terminal, unless
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 4a6c47f..32246fd 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -198,6 +198,10 @@ log.showRoot::
`git log -p` output would be shown without a diff attached.
The default is `true`.
+log.showSignature::
+ If `true`, `git log` and related commands will act as if the
+ `--show-signature` option was passed to them.
+
mailmap.*::
See linkgit:git-shortlog[1].
diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt
index 4d1b871..e3b2a88 100644
--- a/Documentation/git-mailsplit.txt
+++ b/Documentation/git-mailsplit.txt
@@ -8,7 +8,8 @@ git-mailsplit - Simple UNIX mbox splitter program
SYNOPSIS
--------
[verse]
-'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] -o<directory> [--] [(<mbox>|<Maildir>)...]
+'git mailsplit' [-b] [-f<nn>] [-d<prec>] [--keep-cr] [--mboxrd]
+ -o<directory> [--] [(<mbox>|<Maildir>)...]
DESCRIPTION
-----------
@@ -47,6 +48,10 @@ OPTIONS
--keep-cr::
Do not remove `\r` from lines ending with `\r\n`.
+--mboxrd::
+ Input is of the "mboxrd" format and "^>+From " line escaping is
+ reversed.
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 19cdcd0..8973510 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -104,8 +104,8 @@ base-name::
out of memory with a large window, but still be able to take
advantage of the large window for the smaller objects. The
size can be suffixed with "k", "m", or "g".
- `--window-memory=0` makes memory usage unlimited, which is the
- default.
+ `--window-memory=0` makes memory usage unlimited. The default
+ is taken from the `pack.windowMemory` configuration variable.
--max-pack-size=<n>::
Maximum size of each output pack file. The size can be suffixed with
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 93c3527..47b77e6 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
- [-u | --set-upstream]
+ [-u | --set-upstream] [--push-option=<string>]
[--[no-]signed|--sign=(true|false|if-asked)]
[--force-with-lease[=<refname>[:<expect>]]]
[--no-verify] [<repository> [<refspec>...]]
@@ -156,6 +156,12 @@ already exists on the remote side.
Either all refs are updated, or on error, no refs are updated.
If the server does not support atomic pushes the push will fail.
+-o::
+--push-option::
+ Transmit the given string to the server, which passes them to
+ the pre-receive as well as the post-receive hook. The given string
+ must not contain a NUL or LF character.
+
--receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>::
Path to the 'git-receive-pack' program on the remote
@@ -198,10 +204,11 @@ branch we have for it.
+
`--force-with-lease=<refname>:<expect>` will protect the named ref (alone),
if it is going to be updated, by requiring its current value to be
-the same as the specified value <expect> (which is allowed to be
+the same as the specified value `<expect>` (which is allowed to be
different from the remote-tracking branch we have for the refname,
or we do not even have to have such a remote-tracking branch when
-this form is used).
+this form is used). If `<expect>` is the empty string, then the named ref
+must not already exist.
+
Note that all forms other than `--force-with-lease=<refname>:<expect>`
that specifies the expected current value of the ref explicitly are
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 0c03eec..26afe6e 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -100,8 +100,10 @@ other objects in that pack they already have locally.
out of memory with a large window, but still be able to take
advantage of the large window for the smaller objects. The
size can be suffixed with "k", "m", or "g".
- `--window-memory=0` makes memory usage unlimited, which is the
- default.
+ `--window-memory=0` makes memory usage unlimited. The default
+ is taken from the `pack.windowMemory` configuration variable.
+ Note that the actual memory usage will be the limit multiplied
+ by the number of threads used by linkgit:git-pack-objects[1].
--max-pack-size=<n>::
Maximum size of each output pack file. The size can be suffixed with
@@ -128,6 +130,19 @@ other objects in that pack they already have locally.
with `-b` or `repack.writeBitmaps`, as it ensures that the
bitmapped packfile has the necessary objects.
+--unpack-unreachable=<when>::
+ When loosening unreachable objects, do not bother loosening any
+ objects older than `<when>`. This can be used to optimize out
+ the write of any objects that would be immediately pruned by
+ a follow-up `git prune`.
+
+-k::
+--keep-unreachable::
+ When used with `-ad`, any unreachable objects from existing
+ packs will be appended to the end of the packfile instead of
+ being removed. In addition, any unreachable loose objects will
+ be packed (and their loose counterparts removed).
+
Configuration
-------------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 9226c43..bf3bb37 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -15,8 +15,9 @@ SYNOPSIS
'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)
'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
- [-f|--force] [--rebase|--merge] [--reference <repository>]
- [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]
+ [--[no-]recommend-shallow] [-f|--force] [--rebase|--merge]
+ [--reference <repository>] [--depth <depth>] [--recursive]
+ [--jobs <n>] [--] [<path>...]
'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
[commit] [--] [<path>...]
'git submodule' [--quiet] foreach [--recursive] <command>
@@ -384,6 +385,12 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
clone with a history truncated to the specified number of revisions.
See linkgit:git-clone[1]
+--[no-]recommend-shallow::
+ This option is only valid for the update command.
+ The initial clone of a submodule will use the recommended
+ `submodule.<name>.shallow` as provided by the .gitmodules file
+ by default. To ignore the suggestions use `--no-recommend-shallow`.
+
-j <n>::
--jobs <n>::
This option is only valid for the update command.
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 7e17cad..5f9e65b 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -625,6 +625,9 @@ config key: svn.authorsfile
with the committer name as the first argument. The program is
expected to return a single line of the form "Name <email>",
which will be treated as if included in the authors file.
++
+[verse]
+config key: svn.authorsProg
-q::
--quiet::
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index 0abc806..822ad59 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -9,8 +9,8 @@ git-upload-pack - Send objects packed back to git-fetch-pack
SYNOPSIS
--------
[verse]
-'git-upload-pack' [--strict] [--timeout=<n>] <directory>
-
+'git-upload-pack' [--[no-]strict] [--timeout=<n>] [--stateless-rpc]
+ [--advertise-refs] <directory>
DESCRIPTION
-----------
Invoked by 'git fetch-pack', learns what
@@ -25,12 +25,22 @@ repository. For push operations, see 'git send-pack'.
OPTIONS
-------
---strict::
+--[no-]strict::
Do not try <directory>/.git/ if <directory> is no Git directory.
--timeout=<n>::
Interrupt transfer after <n> seconds of inactivity.
+--stateless-rpc::
+ Perform only a single read-write cycle with stdin and stdout.
+ This fits with the HTTP POST request processing model where
+ a program may read the request, write a response, and must exit.
+
+--advertise-refs::
+ Only the initial ref advertisement is output, and the program exits
+ immediately. This fits with the HTTP GET request model, where
+ no request content is received but a response must be produced.
+
<directory>::
The repository to sync from.
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index c622345..0aeb020 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,8 +10,10 @@ SYNOPSIS
--------
[verse]
'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
-'git worktree prune' [-n] [-v] [--expire <expire>]
'git worktree list' [--porcelain]
+'git worktree lock' [--reason <string>] <worktree>
+'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree unlock' <worktree>
DESCRIPTION
-----------
@@ -38,9 +40,8 @@ section "DETAILS" for more information.
If a linked working tree is stored on a portable device or network share
which is not always mounted, you can prevent its administrative files from
-being pruned by creating a file named 'locked' alongside the other
-administrative files, optionally containing a plain text reason that
-pruning should be suppressed. See section "DETAILS" for more information.
+being pruned by issuing the `git worktree lock` command, optionally
+specifying `--reason` to explain why the working tree is locked.
COMMANDS
--------
@@ -48,16 +49,13 @@ add <path> [<branch>]::
Create `<path>` and checkout `<branch>` into it. The new working directory
is linked to the current repository, sharing everything except working
-directory specific files such as HEAD, index, etc.
+directory specific files such as HEAD, index, etc. `-` may also be
+specified as `<branch>`; it is synonymous with `@{-1}`.
+
If `<branch>` is omitted and neither `-b` nor `-B` nor `--detached` used,
then, as a convenience, a new branch based at HEAD is created automatically,
as if `-b $(basename <path>)` was specified.
-prune::
-
-Prune working tree information in $GIT_DIR/worktrees.
-
list::
List details of each worktree. The main worktree is listed first, followed by
@@ -65,6 +63,22 @@ each of the linked worktrees. The output details include if the worktree is
bare, the revision currently checked out, and the branch currently checked out
(or 'detached HEAD' if none).
+lock::
+
+If a working tree is on a portable device or network share which
+is not always mounted, lock it to prevent its administrative
+files from being pruned automatically. This also prevents it from
+being moved or deleted. Optionally, specify a reason for the lock
+with `--reason`.
+
+prune::
+
+Prune working tree information in $GIT_DIR/worktrees.
+
+unlock::
+
+Unlock a working tree, allowing it to be pruned, moved or deleted.
+
OPTIONS
-------
@@ -110,6 +124,18 @@ OPTIONS
--expire <time>::
With `prune`, only expire unused working trees older than <time>.
+--reason <string>::
+ With `lock`, an explanation why the working tree is locked.
+
+<worktree>::
+ Working trees can be identified by path, either relative or
+ absolute.
++
+If the last path components in the working tree's path is unique among
+working trees, it can be used to identify worktrees. For example if
+you only have to working trees at "/abc/def/ghi" and "/abc/def/ggg",
+then "ghi" or "def/ghi" is enough to point to the former working tree.
+
DETAILS
-------
Each linked working tree has a private sub-directory in the repository's
@@ -150,7 +176,8 @@ instead.
To prevent a $GIT_DIR/worktrees entry from being pruned (which
can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), add a file named
+entry's working tree is stored on a portable device), use the
+`git worktree lock` command, which adds a file named
'locked' to the entry's directory. The file contains the reason in
plain text. For example, if a linked working tree's `.git` file points
to `/path/main/.git/worktrees/test-next` then a file named
@@ -226,8 +253,6 @@ performed manually, such as:
- `remove` to remove a linked working tree and its administrative files (and
warn if the working tree is dirty)
- `mv` to move or rename a working tree and update its administrative files
-- `lock` to prevent automatic pruning of administrative files (for instance,
- for a working tree on a portable device)
GIT
---
diff --git a/Documentation/git.txt b/Documentation/git.txt
index ff25701..f4dfc9d 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -1085,6 +1085,14 @@ of clones and fetches.
cloning of shallow repositories.
See `GIT_TRACE` for available trace output options.
+`GIT_TRACE_CURL`::
+ Enables a curl full trace dump of all incoming and outgoing data,
+ including descriptive information, of the git transport protocol.
+ This is similar to doing curl `--trace-ascii` on the command line.
+ This option overrides setting the `GIT_CURL_VERBOSE` environment
+ variable.
+ See `GIT_TRACE` for available trace output options.
+
`GIT_LITERAL_PATHSPECS`::
Setting this variable to `1` will cause Git to treat all
pathspecs literally, rather than as glob patterns. For example,
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index e3b1de8..34db3e2 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -115,6 +115,7 @@ text file is normalized, its line endings are converted to LF in the
repository. To control what line ending style is used in the working
directory, use the `eol` attribute for a single file and the
`core.eol` configuration variable for all text files.
+Note that `core.autocrlf` overrides `core.eol`
Set::
@@ -130,8 +131,9 @@ Unset::
Set to string value "auto"::
When `text` is set to "auto", the path is marked for automatic
- end-of-line normalization. If Git decides that the content is
- text, its line endings are normalized to LF on checkin.
+ end-of-line conversion. If Git decides that the content is
+ text, its line endings are converted to LF on checkin.
+ When the file has been commited with CRLF, no conversion is done.
Unspecified::
@@ -146,7 +148,7 @@ unspecified.
^^^^^
This attribute sets a specific line-ending style to be used in the
-working directory. It enables end-of-line normalization without any
+working directory. It enables end-of-line conversion without any
content checks, effectively setting the `text` attribute.
Set to string value "crlf"::
@@ -186,9 +188,10 @@ the working directory, and prevent .jpg files from being normalized
regardless of their content.
------------------------
+* text=auto
*.txt text
-*.vcproj eol=crlf
-*.sh eol=lf
+*.vcproj text eol=crlf
+*.sh text eol=lf
*.jpg -text
------------------------
@@ -198,7 +201,7 @@ normalization in Git.
If you simply want to have CRLF line endings in your working directory
regardless of the repository you are working with, you can set the
-config variable "core.autocrlf" without changing any attributes.
+config variable "core.autocrlf" without using any attributes.
------------------------
[core]
@@ -374,6 +377,11 @@ substitution. For example:
smudge = git-p4-filter --smudge %f
------------------------
+Note that "%f" is the name of the path that is being worked on. Depending
+on the version that is being filtered, the corresponding file on disk may
+not exist, or may have different contents. So, smudge and clean commands
+should not try to access the file on disk, but only act as filters on the
+content provided to them on standard input.
Interaction between checkin/checkout attributes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -525,6 +533,8 @@ patterns are available:
- `csharp` suitable for source code in the C# language.
+- `css` suitable for cascading style sheets.
+
- `fortran` suitable for source code in the Fortran language.
- `fountain` suitable for Fountain documents.
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index d82e912..9565dc3 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -247,6 +247,15 @@ Both standard output and standard error output are forwarded to
'git send-pack' on the other end, so you can simply `echo` messages
for the user.
+The number of push options given on the command line of
+`git push --push-option=...` can be read from the environment
+variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are
+found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...
+If it is negotiated to not use the push options phase, the
+environment variables will not be set. If the client selects
+to use push options, but doesn't transmit any, the count variable
+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
+
[[update]]
update
~~~~~~
@@ -322,6 +331,15 @@ a sample script `post-receive-email` provided in the `contrib/hooks`
directory in Git distribution, which implements sending commit
emails.
+The number of push options given on the command line of
+`git push --push-option=...` can be read from the environment
+variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are
+found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...
+If it is negotiated to not use the push options phase, the
+environment variables will not be set. If the client selects
+to use push options, but doesn't transmit any, the count variable
+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
+
[[post-update]]
post-update
~~~~~~~~~~~
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index 07cdd73..0849d28 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -79,6 +79,11 @@ submodule.<name>.ignore::
"--ignore-submodule" option. The 'git submodule' commands are not
affected by this setting.
+submodule.<name>.shallow::
+ When set to true, a clone of this submodule will be performed as a
+ shallow clone unless the user explicitely asks for a non-shallow
+ clone.
+
EXAMPLES
--------
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 29b19b9..b95d67e 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -147,8 +147,14 @@ endif::git-rev-list[]
"U" for a good signature with unknown validity and "N" for no signature
- '%GS': show the name of the signer for a signed commit
- '%GK': show the key used to sign a signed commit
-- '%gD': reflog selector, e.g., `refs/stash@{1}`
-- '%gd': shortened reflog selector, e.g., `stash@{1}`
+- '%gD': reflog selector, e.g., `refs/stash@{1}` or
+ `refs/stash@{2 minutes ago`}; the format follows the rules described
+ for the `-g` option. The portion before the `@` is the refname as
+ given on the command line (so `git log -g refs/heads/master` would
+ yield `refs/heads/master@{0}`).
+- '%gd': shortened reflog selector; same as `%gD`, but the refname
+ portion is shortened for human readability (so `refs/heads/master`
+ becomes just `master`).
- '%gn': reflog identity name
- '%gN': reflog identity name (respecting .mailmap, see
linkgit:git-shortlog[1] or linkgit:git-blame[1])
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index c5bd218..a779c9d 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -252,10 +252,25 @@ list.
+
With `--pretty` format other than `oneline` (for obvious reasons),
this causes the output to have two extra lines of information
-taken from the reflog. By default, 'commit@\{Nth}' notation is
-used in the output. When the starting commit is specified as
-'commit@\{now}', output also uses 'commit@\{timestamp}' notation
-instead. Under `--pretty=oneline`, the commit message is
+taken from the reflog. The reflog designator in the output may be shown
+as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
+reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+depending on a few rules:
++
+--
+1. If the starting point is specified as `ref@{Nth}`, show the index
+format.
++
+2. If the starting point was specified as `ref@{now}`, show the
+timestamp format.
++
+3. If neither was used, but `--date` was given on the command line, show
+the timestamp in the format requested by `--date`.
++
+4. Otherwise, show the index format.
+--
++
+Under `--pretty=oneline`, the commit message is
prefixed with this information on the same line.
This option cannot be combined with `--reverse`.
See also linkgit:git-reflog[1].
@@ -274,6 +289,10 @@ ifdef::git-rev-list[]
Try to speed up the traversal using the pack bitmap index (if
one is available). Note that when traversing with `--objects`,
trees and blobs will not have their associated path printed.
+
+--progress=<header>::
+ Show progress reports on stderr as objects are considered. The
+ `<header>` text will be printed with each progress update.
endif::git-rev-list[]
--
@@ -710,8 +729,8 @@ include::pretty-options.txt[]
`iso-local`), the user's local time zone is used instead.
+
`--date=relative` shows dates relative to the current time,
-e.g. ``2 hours ago''. The `-local` option cannot be used with
-`--raw` or `--relative`.
+e.g. ``2 hours ago''. The `-local` option has no effect for
+`--date=relative`.
+
`--date=local` is an alias for `--date=default-local`.
+
@@ -731,7 +750,18 @@ format, often found in email messages.
+
`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.
+
-`--date=raw` shows the date in the internal raw Git format `%s %z` format.
+`--date=raw` shows the date as seconds since the epoch (1970-01-01
+00:00:00 UTC), followed by a space, and then the timezone as an offset
+from UTC (a `+` or `-` with four digits; the first two are hours, and
+the second two are minutes). I.e., as if the timestamp were formatted
+with `strftime("%s %z")`).
+Note that the `-local` option does not affect the seconds-since-epoch
+value (which is always measured in UTC), but does switch the accompanying
+timezone value.
++
+`--date=unix` shows the date as a Unix epoch timestamp (seconds since
+1970). As with `--raw`, this is always in UTC and therefore `-local`
+has no effect.
+
`--date=format:...` feeds the format `...` to your system `strftime`.
Use `--date=format:%c` to show the date in your system locale's
diff --git a/Documentation/technical/api-hashmap.txt b/Documentation/technical/api-hashmap.txt
index ad7a5bd..28f5a8b 100644
--- a/Documentation/technical/api-hashmap.txt
+++ b/Documentation/technical/api-hashmap.txt
@@ -104,6 +104,11 @@ If `free_entries` is true, each hashmap_entry in the map is freed as well
`entry` points to the entry to initialize.
+
`hash` is the hash code of the entry.
++
+The hashmap_entry structure does not hold references to external resources,
+and it is safe to just discard it once you are done with it (i.e. if
+your structure was allocated with xmalloc(), you can just free(3) it,
+and if it is on stack, you can just let it go out of scope).
`void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)`::
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index 8b36343..736f389 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -307,7 +307,7 @@ In multi_ack mode:
ready to make a packfile, it will blindly ACK all 'have' obj-ids
back to the client.
- * the server will then send a 'NACK' and then wait for another response
+ * the server will then send a 'NAK' and then wait for another response
from the client - either a 'done' or another list of 'have' lines.
In multi_ack_detailed mode:
@@ -454,7 +454,8 @@ The reference discovery phase is done nearly the same way as it is in the
fetching protocol. Each reference obj-id and name on the server is sent
in packet-line format to the client, followed by a flush-pkt. The only
real difference is that the capability listing is different - the only
-possible values are 'report-status', 'delete-refs' and 'ofs-delta'.
+possible values are 'report-status', 'delete-refs', 'ofs-delta' and
+'push-options'.
Reference Update Request and Packfile Transfer
----------------------------------------------
@@ -465,9 +466,10 @@ that it wants to update, it sends a line listing the obj-id currently on
the server, the obj-id the client would like to update it to and the name
of the reference.
-This list is followed by a flush-pkt and then the packfile that should
-contain all the objects that the server will need to complete the new
-references.
+This list is followed by a flush-pkt. Then the push options are transmitted
+one per packet followed by another flush-pkt. After that the packfile that
+should contain all the objects that the server will need to complete the new
+references will be sent.
----
update-request = *shallow ( command-list | push-cert ) [packfile]
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index eaab6b4..4c28d3a 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -253,6 +253,15 @@ atomic pushes. If the pushing client requests this capability, the server
will update the refs in one atomic transaction. Either all refs are
updated or none.
+push-options
+------------
+
+If the server sends the 'push-options' capability it is able to accept
+push options after the update commands have been sent, but before the
+packfile is streamed. If the pushing client requests this capability,
+the server will pass the options to the pre- and post- receive hooks
+that process this push request.
+
allow-tip-sha1-in-want
----------------------
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 09f1228..0fe02a6 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.9.2
+DEF_VER=v2.9.0.GIT
LF='
'
diff --git a/Makefile b/Makefile
index de5a030..d96ecb7 100644
--- a/Makefile
+++ b/Makefile
@@ -351,9 +351,12 @@ all::
# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
# return NULL when it receives a bogus time_t.
#
-# Define HAVE_CLOCK_GETTIME if your platform has clock_gettime in librt.
+# Define HAVE_CLOCK_GETTIME if your platform has clock_gettime.
#
-# Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC in librt.
+# Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC.
+#
+# Define NEEDS_LIBRT if your platform requires linking with librt (glibc version
+# before 2.17) for clock_gettime and CLOCK_MONOTONIC.
#
# Define USE_PARENS_AROUND_GETTEXT_N to "yes" if your compiler happily
# compiles the following initialization:
@@ -367,6 +370,14 @@ all::
# Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
#
# Define HAVE_GETDELIM if your system has the getdelim() function.
+#
+# Define PAGER_ENV to a SP separated VAR=VAL pairs to define
+# default environment variables to be passed when a pager is spawned, e.g.
+#
+# PAGER_ENV = LESS=FRX LV=-c
+#
+# to say "export LESS=FRX (and LV=-c) if the environment variable
+# LESS (and LV) is not set, respectively".
GIT-VERSION-FILE: FORCE
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -718,6 +729,7 @@ LIB_OBJS += diff-lib.o
LIB_OBJS += diff-no-index.o
LIB_OBJS += diff.o
LIB_OBJS += dir.o
+LIB_OBJS += dir-iterator.o
LIB_OBJS += editor.o
LIB_OBJS += entry.o
LIB_OBJS += environment.o
@@ -751,6 +763,7 @@ LIB_OBJS += merge.o
LIB_OBJS += merge-blobs.o
LIB_OBJS += merge-recursive.o
LIB_OBJS += mergesort.o
+LIB_OBJS += mru.o
LIB_OBJS += name-hash.o
LIB_OBJS += notes.o
LIB_OBJS += notes-cache.o
@@ -782,6 +795,7 @@ LIB_OBJS += read-cache.o
LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
LIB_OBJS += refs/files-backend.o
+LIB_OBJS += refs/iterator.o
LIB_OBJS += ref-filter.o
LIB_OBJS += remote.o
LIB_OBJS += replace_object.o
@@ -939,7 +953,7 @@ BUILTIN_OBJS += builtin/verify-tag.o
BUILTIN_OBJS += builtin/worktree.o
BUILTIN_OBJS += builtin/write-tree.o
-GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
+GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
EXTLIBS =
GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -1465,13 +1479,16 @@ endif
ifdef HAVE_CLOCK_GETTIME
BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME
- EXTLIBS += -lrt
endif
ifdef HAVE_CLOCK_MONOTONIC
BASIC_CFLAGS += -DHAVE_CLOCK_MONOTONIC
endif
+ifdef NEEDS_LIBRT
+ EXTLIBS += -lrt
+endif
+
ifdef HAVE_BSD_SYSCTL
BASIC_CFLAGS += -DHAVE_BSD_SYSCTL
endif
@@ -1492,6 +1509,10 @@ ifeq ($(PYTHON_PATH),)
NO_PYTHON = NoThanks
endif
+ifndef PAGER_ENV
+PAGER_ENV = LESS=FRX LV=-c
+endif
+
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
QUIET_SUBDIR1 =
@@ -1572,7 +1593,15 @@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
DIFF_SQ = $(subst ','\'',$(DIFF))
PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA))
-LIBS = $(GITLIBS) $(EXTLIBS)
+# We must filter out any object files from $(GITLIBS),
+# as it is typically used like:
+#
+# foo: foo.o $(GITLIBS)
+# $(CC) $(filter %.o,$^) $(LIBS)
+#
+# where we use it as a dependency. Since we also pull object files
+# from the dependency list, that would make each entry appear twice.
+LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS)
BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
$(COMPAT_CFLAGS)
@@ -1613,6 +1642,11 @@ ifdef DEFAULT_HELP_FORMAT
BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
endif
+PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
+PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
+PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
+BASIC_CFLAGS += -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
+
ALL_CFLAGS += $(BASIC_CFLAGS)
ALL_LDFLAGS += $(BASIC_LDFLAGS)
@@ -1708,8 +1742,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 $@ $(ALL_LDFLAGS) git.o \
- $(BUILTIN_OBJS) $(LIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
+ $(filter %.o,$^) $(LIBS)
help.sp help.s help.o: common-cmds.h
@@ -1737,7 +1771,7 @@ common-cmds.h: $(wildcard Documentation/git-*.txt)
SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
$(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
- $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP)
+ $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV)
define cmd_munge_script
$(RM) $@ $@+ && \
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@ -1750,6 +1784,7 @@ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-e 's|@@SANE_TEXT_GREP@@|$(SANE_TEXT_GREP)|g' \
+ -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
$@.sh >$@+
endef
@@ -1902,6 +1937,7 @@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS))
OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
$(XDIFF_OBJS) \
$(VCSSVN_OBJS) \
+ common-main.o \
git.o
ifndef NO_CURL
OBJECTS += http.o http-walker.o remote-curl.o
@@ -2063,7 +2099,10 @@ 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) git-parse-remote.sh
+LOCALIZED_SH = $(SCRIPT_SH)
+LOCALIZED_SH += git-parse-remote.sh
+LOCALIZED_SH += git-rebase--interactive.sh
+LOCALIZED_SH += git-sh-setup.sh
LOCALIZED_PERL = $(SCRIPT_PERL)
ifdef XGETTEXT_INCLUDE_TESTS
@@ -2153,6 +2192,7 @@ GIT-BUILD-OPTIONS: FORCE
@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
+ @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
ifdef TEST_OUTPUT_DIRECTORY
@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
endif
@@ -2225,17 +2265,9 @@ perf: all
.PHONY: test perf
-t/helper/test-ctype$X: ctype.o
-
-t/helper/test-date$X: date.o ctype.o
-
-t/helper/test-delta$X: diff-delta.o patch-delta.o
-
-t/helper/test-line-buffer$X: vcs-svn/lib.a
-
-t/helper/test-parse-options$X: parse-options.o parse-options-cb.o
+t/helper/test-line-buffer$X: $(VCSSVN_LIB)
-t/helper/test-svn-fe$X: vcs-svn/lib.a
+t/helper/test-svn-fe$X: $(VCSSVN_LIB)
.PRECIOUS: $(TEST_OBJS)
diff --git a/RelNotes b/RelNotes
index 779efe8..62615ff 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.9.3.txt \ No newline at end of file
+Documentation/RelNotes/2.10.0.txt \ No newline at end of file
diff --git a/advice.c b/advice.c
index 4dc5cf1..b84ae49 100644
--- a/advice.c
+++ b/advice.c
@@ -79,7 +79,20 @@ int git_default_advice_config(const char *var, const char *value)
int error_resolve_conflict(const char *me)
{
- error("%s is not possible because you have unmerged files.", me);
+ if (!strcmp(me, "cherry-pick"))
+ error(_("Cherry-picking is not possible because you have unmerged files."));
+ else if (!strcmp(me, "commit"))
+ error(_("Committing is not possible because you have unmerged files."));
+ else if (!strcmp(me, "merge"))
+ error(_("Merging is not possible because you have unmerged files."));
+ else if (!strcmp(me, "pull"))
+ error(_("Pulling is not possible because you have unmerged files."));
+ else if (!strcmp(me, "revert"))
+ error(_("Reverting is not possible because you have unmerged files."));
+ else
+ error(_("It is not possible to %s because you have unmerged files."),
+ me);
+
if (advice_resolve_conflict)
/*
* Message used both when 'git commit' fails and when
@@ -93,7 +106,7 @@ int error_resolve_conflict(const char *me)
void NORETURN die_resolve_conflict(const char *me)
{
error_resolve_conflict(me);
- die("Exiting because of an unresolved conflict.");
+ die(_("Exiting because of an unresolved conflict."));
}
void NORETURN die_conclude_merge(void)
@@ -106,14 +119,14 @@ void NORETURN die_conclude_merge(void)
void detach_advice(const char *new_name)
{
- const char fmt[] =
- "Note: checking out '%s'.\n\n"
+ const char *fmt =
+ _("Note: checking out '%s'.\n\n"
"You are in 'detached HEAD' state. You can look around, make experimental\n"
"changes and commit them, and you can discard any commits you make in this\n"
"state without impacting any branches by performing another checkout.\n\n"
"If you want to create a new branch to retain commits you create, you may\n"
"do so (now or later) by using -b with the checkout command again. Example:\n\n"
- " git checkout -b <new-branch-name>\n\n";
+ " git checkout -b <new-branch-name>\n\n");
fprintf(stderr, fmt, new_name);
}
diff --git a/archive-tar.c b/archive-tar.c
index cb99df2..380e3ae 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -18,6 +18,21 @@ static int tar_umask = 002;
static int write_tar_filter_archive(const struct archiver *ar,
struct archiver_args *args);
+/*
+ * This is the max value that a ustar size header can specify, as it is fixed
+ * at 11 octal digits. POSIX specifies that we switch to extended headers at
+ * this size.
+ *
+ * Likewise for the mtime (which happens to use a buffer of the same size).
+ */
+#if ULONG_MAX == 0xFFFFFFFF
+#define USTAR_MAX_SIZE ULONG_MAX
+#define USTAR_MAX_MTIME ULONG_MAX
+#else
+#define USTAR_MAX_SIZE 077777777777UL
+#define USTAR_MAX_MTIME 077777777777UL
+#endif
+
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
{
@@ -137,6 +152,20 @@ static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
strbuf_addch(sb, '\n');
}
+/*
+ * Like strbuf_append_ext_header, but for numeric values.
+ */
+static void strbuf_append_ext_header_uint(struct strbuf *sb,
+ const char *keyword,
+ uintmax_t value)
+{
+ char buf[40]; /* big enough for 2^128 in decimal, plus NUL */
+ int len;
+
+ len = xsnprintf(buf, sizeof(buf), "%"PRIuMAX, value);
+ strbuf_append_ext_header(sb, keyword, buf, len);
+}
+
static unsigned int ustar_header_chksum(const struct ustar_header *header)
{
const unsigned char *p = (const unsigned char *)header;
@@ -184,9 +213,9 @@ static void prepare_header(struct archiver_args *args,
xsnprintf(header->chksum, sizeof(header->chksum), "%07o", ustar_header_chksum(header));
}
-static int write_extended_header(struct archiver_args *args,
- const unsigned char *sha1,
- const void *buffer, unsigned long size)
+static void write_extended_header(struct archiver_args *args,
+ const unsigned char *sha1,
+ const void *buffer, unsigned long size)
{
struct ustar_header header;
unsigned int mode;
@@ -197,7 +226,6 @@ static int write_extended_header(struct archiver_args *args,
prepare_header(args, &header, mode, size);
write_blocked(&header, sizeof(header));
write_blocked(buffer, size);
- return 0;
}
static int write_tar_entry(struct archiver_args *args,
@@ -208,7 +236,7 @@ static int write_tar_entry(struct archiver_args *args,
struct ustar_header header;
struct strbuf ext_header = STRBUF_INIT;
unsigned int old_mode = mode;
- unsigned long size;
+ unsigned long size, size_in_header;
void *buffer;
int err = 0;
@@ -267,15 +295,17 @@ static int write_tar_entry(struct archiver_args *args,
memcpy(header.linkname, buffer, size);
}
- prepare_header(args, &header, mode, size);
+ size_in_header = size;
+ if (S_ISREG(mode) && size > USTAR_MAX_SIZE) {
+ size_in_header = 0;
+ strbuf_append_ext_header_uint(&ext_header, "size", size);
+ }
+
+ prepare_header(args, &header, mode, size_in_header);
if (ext_header.len > 0) {
- err = write_extended_header(args, sha1, ext_header.buf,
- ext_header.len);
- if (err) {
- free(buffer);
- return err;
- }
+ write_extended_header(args, sha1, ext_header.buf,
+ ext_header.len);
}
strbuf_release(&ext_header);
write_blocked(&header, sizeof(header));
@@ -289,15 +319,25 @@ static int write_tar_entry(struct archiver_args *args,
return err;
}
-static int write_global_extended_header(struct archiver_args *args)
+static void write_global_extended_header(struct archiver_args *args)
{
const unsigned char *sha1 = args->commit_sha1;
struct strbuf ext_header = STRBUF_INIT;
struct ustar_header header;
unsigned int mode;
- int err = 0;
- strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
+ if (sha1)
+ strbuf_append_ext_header(&ext_header, "comment",
+ sha1_to_hex(sha1), 40);
+ if (args->time > USTAR_MAX_MTIME) {
+ strbuf_append_ext_header_uint(&ext_header, "mtime",
+ args->time);
+ args->time = USTAR_MAX_MTIME;
+ }
+
+ if (!ext_header.len)
+ return;
+
memset(&header, 0, sizeof(header));
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
mode = 0100666;
@@ -306,7 +346,6 @@ static int write_global_extended_header(struct archiver_args *args)
write_blocked(&header, sizeof(header));
write_blocked(ext_header.buf, ext_header.len);
strbuf_release(&ext_header);
- return err;
}
static struct archiver **tar_filters;
@@ -382,10 +421,8 @@ static int write_tar_archive(const struct archiver *ar,
{
int err = 0;
- if (args->commit_sha1)
- err = write_global_extended_header(args);
- if (!err)
- err = write_archive_entries(args, write_tar_entry);
+ write_global_extended_header(args);
+ err = write_archive_entries(args, write_tar_entry);
if (!err)
write_trailer();
return err;
diff --git a/archive.c b/archive.c
index 5d735ae..42df974 100644
--- a/archive.c
+++ b/archive.c
@@ -322,7 +322,7 @@ static int path_exists(struct tree *tree, const char *path)
pathspec.recursive = 1;
ret = read_tree_recursive(tree, "", 0, 0, &pathspec,
reject_entry, &pathspec);
- free_pathspec(&pathspec);
+ clear_pathspec(&pathspec);
return ret != 0;
}
diff --git a/bisect.c b/bisect.c
index dc13319..6f512c2 100644
--- a/bisect.c
+++ b/bisect.c
@@ -438,12 +438,12 @@ static void read_bisect_paths(struct argv_array *array)
FILE *fp = fopen(filename, "r");
if (!fp)
- die_errno("Could not open file '%s'", filename);
+ die_errno(_("Could not open file '%s'"), filename);
while (strbuf_getline_lf(&str, fp) != EOF) {
strbuf_trim(&str);
if (sq_dequote_to_argv_array(str.buf, array))
- die("Badly quoted content in file '%s': %s",
+ die(_("Badly quoted content in file '%s': %s"),
filename, str.buf);
}
@@ -646,10 +646,13 @@ static void exit_if_skipped_commits(struct commit_list *tried,
printf("There are only 'skip'ped commits left to test.\n"
"The first %s commit could be any of:\n", term_bad);
- print_commit_list(tried, "%s\n", "%s\n");
+
+ for ( ; tried; tried = tried->next)
+ printf("%s\n", oid_to_hex(&tried->item->object.oid));
+
if (bad)
printf("%s\n", oid_to_hex(bad));
- printf("We cannot bisect more!\n");
+ printf(_("We cannot bisect more!\n"));
exit(2);
}
@@ -702,7 +705,7 @@ static struct commit *get_commit_reference(const unsigned char *sha1)
{
struct commit *r = lookup_commit_reference(sha1);
if (!r)
- die("Not a valid commit name %s", sha1_to_hex(sha1));
+ die(_("Not a valid commit name %s"), sha1_to_hex(sha1));
return r;
}
@@ -726,27 +729,27 @@ static void handle_bad_merge_base(void)
char *bad_hex = oid_to_hex(current_bad_oid);
char *good_hex = join_sha1_array_hex(&good_revs, ' ');
if (!strcmp(term_bad, "bad") && !strcmp(term_good, "good")) {
- fprintf(stderr, "The merge base %s is bad.\n"
+ fprintf(stderr, _("The merge base %s is bad.\n"
"This means the bug has been fixed "
- "between %s and [%s].\n",
+ "between %s and [%s].\n"),
bad_hex, bad_hex, good_hex);
} else if (!strcmp(term_bad, "new") && !strcmp(term_good, "old")) {
- fprintf(stderr, "The merge base %s is new.\n"
+ fprintf(stderr, _("The merge base %s is new.\n"
"The property has changed "
- "between %s and [%s].\n",
+ "between %s and [%s].\n"),
bad_hex, bad_hex, good_hex);
} else {
- fprintf(stderr, "The merge base %s is %s.\n"
+ fprintf(stderr, _("The merge base %s is %s.\n"
"This means the first '%s' commit is "
- "between %s and [%s].\n",
+ "between %s and [%s].\n"),
bad_hex, term_bad, term_good, bad_hex, good_hex);
}
exit(3);
}
- fprintf(stderr, "Some %s revs are not ancestor of the %s rev.\n"
+ fprintf(stderr, _("Some %s revs are not ancestor of the %s rev.\n"
"git bisect cannot work properly in this case.\n"
- "Maybe you mistook %s and %s revs?\n",
+ "Maybe you mistook %s and %s revs?\n"),
term_good, term_bad, term_good, term_bad);
exit(1);
}
@@ -754,14 +757,14 @@ static void handle_bad_merge_base(void)
static void handle_skipped_merge_base(const unsigned char *mb)
{
char *mb_hex = sha1_to_hex(mb);
- char *bad_hex = sha1_to_hex(current_bad_oid->hash);
+ char *bad_hex = oid_to_hex(current_bad_oid);
char *good_hex = join_sha1_array_hex(&good_revs, ' ');
- warning("the merge base between %s and [%s] "
+ warning(_("the merge base between %s and [%s] "
"must be skipped.\n"
"So we cannot be sure the first %s commit is "
"between %s and %s.\n"
- "We continue anyway.",
+ "We continue anyway."),
bad_hex, good_hex, term_bad, mb_hex, bad_hex);
free(good_hex);
}
@@ -792,7 +795,7 @@ static void check_merge_bases(int no_checkout)
} else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
handle_skipped_merge_base(mb);
} else {
- printf("Bisecting: a merge base must be tested\n");
+ printf(_("Bisecting: a merge base must be tested\n"));
exit(bisect_checkout(mb, no_checkout));
}
}
@@ -843,7 +846,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
int fd;
if (!current_bad_oid)
- die("a %s revision is needed", term_bad);
+ die(_("a %s revision is needed"), term_bad);
/* Check if file BISECT_ANCESTORS_OK exists. */
if (!stat(filename, &st) && S_ISREG(st.st_mode))
@@ -860,7 +863,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
/* Create file BISECT_ANCESTORS_OK. */
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0)
- warning_errno("could not create file '%s'",
+ warning_errno(_("could not create file '%s'"),
filename);
else
close(fd);
@@ -911,7 +914,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
*read_good = "good";
return;
} else {
- die_errno("could not read file '%s'", filename);
+ die_errno(_("could not read file '%s'"), filename);
}
} else {
strbuf_getline_lf(&str, fp);
@@ -937,10 +940,11 @@ int bisect_next_all(const char *prefix, int no_checkout)
struct commit_list *tried;
int reaches = 0, all = 0, nr, steps;
const unsigned char *bisect_rev;
+ char steps_msg[32];
read_bisect_terms(&term_bad, &term_good);
if (read_bisect_refs())
- die("reading bisect refs failed");
+ die(_("reading bisect refs failed"));
check_good_are_ancestors_of_bad(prefix, no_checkout);
@@ -960,7 +964,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
*/
exit_if_skipped_commits(tried, NULL);
- printf("%s was both %s and %s\n",
+ printf(_("%s was both %s and %s\n"),
oid_to_hex(current_bad_oid),
term_good,
term_bad);
@@ -968,8 +972,8 @@ int bisect_next_all(const char *prefix, int no_checkout)
}
if (!all) {
- fprintf(stderr, "No testable commit found.\n"
- "Maybe you started with bad path parameters?\n");
+ fprintf(stderr, _("No testable commit found.\n"
+ "Maybe you started with bad path parameters?\n"));
exit(4);
}
@@ -986,9 +990,14 @@ int bisect_next_all(const char *prefix, int no_checkout)
nr = all - reaches - 1;
steps = estimate_bisect_steps(all);
- printf("Bisecting: %d revision%s left to test after this "
- "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
- steps, (steps == 1 ? "" : "s"));
+ xsnprintf(steps_msg, sizeof(steps_msg),
+ Q_("(roughly %d step)", "(roughly %d steps)", steps),
+ steps);
+ /* TRANSLATORS: the last %s will be replaced with
+ "(roughly %d steps)" translation */
+ printf(Q_("Bisecting: %d revision left to test after this %s\n",
+ "Bisecting: %d revisions left to test after this %s\n",
+ nr), nr, steps_msg);
return bisect_checkout(bisect_rev, no_checkout);
}
diff --git a/builtin/am.c b/builtin/am.c
index 0d97f2f..739b34d 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -70,7 +70,8 @@ enum patch_format {
PATCH_FORMAT_MBOX,
PATCH_FORMAT_STGIT,
PATCH_FORMAT_STGIT_SERIES,
- PATCH_FORMAT_HG
+ PATCH_FORMAT_HG,
+ PATCH_FORMAT_MBOXRD
};
enum keep_type {
@@ -183,22 +184,22 @@ static inline const char *am_path(const struct am_state *state, const char *path
/**
* For convenience to call write_file()
*/
-static int write_state_text(const struct am_state *state,
- const char *name, const char *string)
+static void write_state_text(const struct am_state *state,
+ const char *name, const char *string)
{
- return write_file(am_path(state, name), "%s", string);
+ write_file(am_path(state, name), "%s", string);
}
-static int write_state_count(const struct am_state *state,
- const char *name, int value)
+static void write_state_count(const struct am_state *state,
+ const char *name, int value)
{
- return write_file(am_path(state, name), "%d", value);
+ write_file(am_path(state, name), "%d", value);
}
-static int write_state_bool(const struct am_state *state,
- const char *name, int value)
+static void write_state_bool(const struct am_state *state,
+ const char *name, int value)
{
- return write_state_text(state, name, value ? "t" : "f");
+ write_state_text(state, name, value ? "t" : "f");
}
/**
@@ -402,13 +403,8 @@ static int read_commit_msg(struct am_state *state)
*/
static void write_commit_msg(const struct am_state *state)
{
- int fd;
const char *filename = am_path(state, "final-commit");
-
- fd = xopen(filename, O_WRONLY | O_CREAT, 0666);
- if (write_in_full(fd, state->msg, state->msg_len) < 0)
- die_errno(_("could not write to %s"), filename);
- close(fd);
+ write_file_buf(filename, state->msg, state->msg_len);
}
/**
@@ -712,7 +708,8 @@ done:
* Splits out individual email patches from `paths`, where each path is either
* a mbox file or a Maildir. Returns 0 on success, -1 on failure.
*/
-static int split_mail_mbox(struct am_state *state, const char **paths, int keep_cr)
+static int split_mail_mbox(struct am_state *state, const char **paths,
+ int keep_cr, int mboxrd)
{
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf last = STRBUF_INIT;
@@ -724,6 +721,8 @@ static int split_mail_mbox(struct am_state *state, const char **paths, int keep_
argv_array_push(&cp.args, "-b");
if (keep_cr)
argv_array_push(&cp.args, "--keep-cr");
+ if (mboxrd)
+ argv_array_push(&cp.args, "--mboxrd");
argv_array_push(&cp.args, "--");
argv_array_pushv(&cp.args, paths);
@@ -965,13 +964,15 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
switch (patch_format) {
case PATCH_FORMAT_MBOX:
- return split_mail_mbox(state, paths, keep_cr);
+ return split_mail_mbox(state, paths, keep_cr, 0);
case PATCH_FORMAT_STGIT:
return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);
case PATCH_FORMAT_STGIT_SERIES:
return split_mail_stgit_series(state, paths, keep_cr);
case PATCH_FORMAT_HG:
return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
+ case PATCH_FORMAT_MBOXRD:
+ return split_mail_mbox(state, paths, keep_cr, 1);
default:
die("BUG: invalid patch_format");
}
@@ -1578,47 +1579,18 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
}
/**
- * Do the three-way merge using fake ancestor, their tree constructed
- * from the fake ancestor and the postimage of the patch, and our
- * state.
- */
-static int run_fallback_merge_recursive(const struct am_state *state,
- unsigned char *orig_tree,
- unsigned char *our_tree,
- unsigned char *their_tree)
-{
- struct child_process cp = CHILD_PROCESS_INIT;
- int status;
-
- cp.git_cmd = 1;
-
- argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
- sha1_to_hex(their_tree), linelen(state->msg), state->msg);
- if (state->quiet)
- argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
-
- argv_array_push(&cp.args, "merge-recursive");
- argv_array_push(&cp.args, sha1_to_hex(orig_tree));
- argv_array_push(&cp.args, "--");
- argv_array_push(&cp.args, sha1_to_hex(our_tree));
- argv_array_push(&cp.args, sha1_to_hex(their_tree));
-
- status = run_command(&cp) ? (-1) : 0;
- discard_cache();
- read_cache();
- return status;
-}
-
-/**
* Attempt a threeway merge, using index_path as the temporary index.
*/
static int fall_back_threeway(const struct am_state *state, const char *index_path)
{
- unsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ],
- our_tree[GIT_SHA1_RAWSZ];
+ struct object_id orig_tree, their_tree, our_tree;
+ const struct object_id *bases[1] = { &orig_tree };
+ struct merge_options o;
+ struct commit *result;
+ char *their_tree_name;
- if (get_sha1("HEAD", our_tree) < 0)
- hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
+ if (get_oid("HEAD", &our_tree) < 0)
+ hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);
if (build_fake_ancestor(state, index_path))
return error("could not build fake ancestor");
@@ -1626,7 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
discard_cache();
read_cache_from(index_path);
- if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))
return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1642,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
init_revisions(&rev_info, NULL);
rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
- add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
+ add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);
diff_setup_done(&rev_info.diffopt);
run_diff_index(&rev_info, 1);
}
@@ -1651,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
return error(_("Did you hand edit your patch?\n"
"It does not apply to blobs recorded in its index."));
- if (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL))
+ if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))
return error("could not write tree");
say(state, stdout, _("Falling back to patching base and 3-way merge..."));
@@ -1667,11 +1639,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
* changes.
*/
- if (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) {
+ init_merge_options(&o);
+
+ o.branch1 = "HEAD";
+ their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+ o.branch2 = their_tree_name;
+
+ if (state->quiet)
+ o.verbosity = 0;
+
+ if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
rerere(state->allow_rerere_autoupdate);
+ free(their_tree_name);
return error(_("Failed to merge in the changes."));
}
+ free(their_tree_name);
return 0;
}
@@ -1839,6 +1822,8 @@ static void am_run(struct am_state *state, int resume)
const char *mail = am_path(state, msgnum(state));
int apply_status;
+ reset_ident_date();
+
if (!file_exists(mail))
goto next;
@@ -2201,6 +2186,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
*opt_value = PATCH_FORMAT_STGIT_SERIES;
else if (!strcmp(arg, "hg"))
*opt_value = PATCH_FORMAT_HG;
+ else if (!strcmp(arg, "mboxrd"))
+ *opt_value = PATCH_FORMAT_MBOXRD;
else
return error(_("Invalid value for --patch-format: %s"), arg);
return 0;
diff --git a/builtin/apply.c b/builtin/apply.c
index c770d7d..1a488f9 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -21,132 +21,161 @@
#include "ll-merge.h"
#include "rerere.h"
-/*
- * --check turns on checking that the working tree matches the
- * files that are being modified, but doesn't apply the patch
- * --stat does just a diffstat, and doesn't actually apply
- * --numstat does numeric diffstat, and doesn't actually apply
- * --index-info shows the old and new index info for paths if available.
- * --index updates the cache as well.
- * --cached updates only the cache without ever touching the working tree.
- */
-static const char *prefix;
-static int prefix_length = -1;
-static int newfd = -1;
-
-static int unidiff_zero;
-static int p_value = 1;
-static int p_value_known;
-static int check_index;
-static int update_index;
-static int cached;
-static int diffstat;
-static int numstat;
-static int summary;
-static int check;
-static int apply = 1;
-static int apply_in_reverse;
-static int apply_with_reject;
-static int apply_verbosely;
-static int allow_overlap;
-static int no_add;
-static int threeway;
-static int unsafe_paths;
-static const char *fake_ancestor;
-static int line_termination = '\n';
-static unsigned int p_context = UINT_MAX;
-static const char * const apply_usage[] = {
- N_("git apply [<options>] [<patch>...]"),
- NULL
-};
-
-static enum ws_error_action {
+enum ws_error_action {
nowarn_ws_error,
warn_on_ws_error,
die_on_ws_error,
correct_ws_error
-} ws_error_action = warn_on_ws_error;
-static int whitespace_error;
-static int squelch_whitespace_errors = 5;
-static int applied_after_fixing_ws;
+};
+
-static enum ws_ignore {
+enum ws_ignore {
ignore_ws_none,
ignore_ws_change
-} ws_ignore_action = ignore_ws_none;
+};
+
+/*
+ * We need to keep track of how symlinks in the preimage are
+ * manipulated by the patches. A patch to add a/b/c where a/b
+ * is a symlink should not be allowed to affect the directory
+ * the symlink points at, but if the same patch removes a/b,
+ * it is perfectly fine, as the patch removes a/b to make room
+ * to create a directory a/b so that a/b/c can be created.
+ *
+ * See also "struct string_list symlink_changes" in "struct
+ * apply_state".
+ */
+#define SYMLINK_GOES_AWAY 01
+#define SYMLINK_IN_RESULT 02
+struct apply_state {
+ const char *prefix;
+ int prefix_length;
+
+ /* These are lock_file related */
+ struct lock_file *lock_file;
+ int newfd;
+
+ /* These control what gets looked at and modified */
+ int apply; /* this is not a dry-run */
+ int cached; /* apply to the index only */
+ int check; /* preimage must match working tree, don't actually apply */
+ int check_index; /* preimage must match the indexed version */
+ int update_index; /* check_index && apply */
+
+ /* These control cosmetic aspect of the output */
+ int diffstat; /* just show a diffstat, and don't actually apply */
+ int numstat; /* just show a numeric diffstat, and don't actually apply */
+ int summary; /* just report creation, deletion, etc, and don't actually apply */
+
+ /* These boolean parameters control how the apply is done */
+ int allow_overlap;
+ int apply_in_reverse;
+ int apply_with_reject;
+ int apply_verbosely;
+ int no_add;
+ int threeway;
+ int unidiff_zero;
+ int unsafe_paths;
+
+ /* Other non boolean parameters */
+ const char *fake_ancestor;
+ const char *patch_input_file;
+ int line_termination;
+ struct strbuf root;
+ int p_value;
+ int p_value_known;
+ unsigned int p_context;
+
+ /* Exclude and include path parameters */
+ struct string_list limit_by_name;
+ int has_include;
+
+ /* Various "current state" */
+ int linenr; /* current line number */
+ struct string_list symlink_changes; /* we have to track symlinks */
-static const char *patch_input_file;
-static struct strbuf root = STRBUF_INIT;
-static int read_stdin = 1;
-static int options;
+ /*
+ * For "diff-stat" like behaviour, we keep track of the biggest change
+ * we've seen, and the longest filename. That allows us to do simple
+ * scaling.
+ */
+ int max_change;
+ int max_len;
-static void parse_whitespace_option(const char *option)
+ /*
+ * Records filenames that have been touched, in order to handle
+ * the case where more than one patches touch the same file.
+ */
+ struct string_list fn_table;
+
+ /* These control whitespace errors */
+ enum ws_error_action ws_error_action;
+ enum ws_ignore ws_ignore_action;
+ const char *whitespace_option;
+ int whitespace_error;
+ int squelch_whitespace_errors;
+ int applied_after_fixing_ws;
+};
+
+static const char * const apply_usage[] = {
+ N_("git apply [<options>] [<patch>...]"),
+ NULL
+};
+
+static void parse_whitespace_option(struct apply_state *state, const char *option)
{
if (!option) {
- ws_error_action = warn_on_ws_error;
+ state->ws_error_action = warn_on_ws_error;
return;
}
if (!strcmp(option, "warn")) {
- ws_error_action = warn_on_ws_error;
+ state->ws_error_action = warn_on_ws_error;
return;
}
if (!strcmp(option, "nowarn")) {
- ws_error_action = nowarn_ws_error;
+ state->ws_error_action = nowarn_ws_error;
return;
}
if (!strcmp(option, "error")) {
- ws_error_action = die_on_ws_error;
+ state->ws_error_action = die_on_ws_error;
return;
}
if (!strcmp(option, "error-all")) {
- ws_error_action = die_on_ws_error;
- squelch_whitespace_errors = 0;
+ state->ws_error_action = die_on_ws_error;
+ state->squelch_whitespace_errors = 0;
return;
}
if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
- ws_error_action = correct_ws_error;
+ state->ws_error_action = correct_ws_error;
return;
}
die(_("unrecognized whitespace option '%s'"), option);
}
-static void parse_ignorewhitespace_option(const char *option)
+static void parse_ignorewhitespace_option(struct apply_state *state,
+ const char *option)
{
if (!option || !strcmp(option, "no") ||
!strcmp(option, "false") || !strcmp(option, "never") ||
!strcmp(option, "none")) {
- ws_ignore_action = ignore_ws_none;
+ state->ws_ignore_action = ignore_ws_none;
return;
}
if (!strcmp(option, "change")) {
- ws_ignore_action = ignore_ws_change;
+ state->ws_ignore_action = ignore_ws_change;
return;
}
die(_("unrecognized whitespace ignore option '%s'"), option);
}
-static void set_default_whitespace_mode(const char *whitespace_option)
+static void set_default_whitespace_mode(struct apply_state *state)
{
- if (!whitespace_option && !apply_default_whitespace)
- ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
+ if (!state->whitespace_option && !apply_default_whitespace)
+ state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error);
}
/*
- * For "diff-stat" like behaviour, we keep track of the biggest change
- * we've seen, and the longest filename. That allows us to do simple
- * scaling.
- */
-static int max_change, max_len;
-
-/*
- * Various "current state", notably line numbers and what
- * file (and how) we're patching right now.. The "is_xxxx"
- * things are flags, where -1 means "don't know yet".
- */
-static int linenr = 1;
-
-/*
* This represents one "hunk" from a patch, starting with
* "@@ -oldpos,oldlines +newpos,newlines @@" marker. The
* patch text is pointed at by patch, and its byte length
@@ -265,13 +294,6 @@ struct image {
struct line *line;
};
-/*
- * Records filenames that have been touched, in order to handle
- * the case where more than one patches touch the same file.
- */
-
-static struct string_list fn_table;
-
static uint32_t hash_line(const char *cp, size_t len)
{
size_t i;
@@ -469,7 +491,10 @@ static char *squash_slash(char *name)
return name;
}
-static char *find_name_gnu(const char *line, const char *def, int p_value)
+static char *find_name_gnu(struct apply_state *state,
+ const char *line,
+ const char *def,
+ int p_value)
{
struct strbuf name = STRBUF_INIT;
char *cp;
@@ -493,8 +518,8 @@ static char *find_name_gnu(const char *line, const char *def, int p_value)
}
strbuf_remove(&name, 0, cp - name.buf);
- if (root.len)
- strbuf_insert(&name, 0, root.buf, root.len);
+ if (state->root.len)
+ strbuf_insert(&name, 0, state->root.buf, state->root.len);
return squash_slash(strbuf_detach(&name, NULL));
}
@@ -657,8 +682,12 @@ static size_t diff_timestamp_len(const char *line, size_t len)
return line + len - end;
}
-static char *find_name_common(const char *line, const char *def,
- int p_value, const char *end, int terminate)
+static char *find_name_common(struct apply_state *state,
+ const char *line,
+ const char *def,
+ int p_value,
+ const char *end,
+ int terminate)
{
int len;
const char *start = NULL;
@@ -696,32 +725,39 @@ static char *find_name_common(const char *line, const char *def,
return squash_slash(xstrdup(def));
}
- if (root.len) {
- char *ret = xstrfmt("%s%.*s", root.buf, len, start);
+ if (state->root.len) {
+ char *ret = xstrfmt("%s%.*s", state->root.buf, len, start);
return squash_slash(ret);
}
return squash_slash(xmemdupz(start, len));
}
-static char *find_name(const char *line, char *def, int p_value, int terminate)
+static char *find_name(struct apply_state *state,
+ const char *line,
+ char *def,
+ int p_value,
+ int terminate)
{
if (*line == '"') {
- char *name = find_name_gnu(line, def, p_value);
+ char *name = find_name_gnu(state, line, def, p_value);
if (name)
return name;
}
- return find_name_common(line, def, p_value, NULL, terminate);
+ return find_name_common(state, line, def, p_value, NULL, terminate);
}
-static char *find_name_traditional(const char *line, char *def, int p_value)
+static char *find_name_traditional(struct apply_state *state,
+ const char *line,
+ char *def,
+ int p_value)
{
size_t len;
size_t date_len;
if (*line == '"') {
- char *name = find_name_gnu(line, def, p_value);
+ char *name = find_name_gnu(state, line, def, p_value);
if (name)
return name;
}
@@ -729,10 +765,10 @@ static char *find_name_traditional(const char *line, char *def, int p_value)
len = strchrnul(line, '\n') - line;
date_len = diff_timestamp_len(line, len);
if (!date_len)
- return find_name_common(line, def, p_value, NULL, TERM_TAB);
+ return find_name_common(state, line, def, p_value, NULL, TERM_TAB);
len -= date_len;
- return find_name_common(line, def, p_value, line + len, 0);
+ return find_name_common(state, line, def, p_value, line + len, 0);
}
static int count_slashes(const char *cp)
@@ -750,30 +786,30 @@ static int count_slashes(const char *cp)
* Given the string after "--- " or "+++ ", guess the appropriate
* p_value for the given patch.
*/
-static int guess_p_value(const char *nameline)
+static int guess_p_value(struct apply_state *state, const char *nameline)
{
char *name, *cp;
int val = -1;
if (is_dev_null(nameline))
return -1;
- name = find_name_traditional(nameline, NULL, 0);
+ name = find_name_traditional(state, nameline, NULL, 0);
if (!name)
return -1;
cp = strchr(name, '/');
if (!cp)
val = 0;
- else if (prefix) {
+ else if (state->prefix) {
/*
* Does it begin with "a/$our-prefix" and such? Then this is
* very likely to apply to our directory.
*/
- if (!strncmp(name, prefix, prefix_length))
- val = count_slashes(prefix);
+ if (!strncmp(name, state->prefix, state->prefix_length))
+ val = count_slashes(state->prefix);
else {
cp++;
- if (!strncmp(cp, prefix, prefix_length))
- val = count_slashes(prefix) + 1;
+ if (!strncmp(cp, state->prefix, state->prefix_length))
+ val = count_slashes(state->prefix) + 1;
}
}
free(name);
@@ -860,36 +896,39 @@ static int has_epoch_timestamp(const char *nameline)
* files, we can happily check the index for a match, but for creating a
* new file we should try to match whatever "patch" does. I have no idea.
*/
-static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
+static void parse_traditional_patch(struct apply_state *state,
+ const char *first,
+ const char *second,
+ struct patch *patch)
{
char *name;
first += 4; /* skip "--- " */
second += 4; /* skip "+++ " */
- if (!p_value_known) {
+ if (!state->p_value_known) {
int p, q;
- p = guess_p_value(first);
- q = guess_p_value(second);
+ p = guess_p_value(state, first);
+ q = guess_p_value(state, second);
if (p < 0) p = q;
if (0 <= p && p == q) {
- p_value = p;
- p_value_known = 1;
+ state->p_value = p;
+ state->p_value_known = 1;
}
}
if (is_dev_null(first)) {
patch->is_new = 1;
patch->is_delete = 0;
- name = find_name_traditional(second, NULL, p_value);
+ name = find_name_traditional(state, second, NULL, state->p_value);
patch->new_name = name;
} else if (is_dev_null(second)) {
patch->is_new = 0;
patch->is_delete = 1;
- name = find_name_traditional(first, NULL, p_value);
+ name = find_name_traditional(state, first, NULL, state->p_value);
patch->old_name = name;
} else {
char *first_name;
- first_name = find_name_traditional(first, NULL, p_value);
- name = find_name_traditional(second, first_name, p_value);
+ first_name = find_name_traditional(state, first, NULL, state->p_value);
+ name = find_name_traditional(state, second, first_name, state->p_value);
free(first_name);
if (has_epoch_timestamp(first)) {
patch->is_new = 1;
@@ -905,10 +944,12 @@ static void parse_traditional_patch(const char *first, const char *second, struc
}
}
if (!name)
- die(_("unable to find filename in patch at line %d"), linenr);
+ die(_("unable to find filename in patch at line %d"), state->linenr);
}
-static int gitdiff_hdrend(const char *line, struct patch *patch)
+static int gitdiff_hdrend(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
return -1;
}
@@ -925,107 +966,135 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
#define DIFF_OLD_NAME 0
#define DIFF_NEW_NAME 1
-static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
+static void gitdiff_verify_name(struct apply_state *state,
+ const char *line,
+ int isnull,
+ char **name,
+ int side)
{
- if (!orig_name && !isnull)
- return find_name(line, NULL, p_value, TERM_TAB);
+ if (!*name && !isnull) {
+ *name = find_name(state, line, NULL, state->p_value, TERM_TAB);
+ return;
+ }
- if (orig_name) {
- int len = strlen(orig_name);
+ if (*name) {
+ int len = strlen(*name);
char *another;
if (isnull)
die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
- orig_name, linenr);
- another = find_name(line, NULL, p_value, TERM_TAB);
- if (!another || memcmp(another, orig_name, len + 1))
+ *name, state->linenr);
+ another = find_name(state, line, NULL, state->p_value, TERM_TAB);
+ if (!another || memcmp(another, *name, len + 1))
die((side == DIFF_NEW_NAME) ?
_("git apply: bad git-diff - inconsistent new filename on line %d") :
- _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
+ _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr);
free(another);
- return orig_name;
} else {
/* expect "/dev/null" */
if (memcmp("/dev/null", line, 9) || line[9] != '\n')
- die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
- return NULL;
+ die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
}
}
-static int gitdiff_oldname(const char *line, struct patch *patch)
+static int gitdiff_oldname(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
- patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
- DIFF_OLD_NAME);
+ gitdiff_verify_name(state, line,
+ patch->is_new, &patch->old_name,
+ DIFF_OLD_NAME);
return 0;
}
-static int gitdiff_newname(const char *line, struct patch *patch)
+static int gitdiff_newname(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
- patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
- DIFF_NEW_NAME);
+ gitdiff_verify_name(state, line,
+ patch->is_delete, &patch->new_name,
+ DIFF_NEW_NAME);
return 0;
}
-static int gitdiff_oldmode(const char *line, struct patch *patch)
+static int gitdiff_oldmode(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->old_mode = strtoul(line, NULL, 8);
return 0;
}
-static int gitdiff_newmode(const char *line, struct patch *patch)
+static int gitdiff_newmode(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->new_mode = strtoul(line, NULL, 8);
return 0;
}
-static int gitdiff_delete(const char *line, struct patch *patch)
+static int gitdiff_delete(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_delete = 1;
free(patch->old_name);
patch->old_name = xstrdup_or_null(patch->def_name);
- return gitdiff_oldmode(line, patch);
+ return gitdiff_oldmode(state, line, patch);
}
-static int gitdiff_newfile(const char *line, struct patch *patch)
+static int gitdiff_newfile(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_new = 1;
free(patch->new_name);
patch->new_name = xstrdup_or_null(patch->def_name);
- return gitdiff_newmode(line, patch);
+ return gitdiff_newmode(state, line, patch);
}
-static int gitdiff_copysrc(const char *line, struct patch *patch)
+static int gitdiff_copysrc(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_copy = 1;
free(patch->old_name);
- patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_copydst(const char *line, struct patch *patch)
+static int gitdiff_copydst(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_copy = 1;
free(patch->new_name);
- patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_renamesrc(const char *line, struct patch *patch)
+static int gitdiff_renamesrc(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_rename = 1;
free(patch->old_name);
- patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_renamedst(const char *line, struct patch *patch)
+static int gitdiff_renamedst(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
patch->is_rename = 1;
free(patch->new_name);
- patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
+ patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0);
return 0;
}
-static int gitdiff_similarity(const char *line, struct patch *patch)
+static int gitdiff_similarity(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
unsigned long val = strtoul(line, NULL, 10);
if (val <= 100)
@@ -1033,7 +1102,9 @@ static int gitdiff_similarity(const char *line, struct patch *patch)
return 0;
}
-static int gitdiff_dissimilarity(const char *line, struct patch *patch)
+static int gitdiff_dissimilarity(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
unsigned long val = strtoul(line, NULL, 10);
if (val <= 100)
@@ -1041,7 +1112,9 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
return 0;
}
-static int gitdiff_index(const char *line, struct patch *patch)
+static int gitdiff_index(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
/*
* index line is N hexadecimal, "..", N hexadecimal,
@@ -1078,7 +1151,9 @@ static int gitdiff_index(const char *line, struct patch *patch)
* This is normal for a diff that doesn't change anything: we'll fall through
* into the next diff. Tell the parser to break out.
*/
-static int gitdiff_unrecognized(const char *line, struct patch *patch)
+static int gitdiff_unrecognized(struct apply_state *state,
+ const char *line,
+ struct patch *patch)
{
return -1;
}
@@ -1087,15 +1162,17 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
* Skip p_value leading components from "line"; as we do not accept
* absolute paths, return NULL in that case.
*/
-static const char *skip_tree_prefix(const char *line, int llen)
+static const char *skip_tree_prefix(struct apply_state *state,
+ const char *line,
+ int llen)
{
int nslash;
int i;
- if (!p_value)
+ if (!state->p_value)
return (llen && line[0] == '/') ? NULL : line;
- nslash = p_value;
+ nslash = state->p_value;
for (i = 0; i < llen; i++) {
int ch = line[i];
if (ch == '/' && --nslash <= 0)
@@ -1112,7 +1189,9 @@ static const char *skip_tree_prefix(const char *line, int llen)
* creation or deletion of an empty file. In any of these cases,
* both sides are the same name under a/ and b/ respectively.
*/
-static char *git_header_name(const char *line, int llen)
+static char *git_header_name(struct apply_state *state,
+ const char *line,
+ int llen)
{
const char *name;
const char *second = NULL;
@@ -1130,7 +1209,7 @@ static char *git_header_name(const char *line, int llen)
goto free_and_fail1;
/* strip the a/b prefix including trailing slash */
- cp = skip_tree_prefix(first.buf, first.len);
+ cp = skip_tree_prefix(state, first.buf, first.len);
if (!cp)
goto free_and_fail1;
strbuf_remove(&first, 0, cp - first.buf);
@@ -1147,7 +1226,7 @@ static char *git_header_name(const char *line, int llen)
if (*second == '"') {
if (unquote_c_style(&sp, second, NULL))
goto free_and_fail1;
- cp = skip_tree_prefix(sp.buf, sp.len);
+ cp = skip_tree_prefix(state, sp.buf, sp.len);
if (!cp)
goto free_and_fail1;
/* They must match, otherwise ignore */
@@ -1158,7 +1237,7 @@ static char *git_header_name(const char *line, int llen)
}
/* unquoted second */
- cp = skip_tree_prefix(second, line + llen - second);
+ cp = skip_tree_prefix(state, second, line + llen - second);
if (!cp)
goto free_and_fail1;
if (line + llen - cp != first.len ||
@@ -1173,7 +1252,7 @@ static char *git_header_name(const char *line, int llen)
}
/* unquoted first name */
- name = skip_tree_prefix(line, llen);
+ name = skip_tree_prefix(state, line, llen);
if (!name)
return NULL;
@@ -1189,7 +1268,7 @@ static char *git_header_name(const char *line, int llen)
if (unquote_c_style(&sp, second, NULL))
goto free_and_fail2;
- np = skip_tree_prefix(sp.buf, sp.len);
+ np = skip_tree_prefix(state, sp.buf, sp.len);
if (!np)
goto free_and_fail2;
@@ -1233,7 +1312,7 @@ static char *git_header_name(const char *line, int llen)
*/
if (!name[len + 1])
return NULL; /* no postimage name */
- second = skip_tree_prefix(name + len + 1,
+ second = skip_tree_prefix(state, name + len + 1,
line_len - (len + 1));
if (!second)
return NULL;
@@ -1249,7 +1328,11 @@ static char *git_header_name(const char *line, int llen)
}
/* Verify that we recognize the lines following a git header */
-static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
+static int parse_git_header(struct apply_state *state,
+ const char *line,
+ int len,
+ unsigned int size,
+ struct patch *patch)
{
unsigned long offset;
@@ -1263,20 +1346,20 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
* or removing or adding empty files), so we get
* the default name from the header.
*/
- patch->def_name = git_header_name(line, len);
- if (patch->def_name && root.len) {
- char *s = xstrfmt("%s%s", root.buf, patch->def_name);
+ patch->def_name = git_header_name(state, line, len);
+ if (patch->def_name && state->root.len) {
+ char *s = xstrfmt("%s%s", state->root.buf, patch->def_name);
free(patch->def_name);
patch->def_name = s;
}
line += len;
size -= len;
- linenr++;
- for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
+ state->linenr++;
+ for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {
static const struct opentry {
const char *str;
- int (*fn)(const char *, struct patch *);
+ int (*fn)(struct apply_state *, const char *, struct patch *);
} optable[] = {
{ "@@ -", gitdiff_hdrend },
{ "--- ", gitdiff_oldname },
@@ -1306,7 +1389,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
int oplen = strlen(p->str);
if (len < oplen || memcmp(p->str, line, oplen))
continue;
- if (p->fn(line + oplen, patch) < 0)
+ if (p->fn(state, line + oplen, patch) < 0)
return offset;
break;
}
@@ -1431,7 +1514,11 @@ static int parse_fragment_header(const char *line, int len, struct fragment *fra
return offset;
}
-static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
+static int find_header(struct apply_state *state,
+ const char *line,
+ unsigned long size,
+ int *hdrsize,
+ struct patch *patch)
{
unsigned long offset, len;
@@ -1440,7 +1527,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
patch->is_new = patch->is_delete = -1;
patch->old_mode = patch->new_mode = 0;
patch->old_name = patch->new_name = NULL;
- for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
+ for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) {
unsigned long nextlen;
len = linelen(line, size);
@@ -1461,7 +1548,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
if (parse_fragment_header(line, len, &dummy) < 0)
continue;
die(_("patch fragment without header at line %d: %.*s"),
- linenr, (int)len-1, line);
+ state->linenr, (int)len-1, line);
}
if (size < len + 6)
@@ -1472,7 +1559,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
* or mode change, so we handle that specially
*/
if (!memcmp("diff --git ", line, 11)) {
- int git_hdr_len = parse_git_header(line, len, size, patch);
+ int git_hdr_len = parse_git_header(state, line, len, size, patch);
if (git_hdr_len <= len)
continue;
if (!patch->old_name && !patch->new_name) {
@@ -1481,14 +1568,14 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
"%d leading pathname component (line %d)",
"git diff header lacks filename information when removing "
"%d leading pathname components (line %d)",
- p_value),
- p_value, linenr);
+ state->p_value),
+ state->p_value, state->linenr);
patch->old_name = xstrdup(patch->def_name);
patch->new_name = xstrdup(patch->def_name);
}
if (!patch->is_delete && !patch->new_name)
die("git diff header lacks filename information "
- "(line %d)", linenr);
+ "(line %d)", state->linenr);
patch->is_toplevel_relative = 1;
*hdrsize = git_hdr_len;
return offset;
@@ -1508,37 +1595,44 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc
continue;
/* Ok, we'll consider it a patch */
- parse_traditional_patch(line, line+len, patch);
+ parse_traditional_patch(state, line, line+len, patch);
*hdrsize = len + nextlen;
- linenr += 2;
+ state->linenr += 2;
return offset;
}
return -1;
}
-static void record_ws_error(unsigned result, const char *line, int len, int linenr)
+static void record_ws_error(struct apply_state *state,
+ unsigned result,
+ const char *line,
+ int len,
+ int linenr)
{
char *err;
if (!result)
return;
- whitespace_error++;
- if (squelch_whitespace_errors &&
- squelch_whitespace_errors < whitespace_error)
+ state->whitespace_error++;
+ if (state->squelch_whitespace_errors &&
+ state->squelch_whitespace_errors < state->whitespace_error)
return;
err = whitespace_error_string(result);
fprintf(stderr, "%s:%d: %s.\n%.*s\n",
- patch_input_file, linenr, err, len, line);
+ state->patch_input_file, linenr, err, len, line);
free(err);
}
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
+static void check_whitespace(struct apply_state *state,
+ const char *line,
+ int len,
+ unsigned ws_rule)
{
unsigned result = ws_check(line + 1, len - 1, ws_rule);
- record_ws_error(result, line + 1, len - 2, linenr);
+ record_ws_error(state, result, line + 1, len - 2, state->linenr);
}
/*
@@ -1547,8 +1641,11 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule)
* between a "---" that is part of a patch, and a "---" that starts
* the next patch is to look at the line counts..
*/
-static int parse_fragment(const char *line, unsigned long size,
- struct patch *patch, struct fragment *fragment)
+static int parse_fragment(struct apply_state *state,
+ const char *line,
+ unsigned long size,
+ struct patch *patch,
+ struct fragment *fragment)
{
int added, deleted;
int len = linelen(line, size), offset;
@@ -1568,11 +1665,11 @@ static int parse_fragment(const char *line, unsigned long size,
/* Parse the thing.. */
line += len;
size -= len;
- linenr++;
+ state->linenr++;
added = deleted = 0;
for (offset = len;
0 < size;
- offset += len, size -= len, line += len, linenr++) {
+ offset += len, size -= len, line += len, state->linenr++) {
if (!oldlines && !newlines)
break;
len = linelen(line, size);
@@ -1588,22 +1685,22 @@ static int parse_fragment(const char *line, unsigned long size,
if (!deleted && !added)
leading++;
trailing++;
- if (!apply_in_reverse &&
- ws_error_action == correct_ws_error)
- check_whitespace(line, len, patch->ws_rule);
+ if (!state->apply_in_reverse &&
+ state->ws_error_action == correct_ws_error)
+ check_whitespace(state, line, len, patch->ws_rule);
break;
case '-':
- if (apply_in_reverse &&
- ws_error_action != nowarn_ws_error)
- check_whitespace(line, len, patch->ws_rule);
+ if (state->apply_in_reverse &&
+ state->ws_error_action != nowarn_ws_error)
+ check_whitespace(state, line, len, patch->ws_rule);
deleted++;
oldlines--;
trailing = 0;
break;
case '+':
- if (!apply_in_reverse &&
- ws_error_action != nowarn_ws_error)
- check_whitespace(line, len, patch->ws_rule);
+ if (!state->apply_in_reverse &&
+ state->ws_error_action != nowarn_ws_error)
+ check_whitespace(state, line, len, patch->ws_rule);
added++;
newlines--;
trailing = 0;
@@ -1657,7 +1754,10 @@ static int parse_fragment(const char *line, unsigned long size,
* The (fragment->patch, fragment->size) pair points into the memory given
* by the caller, not a copy, when we return.
*/
-static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
+static int parse_single_patch(struct apply_state *state,
+ const char *line,
+ unsigned long size,
+ struct patch *patch)
{
unsigned long offset = 0;
unsigned long oldlines = 0, newlines = 0, context = 0;
@@ -1668,10 +1768,10 @@ static int parse_single_patch(const char *line, unsigned long size, struct patch
int len;
fragment = xcalloc(1, sizeof(*fragment));
- fragment->linenr = linenr;
- len = parse_fragment(line, size, patch, fragment);
+ fragment->linenr = state->linenr;
+ len = parse_fragment(state, line, size, patch, fragment);
if (len <= 0)
- die(_("corrupt patch at line %d"), linenr);
+ die(_("corrupt patch at line %d"), state->linenr);
fragment->patch = line;
fragment->size = len;
oldlines += fragment->oldlines;
@@ -1757,7 +1857,8 @@ static char *inflate_it(const void *data, unsigned long size,
* points at an allocated memory that the caller must free, so
* it is marked as "->free_patch = 1".
*/
-static struct fragment *parse_binary_hunk(char **buf_p,
+static struct fragment *parse_binary_hunk(struct apply_state *state,
+ char **buf_p,
unsigned long *sz_p,
int *status_p,
int *used_p)
@@ -1799,13 +1900,13 @@ static struct fragment *parse_binary_hunk(char **buf_p,
else
return NULL;
- linenr++;
+ state->linenr++;
buffer += llen;
while (1) {
int byte_length, max_byte_length, newsize;
llen = linelen(buffer, size);
used += llen;
- linenr++;
+ state->linenr++;
if (llen == 1) {
/* consume the blank line */
buffer++;
@@ -1859,7 +1960,7 @@ static struct fragment *parse_binary_hunk(char **buf_p,
free(data);
*status_p = -1;
error(_("corrupt binary patch at line %d: %.*s"),
- linenr-1, llen-1, buffer);
+ state->linenr-1, llen-1, buffer);
return NULL;
}
@@ -1868,7 +1969,10 @@ static struct fragment *parse_binary_hunk(char **buf_p,
* -1 in case of error,
* the length of the parsed binary patch otherwise
*/
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+static int parse_binary(struct apply_state *state,
+ char *buffer,
+ unsigned long size,
+ struct patch *patch)
{
/*
* We have read "GIT binary patch\n"; what follows is a line
@@ -1889,15 +1993,15 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
int status;
int used, used_1;
- forward = parse_binary_hunk(&buffer, &size, &status, &used);
+ forward = parse_binary_hunk(state, &buffer, &size, &status, &used);
if (!forward && !status)
/* there has to be one hunk (forward hunk) */
- return error(_("unrecognized binary patch at line %d"), linenr-1);
+ return error(_("unrecognized binary patch at line %d"), state->linenr-1);
if (status)
/* otherwise we already gave an error message */
return status;
- reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+ reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1);
if (reverse)
used += used_1;
else if (status) {
@@ -1915,53 +2019,53 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
return used;
}
-static void prefix_one(char **name)
+static void prefix_one(struct apply_state *state, char **name)
{
char *old_name = *name;
if (!old_name)
return;
- *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
+ *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name));
free(old_name);
}
-static void prefix_patch(struct patch *p)
+static void prefix_patch(struct apply_state *state, struct patch *p)
{
- if (!prefix || p->is_toplevel_relative)
+ if (!state->prefix || p->is_toplevel_relative)
return;
- prefix_one(&p->new_name);
- prefix_one(&p->old_name);
+ prefix_one(state, &p->new_name);
+ prefix_one(state, &p->old_name);
}
/*
* include/exclude
*/
-static struct string_list limit_by_name;
-static int has_include;
-static void add_name_limit(const char *name, int exclude)
+static void add_name_limit(struct apply_state *state,
+ const char *name,
+ int exclude)
{
struct string_list_item *it;
- it = string_list_append(&limit_by_name, name);
+ it = string_list_append(&state->limit_by_name, name);
it->util = exclude ? NULL : (void *) 1;
}
-static int use_patch(struct patch *p)
+static int use_patch(struct apply_state *state, struct patch *p)
{
const char *pathname = p->new_name ? p->new_name : p->old_name;
int i;
/* Paths outside are not touched regardless of "--include" */
- if (0 < prefix_length) {
+ if (0 < state->prefix_length) {
int pathlen = strlen(pathname);
- if (pathlen <= prefix_length ||
- memcmp(prefix, pathname, prefix_length))
+ if (pathlen <= state->prefix_length ||
+ memcmp(state->prefix, pathname, state->prefix_length))
return 0;
}
/* See if it matches any of exclude/include rule */
- for (i = 0; i < limit_by_name.nr; i++) {
- struct string_list_item *it = &limit_by_name.items[i];
+ for (i = 0; i < state->limit_by_name.nr; i++) {
+ struct string_list_item *it = &state->limit_by_name.items[i];
if (!wildmatch(it->string, pathname, 0, NULL))
return (it->util != NULL);
}
@@ -1971,7 +2075,7 @@ static int use_patch(struct patch *p)
* not used. Otherwise, we saw bunch of exclude rules (or none)
* and such a path is used.
*/
- return !has_include;
+ return !state->has_include;
}
@@ -1982,25 +2086,27 @@ static int use_patch(struct patch *p)
* Return the number of bytes consumed, so that the caller can call us
* again for the next patch.
*/
-static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
+static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch)
{
int hdrsize, patchsize;
- int offset = find_header(buffer, size, &hdrsize, patch);
+ int offset = find_header(state, buffer, size, &hdrsize, patch);
if (offset < 0)
return offset;
- prefix_patch(patch);
+ prefix_patch(state, patch);
- if (!use_patch(patch))
+ if (!use_patch(state, patch))
patch->ws_rule = 0;
else
patch->ws_rule = whitespace_rule(patch->new_name
? patch->new_name
: patch->old_name);
- patchsize = parse_single_patch(buffer + offset + hdrsize,
- size - offset - hdrsize, patch);
+ patchsize = parse_single_patch(state,
+ buffer + offset + hdrsize,
+ size - offset - hdrsize,
+ patch);
if (!patchsize) {
static const char git_binary[] = "GIT binary patch\n";
@@ -2010,8 +2116,8 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
if (llen == sizeof(git_binary) - 1 &&
!memcmp(git_binary, buffer + hd, llen)) {
int used;
- linenr++;
- used = parse_binary(buffer + hd + llen,
+ state->linenr++;
+ used = parse_binary(state, buffer + hd + llen,
size - hd - llen, patch);
if (used < 0)
return -1;
@@ -2031,7 +2137,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
int len = strlen(binhdr[i]);
if (len < size - hd &&
!memcmp(binhdr[i], buffer + hd, len)) {
- linenr++;
+ state->linenr++;
patch->is_binary = 1;
patchsize = llen;
break;
@@ -2043,9 +2149,9 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
* without metadata change. A binary patch appears
* empty to us here.
*/
- if ((apply || check) &&
+ if ((state->apply || state->check) &&
(!patch->is_binary && !metadata_changes(patch)))
- die(_("patch with only garbage at line %d"), linenr);
+ die(_("patch with only garbage at line %d"), state->linenr);
}
return offset + hdrsize + patchsize;
@@ -2083,7 +2189,7 @@ static const char pluses[] =
static const char minuses[]=
"----------------------------------------------------------------------";
-static void show_stats(struct patch *patch)
+static void show_stats(struct apply_state *state, struct patch *patch)
{
struct strbuf qname = STRBUF_INIT;
char *cp = patch->new_name ? patch->new_name : patch->old_name;
@@ -2094,7 +2200,7 @@ static void show_stats(struct patch *patch)
/*
* "scale" the filename
*/
- max = max_len;
+ max = state->max_len;
if (max > 50)
max = 50;
@@ -2117,13 +2223,13 @@ static void show_stats(struct patch *patch)
/*
* scale the add/delete
*/
- max = max + max_change > 70 ? 70 - max : max_change;
+ max = max + state->max_change > 70 ? 70 - max : state->max_change;
add = patch->lines_added;
del = patch->lines_deleted;
- if (max_change > 0) {
- int total = ((add + del) * max + max_change / 2) / max_change;
- add = (add * max + max_change / 2) / max_change;
+ if (state->max_change > 0) {
+ int total = ((add + del) * max + state->max_change / 2) / state->max_change;
+ add = (add * max + state->max_change / 2) / state->max_change;
del = total - add;
}
printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
@@ -2194,17 +2300,17 @@ static void update_pre_post_images(struct image *preimage,
fixed = preimage->buf;
for (i = reduced = ctx = 0; i < postimage->nr; i++) {
- size_t len = postimage->line[i].len;
+ size_t l_len = postimage->line[i].len;
if (!(postimage->line[i].flag & LINE_COMMON)) {
/* an added line -- no counterparts in preimage */
- memmove(new, old, len);
- old += len;
- new += len;
+ memmove(new, old, l_len);
+ old += l_len;
+ new += l_len;
continue;
}
/* a common context -- skip it in the original postimage */
- old += len;
+ old += l_len;
/* and find the corresponding one in the fixed preimage */
while (ctx < preimage->nr &&
@@ -2223,11 +2329,11 @@ static void update_pre_post_images(struct image *preimage,
}
/* and copy it in, while fixing the line length */
- len = preimage->line[ctx].len;
- memcpy(new, fixed, len);
- new += len;
- fixed += len;
- postimage->line[i].len = len;
+ l_len = preimage->line[ctx].len;
+ memcpy(new, fixed, l_len);
+ new += l_len;
+ fixed += l_len;
+ postimage->line[i].len = l_len;
ctx++;
}
@@ -2242,7 +2348,76 @@ static void update_pre_post_images(struct image *preimage,
postimage->nr -= reduced;
}
-static int match_fragment(struct image *img,
+static int line_by_line_fuzzy_match(struct image *img,
+ struct image *preimage,
+ struct image *postimage,
+ unsigned long try,
+ int try_lno,
+ int preimage_limit)
+{
+ int i;
+ size_t imgoff = 0;
+ size_t preoff = 0;
+ size_t postlen = postimage->len;
+ size_t extra_chars;
+ char *buf;
+ char *preimage_eof;
+ char *preimage_end;
+ struct strbuf fixed;
+ char *fixed_buf;
+ size_t fixed_len;
+
+ for (i = 0; i < preimage_limit; i++) {
+ size_t prelen = preimage->line[i].len;
+ size_t imglen = img->line[try_lno+i].len;
+
+ if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+ preimage->buf + preoff, prelen))
+ return 0;
+ if (preimage->line[i].flag & LINE_COMMON)
+ postlen += imglen - prelen;
+ imgoff += imglen;
+ preoff += prelen;
+ }
+
+ /*
+ * Ok, the preimage matches with whitespace fuzz.
+ *
+ * imgoff now holds the true length of the target that
+ * matches the preimage before the end of the file.
+ *
+ * Count the number of characters in the preimage that fall
+ * beyond the end of the file and make sure that all of them
+ * are whitespace characters. (This can only happen if
+ * we are removing blank lines at the end of the file.)
+ */
+ buf = preimage_eof = preimage->buf + preoff;
+ for ( ; i < preimage->nr; i++)
+ preoff += preimage->line[i].len;
+ preimage_end = preimage->buf + preoff;
+ for ( ; buf < preimage_end; buf++)
+ if (!isspace(*buf))
+ return 0;
+
+ /*
+ * Update the preimage and the common postimage context
+ * lines to use the same whitespace as the target.
+ * If whitespace is missing in the target (i.e.
+ * if the preimage extends beyond the end of the file),
+ * use the whitespace from the preimage.
+ */
+ extra_chars = preimage_end - preimage_eof;
+ strbuf_init(&fixed, imgoff + extra_chars);
+ strbuf_add(&fixed, img->buf + try, imgoff);
+ strbuf_add(&fixed, preimage_eof, extra_chars);
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
+ update_pre_post_images(preimage, postimage,
+ fixed_buf, fixed_len, postlen);
+ return 1;
+}
+
+static int match_fragment(struct apply_state *state,
+ struct image *img,
struct image *preimage,
struct image *postimage,
unsigned long try,
@@ -2263,7 +2438,7 @@ static int match_fragment(struct image *img,
preimage_limit = preimage->nr;
if (match_end && (preimage->nr + try_lno != img->nr))
return 0;
- } else if (ws_error_action == correct_ws_error &&
+ } else if (state->ws_error_action == correct_ws_error &&
(ws_rule & WS_BLANK_AT_EOF)) {
/*
* This hunk extends beyond the end of img, and we are
@@ -2331,63 +2506,11 @@ static int match_fragment(struct image *img,
* fuzzy matching. We collect all the line length information because
* we need it to adjust whitespace if we match.
*/
- if (ws_ignore_action == ignore_ws_change) {
- size_t imgoff = 0;
- size_t preoff = 0;
- size_t postlen = postimage->len;
- size_t extra_chars;
- char *preimage_eof;
- char *preimage_end;
- for (i = 0; i < preimage_limit; i++) {
- size_t prelen = preimage->line[i].len;
- size_t imglen = img->line[try_lno+i].len;
-
- if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
- preimage->buf + preoff, prelen))
- return 0;
- if (preimage->line[i].flag & LINE_COMMON)
- postlen += imglen - prelen;
- imgoff += imglen;
- preoff += prelen;
- }
+ if (state->ws_ignore_action == ignore_ws_change)
+ return line_by_line_fuzzy_match(img, preimage, postimage,
+ try, try_lno, preimage_limit);
- /*
- * Ok, the preimage matches with whitespace fuzz.
- *
- * imgoff now holds the true length of the target that
- * matches the preimage before the end of the file.
- *
- * Count the number of characters in the preimage that fall
- * beyond the end of the file and make sure that all of them
- * are whitespace characters. (This can only happen if
- * we are removing blank lines at the end of the file.)
- */
- buf = preimage_eof = preimage->buf + preoff;
- for ( ; i < preimage->nr; i++)
- preoff += preimage->line[i].len;
- preimage_end = preimage->buf + preoff;
- for ( ; buf < preimage_end; buf++)
- if (!isspace(*buf))
- return 0;
-
- /*
- * Update the preimage and the common postimage context
- * lines to use the same whitespace as the target.
- * If whitespace is missing in the target (i.e.
- * if the preimage extends beyond the end of the file),
- * use the whitespace from the preimage.
- */
- extra_chars = preimage_end - preimage_eof;
- strbuf_init(&fixed, imgoff + extra_chars);
- strbuf_add(&fixed, img->buf + try, imgoff);
- strbuf_add(&fixed, preimage_eof, extra_chars);
- fixed_buf = strbuf_detach(&fixed, &fixed_len);
- update_pre_post_images(preimage, postimage,
- fixed_buf, fixed_len, postlen);
- return 1;
- }
-
- if (ws_error_action != correct_ws_error)
+ if (state->ws_error_action != correct_ws_error)
return 0;
/*
@@ -2499,7 +2622,8 @@ static int match_fragment(struct image *img,
return 0;
}
-static int find_pos(struct image *img,
+static int find_pos(struct apply_state *state,
+ struct image *img,
struct image *preimage,
struct image *postimage,
int line,
@@ -2543,7 +2667,7 @@ static int find_pos(struct image *img,
try_lno = line;
for (i = 0; ; i++) {
- if (match_fragment(img, preimage, postimage,
+ if (match_fragment(state, img, preimage, postimage,
try, try_lno, ws_rule,
match_beginning, match_end))
return try_lno;
@@ -2594,7 +2718,8 @@ static void remove_last_line(struct image *img)
* apply at applied_pos (counts in line numbers) in "img".
* Update "img" to remove "preimage" and replace it with "postimage".
*/
-static void update_image(struct image *img,
+static void update_image(struct apply_state *state,
+ struct image *img,
int applied_pos,
struct image *preimage,
struct image *postimage)
@@ -2659,7 +2784,7 @@ static void update_image(struct image *img,
memcpy(img->line + applied_pos,
postimage->line,
postimage->nr * sizeof(*img->line));
- if (!allow_overlap)
+ if (!state->allow_overlap)
for (i = 0; i < postimage->nr; i++)
img->line[applied_pos + i].flag |= LINE_PATCHED;
img->nr = nr;
@@ -2670,7 +2795,8 @@ static void update_image(struct image *img,
* postimage) for the hunk. Find lines that match "preimage" in "img" and
* replace the part of "img" with "postimage" text.
*/
-static int apply_one_fragment(struct image *img, struct fragment *frag,
+static int apply_one_fragment(struct apply_state *state,
+ struct image *img, struct fragment *frag,
int inaccurate_eof, unsigned ws_rule,
int nth_fragment)
{
@@ -2715,7 +2841,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
if (len < size && patch[len] == '\\')
plen--;
first = *patch;
- if (apply_in_reverse) {
+ if (state->apply_in_reverse) {
if (first == '-')
first = '+';
else if (first == '+')
@@ -2748,17 +2874,17 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
/* Fall-through for ' ' */
case '+':
/* --no-add does not add new lines */
- if (first == '+' && no_add)
+ if (first == '+' && state->no_add)
break;
start = newlines.len;
if (first != '+' ||
- !whitespace_error ||
- ws_error_action != correct_ws_error) {
+ !state->whitespace_error ||
+ state->ws_error_action != correct_ws_error) {
strbuf_add(&newlines, patch + 1, plen);
}
else {
- ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+ ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws);
}
add_line_info(&postimage, newlines.buf + start, newlines.len - start,
(first == '+' ? 0 : LINE_COMMON));
@@ -2771,7 +2897,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
/* Ignore it, we already handled it */
break;
default:
- if (apply_verbosely)
+ if (state->apply_verbosely)
error(_("invalid start of line: '%c'"), first);
applied_pos = -1;
goto out;
@@ -2812,7 +2938,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
* without leading context must match at the beginning.
*/
match_beginning = (!frag->oldpos ||
- (frag->oldpos == 1 && !unidiff_zero));
+ (frag->oldpos == 1 && !state->unidiff_zero));
/*
* A hunk without trailing lines must match at the end.
@@ -2820,7 +2946,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
* from the lack of trailing lines if the patch was generated
* with unidiff without any context.
*/
- match_end = !unidiff_zero && !trailing;
+ match_end = !state->unidiff_zero && !trailing;
pos = frag->newpos ? (frag->newpos - 1) : 0;
preimage.buf = oldlines;
@@ -2832,14 +2958,14 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
for (;;) {
- applied_pos = find_pos(img, &preimage, &postimage, pos,
+ applied_pos = find_pos(state, img, &preimage, &postimage, pos,
ws_rule, match_beginning, match_end);
if (applied_pos >= 0)
break;
/* Am I at my context limits? */
- if ((leading <= p_context) && (trailing <= p_context))
+ if ((leading <= state->p_context) && (trailing <= state->p_context))
break;
if (match_beginning || match_end) {
match_beginning = match_end = 0;
@@ -2868,10 +2994,10 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
if (new_blank_lines_at_end &&
preimage.nr + applied_pos >= img->nr &&
(ws_rule & WS_BLANK_AT_EOF) &&
- ws_error_action != nowarn_ws_error) {
- record_ws_error(WS_BLANK_AT_EOF, "+", 1,
+ state->ws_error_action != nowarn_ws_error) {
+ record_ws_error(state, WS_BLANK_AT_EOF, "+", 1,
found_new_blank_lines_at_end);
- if (ws_error_action == correct_ws_error) {
+ if (state->ws_error_action == correct_ws_error) {
while (new_blank_lines_at_end--)
remove_last_line(&postimage);
}
@@ -2882,13 +3008,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
* apply_patch->check_patch_list->check_patch->
* apply_data->apply_fragments->apply_one_fragment
*/
- if (ws_error_action == die_on_ws_error)
- apply = 0;
+ if (state->ws_error_action == die_on_ws_error)
+ state->apply = 0;
}
- if (apply_verbosely && applied_pos != pos) {
+ if (state->apply_verbosely && applied_pos != pos) {
int offset = applied_pos - pos;
- if (apply_in_reverse)
+ if (state->apply_in_reverse)
offset = 0 - offset;
fprintf_ln(stderr,
Q_("Hunk #%d succeeded at %d (offset %d line).",
@@ -2906,9 +3032,9 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
" to apply fragment at %d"),
leading, trailing, applied_pos+1);
- update_image(img, applied_pos, &preimage, &postimage);
+ update_image(state, img, applied_pos, &preimage, &postimage);
} else {
- if (apply_verbosely)
+ if (state->apply_verbosely)
error(_("while searching for:\n%.*s"),
(int)(old - oldlines), oldlines);
}
@@ -2922,7 +3048,9 @@ out:
return (applied_pos < 0);
}
-static int apply_binary_fragment(struct image *img, struct patch *patch)
+static int apply_binary_fragment(struct apply_state *state,
+ struct image *img,
+ struct patch *patch)
{
struct fragment *fragment = patch->fragments;
unsigned long len;
@@ -2935,7 +3063,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
patch->old_name);
/* Binary patch is irreversible without the optional second hunk */
- if (apply_in_reverse) {
+ if (state->apply_in_reverse) {
if (!fragment->next)
return error("cannot reverse-apply a binary patch "
"without the reverse hunk to '%s'",
@@ -2968,7 +3096,9 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
* but the preimage prepared by the caller in "img" is freed here
* or in the helper function apply_binary_fragment() this calls.
*/
-static int apply_binary(struct image *img, struct patch *patch)
+static int apply_binary(struct apply_state *state,
+ struct image *img,
+ struct patch *patch)
{
const char *name = patch->old_name ? patch->old_name : patch->new_name;
unsigned char sha1[20];
@@ -3029,7 +3159,7 @@ static int apply_binary(struct image *img, struct patch *patch)
* apply the patch data to it, which is stored
* in the patch->fragments->{patch,size}.
*/
- if (apply_binary_fragment(img, patch))
+ if (apply_binary_fragment(state, img, patch))
return error(_("binary patch does not apply to '%s'"),
name);
@@ -3043,7 +3173,7 @@ static int apply_binary(struct image *img, struct patch *patch)
return 0;
}
-static int apply_fragments(struct image *img, struct patch *patch)
+static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch)
{
struct fragment *frag = patch->fragments;
const char *name = patch->old_name ? patch->old_name : patch->new_name;
@@ -3052,13 +3182,13 @@ static int apply_fragments(struct image *img, struct patch *patch)
int nth = 0;
if (patch->is_binary)
- return apply_binary(img, patch);
+ return apply_binary(state, img, patch);
while (frag) {
nth++;
- if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
+ if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) {
error(_("patch failed: %s:%ld"), name, frag->oldpos);
- if (!apply_with_reject)
+ if (!state->apply_with_reject)
return -1;
frag->rejected = 1;
}
@@ -3093,14 +3223,14 @@ static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf
return read_blob_object(buf, ce->sha1, ce->ce_mode);
}
-static struct patch *in_fn_table(const char *name)
+static struct patch *in_fn_table(struct apply_state *state, const char *name)
{
struct string_list_item *item;
if (name == NULL)
return NULL;
- item = string_list_lookup(&fn_table, name);
+ item = string_list_lookup(&state->fn_table, name);
if (item != NULL)
return (struct patch *)item->util;
@@ -3132,7 +3262,7 @@ static int was_deleted(struct patch *patch)
return patch == PATH_WAS_DELETED;
}
-static void add_to_fn_table(struct patch *patch)
+static void add_to_fn_table(struct apply_state *state, struct patch *patch)
{
struct string_list_item *item;
@@ -3142,7 +3272,7 @@ static void add_to_fn_table(struct patch *patch)
* file creations and copies
*/
if (patch->new_name != NULL) {
- item = string_list_insert(&fn_table, patch->new_name);
+ item = string_list_insert(&state->fn_table, patch->new_name);
item->util = patch;
}
@@ -3151,12 +3281,12 @@ static void add_to_fn_table(struct patch *patch)
* later chunks shouldn't patch old names
*/
if ((patch->new_name == NULL) || (patch->is_rename)) {
- item = string_list_insert(&fn_table, patch->old_name);
+ item = string_list_insert(&state->fn_table, patch->old_name);
item->util = PATH_WAS_DELETED;
}
}
-static void prepare_fn_table(struct patch *patch)
+static void prepare_fn_table(struct apply_state *state, struct patch *patch)
{
/*
* store information about incoming file deletion
@@ -3164,7 +3294,7 @@ static void prepare_fn_table(struct patch *patch)
while (patch) {
if ((patch->new_name == NULL) || (patch->is_rename)) {
struct string_list_item *item;
- item = string_list_insert(&fn_table, patch->old_name);
+ item = string_list_insert(&state->fn_table, patch->old_name);
item->util = PATH_TO_BE_DELETED;
}
patch = patch->next;
@@ -3185,7 +3315,9 @@ static int checkout_target(struct index_state *istate,
return 0;
}
-static struct patch *previous_patch(struct patch *patch, int *gone)
+static struct patch *previous_patch(struct apply_state *state,
+ struct patch *patch,
+ int *gone)
{
struct patch *previous;
@@ -3193,7 +3325,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone)
if (patch->is_copy || patch->is_rename)
return NULL; /* "git" patches do not depend on the order */
- previous = in_fn_table(patch->old_name);
+ previous = in_fn_table(state, patch->old_name);
if (!previous)
return NULL;
@@ -3218,15 +3350,16 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st)
#define SUBMODULE_PATCH_WITHOUT_INDEX 1
-static int load_patch_target(struct strbuf *buf,
+static int load_patch_target(struct apply_state *state,
+ struct strbuf *buf,
const struct cache_entry *ce,
struct stat *st,
const char *name,
unsigned expected_mode)
{
- if (cached || check_index) {
+ if (state->cached || state->check_index) {
if (read_file_or_gitlink(ce, buf))
- return error(_("read of %s failed"), name);
+ return error(_("failed to read %s"), name);
} else if (name) {
if (S_ISGITLINK(expected_mode)) {
if (ce)
@@ -3237,7 +3370,7 @@ static int load_patch_target(struct strbuf *buf,
return error(_("reading from '%s' beyond a symbolic link"), name);
} else {
if (read_old_data(st, name, buf))
- return error(_("read of %s failed"), name);
+ return error(_("failed to read %s"), name);
}
}
return 0;
@@ -3250,7 +3383,8 @@ static int load_patch_target(struct strbuf *buf,
* applying a non-git patch that incrementally updates the tree,
* we read from the result of a previous diff.
*/
-static int load_preimage(struct image *image,
+static int load_preimage(struct apply_state *state,
+ struct image *image,
struct patch *patch, struct stat *st,
const struct cache_entry *ce)
{
@@ -3260,7 +3394,7 @@ static int load_preimage(struct image *image,
struct patch *previous;
int status;
- previous = previous_patch(patch, &status);
+ previous = previous_patch(state, patch, &status);
if (status)
return error(_("path %s has been renamed/deleted"),
patch->old_name);
@@ -3268,7 +3402,7 @@ static int load_preimage(struct image *image,
/* We have a patched copy in memory; use that. */
strbuf_add(&buf, previous->result, previous->resultsize);
} else {
- status = load_patch_target(&buf, ce, st,
+ status = load_patch_target(state, &buf, ce, st,
patch->old_name, patch->old_mode);
if (status < 0)
return status;
@@ -3282,7 +3416,7 @@ static int load_preimage(struct image *image,
free_fragment_list(patch->fragments);
patch->fragments = NULL;
} else if (status) {
- return error(_("read of %s failed"), patch->old_name);
+ return error(_("failed to read %s"), patch->old_name);
}
}
@@ -3327,7 +3461,9 @@ static int three_way_merge(struct image *image,
* the current contents of the new_name. In no cases other than that
* this function will be called.
*/
-static int load_current(struct image *image, struct patch *patch)
+static int load_current(struct apply_state *state,
+ struct image *image,
+ struct patch *patch)
{
struct strbuf buf = STRBUF_INIT;
int status, pos;
@@ -3354,7 +3490,7 @@ static int load_current(struct image *image, struct patch *patch)
if (verify_index_match(ce, &st))
return error(_("%s: does not match index"), name);
- status = load_patch_target(&buf, ce, &st, name, mode);
+ status = load_patch_target(state, &buf, ce, &st, name, mode);
if (status < 0)
return status;
else if (status)
@@ -3364,8 +3500,11 @@ static int load_current(struct image *image, struct patch *patch)
return 0;
}
-static int try_threeway(struct image *image, struct patch *patch,
- struct stat *st, const struct cache_entry *ce)
+static int try_threeway(struct apply_state *state,
+ struct image *image,
+ struct patch *patch,
+ struct stat *st,
+ const struct cache_entry *ce)
{
unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
struct strbuf buf = STRBUF_INIT;
@@ -3391,7 +3530,7 @@ static int try_threeway(struct image *image, struct patch *patch,
img = strbuf_detach(&buf, &len);
prepare_image(&tmp_image, img, len, 1);
/* Apply the patch to get the post image */
- if (apply_fragments(&tmp_image, patch) < 0) {
+ if (apply_fragments(state, &tmp_image, patch) < 0) {
clear_image(&tmp_image);
return -1;
}
@@ -3401,11 +3540,11 @@ static int try_threeway(struct image *image, struct patch *patch,
/* our_sha1[] is ours */
if (patch->is_new) {
- if (load_current(&tmp_image, patch))
+ if (load_current(state, &tmp_image, patch))
return error("cannot read the current contents of '%s'",
patch->new_name);
} else {
- if (load_preimage(&tmp_image, patch, st, ce))
+ if (load_preimage(state, &tmp_image, patch, st, ce))
return error("cannot read the current contents of '%s'",
patch->old_name);
}
@@ -3435,22 +3574,23 @@ static int try_threeway(struct image *image, struct patch *patch,
return 0;
}
-static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
+static int apply_data(struct apply_state *state, struct patch *patch,
+ struct stat *st, const struct cache_entry *ce)
{
struct image image;
- if (load_preimage(&image, patch, st, ce) < 0)
+ if (load_preimage(state, &image, patch, st, ce) < 0)
return -1;
if (patch->direct_to_threeway ||
- apply_fragments(&image, patch) < 0) {
+ apply_fragments(state, &image, patch) < 0) {
/* Note: with --reject, apply_fragments() returns 0 */
- if (!threeway || try_threeway(&image, patch, st, ce) < 0)
+ if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0)
return -1;
}
patch->result = image.buf;
patch->resultsize = image.len;
- add_to_fn_table(patch);
+ add_to_fn_table(state, patch);
free(image.line_allocated);
if (0 < patch->is_delete && patch->resultsize)
@@ -3470,7 +3610,10 @@ static int apply_data(struct patch *patch, struct stat *st, const struct cache_e
* check_patch() separately makes sure (and errors out otherwise) that
* the path the patch creates does not exist in the current tree.
*/
-static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
+static int check_preimage(struct apply_state *state,
+ struct patch *patch,
+ struct cache_entry **ce,
+ struct stat *st)
{
const char *old_name = patch->old_name;
struct patch *previous = NULL;
@@ -3481,19 +3624,19 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
return 0;
assert(patch->is_new <= 0);
- previous = previous_patch(patch, &status);
+ previous = previous_patch(state, patch, &status);
if (status)
return error(_("path %s has been renamed/deleted"), old_name);
if (previous) {
st_mode = previous->new_mode;
- } else if (!cached) {
+ } else if (!state->cached) {
stat_ret = lstat(old_name, st);
if (stat_ret && errno != ENOENT)
return error(_("%s: %s"), old_name, strerror(errno));
}
- if (check_index && !previous) {
+ if (state->check_index && !previous) {
int pos = cache_name_pos(old_name, strlen(old_name));
if (pos < 0) {
if (patch->is_new < 0)
@@ -3505,9 +3648,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if (checkout_target(&the_index, *ce, st))
return -1;
}
- if (!cached && verify_index_match(*ce, st))
+ if (!state->cached && verify_index_match(*ce, st))
return error(_("%s: does not match index"), old_name);
- if (cached)
+ if (state->cached)
st_mode = (*ce)->ce_mode;
} else if (stat_ret < 0) {
if (patch->is_new < 0)
@@ -3515,7 +3658,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
return error(_("%s: %s"), old_name, strerror(errno));
}
- if (!cached && !previous)
+ if (!state->cached && !previous)
st_mode = ce_mode_from_stat(*ce, st->st_mode);
if (patch->is_new < 0)
@@ -3543,15 +3686,17 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
#define EXISTS_IN_INDEX 1
#define EXISTS_IN_WORKTREE 2
-static int check_to_create(const char *new_name, int ok_if_exists)
+static int check_to_create(struct apply_state *state,
+ const char *new_name,
+ int ok_if_exists)
{
struct stat nst;
- if (check_index &&
+ if (state->check_index &&
cache_name_pos(new_name, strlen(new_name)) >= 0 &&
!ok_if_exists)
return EXISTS_IN_INDEX;
- if (cached)
+ if (state->cached)
return 0;
if (!lstat(new_name, &nst)) {
@@ -3574,56 +3719,46 @@ static int check_to_create(const char *new_name, int ok_if_exists)
return 0;
}
-/*
- * We need to keep track of how symlinks in the preimage are
- * manipulated by the patches. A patch to add a/b/c where a/b
- * is a symlink should not be allowed to affect the directory
- * the symlink points at, but if the same patch removes a/b,
- * it is perfectly fine, as the patch removes a/b to make room
- * to create a directory a/b so that a/b/c can be created.
- */
-static struct string_list symlink_changes;
-#define SYMLINK_GOES_AWAY 01
-#define SYMLINK_IN_RESULT 02
-
-static uintptr_t register_symlink_changes(const char *path, uintptr_t what)
+static uintptr_t register_symlink_changes(struct apply_state *state,
+ const char *path,
+ uintptr_t what)
{
struct string_list_item *ent;
- ent = string_list_lookup(&symlink_changes, path);
+ ent = string_list_lookup(&state->symlink_changes, path);
if (!ent) {
- ent = string_list_insert(&symlink_changes, path);
+ ent = string_list_insert(&state->symlink_changes, path);
ent->util = (void *)0;
}
ent->util = (void *)(what | ((uintptr_t)ent->util));
return (uintptr_t)ent->util;
}
-static uintptr_t check_symlink_changes(const char *path)
+static uintptr_t check_symlink_changes(struct apply_state *state, const char *path)
{
struct string_list_item *ent;
- ent = string_list_lookup(&symlink_changes, path);
+ ent = string_list_lookup(&state->symlink_changes, path);
if (!ent)
return 0;
return (uintptr_t)ent->util;
}
-static void prepare_symlink_changes(struct patch *patch)
+static void prepare_symlink_changes(struct apply_state *state, struct patch *patch)
{
for ( ; patch; patch = patch->next) {
if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
(patch->is_rename || patch->is_delete))
/* the symlink at patch->old_name is removed */
- register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY);
+ register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY);
if (patch->new_name && S_ISLNK(patch->new_mode))
/* the symlink at patch->new_name is created or remains */
- register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT);
+ register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT);
}
}
-static int path_is_beyond_symlink_1(struct strbuf *name)
+static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name)
{
do {
unsigned int change;
@@ -3633,7 +3768,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
if (!name->len)
break;
name->buf[name->len] = '\0';
- change = check_symlink_changes(name->buf);
+ change = check_symlink_changes(state, name->buf);
if (change & SYMLINK_IN_RESULT)
return 1;
if (change & SYMLINK_GOES_AWAY)
@@ -3644,7 +3779,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
continue;
/* otherwise, check the preimage */
- if (check_index) {
+ if (state->check_index) {
struct cache_entry *ce;
ce = cache_file_exists(name->buf, name->len, ignore_case);
@@ -3659,14 +3794,14 @@ static int path_is_beyond_symlink_1(struct strbuf *name)
return 0;
}
-static int path_is_beyond_symlink(const char *name_)
+static int path_is_beyond_symlink(struct apply_state *state, const char *name_)
{
int ret;
struct strbuf name = STRBUF_INIT;
assert(*name_ != '\0');
strbuf_addstr(&name, name_);
- ret = path_is_beyond_symlink_1(&name);
+ ret = path_is_beyond_symlink_1(state, &name);
strbuf_release(&name);
return ret;
@@ -3693,7 +3828,7 @@ static void die_on_unsafe_path(struct patch *patch)
* Check and apply the patch in-core; leave the result in patch->result
* for the caller to write it out to the final destination.
*/
-static int check_patch(struct patch *patch)
+static int check_patch(struct apply_state *state, struct patch *patch)
{
struct stat st;
const char *old_name = patch->old_name;
@@ -3706,7 +3841,7 @@ static int check_patch(struct patch *patch)
patch->rejected = 1; /* we will drop this after we succeed */
- status = check_preimage(patch, &ce, &st);
+ status = check_preimage(state, patch, &ce, &st);
if (status)
return status;
old_name = patch->old_name;
@@ -3725,7 +3860,7 @@ static int check_patch(struct patch *patch)
* B and rename from A to B is handled the same way by asking
* was_deleted().
*/
- if ((tpatch = in_fn_table(new_name)) &&
+ if ((tpatch = in_fn_table(state, new_name)) &&
(was_deleted(tpatch) || to_be_deleted(tpatch)))
ok_if_exists = 1;
else
@@ -3733,9 +3868,9 @@ static int check_patch(struct patch *patch)
if (new_name &&
((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
- int err = check_to_create(new_name, ok_if_exists);
+ int err = check_to_create(state, new_name, ok_if_exists);
- if (err && threeway) {
+ if (err && state->threeway) {
patch->direct_to_threeway = 1;
} else switch (err) {
case 0:
@@ -3776,7 +3911,7 @@ static int check_patch(struct patch *patch)
}
}
- if (!unsafe_paths)
+ if (!state->unsafe_paths)
die_on_unsafe_path(patch);
/*
@@ -3788,27 +3923,27 @@ static int check_patch(struct patch *patch)
* is not deposited to a path that is beyond a symbolic link
* here.
*/
- if (!patch->is_delete && path_is_beyond_symlink(patch->new_name))
+ if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name))
return error(_("affected file '%s' is beyond a symbolic link"),
patch->new_name);
- if (apply_data(patch, &st, ce) < 0)
+ if (apply_data(state, patch, &st, ce) < 0)
return error(_("%s: patch does not apply"), name);
patch->rejected = 0;
return 0;
}
-static int check_patch_list(struct patch *patch)
+static int check_patch_list(struct apply_state *state, struct patch *patch)
{
int err = 0;
- prepare_symlink_changes(patch);
- prepare_fn_table(patch);
+ prepare_symlink_changes(state, patch);
+ prepare_fn_table(state, patch);
while (patch) {
- if (apply_verbosely)
+ if (state->apply_verbosely)
say_patch_name(stderr,
_("Checking patch %s..."), patch);
- err |= check_patch(patch);
+ err |= check_patch(state, patch);
patch = patch->next;
}
return err;
@@ -3912,7 +4047,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
discard_index(&result);
}
-static void stat_patch_list(struct patch *patch)
+static void stat_patch_list(struct apply_state *state, struct patch *patch)
{
int files, adds, dels;
@@ -3920,13 +4055,14 @@ static void stat_patch_list(struct patch *patch)
files++;
adds += patch->lines_added;
dels += patch->lines_deleted;
- show_stats(patch);
+ show_stats(state, patch);
}
print_stat_summary(stdout, files, adds, dels);
}
-static void numstat_patch_list(struct patch *patch)
+static void numstat_patch_list(struct apply_state *state,
+ struct patch *patch)
{
for ( ; patch; patch = patch->next) {
const char *name;
@@ -3935,7 +4071,7 @@ static void numstat_patch_list(struct patch *patch)
printf("-\t-\t");
else
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
- write_name_quoted(name, stdout, line_termination);
+ write_name_quoted(name, stdout, state->line_termination);
}
}
@@ -4017,49 +4153,53 @@ static void summary_patch_list(struct patch *patch)
}
}
-static void patch_stats(struct patch *patch)
+static void patch_stats(struct apply_state *state, struct patch *patch)
{
int lines = patch->lines_added + patch->lines_deleted;
- if (lines > max_change)
- max_change = lines;
+ if (lines > state->max_change)
+ state->max_change = lines;
if (patch->old_name) {
int len = quote_c_style(patch->old_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->old_name);
- if (len > max_len)
- max_len = len;
+ if (len > state->max_len)
+ state->max_len = len;
}
if (patch->new_name) {
int len = quote_c_style(patch->new_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->new_name);
- if (len > max_len)
- max_len = len;
+ if (len > state->max_len)
+ state->max_len = len;
}
}
-static void remove_file(struct patch *patch, int rmdir_empty)
+static void remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
{
- if (update_index) {
+ if (state->update_index) {
if (remove_file_from_cache(patch->old_name) < 0)
die(_("unable to remove %s from index"), patch->old_name);
}
- if (!cached) {
+ if (!state->cached) {
if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
remove_path(patch->old_name);
}
}
}
-static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
+static void add_index_file(struct apply_state *state,
+ const char *path,
+ unsigned mode,
+ void *buf,
+ unsigned long size)
{
struct stat st;
struct cache_entry *ce;
int namelen = strlen(path);
unsigned ce_size = cache_entry_size(namelen);
- if (!update_index)
+ if (!state->update_index)
return;
ce = xcalloc(1, ce_size);
@@ -4074,7 +4214,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
get_sha1_hex(s, ce->sha1))
die(_("corrupt patch for submodule %s"), path);
} else {
- if (!cached) {
+ if (!state->cached) {
if (lstat(path, &st) < 0)
die_errno(_("unable to stat newly created file '%s'"),
path);
@@ -4126,9 +4266,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
* which is true 99% of the time anyway. If they don't,
* we create them and try again.
*/
-static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
+static void create_one_file(struct apply_state *state,
+ char *path,
+ unsigned mode,
+ const char *buf,
+ unsigned long size)
{
- if (cached)
+ if (state->cached)
return;
if (!try_create_file(path, mode, buf, size))
return;
@@ -4169,13 +4313,14 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
die_errno(_("unable to write file '%s' mode %o"), path, mode);
}
-static void add_conflicted_stages_file(struct patch *patch)
+static void add_conflicted_stages_file(struct apply_state *state,
+ struct patch *patch)
{
int stage, namelen;
unsigned ce_size, mode;
struct cache_entry *ce;
- if (!update_index)
+ if (!state->update_index)
return;
namelen = strlen(patch->new_name);
ce_size = cache_entry_size(namelen);
@@ -4196,7 +4341,7 @@ static void add_conflicted_stages_file(struct patch *patch)
}
}
-static void create_file(struct patch *patch)
+static void create_file(struct apply_state *state, struct patch *patch)
{
char *path = patch->new_name;
unsigned mode = patch->new_mode;
@@ -4205,25 +4350,27 @@ static void create_file(struct patch *patch)
if (!mode)
mode = S_IFREG | 0644;
- create_one_file(path, mode, buf, size);
+ create_one_file(state, path, mode, buf, size);
if (patch->conflicted_threeway)
- add_conflicted_stages_file(patch);
+ add_conflicted_stages_file(state, patch);
else
- add_index_file(path, mode, buf, size);
+ add_index_file(state, path, mode, buf, size);
}
/* phase zero is to remove, phase one is to create */
-static void write_out_one_result(struct patch *patch, int phase)
+static void write_out_one_result(struct apply_state *state,
+ struct patch *patch,
+ int phase)
{
if (patch->is_delete > 0) {
if (phase == 0)
- remove_file(patch, 1);
+ remove_file(state, patch, 1);
return;
}
if (patch->is_new > 0 || patch->is_copy) {
if (phase == 1)
- create_file(patch);
+ create_file(state, patch);
return;
}
/*
@@ -4231,12 +4378,12 @@ static void write_out_one_result(struct patch *patch, int phase)
* thing: remove the old, write the new
*/
if (phase == 0)
- remove_file(patch, patch->is_rename);
+ remove_file(state, patch, patch->is_rename);
if (phase == 1)
- create_file(patch);
+ create_file(state, patch);
}
-static int write_out_one_reject(struct patch *patch)
+static int write_out_one_reject(struct apply_state *state, struct patch *patch)
{
FILE *rej;
char namebuf[PATH_MAX];
@@ -4251,7 +4398,7 @@ static int write_out_one_reject(struct patch *patch)
}
if (!cnt) {
- if (apply_verbosely)
+ if (state->apply_verbosely)
say_patch_name(stderr,
_("Applied patch %s cleanly."), patch);
return 0;
@@ -4307,7 +4454,7 @@ static int write_out_one_reject(struct patch *patch)
return -1;
}
-static int write_out_results(struct patch *list)
+static int write_out_results(struct apply_state *state, struct patch *list)
{
int phase;
int errs = 0;
@@ -4320,9 +4467,9 @@ static int write_out_results(struct patch *list)
if (l->rejected)
errs = 1;
else {
- write_out_one_result(l, phase);
+ write_out_one_result(state, l, phase);
if (phase == 1) {
- if (write_out_one_reject(l))
+ if (write_out_one_reject(state, l))
errs = 1;
if (l->conflicted_threeway) {
string_list_append(&cpath, l->new_name);
@@ -4353,14 +4500,17 @@ static struct lock_file lock_file;
#define INACCURATE_EOF (1<<0)
#define RECOUNT (1<<1)
-static int apply_patch(int fd, const char *filename, int options)
+static int apply_patch(struct apply_state *state,
+ int fd,
+ const char *filename,
+ int options)
{
size_t offset;
struct strbuf buf = STRBUF_INIT; /* owns the patch text */
struct patch *list = NULL, **listp = &list;
int skipped_patch = 0;
- patch_input_file = filename;
+ state->patch_input_file = filename;
read_patch_file(&buf, fd);
offset = 0;
while (offset < buf.len) {
@@ -4370,20 +4520,20 @@ static int apply_patch(int fd, const char *filename, int options)
patch = xcalloc(1, sizeof(*patch));
patch->inaccurate_eof = !!(options & INACCURATE_EOF);
patch->recount = !!(options & RECOUNT);
- nr = parse_chunk(buf.buf + offset, buf.len - offset, patch);
+ nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch);
if (nr < 0) {
free_patch(patch);
break;
}
- if (apply_in_reverse)
+ if (state->apply_in_reverse)
reverse_patches(patch);
- if (use_patch(patch)) {
- patch_stats(patch);
+ if (use_patch(state, patch)) {
+ patch_stats(state, patch);
*listp = patch;
listp = &patch->next;
}
else {
- if (apply_verbosely)
+ if (state->apply_verbosely)
say_patch_name(stderr, _("Skipped patch '%s'."), patch);
free_patch(patch);
skipped_patch++;
@@ -4394,45 +4544,45 @@ static int apply_patch(int fd, const char *filename, int options)
if (!list && !skipped_patch)
die(_("unrecognized input"));
- if (whitespace_error && (ws_error_action == die_on_ws_error))
- apply = 0;
+ if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
+ state->apply = 0;
- update_index = check_index && apply;
- if (update_index && newfd < 0)
- newfd = hold_locked_index(&lock_file, 1);
+ state->update_index = state->check_index && state->apply;
+ if (state->update_index && state->newfd < 0)
+ state->newfd = hold_locked_index(state->lock_file, 1);
- if (check_index) {
+ if (state->check_index) {
if (read_cache() < 0)
die(_("unable to read index file"));
}
- if ((check || apply) &&
- check_patch_list(list) < 0 &&
- !apply_with_reject)
+ if ((state->check || state->apply) &&
+ check_patch_list(state, list) < 0 &&
+ !state->apply_with_reject)
exit(1);
- if (apply && write_out_results(list)) {
- if (apply_with_reject)
+ if (state->apply && write_out_results(state, list)) {
+ if (state->apply_with_reject)
exit(1);
/* with --3way, we still need to write the index out */
return 1;
}
- if (fake_ancestor)
- build_fake_ancestor(list, fake_ancestor);
+ if (state->fake_ancestor)
+ build_fake_ancestor(list, state->fake_ancestor);
- if (diffstat)
- stat_patch_list(list);
+ if (state->diffstat)
+ stat_patch_list(state, list);
- if (numstat)
- numstat_patch_list(list);
+ if (state->numstat)
+ numstat_patch_list(state, list);
- if (summary)
+ if (state->summary)
summary_patch_list(list);
free_patch_list(list);
strbuf_release(&buf);
- string_list_clear(&fn_table, 0);
+ string_list_clear(&state->fn_table, 0);
return 0;
}
@@ -4446,220 +4596,284 @@ static void git_apply_config(void)
static int option_parse_exclude(const struct option *opt,
const char *arg, int unset)
{
- add_name_limit(arg, 1);
+ struct apply_state *state = opt->value;
+ add_name_limit(state, arg, 1);
return 0;
}
static int option_parse_include(const struct option *opt,
const char *arg, int unset)
{
- add_name_limit(arg, 0);
- has_include = 1;
+ struct apply_state *state = opt->value;
+ add_name_limit(state, arg, 0);
+ state->has_include = 1;
return 0;
}
static int option_parse_p(const struct option *opt,
- const char *arg, int unset)
+ const char *arg,
+ int unset)
{
- p_value = atoi(arg);
- p_value_known = 1;
+ struct apply_state *state = opt->value;
+ state->p_value = atoi(arg);
+ state->p_value_known = 1;
return 0;
}
static int option_parse_space_change(const struct option *opt,
- const char *arg, int unset)
+ const char *arg, int unset)
{
+ struct apply_state *state = opt->value;
if (unset)
- ws_ignore_action = ignore_ws_none;
+ state->ws_ignore_action = ignore_ws_none;
else
- ws_ignore_action = ignore_ws_change;
+ state->ws_ignore_action = ignore_ws_change;
return 0;
}
static int option_parse_whitespace(const struct option *opt,
const char *arg, int unset)
{
- const char **whitespace_option = opt->value;
-
- *whitespace_option = arg;
- parse_whitespace_option(arg);
+ struct apply_state *state = opt->value;
+ state->whitespace_option = arg;
+ parse_whitespace_option(state, arg);
return 0;
}
static int option_parse_directory(const struct option *opt,
const char *arg, int unset)
{
- strbuf_reset(&root);
- strbuf_addstr(&root, arg);
- strbuf_complete(&root, '/');
+ struct apply_state *state = opt->value;
+ strbuf_reset(&state->root);
+ strbuf_addstr(&state->root, arg);
+ strbuf_complete(&state->root, '/');
return 0;
}
-int cmd_apply(int argc, const char **argv, const char *prefix_)
+static void init_apply_state(struct apply_state *state,
+ const char *prefix,
+ struct lock_file *lock_file)
+{
+ memset(state, 0, sizeof(*state));
+ state->prefix = prefix;
+ state->prefix_length = state->prefix ? strlen(state->prefix) : 0;
+ state->lock_file = lock_file;
+ state->newfd = -1;
+ state->apply = 1;
+ state->line_termination = '\n';
+ state->p_value = 1;
+ state->p_context = UINT_MAX;
+ state->squelch_whitespace_errors = 5;
+ state->ws_error_action = warn_on_ws_error;
+ state->ws_ignore_action = ignore_ws_none;
+ state->linenr = 1;
+ string_list_init(&state->fn_table, 0);
+ string_list_init(&state->limit_by_name, 0);
+ string_list_init(&state->symlink_changes, 0);
+ strbuf_init(&state->root, 0);
+
+ git_apply_config();
+ if (apply_default_whitespace)
+ parse_whitespace_option(state, apply_default_whitespace);
+ if (apply_default_ignorewhitespace)
+ parse_ignorewhitespace_option(state, apply_default_ignorewhitespace);
+}
+
+static void clear_apply_state(struct apply_state *state)
+{
+ string_list_clear(&state->limit_by_name, 0);
+ string_list_clear(&state->symlink_changes, 0);
+ strbuf_release(&state->root);
+
+ /* &state->fn_table is cleared at the end of apply_patch() */
+}
+
+static void check_apply_state(struct apply_state *state, int force_apply)
+{
+ int is_not_gitdir = !startup_info->have_repository;
+
+ if (state->apply_with_reject && state->threeway)
+ die("--reject and --3way cannot be used together.");
+ if (state->cached && state->threeway)
+ die("--cached and --3way cannot be used together.");
+ if (state->threeway) {
+ if (is_not_gitdir)
+ die(_("--3way outside a repository"));
+ state->check_index = 1;
+ }
+ if (state->apply_with_reject)
+ state->apply = state->apply_verbosely = 1;
+ if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
+ state->apply = 0;
+ if (state->check_index && is_not_gitdir)
+ die(_("--index outside a repository"));
+ if (state->cached) {
+ if (is_not_gitdir)
+ die(_("--cached outside a repository"));
+ state->check_index = 1;
+ }
+ if (state->check_index)
+ state->unsafe_paths = 0;
+ if (!state->lock_file)
+ die("BUG: state->lock_file should not be NULL");
+}
+
+static int apply_all_patches(struct apply_state *state,
+ int argc,
+ const char **argv,
+ int options)
{
int i;
int errs = 0;
- int is_not_gitdir = !startup_info->have_repository;
- int force_apply = 0;
+ int read_stdin = 1;
+
+ for (i = 0; i < argc; i++) {
+ const char *arg = argv[i];
+ int fd;
+
+ if (!strcmp(arg, "-")) {
+ errs |= apply_patch(state, 0, "<stdin>", options);
+ read_stdin = 0;
+ continue;
+ } else if (0 < state->prefix_length)
+ arg = prefix_filename(state->prefix,
+ state->prefix_length,
+ arg);
+
+ fd = open(arg, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("can't open patch '%s'"), arg);
+ read_stdin = 0;
+ set_default_whitespace_mode(state);
+ errs |= apply_patch(state, fd, arg, options);
+ close(fd);
+ }
+ set_default_whitespace_mode(state);
+ if (read_stdin)
+ errs |= apply_patch(state, 0, "<stdin>", options);
+
+ if (state->whitespace_error) {
+ if (state->squelch_whitespace_errors &&
+ state->squelch_whitespace_errors < state->whitespace_error) {
+ int squelched =
+ state->whitespace_error - state->squelch_whitespace_errors;
+ warning(Q_("squelched %d whitespace error",
+ "squelched %d whitespace errors",
+ squelched),
+ squelched);
+ }
+ if (state->ws_error_action == die_on_ws_error)
+ die(Q_("%d line adds whitespace errors.",
+ "%d lines add whitespace errors.",
+ state->whitespace_error),
+ state->whitespace_error);
+ if (state->applied_after_fixing_ws && state->apply)
+ warning("%d line%s applied after"
+ " fixing whitespace errors.",
+ state->applied_after_fixing_ws,
+ state->applied_after_fixing_ws == 1 ? "" : "s");
+ else if (state->whitespace_error)
+ warning(Q_("%d line adds whitespace errors.",
+ "%d lines add whitespace errors.",
+ state->whitespace_error),
+ state->whitespace_error);
+ }
+
+ if (state->update_index) {
+ if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK))
+ die(_("Unable to write new index file"));
+ state->newfd = -1;
+ }
+
+ return !!errs;
+}
- const char *whitespace_option = NULL;
+int cmd_apply(int argc, const char **argv, const char *prefix)
+{
+ int force_apply = 0;
+ int options = 0;
+ int ret;
+ struct apply_state state;
struct option builtin_apply_options[] = {
- { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
+ { OPTION_CALLBACK, 0, "exclude", &state, N_("path"),
N_("don't apply changes matching the given path"),
0, option_parse_exclude },
- { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
+ { OPTION_CALLBACK, 0, "include", &state, N_("path"),
N_("apply changes matching the given path"),
0, option_parse_include },
- { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
+ { OPTION_CALLBACK, 'p', NULL, &state, N_("num"),
N_("remove <num> leading slashes from traditional diff paths"),
0, option_parse_p },
- OPT_BOOL(0, "no-add", &no_add,
+ OPT_BOOL(0, "no-add", &state.no_add,
N_("ignore additions made by the patch")),
- OPT_BOOL(0, "stat", &diffstat,
+ OPT_BOOL(0, "stat", &state.diffstat,
N_("instead of applying the patch, output diffstat for the input")),
OPT_NOOP_NOARG(0, "allow-binary-replacement"),
OPT_NOOP_NOARG(0, "binary"),
- OPT_BOOL(0, "numstat", &numstat,
+ OPT_BOOL(0, "numstat", &state.numstat,
N_("show number of added and deleted lines in decimal notation")),
- OPT_BOOL(0, "summary", &summary,
+ OPT_BOOL(0, "summary", &state.summary,
N_("instead of applying the patch, output a summary for the input")),
- OPT_BOOL(0, "check", &check,
+ OPT_BOOL(0, "check", &state.check,
N_("instead of applying the patch, see if the patch is applicable")),
- OPT_BOOL(0, "index", &check_index,
+ OPT_BOOL(0, "index", &state.check_index,
N_("make sure the patch is applicable to the current index")),
- OPT_BOOL(0, "cached", &cached,
+ OPT_BOOL(0, "cached", &state.cached,
N_("apply a patch without touching the working tree")),
- OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
+ OPT_BOOL(0, "unsafe-paths", &state.unsafe_paths,
N_("accept a patch that touches outside the working area")),
OPT_BOOL(0, "apply", &force_apply,
N_("also apply the patch (use with --stat/--summary/--check)")),
- OPT_BOOL('3', "3way", &threeway,
+ OPT_BOOL('3', "3way", &state.threeway,
N_( "attempt three-way merge if a patch does not apply")),
- OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
+ OPT_FILENAME(0, "build-fake-ancestor", &state.fake_ancestor,
N_("build a temporary index based on embedded index information")),
/* Think twice before adding "--nul" synonym to this */
- OPT_SET_INT('z', NULL, &line_termination,
+ OPT_SET_INT('z', NULL, &state.line_termination,
N_("paths are separated with NUL character"), '\0'),
- OPT_INTEGER('C', NULL, &p_context,
+ OPT_INTEGER('C', NULL, &state.p_context,
N_("ensure at least <n> lines of context match")),
- { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
+ { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"),
N_("detect new or modified lines that have whitespace errors"),
0, option_parse_whitespace },
- { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+ { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL,
N_("ignore changes in whitespace when finding context"),
PARSE_OPT_NOARG, option_parse_space_change },
- { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+ { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL,
N_("ignore changes in whitespace when finding context"),
PARSE_OPT_NOARG, option_parse_space_change },
- OPT_BOOL('R', "reverse", &apply_in_reverse,
+ OPT_BOOL('R', "reverse", &state.apply_in_reverse,
N_("apply the patch in reverse")),
- OPT_BOOL(0, "unidiff-zero", &unidiff_zero,
+ OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero,
N_("don't expect at least one line of context")),
- OPT_BOOL(0, "reject", &apply_with_reject,
+ OPT_BOOL(0, "reject", &state.apply_with_reject,
N_("leave the rejected hunks in corresponding *.rej files")),
- OPT_BOOL(0, "allow-overlap", &allow_overlap,
+ OPT_BOOL(0, "allow-overlap", &state.allow_overlap,
N_("allow overlapping hunks")),
- OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
+ OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")),
OPT_BIT(0, "inaccurate-eof", &options,
N_("tolerate incorrectly detected missing new-line at the end of file"),
INACCURATE_EOF),
OPT_BIT(0, "recount", &options,
N_("do not trust the line counts in the hunk headers"),
RECOUNT),
- { OPTION_CALLBACK, 0, "directory", NULL, N_("root"),
+ { OPTION_CALLBACK, 0, "directory", &state, N_("root"),
N_("prepend <root> to all filenames"),
0, option_parse_directory },
OPT_END()
};
- prefix = prefix_;
- prefix_length = prefix ? strlen(prefix) : 0;
- git_apply_config();
- if (apply_default_whitespace)
- parse_whitespace_option(apply_default_whitespace);
- if (apply_default_ignorewhitespace)
- parse_ignorewhitespace_option(apply_default_ignorewhitespace);
+ init_apply_state(&state, prefix, &lock_file);
- argc = parse_options(argc, argv, prefix, builtin_apply_options,
+ argc = parse_options(argc, argv, state.prefix, builtin_apply_options,
apply_usage, 0);
- if (apply_with_reject && threeway)
- die("--reject and --3way cannot be used together.");
- if (cached && threeway)
- die("--cached and --3way cannot be used together.");
- if (threeway) {
- if (is_not_gitdir)
- die(_("--3way outside a repository"));
- check_index = 1;
- }
- if (apply_with_reject)
- apply = apply_verbosely = 1;
- if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
- apply = 0;
- if (check_index && is_not_gitdir)
- die(_("--index outside a repository"));
- if (cached) {
- if (is_not_gitdir)
- die(_("--cached outside a repository"));
- check_index = 1;
- }
- if (check_index)
- unsafe_paths = 0;
+ check_apply_state(&state, force_apply);
- for (i = 0; i < argc; i++) {
- const char *arg = argv[i];
- int fd;
+ ret = apply_all_patches(&state, argc, argv, options);
- if (!strcmp(arg, "-")) {
- errs |= apply_patch(0, "<stdin>", options);
- read_stdin = 0;
- continue;
- } else if (0 < prefix_length)
- arg = prefix_filename(prefix, prefix_length, arg);
+ clear_apply_state(&state);
- fd = open(arg, O_RDONLY);
- if (fd < 0)
- die_errno(_("can't open patch '%s'"), arg);
- read_stdin = 0;
- set_default_whitespace_mode(whitespace_option);
- errs |= apply_patch(fd, arg, options);
- close(fd);
- }
- set_default_whitespace_mode(whitespace_option);
- if (read_stdin)
- errs |= apply_patch(0, "<stdin>", options);
- if (whitespace_error) {
- if (squelch_whitespace_errors &&
- squelch_whitespace_errors < whitespace_error) {
- int squelched =
- whitespace_error - squelch_whitespace_errors;
- warning(Q_("squelched %d whitespace error",
- "squelched %d whitespace errors",
- squelched),
- squelched);
- }
- if (ws_error_action == die_on_ws_error)
- die(Q_("%d line adds whitespace errors.",
- "%d lines add whitespace errors.",
- whitespace_error),
- whitespace_error);
- if (applied_after_fixing_ws && apply)
- warning("%d line%s applied after"
- " fixing whitespace errors.",
- applied_after_fixing_ws,
- applied_after_fixing_ws == 1 ? "" : "s");
- else if (whitespace_error)
- warning(Q_("%d line adds whitespace errors.",
- "%d lines add whitespace errors.",
- whitespace_error),
- whitespace_error);
- }
-
- if (update_index) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("Unable to write new index file"));
- }
-
- return !!errs;
+ return ret;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 9a03500..7ec7823 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -56,7 +56,7 @@ static int show_progress;
static struct date_mode blame_date_mode = { DATE_ISO8601 };
static size_t blame_date_width;
-static struct string_list mailmap;
+static struct string_list mailmap = STRING_LIST_INIT_NODUP;
#ifndef DEBUG
#define DEBUG 0
@@ -598,7 +598,7 @@ static struct origin *find_origin(struct scoreboard *sb,
p->status);
case 'M':
porigin = get_origin(sb, parent, origin->path);
- hashcpy(porigin->blob_sha1, p->one->sha1);
+ hashcpy(porigin->blob_sha1, p->one->oid.hash);
porigin->mode = p->one->mode;
break;
case 'A':
@@ -608,7 +608,7 @@ static struct origin *find_origin(struct scoreboard *sb,
}
}
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
return porigin;
}
@@ -644,13 +644,13 @@ static struct origin *find_rename(struct scoreboard *sb,
if ((p->status == 'R' || p->status == 'C') &&
!strcmp(p->two->path, origin->path)) {
porigin = get_origin(sb, parent, p->one->path);
- hashcpy(porigin->blob_sha1, p->one->sha1);
+ hashcpy(porigin->blob_sha1, p->one->oid.hash);
porigin->mode = p->one->mode;
break;
}
}
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
return porigin;
}
@@ -1308,7 +1308,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
continue;
norigin = get_origin(sb, parent, p->one->path);
- hashcpy(norigin->blob_sha1, p->one->sha1);
+ hashcpy(norigin->blob_sha1, p->one->oid.hash);
norigin->mode = p->one->mode;
fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
if (!file_p.ptr)
@@ -1342,7 +1342,7 @@ static void find_copy_in_parent(struct scoreboard *sb,
} while (unblamed);
target->suspects = reverse_blame(leftover, NULL);
diff_flush(&diff_opts);
- free_pathspec(&diff_opts.pathspec);
+ clear_pathspec(&diff_opts.pathspec);
}
/*
@@ -2229,6 +2229,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
static void verify_working_tree_path(struct commit *work_tree, const char *path)
{
struct commit_list *parents;
+ int pos;
for (parents = work_tree->parents; parents; parents = parents->next) {
const unsigned char *commit_sha1 = parents->item->object.oid.hash;
@@ -2239,7 +2240,14 @@ static void verify_working_tree_path(struct commit *work_tree, const char *path)
sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
return;
}
- die("no such path '%s' in HEAD", path);
+
+ pos = cache_name_pos(path, strlen(path));
+ if (pos >= 0)
+ ; /* path is in the index */
+ else if (!strcmp(active_cache[-1 - pos]->name, path))
+ ; /* path is in the index, unmerged */
+ else
+ die("no such path '%s' in HEAD", path);
}
static struct commit_list **append_parent(struct commit_list **tail, const unsigned char *sha1)
@@ -2519,12 +2527,12 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
enum object_type type;
struct commit *final_commit = NULL;
- static struct string_list range_list;
- static int output_option = 0, opt = 0;
- static int show_stats = 0;
- static const char *revs_file = NULL;
- static const char *contents_from = NULL;
- static const struct option options[] = {
+ struct string_list range_list = STRING_LIST_INIT_NODUP;
+ int output_option = 0, opt = 0;
+ int show_stats = 0;
+ const char *revs_file = NULL;
+ const char *contents_from = NULL;
+ const struct option options[] = {
OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
@@ -2625,6 +2633,9 @@ parse_done:
case DATE_RAW:
blame_date_width = sizeof("1161298804 -0700");
break;
+ case DATE_UNIX:
+ blame_date_width = sizeof("1161298804");
+ break;
case DATE_SHORT:
blame_date_width = sizeof("2006-10-19");
break;
@@ -2797,7 +2808,7 @@ parse_done:
lno = prepare_lines(&sb);
if (lno && !range_list.nr)
- string_list_append(&range_list, xstrdup("1"));
+ string_list_append(&range_list, "1");
anchor = 1;
range_set_init(&ranges, range_list.nr);
diff --git a/builtin/branch.c b/builtin/branch.c
index 2ecde53..7df0543 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -212,7 +212,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
die(_("Couldn't look up commit object for HEAD"));
}
for (i = 0; i < argc; i++, strbuf_release(&bname)) {
- const char *target;
+ char *target = NULL;
int flags = 0;
strbuf_branchname(&bname, argv[i]);
@@ -231,11 +231,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
}
- target = resolve_ref_unsafe(name,
- RESOLVE_REF_READING
- | RESOLVE_REF_NO_RECURSE
- | RESOLVE_REF_ALLOW_BAD_NAME,
- sha1, &flags);
+ target = resolve_refdup(name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ sha1, &flags);
if (!target) {
error(remote_branch
? _("remote-tracking branch '%s' not found.")
@@ -248,7 +248,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
force)) {
ret = 1;
- continue;
+ goto next;
}
if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
@@ -258,7 +258,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
: _("Error deleting branch '%s'"),
bname.buf);
ret = 1;
- continue;
+ goto next;
}
if (!quiet) {
printf(remote_branch
@@ -270,6 +270,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
: find_unique_abbrev(sha1, DEFAULT_ABBREV));
}
delete_branch_config(bname.buf);
+
+ next:
+ free(target);
}
free(name);
@@ -614,14 +617,11 @@ static int edit_branch_description(const char *branch_name)
if (!buf.len || buf.buf[buf.len-1] != '\n')
strbuf_addch(&buf, '\n');
strbuf_commented_addf(&buf,
- "Please edit the description for the branch\n"
- " %s\n"
- "Lines starting with '%c' will be stripped.\n",
+ _("Please edit the description for the branch\n"
+ " %s\n"
+ "Lines starting with '%c' will be stripped.\n"),
branch_name, comment_line_char);
- if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
- strbuf_release(&buf);
- return error_errno(_("could not write branch description template"));
- }
+ write_file_buf(git_path(edit_description), buf.buf, buf.len);
strbuf_reset(&buf);
if (launch_editor(git_path(edit_description), &buf, NULL)) {
strbuf_release(&buf);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 618103f..2dfe626 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -131,7 +131,7 @@ struct expand_data {
unsigned char sha1[20];
enum object_type type;
unsigned long size;
- unsigned long disk_size;
+ off_t disk_size;
const char *rest;
unsigned char delta_base_sha1[20];
@@ -191,7 +191,7 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
if (data->mark_query)
data->info.disk_sizep = &data->disk_size;
else
- strbuf_addf(sb, "%lu", data->disk_size);
+ strbuf_addf(sb, "%"PRIuMAX, (uintmax_t)data->disk_size);
} else if (is_atom("rest", atom, len)) {
if (data->mark_query)
data->split_on_whitespace = 1;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index fa08b73..4866111 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -276,7 +276,7 @@ static int checkout_paths(const struct checkout_opts *opts,
hold_locked_index(lock_file, 1);
if (read_cache_preload(&opts->pathspec) < 0)
- return error(_("corrupt index file"));
+ return error(_("index file corrupt"));
if (opts->source_tree)
read_tree_some(opts->source_tree, &opts->pathspec);
@@ -470,7 +470,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
hold_locked_index(lock_file, 1);
if (read_cache_preload(NULL) < 0)
- return error(_("corrupt index file"));
+ return error(_("index file corrupt"));
resolve_undo_clear();
if (opts->force) {
@@ -567,10 +567,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.ancestor = old->name;
o.branch1 = new->name;
o.branch2 = "local";
- merge_trees(&o, new->commit->tree, work,
+ ret = merge_trees(&o, new->commit->tree, work,
old->commit->tree, &result);
+ if (ret < 0)
+ exit(128);
ret = reset_tree(new->commit->tree, opts, 0,
writeout_error);
+ strbuf_release(&o.obuf);
if (ret)
return ret;
}
@@ -1137,7 +1140,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
N_("create/reset and checkout a branch")),
OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
- OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
+ OPT_BOOL(0, "detach", &opts.force_detach, N_("detach HEAD at named commit")),
OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"),
BRANCH_TRACK_EXPLICIT),
OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
diff --git a/builtin/clone.c b/builtin/clone.c
index 8f7db98..f044a8c 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -49,8 +49,8 @@ static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
static enum transport_family family;
-static struct string_list option_config;
-static struct string_list option_reference;
+static struct string_list option_config = STRING_LIST_INIT_NODUP;
+static struct string_list option_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate;
static int max_jobs = -1;
@@ -624,13 +624,13 @@ static void update_remote_refs(const struct ref *refs,
const struct ref *rm = mapped_refs;
if (check_connectivity) {
- if (transport->progress)
- fprintf(stderr, _("Checking connectivity... "));
- if (check_everything_connected_with_transport(iterate_ref_map,
- 0, &rm, transport))
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+ opt.transport = transport;
+ opt.progress = transport->progress;
+
+ if (check_connected(iterate_ref_map, &rm, &opt))
die(_("remote did not send all necessary objects"));
- if (transport->progress)
- fprintf(stderr, _("done.\n"));
}
if (refs) {
diff --git a/builtin/commit.c b/builtin/commit.c
index 37702f3..77e3dc8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -92,8 +92,9 @@ N_("If you wish to skip this commit, use:\n"
"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
"the remaining commits.\n");
+static GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
+
static const char *use_message_buffer;
-static const char commit_editmsg[] = "COMMIT_EDITMSG";
static struct lock_file index_lock; /* real index */
static struct lock_file false_lock; /* used only for partial commits */
static enum {
@@ -772,9 +773,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
hook_arg2 = "";
}
- s->fp = fopen_for_writing(git_path(commit_editmsg));
+ s->fp = fopen_for_writing(git_path_commit_editmsg());
if (s->fp == NULL)
- die_errno(_("could not open '%s'"), git_path(commit_editmsg));
+ die_errno(_("could not open '%s'"), git_path_commit_editmsg());
/* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
old_display_comment_prefix = s->display_comment_prefix;
@@ -951,7 +952,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
- git_path(commit_editmsg), hook_arg1, hook_arg2, NULL))
+ git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL))
return 0;
if (use_editor) {
@@ -959,7 +960,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
const char *env[2] = { NULL };
env[0] = index;
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
- if (launch_editor(git_path(commit_editmsg), NULL, env)) {
+ if (launch_editor(git_path_commit_editmsg(), NULL, env)) {
fprintf(stderr,
_("Please supply the message using either -m or -F option.\n"));
exit(1);
@@ -967,7 +968,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
if (!no_verify &&
- run_commit_hook(use_editor, index_file, "commit-msg", git_path(commit_editmsg), NULL)) {
+ run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) {
return 0;
}
@@ -1616,7 +1617,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
OPT_BOOL('o', "only", &only, N_("commit only specified files")),
- OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
+ OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
STATUS_FORMAT_SHORT),
@@ -1738,7 +1739,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
/* Finally, get the commit message */
strbuf_reset(&sb);
- if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+ if (strbuf_read_file(&sb, git_path_commit_editmsg(), 0) < 0) {
int saved_errno = errno;
rollback_index_files();
die(_("could not read commit message: %s"), strerror(saved_errno));
diff --git a/builtin/config.c b/builtin/config.c
index 1d7c6ef..6cbf733 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -25,7 +25,6 @@ static char term = '\n';
static int use_global_config, use_system_config, use_local_config;
static struct git_config_source given_config_source;
static int actions, types;
-static const char *get_color_slot, *get_colorbool_slot;
static int end_null;
static int respect_includes = -1;
static int show_origin;
@@ -604,7 +603,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
given_config_source.file : git_path("config"));
if (use_global_config) {
int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (fd) {
+ if (fd >= 0) {
char *content = default_user_config();
write_str_in_full(fd, content);
free(content);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 8164b58..c0652a7 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -368,7 +368,7 @@ static void show_filemodify(struct diff_queue_struct *q,
print_path(spec->path);
putchar('\n');
- if (!hashcmp(ospec->sha1, spec->sha1) &&
+ if (!oidcmp(&ospec->oid, &spec->oid) &&
ospec->mode == spec->mode)
break;
/* fallthrough */
@@ -383,10 +383,10 @@ 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(anonymize ?
- anonymize_sha1(spec->sha1) :
- spec->sha1));
+ anonymize_sha1(spec->oid.hash) :
+ spec->oid.hash));
else {
- struct object *object = lookup_object(spec->sha1);
+ struct object *object = lookup_object(spec->oid.hash);
printf("M %06o :%d ", spec->mode,
get_object_mark(object));
}
@@ -572,7 +572,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
/* Export the referenced blobs, and remember the marks. */
for (i = 0; i < diff_queued_diff.nr; i++)
if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
- export_blob(diff_queued_diff.queue[i]->two->sha1);
+ export_blob(diff_queued_diff.queue[i]->two->oid.hash);
refname = commit->util;
if (anonymize) {
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f896aa1..164623b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -15,6 +15,7 @@
#include "submodule.h"
#include "connected.h"
#include "argv-array.h"
+#include "utf8.h"
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@ -449,7 +450,132 @@ fail:
: STORE_REF_ERROR_OTHER;
}
-#define REFCOL_WIDTH 10
+static int refcol_width = 10;
+static int compact_format;
+
+static void adjust_refcol_width(const struct ref *ref)
+{
+ int max, rlen, llen, len;
+
+ /* uptodate lines are only shown on high verbosity level */
+ if (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))
+ return;
+
+ max = term_columns();
+ rlen = utf8_strwidth(prettify_refname(ref->name));
+
+ llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+ /*
+ * rough estimation to see if the output line is too long and
+ * should not be counted (we can't do precise calculation
+ * anyway because we don't know if the error explanation part
+ * will be printed in update_local_ref)
+ */
+ if (compact_format) {
+ llen = 0;
+ max = max * 2 / 3;
+ }
+ len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+ if (len >= max)
+ return;
+
+ /*
+ * Not precise calculation for compact mode because '*' can
+ * appear on the left hand side of '->' and shrink the column
+ * back.
+ */
+ if (refcol_width < rlen)
+ refcol_width = rlen;
+}
+
+static void prepare_format_display(struct ref *ref_map)
+{
+ struct ref *rm;
+ const char *format = "full";
+
+ git_config_get_string_const("fetch.output", &format);
+ if (!strcasecmp(format, "full"))
+ compact_format = 0;
+ else if (!strcasecmp(format, "compact"))
+ compact_format = 1;
+ else
+ die(_("configuration fetch.output contains invalid value %s"),
+ format);
+
+ for (rm = ref_map; rm; rm = rm->next) {
+ if (rm->status == REF_STATUS_REJECT_SHALLOW ||
+ !rm->peer_ref ||
+ !strcmp(rm->name, "HEAD"))
+ continue;
+
+ adjust_refcol_width(rm);
+ }
+}
+
+static void print_remote_to_local(struct strbuf *display,
+ const char *remote, const char *local)
+{
+ strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+}
+
+static int find_and_replace(struct strbuf *haystack,
+ const char *needle,
+ const char *placeholder)
+{
+ const char *p = strstr(haystack->buf, needle);
+ int plen, nlen;
+
+ if (!p)
+ return 0;
+
+ if (p > haystack->buf && p[-1] != '/')
+ return 0;
+
+ plen = strlen(p);
+ nlen = strlen(needle);
+ if (plen > nlen && p[nlen] != '/')
+ return 0;
+
+ strbuf_splice(haystack, p - haystack->buf, nlen,
+ placeholder, strlen(placeholder));
+ return 1;
+}
+
+static void print_compact(struct strbuf *display,
+ const char *remote, const char *local)
+{
+ struct strbuf r = STRBUF_INIT;
+ struct strbuf l = STRBUF_INIT;
+
+ if (!strcmp(remote, local)) {
+ strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+ return;
+ }
+
+ strbuf_addstr(&r, remote);
+ strbuf_addstr(&l, local);
+
+ if (!find_and_replace(&r, local, "*"))
+ find_and_replace(&l, remote, "*");
+ print_remote_to_local(display, r.buf, l.buf);
+
+ strbuf_release(&r);
+ strbuf_release(&l);
+}
+
+static void format_display(struct strbuf *display, char code,
+ const char *summary, const char *error,
+ const char *remote, const char *local)
+{
+ strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(summary));
+ if (!compact_format)
+ print_remote_to_local(display, remote, local);
+ else
+ print_compact(display, remote, local);
+ if (error)
+ strbuf_addf(display, " (%s)", error);
+}
static int update_local_ref(struct ref *ref,
const char *remote,
@@ -467,9 +593,8 @@ static int update_local_ref(struct ref *ref,
if (!oidcmp(&ref->old_oid, &ref->new_oid)) {
if (verbosity > 0)
- strbuf_addf(display, "= %-*s %-*s -> %s",
- TRANSPORT_SUMMARY(_("[up to date]")),
- REFCOL_WIDTH, remote, pretty_ref);
+ format_display(display, '=', _("[up to date]"), NULL,
+ remote, pretty_ref);
return 0;
}
@@ -481,10 +606,9 @@ static int update_local_ref(struct ref *ref,
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
- strbuf_addf(display,
- _("! %-*s %-*s -> %s (can't fetch in current branch)"),
- TRANSPORT_SUMMARY(_("[rejected]")),
- REFCOL_WIDTH, remote, pretty_ref);
+ format_display(display, '!', _("[rejected]"),
+ _("can't fetch in current branch"),
+ remote, pretty_ref);
return 1;
}
@@ -492,11 +616,9 @@ static int update_local_ref(struct ref *ref,
starts_with(ref->name, "refs/tags/")) {
int r;
r = s_update_ref("updating tag", ref, 0);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : '-',
- TRANSPORT_SUMMARY(_("[tag update]")),
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : 't', _("[tag update]"),
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
return r;
}
@@ -527,11 +649,9 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref(msg, ref, 0);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : '*',
- TRANSPORT_SUMMARY(what),
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : '*', what,
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
return r;
}
@@ -545,11 +665,9 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref("fast-forward", ref, 1);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : ' ',
- TRANSPORT_SUMMARY_WIDTH, quickref.buf,
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : ' ', quickref.buf,
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
@@ -562,18 +680,14 @@ static int update_local_ref(struct ref *ref,
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref("forced-update", ref, 1);
- strbuf_addf(display, "%c %-*s %-*s -> %s (%s)",
- r ? '!' : '+',
- TRANSPORT_SUMMARY_WIDTH, quickref.buf,
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _("unable to update local ref") : _("forced update"));
+ format_display(display, r ? '!' : '+', quickref.buf,
+ r ? _("unable to update local ref") : _("forced update"),
+ remote, pretty_ref);
strbuf_release(&quickref);
return r;
} else {
- strbuf_addf(display, "! %-*s %-*s -> %s %s",
- TRANSPORT_SUMMARY(_("[rejected]")),
- REFCOL_WIDTH, remote, pretty_ref,
- _("(non-fast-forward)"));
+ format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
+ remote, pretty_ref);
return 1;
}
}
@@ -615,11 +729,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
url = xstrdup("foreign");
rm = ref_map;
- if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+ if (check_connected(iterate_ref_map, &rm, NULL)) {
rc = error(_("%s did not send all necessary objects\n"), url);
goto abort;
}
+ prepare_format_display(ref_map);
+
/*
* We do a pass for each fetch_head_status type in their enum order, so
* merged entries are written before not-for-merge. That lets readers
@@ -714,11 +830,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
rc |= update_local_ref(ref, what, rm, &note);
free(ref);
} else
- strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
- TRANSPORT_SUMMARY_WIDTH,
- *kind ? kind : "branch",
- REFCOL_WIDTH,
- *what ? what : "HEAD");
+ format_display(&note, '*',
+ *kind ? kind : "branch", NULL,
+ *what ? what : "HEAD",
+ "FETCH_HEAD");
if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
@@ -751,6 +866,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
static int quickfetch(struct ref *ref_map)
{
struct ref *rm = ref_map;
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
/*
* If we are deepening a shallow clone we already have these
@@ -761,7 +877,8 @@ static int quickfetch(struct ref *ref_map)
*/
if (depth)
return -1;
- return check_everything_connected(iterate_ref_map, 1, &rm);
+ opt.quiet = 1;
+ return check_connected(iterate_ref_map, &rm, &opt);
}
static int fetch_refs(struct transport *transport, struct ref *ref_map)
@@ -806,19 +923,21 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
for (ref = stale_refs; ref; ref = ref->next)
string_list_append(&refnames, ref->name);
- result = delete_refs(&refnames);
+ result = delete_refs(&refnames, 0);
string_list_clear(&refnames, 0);
}
if (verbosity >= 0) {
for (ref = stale_refs; ref; ref = ref->next) {
+ struct strbuf sb = STRBUF_INIT;
if (!shown_url) {
fprintf(stderr, _("From %.*s\n"), url_len, url);
shown_url = 1;
}
- fprintf(stderr, " x %-*s %-*s -> %s\n",
- TRANSPORT_SUMMARY(_("[deleted]")),
- REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
+ format_display(&sb, '-', _("[deleted]"), NULL,
+ _("(none)"), prettify_refname(ref->name));
+ fprintf(stderr, " %s\n",sb.buf);
+ strbuf_release(&sb);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index e5658c3..ac84e99 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -272,7 +272,7 @@ static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
static void add_people_count(struct strbuf *out, struct string_list *people)
{
if (people->nr == 1)
- strbuf_addf(out, "%s", people->items[0].string);
+ strbuf_addstr(out, people->items[0].string);
else if (people->nr == 2)
strbuf_addf(out, "%s (%d) and %s (%d)",
people->items[0].string,
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 3f27456..2de272e 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -13,6 +13,7 @@
#include "dir.h"
#include "progress.h"
#include "streaming.h"
+#include "decorate.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -35,11 +36,26 @@ static int write_lost_and_found;
static int verbose;
static int show_progress = -1;
static int show_dangling = 1;
+static int name_objects;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
#define ERROR_PACK 04
#define ERROR_REFS 010
+static const char *describe_object(struct object *obj)
+{
+ static struct strbuf buf = STRBUF_INIT;
+ char *name = name_objects ?
+ lookup_decoration(fsck_walk_options.object_names, obj) : NULL;
+
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, oid_to_hex(&obj->oid));
+ if (name)
+ strbuf_addf(&buf, " (%s)", name);
+
+ return buf.buf;
+}
+
static int fsck_config(const char *var, const char *value, void *cb)
{
if (strcmp(var, "fsck.skiplist") == 0) {
@@ -67,7 +83,7 @@ static void objreport(struct object *obj, const char *msg_type,
const char *err)
{
fprintf(stderr, "%s in %s %s: %s\n",
- msg_type, typename(obj->type), oid_to_hex(&obj->oid), err);
+ msg_type, typename(obj->type), describe_object(obj), err);
}
static int objerror(struct object *obj, const char *err)
@@ -77,7 +93,8 @@ static int objerror(struct object *obj, const char *err)
return -1;
}
-static int fsck_error_func(struct object *obj, int type, const char *message)
+static int fsck_error_func(struct fsck_options *o,
+ struct object *obj, int type, const char *message)
{
objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
return (type == FSCK_WARN) ? 0 : 1;
@@ -97,7 +114,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
if (!obj) {
/* ... these references to parent->fld are safe here */
printf("broken link from %7s %s\n",
- typename(parent->type), oid_to_hex(&parent->oid));
+ typename(parent->type), describe_object(parent));
printf("broken link from %7s %s\n",
(type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
errors_found |= ERROR_REACHABLE;
@@ -114,9 +131,9 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
if (!(obj->flags & HAS_OBJ)) {
if (parent && !has_object_file(&obj->oid)) {
printf("broken link from %7s %s\n",
- typename(parent->type), oid_to_hex(&parent->oid));
+ typename(parent->type), describe_object(parent));
printf(" to %7s %s\n",
- typename(obj->type), oid_to_hex(&obj->oid));
+ typename(obj->type), describe_object(obj));
errors_found |= ERROR_REACHABLE;
}
return 1;
@@ -190,7 +207,8 @@ static void check_reachable_object(struct object *obj)
return; /* it is in pack - forget about it */
if (connectivity_only && has_object_file(&obj->oid))
return;
- printf("missing %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
+ printf("missing %s %s\n", typename(obj->type),
+ describe_object(obj));
errors_found |= ERROR_REACHABLE;
return;
}
@@ -215,7 +233,8 @@ static void check_unreachable_object(struct object *obj)
* since this is something that is prunable.
*/
if (show_unreachable) {
- printf("unreachable %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
+ printf("unreachable %s %s\n", typename(obj->type),
+ describe_object(obj));
return;
}
@@ -234,11 +253,11 @@ static void check_unreachable_object(struct object *obj)
if (!obj->used) {
if (show_dangling)
printf("dangling %s %s\n", typename(obj->type),
- oid_to_hex(&obj->oid));
+ describe_object(obj));
if (write_lost_and_found) {
char *filename = git_pathdup("lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
- oid_to_hex(&obj->oid));
+ describe_object(obj));
FILE *f;
if (safe_create_leading_directories_const(filename)) {
@@ -252,7 +271,7 @@ static void check_unreachable_object(struct object *obj)
if (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))
die_errno("Could not write '%s'", filename);
} else
- fprintf(f, "%s\n", oid_to_hex(&obj->oid));
+ fprintf(f, "%s\n", describe_object(obj));
if (fclose(f))
die_errno("Could not finish '%s'",
filename);
@@ -271,7 +290,7 @@ static void check_unreachable_object(struct object *obj)
static void check_object(struct object *obj)
{
if (verbose)
- fprintf(stderr, "Checking %s\n", oid_to_hex(&obj->oid));
+ fprintf(stderr, "Checking %s\n", describe_object(obj));
if (obj->flags & REACHABLE)
check_reachable_object(obj);
@@ -307,7 +326,7 @@ static int fsck_obj(struct object *obj)
if (verbose)
fprintf(stderr, "Checking %s %s\n",
- typename(obj->type), oid_to_hex(&obj->oid));
+ typename(obj->type), describe_object(obj));
if (fsck_walk(obj, NULL, &fsck_obj_options))
objerror(obj, "broken links");
@@ -326,15 +345,17 @@ static int fsck_obj(struct object *obj)
free_commit_buffer(commit);
if (!commit->parents && show_root)
- printf("root %s\n", oid_to_hex(&commit->object.oid));
+ printf("root %s\n", describe_object(&commit->object));
}
if (obj->type == OBJ_TAG) {
struct tag *tag = (struct tag *) obj;
if (show_tags && tag->tagged) {
- printf("tagged %s %s", typename(tag->tagged->type), oid_to_hex(&tag->tagged->oid));
- printf(" (%s) in %s\n", tag->tag, oid_to_hex(&tag->object.oid));
+ printf("tagged %s %s", typename(tag->tagged->type),
+ describe_object(tag->tagged));
+ printf(" (%s) in %s\n", tag->tag,
+ describe_object(&tag->object));
}
}
@@ -356,6 +377,10 @@ static int fsck_sha1(const unsigned char *sha1)
static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
unsigned long size, void *buffer, int *eaten)
{
+ /*
+ * Note, buffer may be NULL if type is OBJ_BLOB. See
+ * verify_packfile(), data_valid variable for details.
+ */
struct object *obj;
obj = parse_object_buffer(sha1, type, size, buffer, eaten);
if (!obj) {
@@ -368,13 +393,18 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
static int default_refs;
-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)
+static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
+ unsigned long timestamp)
{
struct object *obj;
if (!is_null_sha1(sha1)) {
obj = lookup_object(sha1);
if (obj) {
+ if (timestamp && name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj,
+ xstrfmt("%s@{%ld}", refname, timestamp));
obj->used = 1;
mark_object_reachable(obj);
} else {
@@ -394,8 +424,8 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
fprintf(stderr, "Checking reflog %s->%s\n",
sha1_to_hex(osha1), sha1_to_hex(nsha1));
- fsck_handle_reflog_sha1(refname, osha1);
- fsck_handle_reflog_sha1(refname, nsha1);
+ fsck_handle_reflog_sha1(refname, osha1, 0);
+ fsck_handle_reflog_sha1(refname, nsha1, timestamp);
return 0;
}
@@ -424,6 +454,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
}
default_refs++;
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(refname));
mark_object_reachable(obj);
return 0;
@@ -539,6 +572,9 @@ static int fsck_cache_tree(struct cache_tree *it)
return 1;
}
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(":"));
mark_object_reachable(obj);
if (obj->type != OBJ_TREE)
err |= objerror(obj, "non-tree in cache-tree");
@@ -567,6 +603,7 @@ static struct option fsck_opts[] = {
OPT_BOOL(0, "lost-found", &write_lost_and_found,
N_("write dangling objects in .git/lost-found")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+ OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
OPT_END(),
};
@@ -596,6 +633,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
include_reflogs = 0;
}
+ if (name_objects)
+ fsck_walk_options.object_names =
+ xcalloc(1, sizeof(struct decoration));
+
git_config(fsck_config, NULL);
fsck_head_link();
@@ -651,6 +692,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
continue;
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj, xstrdup(arg));
mark_object_reachable(obj);
heads++;
continue;
@@ -683,6 +727,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
continue;
obj = &blob->object;
obj->used = 1;
+ if (name_objects)
+ add_decoration(fsck_walk_options.object_names,
+ obj,
+ xstrfmt(":%s", active_cache[i]->name));
mark_object_reachable(obj);
}
if (active_cache_tree)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index e8c71fc..1d2ea58 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -77,6 +77,7 @@ static int strict;
static int do_fsck_object;
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
static int verbose;
+static int show_resolving_progress;
static int show_stat;
static int check_self_contained_and_connected;
@@ -338,10 +339,10 @@ static void parse_pack_header(void)
use(sizeof(struct pack_header));
}
-static NORETURN void bad_object(unsigned long offset, const char *format,
+static NORETURN void bad_object(off_t offset, const char *format,
...) __attribute__((format (printf, 2, 3)));
-static NORETURN void bad_object(unsigned long offset, const char *format, ...)
+static NORETURN void bad_object(off_t offset, const char *format, ...)
{
va_list params;
char buf[1024];
@@ -349,7 +350,8 @@ static NORETURN void bad_object(unsigned long offset, const char *format, ...)
va_start(params, format);
vsnprintf(buf, sizeof(buf), format, params);
va_end(params);
- die(_("pack has bad object at offset %lu: %s"), offset, buf);
+ die(_("pack has bad object at offset %"PRIuMAX": %s"),
+ (uintmax_t)offset, buf);
}
static inline struct thread_local *get_thread_data(void)
@@ -429,7 +431,7 @@ static int is_delta_type(enum object_type type)
return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
}
-static void *unpack_entry_data(unsigned long offset, unsigned long size,
+static void *unpack_entry_data(off_t offset, unsigned long size,
enum object_type type, unsigned char *sha1)
{
static char fixed_buf[8192];
@@ -549,13 +551,13 @@ static void *unpack_data(struct object_entry *obj,
void *cb_data)
{
off_t from = obj[0].idx.offset + obj[0].hdr_size;
- unsigned long len = obj[1].idx.offset - from;
+ off_t len = obj[1].idx.offset - from;
unsigned char *data, *inbuf;
git_zstream stream;
int status;
data = xmallocz(consume ? 64*1024 : obj->size);
- inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
+ inbuf = xmalloc((len < 64*1024) ? (int)len : 64*1024);
memset(&stream, 0, sizeof(stream));
git_inflate_init(&stream);
@@ -563,15 +565,15 @@ static void *unpack_data(struct object_entry *obj,
stream.avail_out = consume ? 64*1024 : obj->size;
do {
- ssize_t n = (len < 64*1024) ? len : 64*1024;
+ ssize_t n = (len < 64*1024) ? (ssize_t)len : 64*1024;
n = xpread(get_thread_data()->pack_fd, inbuf, n, from);
if (n < 0)
die_errno(_("cannot pread pack file"));
if (!n)
- die(Q_("premature end of pack file, %lu byte missing",
- "premature end of pack file, %lu bytes missing",
- len),
- len);
+ die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
+ "premature end of pack file, %"PRIuMAX" bytes missing",
+ (unsigned int)len),
+ (uintmax_t)len);
from += n;
len -= n;
stream.next_in = inbuf;
@@ -1190,7 +1192,7 @@ static void resolve_deltas(void)
qsort(ref_deltas, nr_ref_deltas, sizeof(struct ref_delta_entry),
compare_ref_delta_entry);
- if (verbose)
+ if (verbose || show_resolving_progress)
progress = start_progress(_("Resolving deltas"),
nr_ref_deltas + nr_ofs_deltas);
@@ -1625,6 +1627,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
struct pack_idx_option opts;
unsigned char pack_sha1[20];
unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */
+ int report_end_of_input = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(index_pack_usage);
@@ -1694,6 +1697,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
input_len = sizeof(*hdr);
} else if (!strcmp(arg, "-v")) {
verbose = 1;
+ } else if (!strcmp(arg, "--show-resolving-progress")) {
+ show_resolving_progress = 1;
+ } else if (!strcmp(arg, "--report-end-of-input")) {
+ report_end_of_input = 1;
} else if (!strcmp(arg, "-o")) {
if (index_name || (i+1) >= argc)
usage(index_pack_usage);
@@ -1751,6 +1758,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat));
ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry));
parse_pack_objects(pack_sha1);
+ if (report_end_of_input)
+ write_in_full(2, "\0", 1);
resolve_deltas();
conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
free(ofs_deltas);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index b2d8d40..3a45f0b 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -397,13 +397,16 @@ int init_db(const char *template_dir, unsigned int flags)
if (!(flags & INIT_DB_QUIET)) {
int len = strlen(git_dir);
- /* TRANSLATORS: The first '%s' is either "Reinitialized
- existing" or "Initialized empty", the second " shared" or
- "", and the last '%s%s' is the verbatim directory name. */
- printf(_("%s%s Git repository in %s%s\n"),
- reinit ? _("Reinitialized existing") : _("Initialized empty"),
- get_shared_repository() ? _(" shared") : "",
- git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+ if (reinit)
+ printf(get_shared_repository()
+ ? _("Reinitialized existing shared Git repository in %s%s\n")
+ : _("Reinitialized existing Git repository in %s%s\n"),
+ git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+ else
+ printf(get_shared_repository()
+ ? _("Initialized empty shared Git repository in %s%s\n")
+ : _("Initialized empty Git repository in %s%s\n"),
+ git_dir, len && git_dir[len-1] != '/' ? "/" : "");
}
return 0;
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index b99ae4b..175f147 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -20,7 +20,7 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
{
int in_place = 0;
int trim_empty = 0;
- struct string_list trailers = STRING_LIST_INIT_DUP;
+ struct string_list trailers = STRING_LIST_INIT_NODUP;
struct option options[] = {
OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
diff --git a/builtin/log.c b/builtin/log.c
index 099f4f7..1f116be 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -33,6 +33,7 @@ static const char *default_date_mode = NULL;
static int default_abbrev_commit;
static int default_show_root = 1;
static int default_follow;
+static int default_show_signature;
static int decoration_style;
static int decoration_given;
static int use_mailmap_config;
@@ -119,6 +120,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
rev->abbrev_commit = default_abbrev_commit;
rev->show_root_diff = default_show_root;
rev->subject_prefix = fmt_patch_subject_prefix;
+ rev->show_signature = default_show_signature;
DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
if (default_date_mode)
@@ -236,16 +238,17 @@ static void show_early_header(struct rev_info *rev, const char *stage, int nr)
if (rev->commit_format != CMIT_FMT_ONELINE)
putchar(rev->diffopt.line_termination);
}
- printf(_("Final output: %d %s\n"), nr, stage);
+ fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage);
}
static struct itimerval early_output_timer;
static void log_show_early(struct rev_info *revs, struct commit_list *list)
{
- int i = revs->early_output;
+ int i = revs->early_output, close_file = revs->diffopt.close_file;
int show_header = 1;
+ revs->diffopt.close_file = 0;
sort_in_topological_order(&list, revs->sort_order);
while (list && i) {
struct commit *commit = list->item;
@@ -262,14 +265,19 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
case commit_ignore:
break;
case commit_error:
+ if (close_file)
+ fclose(revs->diffopt.file);
return;
}
list = list->next;
}
/* Did we already get enough commits for the early output? */
- if (!i)
+ if (!i) {
+ if (close_file)
+ fclose(revs->diffopt.file);
return;
+ }
/*
* ..if no, then repeat it twice a second until we
@@ -331,7 +339,7 @@ static int cmd_log_walk(struct rev_info *rev)
{
struct commit *commit;
int saved_nrl = 0;
- int saved_dcctc = 0;
+ int saved_dcctc = 0, close_file = rev->diffopt.close_file;
if (rev->early_output)
setup_early_output(rev);
@@ -347,6 +355,7 @@ static int cmd_log_walk(struct rev_info *rev)
* and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
* retain that state information if replacing rev->diffopt in this loop
*/
+ rev->diffopt.close_file = 0;
while ((commit = get_revision(rev)) != NULL) {
if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
/*
@@ -367,6 +376,8 @@ static int cmd_log_walk(struct rev_info *rev)
}
rev->diffopt.degraded_cc_to_c = saved_dcctc;
rev->diffopt.needed_rename_limit = saved_nrl;
+ if (close_file)
+ fclose(rev->diffopt.file);
if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
@@ -409,6 +420,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
use_mailmap_config = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "log.showsignature")) {
+ default_show_signature = git_config_bool(var, value);
+ return 0;
+ }
if (grep_config(var, value, cb) < 0)
return -1;
@@ -445,7 +460,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)
pp.fmt = rev->commit_format;
pp.date_mode = rev->date_mode;
pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
- printf("%s", out.buf);
+ fprintf(rev->diffopt.file, "%s", out.buf);
strbuf_release(&out);
}
@@ -456,7 +471,7 @@ static int show_blob_object(const unsigned char *sha1, struct rev_info *rev, con
char *buf;
unsigned long size;
- fflush(stdout);
+ fflush(rev->diffopt.file);
if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
!DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
return stream_blob_to_fd(1, sha1, NULL, 0);
@@ -496,7 +511,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
}
if (offset < size)
- fwrite(buf + offset, size - offset, 1, stdout);
+ fwrite(buf + offset, size - offset, 1, rev->diffopt.file);
free(buf);
return 0;
}
@@ -505,7 +520,8 @@ static int show_tree_object(const unsigned char *sha1,
struct strbuf *base,
const char *pathname, unsigned mode, int stage, void *context)
{
- printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+ FILE *file = context;
+ fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
return 0;
}
@@ -565,7 +581,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
if (rev.shown_one)
putchar('\n');
- printf("%stag %s%s\n",
+ fprintf(rev.diffopt.file, "%stag %s%s\n",
diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
t->tag,
diff_get_color_opt(&rev.diffopt, DIFF_RESET));
@@ -584,12 +600,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)
case OBJ_TREE:
if (rev.shown_one)
putchar('\n');
- printf("%stree %s%s\n\n",
+ fprintf(rev.diffopt.file, "%stree %s%s\n\n",
diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
name,
diff_get_color_opt(&rev.diffopt, DIFF_RESET));
read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
- show_tree_object, NULL);
+ show_tree_object, rev.diffopt.file);
rev.shown_one = 1;
break;
case OBJ_COMMIT:
@@ -674,9 +690,9 @@ static int auto_number = 1;
static char *default_attach = NULL;
-static struct string_list extra_hdr;
-static struct string_list extra_to;
-static struct string_list extra_cc;
+static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
+static struct string_list extra_to = STRING_LIST_INIT_NODUP;
+static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
static void add_header(const char *value)
{
@@ -703,6 +719,7 @@ static void add_header(const char *value)
static int thread;
static int do_signoff;
static int base_auto;
+static char *from;
static const char *signature = git_version_string;
static const char *signature_file;
static int config_cover_letter;
@@ -791,15 +808,25 @@ static int git_format_config(const char *var, const char *value, void *cb)
base_auto = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "format.from")) {
+ int b = git_config_maybe_bool(var, value);
+ free(from);
+ if (b < 0)
+ from = xstrdup(value);
+ else if (b)
+ from = xstrdup(git_committer_info(IDENT_NO_DATE));
+ else
+ from = NULL;
+ return 0;
+ }
return git_log_config(var, value, cb);
}
-static FILE *realstdout = NULL;
static const char *output_directory = NULL;
static int outdir_offset;
-static int reopen_stdout(struct commit *commit, const char *subject,
+static int open_next_file(struct commit *commit, const char *subject,
struct rev_info *rev, int quiet)
{
struct strbuf filename = STRBUF_INIT;
@@ -821,9 +848,9 @@ static int reopen_stdout(struct commit *commit, const char *subject,
fmt_output_subject(&filename, subject, rev);
if (!quiet)
- fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
+ printf("%s\n", filename.buf + outdir_offset);
- if (freopen(filename.buf, "w", stdout) == NULL)
+ if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL)
return error(_("Cannot open patch file %s"), filename.buf);
strbuf_release(&filename);
@@ -882,15 +909,15 @@ static void gen_message_id(struct rev_info *info, char *base)
info->message_id = strbuf_detach(&buf, NULL);
}
-static void print_signature(void)
+static void print_signature(FILE *file)
{
if (!signature || !*signature)
return;
- printf("-- \n%s", signature);
+ fprintf(file, "-- \n%s", signature);
if (signature[strlen(signature)-1] != '\n')
- putchar('\n');
- putchar('\n');
+ putc('\n', file);
+ putc('\n', file);
}
static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@ -953,13 +980,13 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
struct pretty_print_context pp = {0};
struct commit *head = list[0];
- if (rev->commit_format != CMIT_FMT_EMAIL)
+ if (!cmit_fmt_is_mail(rev->commit_format))
die(_("Cover letter needs email format"));
committer = git_committer_info(0);
if (!use_stdout &&
- reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
+ open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
return;
log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
@@ -982,7 +1009,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
pp_remainder(&pp, &msg, &sb, 0);
add_branch_description(&sb, branch_name);
- printf("%s\n", sb.buf);
+ fprintf(rev->diffopt.file, "%s\n", sb.buf);
strbuf_release(&sb);
@@ -991,6 +1018,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
log.wrap = 72;
log.in1 = 2;
log.in2 = 4;
+ log.file = rev->diffopt.file;
for (i = 0; i < nr; i++)
shortlog_add_commit(&log, list[i]);
@@ -1013,8 +1041,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
diffcore_std(&opts);
diff_flush(&opts);
- printf("\n");
- print_signature();
+ fprintf(rev->diffopt.file, "\n");
+ print_signature(rev->diffopt.file);
}
static const char *clean_message_id(const char *msg_id)
@@ -1324,7 +1352,7 @@ static void prepare_bases(struct base_tree_info *bases,
}
}
-static void print_bases(struct base_tree_info *bases)
+static void print_bases(struct base_tree_info *bases, FILE *file)
{
int i;
@@ -1333,11 +1361,11 @@ static void print_bases(struct base_tree_info *bases)
return;
/* Show the base commit */
- printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
+ fprintf(file, "base-commit: %s\n", oid_to_hex(&bases->base_commit));
/* Show the prerequisite patches */
for (i = bases->nr_patch_id - 1; i >= 0; i--)
- printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
+ fprintf(file, "prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
free(bases->patch_id);
bases->nr_patch_id = 0;
@@ -1368,7 +1396,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int quiet = 0;
int reroll_count = -1;
char *branch_name = NULL;
- char *from = NULL;
char *base_commit = NULL;
struct base_tree_info bases;
@@ -1569,6 +1596,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
setup_pager();
if (output_directory) {
+ if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
+ rev.diffopt.use_color = GIT_COLOR_NEVER;
if (use_stdout)
die(_("standard output, or directory, which one?"));
if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
@@ -1626,9 +1655,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
get_patch_ids(&rev, &ids);
}
- if (!use_stdout)
- realstdout = xfdopen(xdup(1), "w");
-
if (prepare_revision_walk(&rev))
die(_("revision walk setup failed"));
rev.boundary = 1;
@@ -1693,7 +1719,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout,
origin, nr, list, branch_name, quiet);
- print_bases(&bases);
+ print_bases(&bases, rev.diffopt.file);
total++;
start_number--;
}
@@ -1739,7 +1765,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
if (!use_stdout &&
- reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
+ open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
die(_("Failed to create output files"));
shown = log_tree_commit(&rev, commit);
free_commit_buffer(commit);
@@ -1754,15 +1780,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.shown_one = 0;
if (shown) {
if (rev.mime_boundary)
- printf("\n--%s%s--\n\n\n",
+ fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n",
mime_boundary_leader,
rev.mime_boundary);
else
- print_signature();
- print_bases(&bases);
+ print_signature(rev.diffopt.file);
+ print_bases(&bases, rev.diffopt.file);
}
if (!use_stdout)
- fclose(stdout);
+ fclose(rev.diffopt.file);
}
free(list);
free(branch_name);
@@ -1794,15 +1820,15 @@ static const char * const cherry_usage[] = {
};
static void print_commit(char sign, struct commit *commit, int verbose,
- int abbrev)
+ int abbrev, FILE *file)
{
if (!verbose) {
- printf("%c %s\n", sign,
+ fprintf(file, "%c %s\n", sign,
find_unique_abbrev(commit->object.oid.hash, abbrev));
} else {
struct strbuf buf = STRBUF_INIT;
pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
- printf("%c %s %s\n", sign,
+ fprintf(file, "%c %s %s\n", sign,
find_unique_abbrev(commit->object.oid.hash, abbrev),
buf.buf);
strbuf_release(&buf);
@@ -1883,7 +1909,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
commit = list->item;
if (has_commit_patch_id(commit, &ids))
sign = '-';
- print_commit(sign, commit, verbose, abbrev);
+ print_commit(sign, commit, verbose, abbrev, revs.diffopt.file);
list = list->next;
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index f02e3d2..00ea91a 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)
*/
pos = cache_name_pos(ent->name, ent->len);
if (0 <= pos)
- die("bug in show-killed-files");
+ die("BUG: killed-file %.*s not found",
+ ent->len, ent->name);
pos = -pos - 1;
while (pos < active_nr &&
ce_stage(active_cache[pos]))
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 4859ede..3068168 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -45,6 +45,19 @@ static int is_from_line(const char *line, int len)
static struct strbuf buf = STRBUF_INIT;
static int keep_cr;
+static int mboxrd;
+
+static int is_gtfrom(const struct strbuf *buf)
+{
+ size_t min = strlen(">From ");
+ size_t ngt;
+
+ if (buf->len < min)
+ return 0;
+
+ ngt = strspn(buf->buf, ">");
+ return ngt && starts_with(buf->buf + ngt, "From ");
+}
/* Called with the first line (potentially partial)
* already in buf[] -- normally that should begin with
@@ -77,6 +90,9 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
strbuf_addch(&buf, '\n');
}
+ if (mboxrd && is_gtfrom(&buf))
+ strbuf_remove(&buf, 0, 1);
+
if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
die_errno("cannot write output");
@@ -271,6 +287,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
keep_cr = 1;
} else if ( arg[1] == 'o' && arg[2] ) {
dir = arg+2;
+ } else if (!strcmp(arg, "--mboxrd")) {
+ mboxrd = 1;
} else if ( arg[1] == '-' && !arg[2] ) {
argp++; /* -- marks end of options */
break;
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index 491efd5..fd2c455 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -9,10 +9,10 @@ static const char builtin_merge_recursive_usage[] =
static const char *better_branch_name(const char *branch)
{
- static char githead_env[8 + 40 + 1];
+ static char githead_env[8 + GIT_SHA1_HEXSZ + 1];
char *name;
- if (strlen(branch) != 40)
+ if (strlen(branch) != GIT_SHA1_HEXSZ)
return branch;
xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
name = getenv(githead_env);
@@ -21,10 +21,10 @@ static const char *better_branch_name(const char *branch)
int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
{
- const unsigned char *bases[21];
+ const struct object_id *bases[21];
unsigned bases_count = 0;
int i, failed;
- unsigned char h1[20], h2[20];
+ struct object_id h1, h2;
struct merge_options o;
struct commit *result;
@@ -46,10 +46,10 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
continue;
}
if (bases_count < ARRAY_SIZE(bases)-1) {
- unsigned char *sha = xmalloc(20);
- if (get_sha1(argv[i], sha))
+ struct object_id *oid = xmalloc(sizeof(struct object_id));
+ if (get_oid(argv[i], oid))
die("Could not parse object '%s'", argv[i]);
- bases[bases_count++] = sha;
+ bases[bases_count++] = oid;
}
else
warning("Cannot handle more than %d bases. "
@@ -62,9 +62,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
o.branch1 = argv[++i];
o.branch2 = argv[++i];
- if (get_sha1(o.branch1, h1))
+ if (get_oid(o.branch1, &h1))
die("Could not resolve ref '%s'", o.branch1);
- if (get_sha1(o.branch2, h2))
+ if (get_oid(o.branch2, &h2))
die("Could not resolve ref '%s'", o.branch2);
o.branch1 = better_branch_name(o.branch1);
@@ -73,7 +73,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
if (o.verbosity >= 3)
printf("Merging %s with %s\n", o.branch1, o.branch2);
- failed = merge_recursive_generic(&o, h1, h2, bases_count, bases, &result);
+ failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
if (failed < 0)
return 128; /* die() error code */
return failed;
diff --git a/builtin/merge.c b/builtin/merge.c
index b555a1b..0ae099f 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -30,6 +30,7 @@
#include "fmt-merge-msg.h"
#include "gpg-interface.h"
#include "sequencer.h"
+#include "string-list.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -211,7 +212,7 @@ static struct option builtin_merge_options[] = {
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, FF_ONLY },
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
OPT_BOOL(0, "verify-signatures", &verify_signatures,
- N_("Verify that the named commit has a valid GPG signature")),
+ N_("verify that the named commit has a valid GPG signature")),
OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
N_("merge strategy to use"), option_parse_strategy),
OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
@@ -336,15 +337,9 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
struct rev_info rev;
struct strbuf out = STRBUF_INIT;
struct commit_list *j;
- const char *filename;
- int fd;
struct pretty_print_context ctx = {0};
printf(_("Squash commit -- not updating HEAD\n"));
- filename = git_path_squash_msg();
- fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not write to '%s'"), filename);
init_revisions(&rev, NULL);
rev.ignore_merges = 1;
@@ -371,10 +366,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
oid_to_hex(&commit->object.oid));
pretty_print_commit(&ctx, commit, &out);
}
- if (write_in_full(fd, out.buf, out.len) != out.len)
- die_errno(_("Writing SQUASH_MSG"));
- if (close(fd))
- die_errno(_("Finishing SQUASH_MSG"));
+ write_file_buf(git_path_squash_msg(), out.buf, out.len);
strbuf_release(&out);
}
@@ -501,7 +493,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (ref_exists(truname.buf)) {
strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n",
- sha1_to_hex(remote_head->object.oid.hash),
+ oid_to_hex(&remote_head->object.oid),
truname.buf + 11,
(early ? " (early part)" : ""));
strbuf_release(&truname);
@@ -515,7 +507,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
desc = merge_remote_util(remote_head);
if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
strbuf_addf(msg, "%s\t\t%s '%s'\n",
- sha1_to_hex(desc->obj->oid.hash),
+ oid_to_hex(&desc->obj->oid),
typename(desc->obj->type),
remote);
goto cleanup;
@@ -523,7 +515,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
}
strbuf_addf(msg, "%s\t\tcommit '%s'\n",
- sha1_to_hex(remote_head->object.oid.hash), remote);
+ oid_to_hex(&remote_head->object.oid), remote);
cleanup:
strbuf_release(&buf);
strbuf_release(&bname);
@@ -682,6 +674,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
hold_locked_index(&lock, 1);
clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result);
+ if (clean < 0)
+ exit(128);
if (active_cache_changed &&
write_locked_index(&the_index, &lock, COMMIT_LOCK))
die (_("unable to write %s"), get_index_file());
@@ -712,42 +706,17 @@ static int count_unmerged_entries(void)
return ret;
}
-static void split_merge_strategies(const char *string, struct strategy **list,
- int *nr, int *alloc)
-{
- char *p, *q, *buf;
-
- if (!string)
- return;
-
- buf = xstrdup(string);
- q = buf;
- for (;;) {
- p = strchr(q, ' ');
- if (!p) {
- ALLOC_GROW(*list, *nr + 1, *alloc);
- (*list)[(*nr)++].name = xstrdup(q);
- free(buf);
- return;
- } else {
- *p = '\0';
- ALLOC_GROW(*list, *nr + 1, *alloc);
- (*list)[(*nr)++].name = xstrdup(q);
- q = ++p;
- }
- }
-}
-
static void add_strategies(const char *string, unsigned attr)
{
- struct strategy *list = NULL;
- int list_alloc = 0, list_nr = 0, i;
-
- memset(&list, 0, sizeof(list));
- split_merge_strategies(string, &list, &list_nr, &list_alloc);
- if (list) {
- for (i = 0; i < list_nr; i++)
- append_strategy(get_strategy(list[i].name));
+ int i;
+
+ if (string) {
+ struct string_list list = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
+ string_list_split(&list, string, ' ', -1);
+ for_each_string_list_item(item, &list)
+ append_strategy(get_strategy(item->string));
+ string_list_clear(&list, 0);
return;
}
for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
@@ -756,18 +725,6 @@ static void add_strategies(const char *string, unsigned attr)
}
-static void write_merge_msg(struct strbuf *msg)
-{
- const char *filename = git_path_merge_msg();
- int fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- filename);
- if (write_in_full(fd, msg->buf, msg->len) != msg->len)
- die_errno(_("Could not write to '%s'"), filename);
- close(fd);
-}
-
static void read_merge_msg(struct strbuf *msg)
{
const char *filename = git_path_merge_msg();
@@ -801,7 +758,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
strbuf_addch(&msg, '\n');
if (0 < option_edit)
strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
- write_merge_msg(&msg);
+ write_file_buf(git_path_merge_msg(), msg.buf, msg.len);
if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
git_path_merge_msg(), "merge", NULL))
abort_commit(remoteheads, NULL);
@@ -964,8 +921,6 @@ static int setup_with_upstream(const char ***argv)
static void write_merge_state(struct commit_list *remoteheads)
{
- const char *filename;
- int fd;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
@@ -979,26 +934,14 @@ static void write_merge_state(struct commit_list *remoteheads)
}
strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
}
- filename = git_path_merge_head();
- fd = open(filename, O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"), filename);
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), filename);
- close(fd);
+ write_file_buf(git_path_merge_head(), buf.buf, buf.len);
strbuf_addch(&merge_msg, '\n');
- write_merge_msg(&merge_msg);
+ write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
- filename = git_path_merge_mode();
- fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"), filename);
strbuf_reset(&buf);
if (fast_forward == FF_NO)
strbuf_addf(&buf, "no-ff");
- if (write_in_full(fd, buf.buf, buf.len) != buf.len)
- die_errno(_("Could not write to '%s'"), filename);
- close(fd);
+ write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
}
static int default_edit_option(void)
@@ -1014,7 +957,7 @@ static int default_edit_option(void)
if (e) {
int v = git_config_maybe_bool(name, e);
if (v < 0)
- die("Bad value '%s' in environment '%s'", e, name);
+ die(_("Bad value '%s' in environment '%s'"), e, name);
return v;
}
@@ -1115,7 +1058,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
if (!commit) {
if (ptr)
*ptr = '\0';
- die("not something we can merge in %s: %s",
+ die(_("not something we can merge in %s: %s"),
filename, merge_names->buf + pos);
}
remotes = &commit_list_insert(commit, remotes)->next;
@@ -1149,7 +1092,7 @@ static struct commit_list *collect_parents(struct commit *head_commit,
struct commit *commit = get_merge_parent(argv[i]);
if (!commit)
help_unknown_ref(argv[i], "merge",
- "not something we can merge");
+ _("not something we can merge"));
remotes = &commit_list_insert(commit, remotes)->next;
}
remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
@@ -1366,7 +1309,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
for (p = remoteheads; p; p = p->next) {
struct commit *commit = p->item;
strbuf_addf(&buf, "GITHEAD_%s",
- sha1_to_hex(commit->object.oid.hash));
+ oid_to_hex(&commit->object.oid));
setenv(buf.buf, merge_remote_util(commit)->name, 1);
strbuf_reset(&buf);
if (fast_forward != FF_ONLY &&
@@ -1421,11 +1364,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* If head can reach all the merge then we are up to date.
* but first the most common case of merging one remote.
*/
- finish_up_to_date("Already up-to-date.");
+ finish_up_to_date(_("Already up-to-date."));
goto done;
} else if (fast_forward != FF_NO && !remoteheads->next &&
!common->next &&
- !hashcmp(common->item->object.oid.hash, head_commit->object.oid.hash)) {
+ !oidcmp(&common->item->object.oid, &head_commit->object.oid)) {
/* Again the most common case of merging one remote. */
struct strbuf msg = STRBUF_INIT;
struct commit *commit;
@@ -1499,14 +1442,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* HEAD^^" would be missed.
*/
common_one = get_merge_bases(head_commit, j->item);
- if (hashcmp(common_one->item->object.oid.hash,
- j->item->object.oid.hash)) {
+ if (oidcmp(&common_one->item->object.oid, &j->item->object.oid)) {
up_to_date = 0;
break;
}
}
if (up_to_date) {
- finish_up_to_date("Already up-to-date. Yeeah!");
+ finish_up_to_date(_("Already up-to-date. Yeeah!"));
goto done;
}
}
@@ -1530,7 +1472,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Stash away the local changes so that we can try more than one.
*/
save_state(stash))
- hashcpy(stash, null_sha1);
+ hashclr(stash);
for (i = 0; i < use_strategies_nr; i++) {
int ret;
diff --git a/builtin/notes.c b/builtin/notes.c
index c65b59a..f848b89 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -91,7 +91,7 @@ static const char * const git_notes_get_ref_usage[] = {
};
static const char note_template[] =
- "\nWrite/edit the notes for the following object:\n";
+ N_("Write/edit the notes for the following object:");
struct note_data {
int given;
@@ -179,7 +179,8 @@ static void prepare_note_data(const unsigned char *object, struct note_data *d,
copy_obj_to_fd(fd, old_note);
strbuf_addch(&buf, '\n');
- strbuf_add_commented_lines(&buf, note_template, strlen(note_template));
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
+ strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
strbuf_addch(&buf, '\n');
write_or_die(fd, buf.buf, buf.len);
@@ -749,7 +750,7 @@ static int git_config_get_notes_strategy(const char *key,
if (git_config_get_string(key, &value))
return 1;
if (parse_notes_merge_strategy(value, strategy))
- git_die_config(key, "unknown notes merge strategy %s", value);
+ git_die_config(key, _("unknown notes merge strategy %s"), value);
free(value);
return 0;
@@ -788,15 +789,15 @@ static int merge(int argc, const char **argv, const char *prefix)
if (strategy || do_commit + do_abort == 0)
do_merge = 1;
if (do_merge + do_commit + do_abort != 1) {
- error("cannot mix --commit, --abort or -s/--strategy");
+ error(_("cannot mix --commit, --abort or -s/--strategy"));
usage_with_options(git_notes_merge_usage, options);
}
if (do_merge && argc != 1) {
- error("Must specify a notes ref to merge");
+ error(_("Must specify a notes ref to merge"));
usage_with_options(git_notes_merge_usage, options);
} else if (!do_merge && argc) {
- error("too many parameters");
+ error(_("too many parameters"));
usage_with_options(git_notes_merge_usage, options);
}
@@ -817,7 +818,7 @@ static int merge(int argc, const char **argv, const char *prefix)
if (strategy) {
if (parse_notes_merge_strategy(strategy, &o.strategy)) {
- error("Unknown -s/--strategy: %s", strategy);
+ error(_("Unknown -s/--strategy: %s"), strategy);
usage_with_options(git_notes_merge_usage, options);
}
} else {
@@ -857,11 +858,11 @@ static int merge(int argc, const char **argv, const char *prefix)
die(_("A notes merge into %s is already in-progress at %s"),
default_notes_ref(), wt->path);
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
- die("Failed to store link to current notes ref (%s)",
+ die(_("Failed to store link to current notes ref (%s)"),
default_notes_ref());
- printf("Automatic notes merge failed. Fix conflicts in %s and "
- "commit the result with 'git notes merge --commit', or "
- "abort the merge with 'git notes merge --abort'.\n",
+ printf(_("Automatic notes merge failed. Fix conflicts in %s and "
+ "commit the result with 'git notes merge --commit', or "
+ "abort the merge with 'git notes merge --abort'.\n"),
git_path(NOTES_MERGE_WORKTREE));
}
@@ -934,8 +935,8 @@ static int prune(int argc, const char **argv, const char *prefix)
struct notes_tree *t;
int show_only = 0, verbose = 0;
struct option options[] = {
- OPT__DRY_RUN(&show_only, "do not remove, show only"),
- OPT__VERBOSE(&verbose, "report pruned notes"),
+ OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
+ OPT__VERBOSE(&verbose, N_("report pruned notes")),
OPT_END()
};
@@ -964,7 +965,7 @@ static int get_ref(int argc, const char **argv, const char *prefix)
git_notes_get_ref_usage, 0);
if (argc) {
- error("too many parameters");
+ error(_("too many parameters"));
usage_with_options(git_notes_get_ref_usage, options);
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 8f5e358..4a63398 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,9 @@ static int non_empty;
static int reuse_delta = 1, reuse_object = 1;
static int keep_unreachable, unpack_unreachable, include_tag;
static unsigned long unpack_unreachable_expiration;
+static int pack_loose_unreachable;
static int local;
+static int have_non_local_packs;
static int incremental;
static int ignore_packed_keep;
static int allow_ofs_delta;
@@ -341,15 +343,15 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
}
/* Return 0 if we will bust the pack-size limit */
-static unsigned long write_reuse_object(struct sha1file *f, struct object_entry *entry,
- unsigned long limit, int usable_delta)
+static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
+ unsigned long limit, int usable_delta)
{
struct packed_git *p = entry->in_pack;
struct pack_window *w_curs = NULL;
struct revindex_entry *revidx;
off_t offset;
enum object_type type = entry->type;
- unsigned long datalen;
+ off_t datalen;
unsigned char header[10], dheader[10];
unsigned hdrlen;
@@ -415,11 +417,12 @@ static unsigned long write_reuse_object(struct sha1file *f, struct object_entry
}
/* Return 0 if we will bust the pack-size limit */
-static unsigned long write_object(struct sha1file *f,
- struct object_entry *entry,
- off_t write_offset)
+static off_t write_object(struct sha1file *f,
+ struct object_entry *entry,
+ off_t write_offset)
{
- unsigned long limit, len;
+ unsigned long limit;
+ off_t len;
int usable_delta, to_reuse;
if (!pack_to_stdout)
@@ -491,7 +494,7 @@ static enum write_one_status write_one(struct sha1file *f,
struct object_entry *e,
off_t *offset)
{
- unsigned long size;
+ off_t size;
int recursing;
/*
@@ -976,6 +979,23 @@ static int want_object_in_pack(const unsigned char *sha1,
return 1;
if (incremental)
return 0;
+
+ /*
+ * When asked to do --local (do not include an
+ * object that appears in a pack we borrow
+ * from elsewhere) or --honor-pack-keep (do not
+ * include an object that appears in a pack marked
+ * with .keep), we need to make sure no copy of this
+ * object come from in _any_ pack that causes us to
+ * omit it, and need to complete this loop. When
+ * neither option is in effect, we know the object
+ * we just found is going to be packed, so break
+ * out of the loop to return 1 now.
+ */
+ if (!ignore_packed_keep &&
+ (!local || !have_non_local_packs))
+ break;
+
if (local && !p->pack_local)
return 0;
if (ignore_packed_keep && p->pack_local && p->pack_keep)
@@ -2378,6 +2398,32 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
free(in_pack.array);
}
+static int add_loose_object(const unsigned char *sha1, const char *path,
+ void *data)
+{
+ enum object_type type = sha1_object_info(sha1, NULL);
+
+ if (type < 0) {
+ warning("loose object at %s could not be examined", path);
+ return 0;
+ }
+
+ add_object_entry(sha1, type, "", 0);
+ return 0;
+}
+
+/*
+ * We actually don't even have to worry about reachability here.
+ * add_object_entry will weed out duplicates, so we just add every
+ * loose object we find.
+ */
+static void add_unreachable_loose_objects(void)
+{
+ for_each_loose_file_in_objdir(get_object_directory(),
+ add_loose_object,
+ NULL, NULL, NULL);
+}
+
static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
{
static struct packed_git *last_found = (void *)1;
@@ -2547,6 +2593,8 @@ static void get_object_list(int ac, const char **av)
if (keep_unreachable)
add_objects_in_unpacked_packs(&revs);
+ if (pack_loose_unreachable)
+ add_unreachable_loose_objects();
if (unpack_unreachable)
loosen_unused_packed_objects(&revs);
@@ -2647,6 +2695,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
N_("include tag objects that refer to objects to be packed")),
OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
N_("keep unreachable objects")),
+ OPT_BOOL(0, "pack-loose-unreachable", &pack_loose_unreachable,
+ N_("pack loose unreachable objects")),
{ OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"),
N_("unpack unreachable objects newer than <time>"),
PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
@@ -2752,6 +2802,28 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
progress = 2;
prepare_packed_git();
+ if (ignore_packed_keep) {
+ struct packed_git *p;
+ for (p = packed_git; p; p = p->next)
+ if (p->pack_local && p->pack_keep)
+ break;
+ if (!p) /* no keep-able packs found */
+ ignore_packed_keep = 0;
+ }
+ if (local) {
+ /*
+ * unlike ignore_packed_keep above, we do not want to
+ * unset "local" based on looking at packs, as it
+ * also covers non-local objects
+ */
+ struct packed_git *p;
+ for (p = packed_git; p; p = p->next) {
+ if (!p->pack_local) {
+ have_non_local_packs = 1;
+ break;
+ }
+ }
+ }
if (progress)
progress_state = start_progress(_("Counting objects"), 0);
diff --git a/builtin/pull.c b/builtin/pull.c
index 1d7333c..398aae1 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -815,6 +815,9 @@ static int run_rebase(const unsigned char *curr_head,
argv_array_push(&args, "--no-autostash");
else if (opt_autostash == 1)
argv_array_push(&args, "--autostash");
+ if (opt_verify_signatures &&
+ !strcmp(opt_verify_signatures, "--verify-signatures"))
+ warning(_("ignoring --verify-signatures for rebase"));
argv_array_push(&args, "--onto");
argv_array_push(&args, sha1_to_hex(merge_head));
@@ -852,7 +855,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
git_config(git_pull_config, NULL);
if (read_cache_unmerged())
- die_resolve_conflict("Pull");
+ die_resolve_conflict("pull");
if (file_exists(git_path("MERGE_HEAD")))
die_conclude_merge();
diff --git a/builtin/push.c b/builtin/push.c
index 4e9e4db..3bb9d6b 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -353,7 +353,8 @@ static int push_with_options(struct transport *transport, int flags)
return 1;
}
-static int do_push(const char *repo, int flags)
+static int do_push(const char *repo, int flags,
+ const struct string_list *push_options)
{
int i, errs;
struct remote *remote = pushremote_get(repo);
@@ -376,6 +377,9 @@ static int do_push(const char *repo, int flags)
if (remote->mirror)
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+ if (push_options->nr)
+ flags |= TRANSPORT_PUSH_OPTIONS;
+
if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
if (!strcmp(*refspec, "refs/tags/*"))
return error(_("--all and --tags are incompatible"));
@@ -406,13 +410,16 @@ static int do_push(const char *repo, int flags)
for (i = 0; i < url_nr; i++) {
struct transport *transport =
transport_get(remote, url[i]);
+ if (flags & TRANSPORT_PUSH_OPTIONS)
+ transport->push_options = push_options;
if (push_with_options(transport, flags))
errs++;
}
} else {
struct transport *transport =
transport_get(remote, NULL);
-
+ if (flags & TRANSPORT_PUSH_OPTIONS)
+ transport->push_options = push_options;
if (push_with_options(transport, flags))
errs++;
}
@@ -500,6 +507,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
int push_cert = -1;
int rc;
const char *repo = NULL; /* default repository */
+ static struct string_list push_options = STRING_LIST_INIT_DUP;
+ static struct string_list_item *item;
+
struct option options[] = {
OPT__VERBOSITY(&verbosity),
OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
@@ -533,6 +543,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
PARSE_OPT_OPTARG, option_parse_push_signed },
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
+ OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -563,7 +574,11 @@ int cmd_push(int argc, const char **argv, const char *prefix)
set_refspecs(argv + 1, argc - 1, repo);
}
- rc = do_push(repo, flags);
+ for_each_string_list_item(item, &push_options)
+ if (strchr(item->string, '\n'))
+ die(_("push options must not have new line characters"));
+
+ rc = do_push(repo, flags, &push_options);
if (rc == -1)
usage_with_options(push_usage, options);
else
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index a744437..92e1213 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -44,10 +44,12 @@ static struct strbuf fsck_msg_types = STRBUF_INIT;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int advertise_atomic_push = 1;
+static int advertise_push_options;
static int unpack_limit = 100;
static int report_status;
static int use_sideband;
static int use_atomic;
+static int use_push_options;
static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
@@ -76,6 +78,13 @@ static long nonce_stamp_slop;
static unsigned long nonce_stamp_slop_limit;
static struct ref_transaction *transaction;
+static enum {
+ KEEPALIVE_NEVER = 0,
+ KEEPALIVE_AFTER_NUL,
+ KEEPALIVE_ALWAYS
+} use_keepalive;
+static int keepalive_in_sec = 5;
+
static enum deny_action parse_deny_action(const char *var, const char *value)
{
if (value) {
@@ -193,6 +202,16 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (strcmp(var, "receive.advertisepushoptions") == 0) {
+ advertise_push_options = git_config_bool(var, value);
+ return 0;
+ }
+
+ if (strcmp(var, "receive.keepalive") == 0) {
+ keepalive_in_sec = git_config_int(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
@@ -211,6 +230,8 @@ static void show_ref(const char *path, const unsigned char *sha1)
strbuf_addstr(&cap, " ofs-delta");
if (push_cert_nonce)
strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
+ if (advertise_push_options)
+ strbuf_addstr(&cap, " push-options");
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);
@@ -319,10 +340,60 @@ static void rp_error(const char *err, ...)
static int copy_to_sideband(int in, int out, void *arg)
{
char data[128];
+ int keepalive_active = 0;
+
+ if (keepalive_in_sec <= 0)
+ use_keepalive = KEEPALIVE_NEVER;
+ if (use_keepalive == KEEPALIVE_ALWAYS)
+ keepalive_active = 1;
+
while (1) {
- ssize_t sz = xread(in, data, sizeof(data));
+ ssize_t sz;
+
+ if (keepalive_active) {
+ struct pollfd pfd;
+ int ret;
+
+ pfd.fd = in;
+ pfd.events = POLLIN;
+ ret = poll(&pfd, 1, 1000 * keepalive_in_sec);
+
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ } else if (ret == 0) {
+ /* no data; send a keepalive packet */
+ static const char buf[] = "0005\1";
+ write_or_die(1, buf, sizeof(buf) - 1);
+ continue;
+ } /* else there is actual data to read */
+ }
+
+ sz = xread(in, data, sizeof(data));
if (sz <= 0)
break;
+
+ if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) {
+ const char *p = memchr(data, '\0', sz);
+ if (p) {
+ /*
+ * The NUL tells us to start sending keepalives. Make
+ * sure we send any other data we read along
+ * with it.
+ */
+ keepalive_active = 1;
+ send_sideband(1, 2, data, p - data, use_sideband);
+ send_sideband(1, 2, p + 1, sz - (p - data + 1), use_sideband);
+ continue;
+ }
+ }
+
+ /*
+ * Either we're not looking for a NUL signal, or we didn't see
+ * it yet; just pass along the data.
+ */
send_sideband(1, 2, data, sz, use_sideband);
}
close(in);
@@ -550,8 +621,16 @@ static void prepare_push_cert_sha1(struct child_process *proc)
}
}
+struct receive_hook_feed_state {
+ struct command *cmd;
+ int skip_broken;
+ struct strbuf buf;
+ const struct string_list *push_options;
+};
+
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)
+static int run_and_feed_hook(const char *hook_name, feed_fn feed,
+ struct receive_hook_feed_state *feed_state)
{
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
@@ -567,6 +646,16 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
proc.argv = argv;
proc.in = -1;
proc.stdout_to_stderr = 1;
+ if (feed_state->push_options) {
+ int i;
+ for (i = 0; i < feed_state->push_options->nr; i++)
+ argv_array_pushf(&proc.env_array,
+ "GIT_PUSH_OPTION_%d=%s", i,
+ feed_state->push_options->items[i].string);
+ argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d",
+ feed_state->push_options->nr);
+ } else
+ argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
if (use_sideband) {
memset(&muxer, 0, sizeof(muxer));
@@ -606,12 +695,6 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
return finish_command(&proc);
}
-struct receive_hook_feed_state {
- struct command *cmd;
- int skip_broken;
- struct strbuf buf;
-};
-
static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
{
struct receive_hook_feed_state *state = state_;
@@ -634,8 +717,10 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
return 0;
}
-static int run_receive_hook(struct command *commands, const char *hook_name,
- int skip_broken)
+static int run_receive_hook(struct command *commands,
+ const char *hook_name,
+ int skip_broken,
+ const struct string_list *push_options)
{
struct receive_hook_feed_state state;
int status;
@@ -646,6 +731,7 @@ static int run_receive_hook(struct command *commands, const char *hook_name,
if (feed_receive_hook(&state, NULL, NULL))
return 0;
state.cmd = commands;
+ state.push_options = push_options;
status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
strbuf_release(&state.buf);
return status;
@@ -737,7 +823,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
{
static struct lock_file shallow_lock;
struct sha1_array extra = SHA1_ARRAY_INIT;
- const char *alt_file;
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
uint32_t mask = 1 << (cmd->index % 32);
int i;
@@ -749,9 +835,8 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
!delayed_reachability_test(si, i))
sha1_array_append(&extra, si->shallow->sha1[i]);
- setup_alternate_shallow(&shallow_lock, &alt_file, &extra);
- if (check_shallow_connected(command_singleton_iterator,
- 0, cmd, alt_file)) {
+ setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
+ if (check_connected(command_singleton_iterator, cmd, &opt)) {
rollback_lock_file(&shallow_lock);
sha1_array_clear(&extra);
return -1;
@@ -1160,8 +1245,8 @@ static void set_connectivity_errors(struct command *commands,
if (shallow_update && si->shallow_ref[cmd->index])
/* to be checked in update_shallow_ref() */
continue;
- if (!check_everything_connected(command_singleton_iterator,
- 0, &singleton))
+ if (!check_connected(command_singleton_iterator, &singleton,
+ NULL))
continue;
cmd->error_string = "missing necessary objects";
}
@@ -1316,11 +1401,15 @@ cleanup:
static void execute_commands(struct command *commands,
const char *unpacker_error,
- struct shallow_info *si)
+ struct shallow_info *si,
+ const struct string_list *push_options)
{
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
struct command *cmd;
unsigned char sha1[20];
struct iterate_data data;
+ struct async muxer;
+ int err_fd = 0;
if (unpacker_error) {
for (cmd = commands; cmd; cmd = cmd->next)
@@ -1328,14 +1417,28 @@ static void execute_commands(struct command *commands,
return;
}
+ if (use_sideband) {
+ memset(&muxer, 0, sizeof(muxer));
+ muxer.proc = copy_to_sideband;
+ muxer.in = -1;
+ if (!start_async(&muxer))
+ err_fd = muxer.in;
+ /* ...else, continue without relaying sideband */
+ }
+
data.cmds = commands;
data.si = si;
- if (check_everything_connected(iterate_receive_command_list, 0, &data))
+ opt.err_fd = err_fd;
+ opt.progress = err_fd && !quiet;
+ if (check_connected(iterate_receive_command_list, &data, &opt))
set_connectivity_errors(commands, si);
+ if (use_sideband)
+ finish_async(&muxer);
+
reject_updates_to_hidden(commands);
- if (run_receive_hook(commands, "pre-receive", 0)) {
+ if (run_receive_hook(commands, "pre-receive", 0, push_options)) {
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string)
cmd->error_string = "pre-receive hook declined";
@@ -1439,6 +1542,9 @@ static struct command *read_head_info(struct sha1_array *shallow)
if (advertise_atomic_push
&& parse_feature_request(feature_list, "atomic"))
use_atomic = 1;
+ if (advertise_push_options
+ && parse_feature_request(feature_list, "push-options"))
+ use_push_options = 1;
}
if (!strcmp(line, "push-cert")) {
@@ -1471,6 +1577,21 @@ static struct command *read_head_info(struct sha1_array *shallow)
return commands;
}
+static void read_push_options(struct string_list *options)
+{
+ while (1) {
+ char *line;
+ int len;
+
+ line = packet_read_line(0, &len);
+
+ if (!line)
+ break;
+
+ string_list_append(options, line);
+ }
+}
+
static const char *parse_pack_header(struct pack_header *hdr)
{
switch (read_pack_header(0, hdr)) {
@@ -1548,6 +1669,10 @@ static const char *unpack(int err_fd, struct shallow_info *si)
(uintmax_t)getpid(),
hostname);
+ if (!quiet && err_fd)
+ argv_array_push(&child.args, "--show-resolving-progress");
+ if (use_sideband)
+ argv_array_push(&child.args, "--report-end-of-input");
if (fsck_objects)
argv_array_pushf(&child.args, "--strict%s",
fsck_msg_types.buf);
@@ -1577,6 +1702,7 @@ static const char *unpack_with_sideband(struct shallow_info *si)
if (!use_sideband)
return unpack(0, si);
+ use_keepalive = KEEPALIVE_AFTER_NUL;
memset(&muxer, 0, sizeof(muxer));
muxer.proc = copy_to_sideband;
muxer.in = -1;
@@ -1756,6 +1882,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
if ((commands = read_head_info(&shallow)) != NULL) {
const char *unpack_status = NULL;
+ struct string_list push_options = STRING_LIST_INIT_DUP;
+
+ if (use_push_options)
+ read_push_options(&push_options);
prepare_shallow_info(&si, &shallow);
if (!si.nr_ours && !si.nr_theirs)
@@ -1764,20 +1894,36 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
unpack_status = unpack_with_sideband(&si);
update_shallow_info(commands, &si, &ref);
}
- execute_commands(commands, unpack_status, &si);
+ use_keepalive = KEEPALIVE_ALWAYS;
+ execute_commands(commands, unpack_status, &si,
+ &push_options);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
if (report_status)
report(commands, unpack_status);
- run_receive_hook(commands, "post-receive", 1);
+ run_receive_hook(commands, "post-receive", 1,
+ &push_options);
run_update_post_hook(commands);
+ if (push_options.nr)
+ string_list_clear(&push_options, 0);
if (auto_gc) {
const char *argv_gc_auto[] = {
"gc", "--auto", "--quiet", NULL,
};
- int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR;
+ struct child_process proc = CHILD_PROCESS_INIT;
+
+ proc.no_stdin = 1;
+ proc.stdout_to_stderr = 1;
+ proc.err = use_sideband ? -1 : 0;
+ proc.git_cmd = 1;
+ proc.argv = argv_gc_auto;
+
close_all_packs();
- run_command_v_opt(argv_gc_auto, opt);
+ if (!start_command(&proc)) {
+ if (use_sideband)
+ copy_to_sideband(proc.err, -1, NULL);
+ finish_command(&proc);
+ }
}
if (auto_update_server_info)
update_server_info(0);
diff --git a/builtin/remote.c b/builtin/remote.c
index d33766b..9f6a6b3 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -247,7 +247,7 @@ struct branch_info {
enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase;
};
-static struct string_list branch_list;
+static struct string_list branch_list = STRING_LIST_INIT_NODUP;
static const char *abbrev_ref(const char *name, const char *prefix)
{
@@ -539,10 +539,6 @@ static int add_branch_for_removal(const char *refname,
return 0;
}
- /* make sure that symrefs are deleted */
- if (flags & REF_ISSYMREF)
- return unlink(git_path("%s", refname));
-
string_list_append(branches->branches, refname);
return 0;
@@ -788,7 +784,7 @@ static int rm(int argc, const char **argv)
strbuf_release(&buf);
if (!result)
- result = delete_refs(&branches);
+ result = delete_refs(&branches, REF_NODEREF);
string_list_clear(&branches, 0);
if (skipped.nr) {
@@ -952,7 +948,7 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
struct show_info *show_info = cb_data;
struct branch_info *branch_info = item->util;
struct string_list *merge = &branch_info->merge;
- const char *also;
+ int width = show_info->width + 4;
int i;
if (branch_info->rebase && branch_info->merge.nr > 1) {
@@ -963,19 +959,18 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
printf(" %-*s ", show_info->width, item->string);
if (branch_info->rebase) {
- printf_ln(_(branch_info->rebase == INTERACTIVE_REBASE ?
- "rebases interactively onto remote %s" :
- "rebases onto remote %s"), merge->items[0].string);
+ printf_ln(branch_info->rebase == INTERACTIVE_REBASE
+ ? _("rebases interactively onto remote %s")
+ : _("rebases onto remote %s"), merge->items[0].string);
return 0;
} else if (show_info->any_rebase) {
printf_ln(_(" merges with remote %s"), merge->items[0].string);
- also = _(" and with remote");
+ width++;
} else {
printf_ln(_("merges with remote %s"), merge->items[0].string);
- also = _(" and with remote");
}
for (i = 1; i < merge->nr; i++)
- printf(" %-*s %s %s\n", show_info->width, "", also,
+ printf(_("%-*s and with remote %s\n"), width, "",
merge->items[i].string);
return 0;
@@ -1158,11 +1153,11 @@ static int show(int argc, const char **argv)
the one in " Fetch URL: %s" translation */
printf_ln(_(" Push URL: %s"), url[i]);
if (!i)
- printf_ln(_(" Push URL: %s"), "(no URL)");
+ printf_ln(_(" Push URL: %s"), _("(no URL)"));
if (no_query)
- printf_ln(_(" HEAD branch: %s"), "(not queried)");
+ printf_ln(_(" HEAD branch: %s"), _("(not queried)"));
else if (!states.heads.nr)
- printf_ln(_(" HEAD branch: %s"), "(unknown)");
+ printf_ln(_(" HEAD branch: %s"), _("(unknown)"));
else if (states.heads.nr == 1)
printf_ln(_(" HEAD branch: %s"), states.heads.items[0].string);
else {
@@ -1305,7 +1300,7 @@ static int prune_remote(const char *remote, int dry_run)
string_list_sort(&refs_to_prune);
if (!dry_run)
- result |= delete_refs(&refs_to_prune);
+ result |= delete_refs(&refs_to_prune, 0);
for_each_string_list_item(item, &states.stale) {
const char *refname = item->util;
diff --git a/builtin/repack.c b/builtin/repack.c
index 858db38..80dd06b 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -146,6 +146,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int pack_everything = 0;
int delete_redundant = 0;
const char *unpack_unreachable = NULL;
+ int keep_unreachable = 0;
const char *window = NULL, *window_memory = NULL;
const char *depth = NULL;
const char *max_pack_size = NULL;
@@ -175,6 +176,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("write bitmap index")),
OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
N_("with -A, do not loosen objects older than this")),
+ OPT_BOOL('k', "keep-unreachable", &keep_unreachable,
+ N_("with -a, repack unreachable objects")),
OPT_STRING(0, "window", &window, N_("n"),
N_("size of the window used for delta compression")),
OPT_STRING(0, "window-memory", &window_memory, N_("bytes"),
@@ -196,6 +199,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (delete_redundant && repository_format_precious_objects)
die(_("cannot delete packs in a precious-objects repo"));
+ if (keep_unreachable &&
+ (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
+ die(_("--keep-unreachable and -A are incompatible"));
+
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps;
@@ -239,6 +246,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
} else if (pack_everything & LOOSEN_UNREACHABLE) {
argv_array_push(&cmd.args,
"--unpack-unreachable");
+ } else if (keep_unreachable) {
+ argv_array_push(&cmd.args, "--keep-unreachable");
+ argv_array_push(&cmd.args, "--pack-loose-unreachable");
} else {
argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
}
@@ -378,7 +388,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
item->string,
exts[ext].name);
if (remove_path(fname))
- warning(_("removing '%s' failed"), fname);
+ warning(_("failed to remove '%s'"), fname);
free(fname);
}
}
diff --git a/builtin/reset.c b/builtin/reset.c
index e8c5767..9020ec6 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -121,7 +121,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
for (i = 0; i < q->nr; i++) {
struct diff_filespec *one = q->queue[i]->one;
- int is_missing = !(one->mode && !is_null_sha1(one->sha1));
+ int is_missing = !(one->mode && !is_null_oid(&one->oid));
struct cache_entry *ce;
if (is_missing && !intent_to_add) {
@@ -129,7 +129,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
continue;
}
- ce = make_cache_entry(one->mode, one->sha1, one->path,
+ ce = make_cache_entry(one->mode, one->oid.hash, one->path,
0, 0);
if (!ce)
die(_("make_cache_entry failed for path '%s'"),
@@ -158,7 +158,7 @@ static int read_from_tree(const struct pathspec *pathspec,
return 1;
diffcore_std(&opt);
diff_flush(&opt);
- free_pathspec(&opt.pathspec);
+ clear_pathspec(&opt.pathspec);
return 0;
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index b82bcc3..0ba82b1 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -9,6 +9,7 @@
#include "log-tree.h"
#include "graph.h"
#include "bisect.h"
+#include "progress.h"
static const char rev_list_usage[] =
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -49,12 +50,17 @@ static const char rev_list_usage[] =
" --bisect-all"
;
+static struct progress *progress;
+static unsigned progress_counter;
+
static void finish_commit(struct commit *commit, void *data);
static void show_commit(struct commit *commit, void *data)
{
struct rev_list_info *info = data;
struct rev_info *revs = info->revs;
+ display_progress(progress, ++progress_counter);
+
if (info->flags & REV_LIST_QUIET) {
finish_commit(commit, data);
return;
@@ -190,6 +196,7 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
{
struct rev_list_info *info = cb_data;
finish_object(obj, name, cb_data);
+ display_progress(progress, ++progress_counter);
if (info->flags & REV_LIST_QUIET)
return;
show_object_with_name(stdout, obj, name);
@@ -276,6 +283,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
int bisect_show_vars = 0;
int bisect_find_all = 0;
int use_bitmap_index = 0;
+ const char *show_progress = NULL;
git_config(git_default_config, NULL);
init_revisions(&revs, prefix);
@@ -325,6 +333,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
test_bitmap_walk(&revs);
return 0;
}
+ if (skip_prefix(arg, "--progress=", &arg)) {
+ show_progress = arg;
+ continue;
+ }
usage(rev_list_usage);
}
@@ -355,6 +367,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (bisect_list)
revs.limited = 1;
+ if (show_progress)
+ progress = start_progress_delay(show_progress, 0, 0, 2);
+
if (use_bitmap_index && !revs.prune) {
if (revs.count && !revs.left_right && !revs.cherry_mark) {
uint32_t commit_count;
@@ -392,6 +407,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
traverse_commit_list(&revs, show_commit, show_object, &info);
+ stop_progress(&progress);
+
if (revs.count) {
if (revs.left_right && revs.cherry_mark)
printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index c961b74..76cf05e 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -469,7 +469,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
(stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
PARSE_OPT_SHELL_EVAL);
- strbuf_addf(&parsed, " --");
+ strbuf_addstr(&parsed, " --");
sq_quote_argv(&parsed, argv, 0);
puts(parsed.buf);
return 0;
diff --git a/builtin/revert.c b/builtin/revert.c
index 56a2c36..4e69380 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -76,7 +76,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
int cmd = 0;
- struct option options[] = {
+ struct option base_options[] = {
OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
@@ -91,13 +91,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
N_("option for merge strategy"), option_parse_x),
{ OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- OPT_END(),
- OPT_END(),
- OPT_END(),
- OPT_END(),
- OPT_END(),
- OPT_END(),
+ OPT_END()
};
+ struct option *options = base_options;
if (opts->action == REPLAY_PICK) {
struct option cp_extra[] = {
@@ -108,8 +104,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
OPT_END(),
};
- if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
- die(_("program error"));
+ options = parse_options_concat(options, cp_extra);
}
argc = parse_options(argc, argv, NULL, options, usage_str,
diff --git a/builtin/rm.c b/builtin/rm.c
index 8abb020..b2fee3e 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -387,6 +387,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
*/
if (!index_only) {
int removed = 0, gitmodules_modified = 0;
+ struct strbuf buf = STRBUF_INIT;
for (i = 0; i < list.nr; i++) {
const char *path = list.entry[i].name;
if (list.entry[i].is_submodule) {
@@ -398,7 +399,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
continue;
}
} else {
- struct strbuf buf = STRBUF_INIT;
+ strbuf_reset(&buf);
strbuf_addstr(&buf, path);
if (!remove_dir_recursively(&buf, 0)) {
removed = 1;
@@ -410,7 +411,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
/* Submodule was removed by user */
if (!remove_path_from_gitmodules(path))
gitmodules_modified = 1;
- strbuf_release(&buf);
/* Fallthrough and let remove_path() fail. */
}
}
@@ -421,6 +421,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!removed)
die_errno("git rm: '%s'", path);
}
+ strbuf_release(&buf);
if (gitmodules_modified)
stage_updated_gitmodules();
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index bfc082e..25fa8a6 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -233,11 +233,11 @@ void shortlog_init(struct shortlog *log)
int cmd_shortlog(int argc, const char **argv, const char *prefix)
{
- static struct shortlog log;
- static struct rev_info rev;
+ struct shortlog log = { STRING_LIST_INIT_NODUP };
+ struct rev_info rev;
int nongit = !startup_info->have_repository;
- static const struct option options[] = {
+ const struct option options[] = {
OPT_BOOL('n', "numbered", &log.sort_by_number,
N_("sort output according to the number of commits per author")),
OPT_BOOL('s', "summary", &log.summary,
@@ -276,6 +276,7 @@ parse_done:
log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
log.abbrev = rev.abbrev;
+ log.file = rev.diffopt.file;
/* assume HEAD if from a tty */
if (!nongit && !rev.pending.nr && isatty(0))
@@ -289,6 +290,8 @@ parse_done:
get_from_rev(&rev, &log);
shortlog_output(&log);
+ if (log.file != stdout)
+ fclose(log.file);
return 0;
}
@@ -310,22 +313,24 @@ void shortlog_output(struct shortlog *log)
for (i = 0; i < log->list.nr; i++) {
const struct string_list_item *item = &log->list.items[i];
if (log->summary) {
- printf("%6d\t%s\n", (int)UTIL_TO_INT(item), item->string);
+ fprintf(log->file, "%6d\t%s\n",
+ (int)UTIL_TO_INT(item), item->string);
} else {
struct string_list *onelines = item->util;
- printf("%s (%d):\n", item->string, onelines->nr);
+ fprintf(log->file, "%s (%d):\n",
+ item->string, onelines->nr);
for (j = onelines->nr - 1; j >= 0; j--) {
const char *msg = onelines->items[j].string;
if (log->wrap_lines) {
strbuf_reset(&sb);
add_wrapped_shortlog_msg(&sb, msg, log);
- fwrite(sb.buf, sb.len, 1, stdout);
+ fwrite(sb.buf, sb.len, 1, log->file);
}
else
- printf(" %s\n", msg);
+ fprintf(log->file, " %s\n", msg);
}
- putchar('\n');
+ putc('\n', log->file);
onelines->strdup_strings = 1;
string_list_clear(onelines, 0);
free(onelines);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 926d205..e79790f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -444,8 +444,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
static int clone_submodule(const char *path, const char *gitdir, const char *url,
const char *depth, const char *reference, int quiet)
{
- struct child_process cp;
- child_process_init(&cp);
+ struct child_process cp = CHILD_PROCESS_INIT;
argv_array_push(&cp.args, "clone");
argv_array_push(&cp.args, "--no-checkout");
@@ -579,6 +578,7 @@ struct submodule_update_clone {
/* configuration parameters which are passed on to the children */
int quiet;
+ int recommend_shallow;
const char *reference;
const char *depth;
const char *recursive_prefix;
@@ -589,10 +589,14 @@ struct submodule_update_clone {
/* If we want to stop as fast as possible and return an error */
unsigned quickstop : 1;
+
+ /* failed clones to be retried again */
+ const struct cache_entry **failed_clones;
+ int failed_clones_nr, failed_clones_alloc;
};
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
- SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \
- STRING_LIST_INIT_DUP, 0}
+ SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \
+ STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@ -696,6 +700,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_push(&child->args, "--quiet");
if (suc->prefix)
argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+ if (suc->recommend_shallow && sub->recommend_shallow == 1)
+ argv_array_push(&child->args, "--depth=1");
argv_array_pushl(&child->args, "--path", sub->path, NULL);
argv_array_pushl(&child->args, "--name", sub->name, NULL);
argv_array_pushl(&child->args, "--url", url, NULL);
@@ -715,23 +721,51 @@ cleanup:
static int update_clone_get_next_task(struct child_process *child,
struct strbuf *err,
void *suc_cb,
- void **void_task_cb)
+ void **idx_task_cb)
{
struct submodule_update_clone *suc = suc_cb;
+ const struct cache_entry *ce;
+ int index;
for (; suc->current < suc->list.nr; suc->current++) {
- const struct cache_entry *ce = suc->list.entries[suc->current];
+ ce = suc->list.entries[suc->current];
if (prepare_to_clone_next_submodule(ce, child, suc, err)) {
+ int *p = xmalloc(sizeof(*p));
+ *p = suc->current;
+ *idx_task_cb = p;
suc->current++;
return 1;
}
}
+
+ /*
+ * The loop above tried cloning each submodule once, now try the
+ * stragglers again, which we can imagine as an extension of the
+ * entry list.
+ */
+ index = suc->current - suc->list.nr;
+ if (index < suc->failed_clones_nr) {
+ int *p;
+ ce = suc->failed_clones[index];
+ if (!prepare_to_clone_next_submodule(ce, child, suc, err)) {
+ suc->current ++;
+ strbuf_addf(err, "BUG: submodule considered for cloning,"
+ "doesn't need cloning any more?\n");
+ return 0;
+ }
+ p = xmalloc(sizeof(*p));
+ *p = suc->current;
+ *idx_task_cb = p;
+ suc->current ++;
+ return 1;
+ }
+
return 0;
}
static int update_clone_start_failure(struct strbuf *err,
void *suc_cb,
- void *void_task_cb)
+ void *idx_task_cb)
{
struct submodule_update_clone *suc = suc_cb;
suc->quickstop = 1;
@@ -741,15 +775,39 @@ static int update_clone_start_failure(struct strbuf *err,
static int update_clone_task_finished(int result,
struct strbuf *err,
void *suc_cb,
- void *void_task_cb)
+ void *idx_task_cb)
{
+ const struct cache_entry *ce;
struct submodule_update_clone *suc = suc_cb;
+ int *idxP = *(int**)idx_task_cb;
+ int idx = *idxP;
+ free(idxP);
+
if (!result)
return 0;
- suc->quickstop = 1;
- return 1;
+ if (idx < suc->list.nr) {
+ ce = suc->list.entries[idx];
+ strbuf_addf(err, _("Failed to clone '%s'. Retry scheduled"),
+ ce->name);
+ strbuf_addch(err, '\n');
+ ALLOC_GROW(suc->failed_clones,
+ suc->failed_clones_nr + 1,
+ suc->failed_clones_alloc);
+ suc->failed_clones[suc->failed_clones_nr++] = ce;
+ return 0;
+ } else {
+ idx -= suc->list.nr;
+ ce = suc->failed_clones[idx];
+ strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"),
+ ce->name);
+ strbuf_addch(err, '\n');
+ suc->quickstop = 1;
+ return 1;
+ }
+
+ return 0;
}
static int update_clone(int argc, const char **argv, const char *prefix)
@@ -778,6 +836,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
"specified number of revisions")),
OPT_INTEGER('j', "jobs", &max_jobs,
N_("parallel jobs")),
+ OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
+ N_("whether the initial clone should follow the shallow recommendation")),
OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
OPT_END()
};
@@ -835,13 +895,64 @@ static int resolve_relative_path(int argc, const char **argv, const char *prefix
{
struct strbuf sb = STRBUF_INIT;
if (argc != 3)
- die("submodule--helper relative_path takes exactly 2 arguments, got %d", argc);
+ die("submodule--helper relative-path takes exactly 2 arguments, got %d", argc);
printf("%s", relative_path(argv[1], argv[2], &sb));
strbuf_release(&sb);
return 0;
}
+static const char *remote_submodule_branch(const char *path)
+{
+ const struct submodule *sub;
+ gitmodules_config();
+ git_config(submodule_config, NULL);
+
+ sub = submodule_from_path(null_sha1, path);
+ if (!sub)
+ return NULL;
+
+ if (!sub->branch)
+ return "master";
+
+ if (!strcmp(sub->branch, ".")) {
+ unsigned char sha1[20];
+ const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+
+ if (!refname)
+ die(_("No such ref: %s"), "HEAD");
+
+ /* detached HEAD */
+ if (!strcmp(refname, "HEAD"))
+ die(_("Submodule (%s) branch configured to inherit "
+ "branch from superproject, but the superproject "
+ "is not on any branch"), sub->name);
+
+ if (!skip_prefix(refname, "refs/heads/", &refname))
+ die(_("Expecting a full ref name, got %s"), refname);
+ return refname;
+ }
+
+ return sub->branch;
+}
+
+static int resolve_remote_submodule_branch(int argc, const char **argv,
+ const char *prefix)
+{
+ const char *ret;
+ struct strbuf sb = STRBUF_INIT;
+ if (argc != 2)
+ die("submodule--helper remote-branch takes exactly one arguments, got %d", argc);
+
+ ret = remote_submodule_branch(argv[1]);
+ if (!ret)
+ die("submodule %s doesn't exist", argv[1]);
+
+ printf("%s", ret);
+ strbuf_release(&sb);
+ return 0;
+}
+
struct cmd_struct {
const char *cmd;
int (*fn)(int, const char **, const char *);
@@ -855,7 +966,8 @@ static struct cmd_struct commands[] = {
{"relative-path", resolve_relative_path},
{"resolve-relative-url", resolve_relative_url},
{"resolve-relative-url-test", resolve_relative_url_test},
- {"init", module_init}
+ {"init", module_init},
+ {"remote-branch", resolve_remote_submodule_branch}
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 875e7ed..172470b 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -355,7 +355,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
return; /* we are done */
else {
/* cannot resolve yet --- queue it */
- hashcpy(obj_list[nr].sha1, null_sha1);
+ hashclr(obj_list[nr].sha1);
add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
return;
}
@@ -406,7 +406,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
* The delta base object is itself a delta that
* has not been resolved yet.
*/
- hashcpy(obj_list[nr].sha1, null_sha1);
+ hashclr(obj_list[nr].sha1);
add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
return;
}
diff --git a/builtin/update-index.c b/builtin/update-index.c
index b8b8522..ba04b19 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -759,7 +759,7 @@ static int do_reupdate(int ac, const char **av,
if (save_nr != active_nr)
goto redo;
}
- free_pathspec(&pathspec);
+ clear_pathspec(&pathspec);
return 0;
}
@@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
break;
default:
- die("Bug: bad untracked_cache value: %d", untracked_cache);
+ die("BUG: bad untracked_cache value: %d", untracked_cache);
}
if (active_cache_changed) {
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 96a2834..6dcf7bd 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -13,8 +13,10 @@
static const char * const worktree_usage[] = {
N_("git worktree add [<options>] <path> [<branch>]"),
- N_("git worktree prune [<options>]"),
N_("git worktree list [<options>]"),
+ N_("git worktree lock [<options>] <path>"),
+ N_("git worktree prune [<options>]"),
+ N_("git worktree unlock <path>"),
NULL
};
@@ -95,7 +97,7 @@ static void prune_worktrees(void)
if (!dir)
return;
while ((d = readdir(dir)) != NULL) {
- if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ if (is_dot_or_dotdot(d->d_name))
continue;
strbuf_reset(&reason);
if (!prune_worktree(d->d_name, &reason))
@@ -192,7 +194,7 @@ static int add_worktree(const char *path, const char *refname,
struct strbuf sb = STRBUF_INIT;
const char *name;
struct stat st;
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
struct argv_array child_env = ARGV_ARRAY_INIT;
int counter = 0, len, ret;
struct strbuf symref = STRBUF_INIT;
@@ -262,7 +264,7 @@ static int add_worktree(const char *path, const char *refname,
*/
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
- write_file(sb.buf, "0000000000000000000000000000000000000000");
+ write_file(sb.buf, "%s", sha1_to_hex(null_sha1));
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
@@ -271,7 +273,6 @@ static int add_worktree(const char *path, const char *refname,
argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
- memset(&cp, 0, sizeof(cp));
cp.git_cmd = 1;
if (commit)
@@ -337,9 +338,12 @@ static int add(int ac, const char **av, const char *prefix)
if (ac < 1 || ac > 2)
usage_with_options(worktree_usage, options);
- path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
+ path = prefix_filename(prefix, strlen(prefix), av[0]);
branch = ac < 2 ? "HEAD" : av[1];
+ if (!strcmp(branch, "-"))
+ branch = "@{-1}";
+
opts.force_new_branch = !!new_branch_force;
if (opts.force_new_branch) {
struct strbuf symref = STRBUF_INIT;
@@ -360,8 +364,7 @@ static int add(int ac, const char **av, const char *prefix)
}
if (opts.new_branch) {
- struct child_process cp;
- memset(&cp, 0, sizeof(cp));
+ struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
argv_array_push(&cp.args, "branch");
if (opts.force_new_branch)
@@ -459,6 +462,66 @@ static int list(int ac, const char **av, const char *prefix)
return 0;
}
+static int lock_worktree(int ac, const char **av, const char *prefix)
+{
+ const char *reason = "", *old_reason;
+ struct option options[] = {
+ OPT_STRING(0, "reason", &reason, N_("string"),
+ N_("reason for locking")),
+ OPT_END()
+ };
+ struct worktree **worktrees, *wt;
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac != 1)
+ usage_with_options(worktree_usage, options);
+
+ worktrees = get_worktrees();
+ wt = find_worktree(worktrees, prefix, av[0]);
+ if (!wt)
+ die(_("'%s' is not a working tree"), av[0]);
+ if (is_main_worktree(wt))
+ die(_("The main working tree cannot be locked or unlocked"));
+
+ old_reason = is_worktree_locked(wt);
+ if (old_reason) {
+ if (*old_reason)
+ die(_("'%s' is already locked, reason: %s"),
+ av[0], old_reason);
+ die(_("'%s' is already locked"), av[0]);
+ }
+
+ write_file(git_common_path("worktrees/%s/locked", wt->id),
+ "%s", reason);
+ free_worktrees(worktrees);
+ return 0;
+}
+
+static int unlock_worktree(int ac, const char **av, const char *prefix)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ struct worktree **worktrees, *wt;
+ int ret;
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac != 1)
+ usage_with_options(worktree_usage, options);
+
+ worktrees = get_worktrees();
+ wt = find_worktree(worktrees, prefix, av[0]);
+ if (!wt)
+ die(_("'%s' is not a working tree"), av[0]);
+ if (is_main_worktree(wt))
+ die(_("The main working tree cannot be locked or unlocked"));
+ if (!is_worktree_locked(wt))
+ die(_("'%s' is not locked"), av[0]);
+ ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+ free_worktrees(worktrees);
+ return ret;
+}
+
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
@@ -467,11 +530,17 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
if (ac < 2)
usage_with_options(worktree_usage, options);
+ if (!prefix)
+ prefix = "";
if (!strcmp(av[1], "add"))
return add(ac - 1, av + 1, prefix);
if (!strcmp(av[1], "prune"))
return prune(ac - 1, av + 1, prefix);
if (!strcmp(av[1], "list"))
return list(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "lock"))
+ return lock_worktree(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "unlock"))
+ return unlock_worktree(ac - 1, av + 1, prefix);
usage_with_options(worktree_usage, options);
}
diff --git a/cache-tree.c b/cache-tree.c
index ddf0cc9..f28b1f4 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -319,12 +319,13 @@ static int update_one(struct cache_tree *it,
i = 0;
while (i < entries) {
const struct cache_entry *ce = cache[i];
- struct cache_tree_sub *sub;
+ struct cache_tree_sub *sub = NULL;
const char *path, *slash;
int pathlen, entlen;
const unsigned char *sha1;
unsigned mode;
int expected_missing = 0;
+ int contains_ita = 0;
path = ce->name;
pathlen = ce_namelen(ce);
@@ -341,7 +342,8 @@ 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) {
+ contains_ita = sub->cache_tree->entry_count < 0;
+ if (contains_ita) {
to_invalidate = 1;
expected_missing = 1;
}
@@ -375,11 +377,17 @@ static int update_one(struct cache_tree *it,
* they are not part of generated trees. Invalidate up
* to root to force cache-tree users to read elsewhere.
*/
- if (ce_intent_to_add(ce)) {
+ if (!sub && ce_intent_to_add(ce)) {
to_invalidate = 1;
continue;
}
+ /*
+ * "sub" can be an empty tree if all subentries are i-t-a.
+ */
+ if (contains_ita && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN))
+ continue;
+
strbuf_grow(&buffer, entlen + 100);
strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
strbuf_add(&buffer, sha1, 20);
diff --git a/cache.h b/cache.h
index c73becb..b780a91 100644
--- a/cache.h
+++ b/cache.h
@@ -632,6 +632,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
#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);
+extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
extern void update_index_if_able(struct index_state *, struct lock_file *);
@@ -1003,6 +1004,11 @@ int adjust_shared_perm(const char *path);
* directory while we were working. To be robust against this kind of
* race, callers might want to try invoking the function again when it
* returns SCLD_VANISHED.
+ *
+ * safe_create_leading_directories() temporarily changes path while it
+ * is working but restores it before returning.
+ * safe_create_leading_directories_const() doesn't modify path, even
+ * temporarily.
*/
enum scld_error {
SCLD_OK = 0,
@@ -1193,6 +1199,7 @@ extern int get_oid_hex(const char *hex, struct object_id *sha1);
* printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
*/
extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
+extern char *oid_to_hex_r(char *out, const struct object_id *oid);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
@@ -1223,7 +1230,8 @@ struct date_mode {
DATE_ISO8601_STRICT,
DATE_RFC2822,
DATE_STRFTIME,
- DATE_RAW
+ DATE_RAW,
+ DATE_UNIX
} type;
const char *strftime_fmt;
int local;
@@ -1262,6 +1270,7 @@ extern const char *ident_default_email(void);
extern const char *git_editor(void);
extern const char *git_pager(int stdout_is_tty);
extern int git_ident_config(const char *, const char *, void *);
+extern void reset_ident_date(void);
struct ident_split {
const char *name_begin;
@@ -1370,6 +1379,13 @@ extern struct packed_git {
char pack_name[FLEX_ARRAY]; /* more */
} *packed_git;
+/*
+ * A most-recently-used ordered version of the packed_git list, which can
+ * be iterated instead of packed_git (and marked via mru_mark).
+ */
+struct mru;
+extern struct mru *packed_git_mru;
+
struct pack_entry {
off_t offset;
unsigned char sha1[20];
@@ -1409,7 +1425,6 @@ extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t
extern void close_pack_windows(struct packed_git *);
extern void close_all_packs(void);
extern void unuse_pack(struct pack_window **);
-extern void free_pack_by_name(const char *);
extern void clear_delta_base_cache(void);
extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
@@ -1508,7 +1523,7 @@ struct object_info {
/* Request */
enum object_type *typep;
unsigned long *sizep;
- unsigned long *disk_sizep;
+ off_t *disk_sizep;
unsigned char *delta_base_sha1;
struct strbuf *typename;
@@ -1559,10 +1574,18 @@ struct git_config_source {
const char *blob;
};
+enum config_origin_type {
+ CONFIG_ORIGIN_BLOB,
+ CONFIG_ORIGIN_FILE,
+ CONFIG_ORIGIN_STDIN,
+ CONFIG_ORIGIN_SUBMODULE_BLOB,
+ CONFIG_ORIGIN_CMDLINE
+};
+
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int git_default_config(const char *, const char *, void *);
extern int git_config_from_file(config_fn_t fn, const char *, void *);
-extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
+extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
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);
@@ -1604,6 +1627,16 @@ extern const char *get_log_output_encoding(void);
extern const char *get_commit_output_encoding(void);
extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+
+enum config_scope {
+ CONFIG_SCOPE_UNKNOWN = 0,
+ CONFIG_SCOPE_SYSTEM,
+ CONFIG_SCOPE_GLOBAL,
+ CONFIG_SCOPE_REPO,
+ CONFIG_SCOPE_CMDLINE,
+};
+
+extern enum config_scope current_config_scope(void);
extern const char *current_config_origin_type(void);
extern const char *current_config_name(void);
@@ -1696,6 +1729,8 @@ extern int ignore_untracked_cache_config;
struct key_value_info {
const char *filename;
int linenr;
+ enum config_origin_type origin_type;
+ enum config_scope scope;
};
extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
@@ -1721,8 +1756,6 @@ 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);
extern void write_or_die(int fd, const void *buf, size_t count);
-extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
-extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
extern void fsync_or_die(int fd, const char *);
extern ssize_t read_in_full(int fd, void *buf, size_t count);
@@ -1734,8 +1767,21 @@ static inline ssize_t write_str_in_full(int fd, const char *str)
return write_in_full(fd, str, strlen(str));
}
-extern int write_file(const char *path, const char *fmt, ...);
-extern int write_file_gently(const char *path, const char *fmt, ...);
+/**
+ * Open (and truncate) the file at path, write the contents of buf to it,
+ * and close it. Dies if any errors are encountered.
+ */
+extern void write_file_buf(const char *path, const char *buf, size_t len);
+
+/**
+ * Like write_file_buf(), but format the contents into a buffer first.
+ * Additionally, write_file() will append a newline if one is not already
+ * present, making it convenient to write text files:
+ *
+ * write_file(path, "counter: %d", ctr);
+ */
+__attribute__((format (printf, 2, 3)))
+extern void write_file(const char *path, const char *fmt, ...);
/* pager.c */
extern void setup_pager(void);
diff --git a/color.c b/color.c
index 8f85153..81c2676 100644
--- a/color.c
+++ b/color.c
@@ -123,19 +123,34 @@ static int parse_color(struct color *out, const char *name, int len)
return -1;
}
-static int parse_attr(const char *name, int len)
+static int parse_attr(const char *name, size_t len)
{
- static const int attr_values[] = { 1, 2, 4, 5, 7,
- 22, 22, 24, 25, 27 };
- static const char * const attr_names[] = {
- "bold", "dim", "ul", "blink", "reverse",
- "nobold", "nodim", "noul", "noblink", "noreverse"
+ static const struct {
+ const char *name;
+ size_t len;
+ int val, neg;
+ } attrs[] = {
+#define ATTR(x, val, neg) { (x), sizeof(x)-1, (val), (neg) }
+ ATTR("bold", 1, 22),
+ ATTR("dim", 2, 22),
+ ATTR("italic", 3, 23),
+ ATTR("ul", 4, 24),
+ ATTR("blink", 5, 25),
+ ATTR("reverse", 7, 27),
+ ATTR("strike", 9, 29)
+#undef ATTR
};
+ int negate = 0;
int i;
- for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
- const char *str = attr_names[i];
- if (!strncasecmp(name, str, len) && !str[len])
- return attr_values[i];
+
+ if (skip_prefix_mem(name, len, "no", &name, &len)) {
+ skip_prefix_mem(name, len, "-", &name, &len);
+ negate = 1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(attrs); i++) {
+ if (attrs[i].len == len && !memcmp(attrs[i].name, name, len))
+ return negate ? attrs[i].neg : attrs[i].val;
}
return -1;
}
diff --git a/color.h b/color.h
index b9ead16..9062765 100644
--- a/color.h
+++ b/color.h
@@ -3,20 +3,23 @@
struct strbuf;
-/* 2 + (2 * num_attrs) + 8 + 1 + 8 + 'm' + NUL */
-/* "\033[1;2;4;5;7;38;5;2xx;48;5;2xxm\0" */
/*
* The maximum length of ANSI color sequence we would generate:
* - leading ESC '[' 2
- * - attr + ';' 3 * 10 (e.g. "1;")
+ * - attr + ';' 2 * num_attr (e.g. "1;")
+ * - no-attr + ';' 3 * num_attr (e.g. "22;")
* - fg color + ';' 17 (e.g. "38;2;255;255;255;")
* - bg color + ';' 17 (e.g. "48;2;255;255;255;")
* - terminating 'm' NUL 2
*
- * The above overcounts attr (we only use 5 not 8) and one semicolon
- * but it is close enough.
+ * The above overcounts by one semicolon but it is close enough.
+ *
+ * The space for attributes is also slightly overallocated, as
+ * the negation for some attributes is the same (e.g., nobold and nodim).
+ *
+ * We allocate space for 7 attributes.
*/
-#define COLOR_MAXLEN 70
+#define COLOR_MAXLEN 75
#define GIT_COLOR_NORMAL ""
#define GIT_COLOR_RESET "\033[m"
diff --git a/combine-diff.c b/combine-diff.c
index 8f2313d..8e2a577 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -44,9 +44,9 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
memset(p->parent, 0,
sizeof(p->parent[0]) * num_parent);
- hashcpy(p->oid.hash, q->queue[i]->two->sha1);
+ oidcpy(&p->oid, &q->queue[i]->two->oid);
p->mode = q->queue[i]->two->mode;
- hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1);
+ oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
p->parent[n].mode = q->queue[i]->one->mode;
p->parent[n].status = q->queue[i]->status;
*tail = p;
@@ -77,7 +77,7 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
continue;
}
- hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1);
+ oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
p->parent[n].mode = q->queue[i]->one->mode;
p->parent[n].status = q->queue[i]->status;
@@ -1268,16 +1268,16 @@ static struct diff_filepair *combined_pair(struct combine_diff_path *p,
for (i = 0; i < num_parent; i++) {
pair->one[i].path = p->path;
pair->one[i].mode = p->parent[i].mode;
- hashcpy(pair->one[i].sha1, p->parent[i].oid.hash);
- pair->one[i].sha1_valid = !is_null_oid(&p->parent[i].oid);
+ oidcpy(&pair->one[i].oid, &p->parent[i].oid);
+ pair->one[i].oid_valid = !is_null_oid(&p->parent[i].oid);
pair->one[i].has_more_entries = 1;
}
pair->one[num_parent - 1].has_more_entries = 0;
pair->two->path = p->path;
pair->two->mode = p->mode;
- hashcpy(pair->two->sha1, p->oid.hash);
- pair->two->sha1_valid = !is_null_oid(&p->oid);
+ oidcpy(&pair->two->oid, &p->oid);
+ pair->two->oid_valid = !is_null_oid(&p->oid);
return pair;
}
@@ -1525,7 +1525,7 @@ void diff_tree_combined(const unsigned char *sha1,
free(tmp);
}
- free_pathspec(&diffopts.pathspec);
+ clear_pathspec(&diffopts.pathspec);
}
void diff_tree_combined_merge(const struct commit *commit, int dense,
diff --git a/commit-slab.h b/commit-slab.h
index f84b449..006a50b 100644
--- a/commit-slab.h
+++ b/commit-slab.h
@@ -126,16 +126,16 @@ static MAYBE_UNUSED elemtype *slabname## _peek(struct slabname *s, \
return slabname##_at_peek(s, c, 0); \
} \
\
-static int stat_ ##slabname## realloc
+struct slabname
/*
- * Note that this seemingly redundant second declaration is required
+ * Note that this redundant forward declaration is required
* to allow a terminating semicolon, which makes instantiations look
* like function declarations. I.e., the expansion of
*
* define_commit_slab(indegree, int);
*
- * ends in 'static int stat_indegreerealloc;'. This would otherwise
+ * ends in 'struct indegree;'. This would otherwise
* be a syntax error according (at least) to ISO C. It's hard to
* catch because GCC silently parses it by default.
*/
diff --git a/commit.c b/commit.c
index 2a90e37..71a360d 100644
--- a/commit.c
+++ b/commit.c
@@ -1622,16 +1622,6 @@ struct commit_list **commit_list_append(struct commit *commit,
return &new->next;
}
-void print_commit_list(struct commit_list *list,
- const char *format_cur,
- const char *format_last)
-{
- for ( ; list; list = list->next) {
- const char *format = list->next ? format_cur : format_last;
- printf(format, oid_to_hex(&list->item->object.oid));
- }
-}
-
const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
{
int key_len = strlen(key);
diff --git a/commit.h b/commit.h
index adf57d6..23ae0c1 100644
--- a/commit.h
+++ b/commit.h
@@ -131,11 +131,17 @@ enum cmit_fmt {
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
CMIT_FMT_EMAIL,
+ CMIT_FMT_MBOXRD,
CMIT_FMT_USERFORMAT,
CMIT_FMT_UNSPECIFIED
};
+static inline int cmit_fmt_is_mail(enum cmit_fmt fmt)
+{
+ return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD);
+}
+
struct pretty_print_context {
/*
* Callers should tweak these to change the behavior of pp_* functions.
@@ -371,10 +377,6 @@ extern int parse_signed_commit(const struct commit *commit,
struct strbuf *message, struct strbuf *signature);
extern int remove_signature(struct strbuf *buf);
-extern void print_commit_list(struct commit_list *list,
- const char *format_cur,
- const char *format_last);
-
/*
* Check the signature of the given commit. The result of the check is stored
* in sig->check_result, 'G' for a good signature, 'U' for a good signature
diff --git a/common-main.c b/common-main.c
new file mode 100644
index 0000000..44a29e8
--- /dev/null
+++ b/common-main.c
@@ -0,0 +1,41 @@
+#include "cache.h"
+#include "exec_cmd.h"
+
+/*
+ * 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, const char **argv)
+{
+ /*
+ * Always open file descriptors 0/1/2 to avoid clobbering files
+ * in die(). It also avoids messing up when the pipes are dup'ed
+ * onto stdin/stdout/stderr in the child processes we spawn.
+ */
+ sanitize_stdfds();
+
+ git_setup_gettext();
+
+ argv[0] = git_extract_argv0_path(argv[0]);
+
+ restore_sigpipe_to_default();
+
+ return cmd_main(argc, argv);
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index 9a8803b..95e128f 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -73,6 +73,9 @@ typedef int pid_t;
#ifndef ECONNABORTED
#define ECONNABORTED WSAECONNABORTED
#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK WSAENOTSOCK
+#endif
struct passwd {
char *pw_name;
@@ -535,7 +538,7 @@ extern CRITICAL_SECTION pinfo_cs;
void mingw_startup(void);
#define main(c,v) dummy_decl_mingw_main(void); \
static int mingw_main(c,v); \
-int main(int argc, char **argv) \
+int main(int argc, const char **argv) \
{ \
mingw_startup(); \
return mingw_main(__argc, (void *)__argv); \
diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
index a0a16eb..2d4ef59 100644
--- a/compat/nedmalloc/nedmalloc.c
+++ b/compat/nedmalloc/nedmalloc.c
@@ -938,10 +938,10 @@ void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **
void **ret;
threadcache *tc;
int mymspace;
- size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
- if(!adjustedsizes) return 0;
- for(i=0; i<elems; i++)
- adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
+ size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
+ if(!adjustedsizes) return 0;
+ for(i=0; i<elems; i++)
+ adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
GetThreadCache(&p, &tc, &mymspace, 0);
GETMSPACE(m, p, tc, mymspace, 0,
ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
@@ -955,12 +955,11 @@ void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **
*/
char *strdup(const char *s1)
{
- char *s2 = 0;
- if (s1) {
- size_t len = strlen(s1) + 1;
- s2 = malloc(len);
+ size_t len = strlen(s1) + 1;
+ char *s2 = malloc(len);
+
+ if (s2)
memcpy(s2, s1, len);
- }
return s2;
}
#endif
diff --git a/config.c b/config.c
index d7ce34b..584cacf 100644
--- a/config.c
+++ b/config.c
@@ -24,7 +24,7 @@ struct config_source {
size_t pos;
} buf;
} u;
- const char *origin_type;
+ enum config_origin_type origin_type;
const char *name;
const char *path;
int die_on_error;
@@ -38,7 +38,33 @@ struct config_source {
long (*do_ftell)(struct config_source *c);
};
+/*
+ * These variables record the "current" config source, which
+ * can be accessed by parsing callbacks.
+ *
+ * The "cf" variable will be non-NULL only when we are actually parsing a real
+ * config source (file, blob, cmdline, etc).
+ *
+ * The "current_config_kvi" variable will be non-NULL only when we are feeding
+ * cached config from a configset into a callback.
+ *
+ * They should generally never be non-NULL at the same time. If they are both
+ * NULL, then we aren't parsing anything (and depending on the function looking
+ * at the variables, it's either a bug for it to be called in the first place,
+ * or it's a function which can be reused for non-config purposes, and should
+ * fall back to some sane behavior).
+ */
static struct config_source *cf;
+static struct key_value_info *current_config_kvi;
+
+/*
+ * Similar to the variables above, this gives access to the "scope" of the
+ * current value (repo, global, etc). For cached values, it can be found via
+ * the current_config_kvi as above. During parsing, the current value can be
+ * found in this variable. It's not part of "cf" because it transcends a single
+ * file (i.e., a file included from .git/config is still in "repo" scope).
+ */
+static enum config_scope current_parsing_scope;
static int zlib_compression_seen;
@@ -131,7 +157,9 @@ static int handle_path_include(const char *path, struct config_include_data *inc
if (!access_or_die(path, R_OK, 0)) {
if (++inc->depth > MAX_INCLUDE_DEPTH)
die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
- cf && cf->name ? cf->name : "the command line");
+ !cf ? "<unknown>" :
+ cf->name ? cf->name :
+ "the command line");
ret = git_config_from_file(git_config_include, path, inc);
inc->depth--;
}
@@ -205,32 +233,41 @@ int git_config_parse_parameter(const char *text,
int git_config_from_parameters(config_fn_t fn, void *data)
{
const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
+ int ret = 0;
char *envw;
const char **argv = NULL;
int nr = 0, alloc = 0;
int i;
+ struct config_source source;
if (!env)
return 0;
+
+ memset(&source, 0, sizeof(source));
+ source.prev = cf;
+ source.origin_type = CONFIG_ORIGIN_CMDLINE;
+ cf = &source;
+
/* sq_dequote will write over it */
envw = xstrdup(env);
if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
- free(envw);
- return error("bogus format in " CONFIG_DATA_ENVIRONMENT);
+ ret = error("bogus format in " CONFIG_DATA_ENVIRONMENT);
+ goto out;
}
for (i = 0; i < nr; i++) {
if (git_config_parse_parameter(argv[i], fn, data) < 0) {
- free(argv);
- free(envw);
- return -1;
+ ret = -1;
+ goto out;
}
}
+out:
free(argv);
free(envw);
- return nr > 0;
+ cf = source.prev;
+ return ret;
}
static int get_next_char(void)
@@ -417,6 +454,8 @@ static int git_parse_source(config_fn_t fn, void *data)
int comment = 0;
int baselen = 0;
struct strbuf *var = &cf->var;
+ int error_return = 0;
+ char *error_msg = NULL;
/* U+FEFF Byte Order Mark in UTF8 */
const char *bomptr = utf8_bom;
@@ -471,10 +510,40 @@ static int git_parse_source(config_fn_t fn, void *data)
if (get_value(fn, data, var) < 0)
break;
}
+
+ switch (cf->origin_type) {
+ case CONFIG_ORIGIN_BLOB:
+ error_msg = xstrfmt(_("bad config line %d in blob %s"),
+ cf->linenr, cf->name);
+ break;
+ case CONFIG_ORIGIN_FILE:
+ error_msg = xstrfmt(_("bad config line %d in file %s"),
+ cf->linenr, cf->name);
+ break;
+ case CONFIG_ORIGIN_STDIN:
+ error_msg = xstrfmt(_("bad config line %d in standard input"),
+ cf->linenr);
+ break;
+ case CONFIG_ORIGIN_SUBMODULE_BLOB:
+ error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
+ cf->linenr, cf->name);
+ break;
+ case CONFIG_ORIGIN_CMDLINE:
+ error_msg = xstrfmt(_("bad config line %d in command line %s"),
+ cf->linenr, cf->name);
+ break;
+ default:
+ error_msg = xstrfmt(_("bad config line %d in %s"),
+ cf->linenr, cf->name);
+ }
+
if (cf->die_on_error)
- die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+ die("%s", error_msg);
else
- return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
+ error_return = error("%s", error_msg);
+
+ free(error_msg);
+ return error_return;
}
static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -583,16 +652,47 @@ int git_parse_ulong(const char *value, unsigned long *ret)
NORETURN
static void die_bad_number(const char *name, const char *value)
{
- const char *reason = errno == ERANGE ?
- "out of range" :
- "invalid unit";
if (!value)
value = "";
- if (cf && cf->origin_type && cf->name)
- die(_("bad numeric config value '%s' for '%s' in %s %s: %s"),
- value, name, cf->origin_type, cf->name, reason);
- die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
+ if (!(cf && cf->name))
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s': out of range")
+ : _("bad numeric config value '%s' for '%s': invalid unit"),
+ value, name);
+
+ switch (cf->origin_type) {
+ case CONFIG_ORIGIN_BLOB:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in blob %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in blob %s: invalid unit"),
+ value, name, cf->name);
+ case CONFIG_ORIGIN_FILE:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in file %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in file %s: invalid unit"),
+ value, name, cf->name);
+ case CONFIG_ORIGIN_STDIN:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in standard input: out of range")
+ : _("bad numeric config value '%s' for '%s' in standard input: invalid unit"),
+ value, name);
+ case CONFIG_ORIGIN_SUBMODULE_BLOB:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in submodule-blob %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in submodule-blob %s: invalid unit"),
+ value, name, cf->name);
+ case CONFIG_ORIGIN_CMDLINE:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in command line %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in command line %s: invalid unit"),
+ value, name, cf->name);
+ default:
+ die(errno == ERANGE
+ ? _("bad numeric config value '%s' for '%s' in %s: out of range")
+ : _("bad numeric config value '%s' for '%s' in %s: invalid unit"),
+ value, name, cf->name);
+ }
}
int git_config_int(const char *name, const char *value)
@@ -1069,7 +1169,8 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
}
static int do_config_from_file(config_fn_t fn,
- const char *origin_type, const char *name, const char *path, FILE *f,
+ const enum config_origin_type origin_type,
+ const char *name, const char *path, FILE *f,
void *data)
{
struct config_source top;
@@ -1088,7 +1189,7 @@ static int do_config_from_file(config_fn_t fn,
static int git_config_from_stdin(config_fn_t fn, void *data)
{
- return do_config_from_file(fn, "standard input", "", NULL, stdin, data);
+ return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
}
int git_config_from_file(config_fn_t fn, const char *filename, void *data)
@@ -1099,14 +1200,14 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
f = fopen(filename, "r");
if (f) {
flockfile(f);
- ret = do_config_from_file(fn, "file", filename, filename, f, data);
+ ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
funlockfile(f);
fclose(f);
}
return ret;
}
-int git_config_from_mem(config_fn_t fn, const char *origin_type,
+int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
const char *name, const char *buf, size_t len, void *data)
{
struct config_source top;
@@ -1143,7 +1244,7 @@ static int git_config_from_blob_sha1(config_fn_t fn,
return error("reference '%s' does not point to a blob", name);
}
- ret = git_config_from_mem(fn, "blob", name, buf, size, data);
+ ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, data);
free(buf);
return ret;
@@ -1197,47 +1298,36 @@ int git_config_system(void)
static int do_git_config_sequence(config_fn_t fn, void *data)
{
- int ret = 0, found = 0;
+ int ret = 0;
char *xdg_config = xdg_config_home("config");
char *user_config = expand_user_path("~/.gitconfig");
char *repo_config = git_pathdup("config");
- if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
+ current_parsing_scope = CONFIG_SCOPE_SYSTEM;
+ if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
ret += git_config_from_file(fn, git_etc_gitconfig(),
data);
- found += 1;
- }
- if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) {
+ current_parsing_scope = CONFIG_SCOPE_GLOBAL;
+ if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
ret += git_config_from_file(fn, xdg_config, data);
- found += 1;
- }
- if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) {
+ if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
ret += git_config_from_file(fn, user_config, data);
- found += 1;
- }
- if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
+ current_parsing_scope = CONFIG_SCOPE_REPO;
+ if (repo_config && !access_or_die(repo_config, R_OK, 0))
ret += git_config_from_file(fn, repo_config, data);
- found += 1;
- }
- switch (git_config_from_parameters(fn, data)) {
- case -1: /* error */
+ current_parsing_scope = CONFIG_SCOPE_CMDLINE;
+ if (git_config_from_parameters(fn, data) < 0)
die(_("unable to parse command-line config"));
- break;
- case 0: /* found nothing */
- break;
- default: /* found at least one item */
- found++;
- break;
- }
+ current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
free(xdg_config);
free(user_config);
free(repo_config);
- return ret == 0 ? found : ret;
+ return ret;
}
int git_config_with_options(config_fn_t fn, void *data,
@@ -1272,7 +1362,7 @@ 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
+ * zero, as most errors are fatal, and
* non-fatal potential errors are guarded by "if"
* statements that are entered only when no error is
* possible.
@@ -1290,16 +1380,20 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
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);
- }
+
+ current_config_kvi = values->items[value_index].util;
+
+ if (fn(entry->key, values->items[value_index].string, data) < 0)
+ git_die_config_linenr(entry->key,
+ current_config_kvi->filename,
+ current_config_kvi->linenr);
+
+ current_config_kvi = NULL;
}
}
@@ -1356,14 +1450,19 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
- if (cf) {
+ if (!cf)
+ die("BUG: configset_add_value has no source");
+ if (cf->name) {
kv_info->filename = strintern(cf->name);
kv_info->linenr = cf->linenr;
+ kv_info->origin_type = cf->origin_type;
} else {
/* for values read from `git_config_from_parameters()` */
kv_info->filename = NULL;
kv_info->linenr = -1;
+ kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
}
+ kv_info->scope = current_parsing_scope;
si->util = kv_info;
return 0;
@@ -2442,10 +2541,46 @@ int parse_config_key(const char *var,
const char *current_config_origin_type(void)
{
- return cf && cf->origin_type ? cf->origin_type : "command line";
+ int type;
+ if (current_config_kvi)
+ type = current_config_kvi->origin_type;
+ else if(cf)
+ type = cf->origin_type;
+ else
+ die("BUG: current_config_origin_type called outside config callback");
+
+ switch (type) {
+ case CONFIG_ORIGIN_BLOB:
+ return "blob";
+ case CONFIG_ORIGIN_FILE:
+ return "file";
+ case CONFIG_ORIGIN_STDIN:
+ return "standard input";
+ case CONFIG_ORIGIN_SUBMODULE_BLOB:
+ return "submodule-blob";
+ case CONFIG_ORIGIN_CMDLINE:
+ return "command line";
+ default:
+ die("BUG: unknown config origin type");
+ }
}
const char *current_config_name(void)
{
- return cf && cf->name ? cf->name : "";
+ const char *name;
+ if (current_config_kvi)
+ name = current_config_kvi->filename;
+ else if (cf)
+ name = cf->name;
+ else
+ die("BUG: current_config_name called outside config callback");
+ return name ? name : "";
+}
+
+enum config_scope current_config_scope(void)
+{
+ if (current_config_kvi)
+ return current_config_kvi->scope;
+ else
+ return current_parsing_scope;
}
diff --git a/config.mak.uname b/config.mak.uname
index a88f139..b232908 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -36,6 +36,8 @@ ifeq ($(uname_S),Linux)
HAVE_DEV_TTY = YesPlease
HAVE_CLOCK_GETTIME = YesPlease
HAVE_CLOCK_MONOTONIC = YesPlease
+ # -lrt is needed for clock_gettime on glibc <= 2.16
+ NEEDS_LIBRT = YesPlease
HAVE_GETDELIM = YesPlease
SANE_TEXT_GREP=-a
endif
@@ -203,9 +205,11 @@ ifeq ($(uname_S),FreeBSD)
NO_STRTOUMAX = YesPlease
endif
PYTHON_PATH = /usr/local/bin/python
+ PERL_PATH = /usr/local/bin/perl
HAVE_PATHS_H = YesPlease
GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
HAVE_BSD_SYSCTL = YesPlease
+ PAGER_ENV = LESS=FRX LV=-c MORE=FRX
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
diff --git a/configure.ac b/configure.ac
index c279025..aa9c91d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1108,14 +1108,19 @@ GIT_CONF_SUBST([HAVE_BSD_SYSCTL])
AC_DEFUN([PTHREADTEST_SRC], [
AC_LANG_PROGRAM([[
#include <pthread.h>
+static void *noop(void *ignore) { return ignore; }
]], [[
pthread_mutex_t test_mutex;
pthread_key_t test_key;
+ pthread_t th;
int retcode = 0;
+ void *ret = (void *)0;
retcode |= pthread_key_create(&test_key, (void *)0);
retcode |= pthread_mutex_init(&test_mutex,(void *)0);
retcode |= pthread_mutex_lock(&test_mutex);
retcode |= pthread_mutex_unlock(&test_mutex);
+ retcode |= pthread_create(&th, ret, noop, ret);
+ retcode |= pthread_join(th, &ret);
return retcode;
]])])
diff --git a/connect.c b/connect.c
index c53f3f1..722dc3f 100644
--- a/connect.c
+++ b/connect.c
@@ -658,6 +658,19 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
static struct child_process no_fork = CHILD_PROCESS_INIT;
+static const char *get_ssh_command(void)
+{
+ const char *ssh;
+
+ if ((ssh = getenv("GIT_SSH_COMMAND")))
+ return ssh;
+
+ if (!git_config_get_string_const("core.sshcommand", &ssh))
+ return ssh;
+
+ return NULL;
+}
+
/*
* This returns a dummy child_process if the transport protocol does not
* need fork(2), or a struct child_process object if it does. Once done,
@@ -758,7 +771,7 @@ struct child_process *git_connect(int fd[2], const char *url,
return NULL;
}
- ssh = getenv("GIT_SSH_COMMAND");
+ ssh = get_ssh_command();
if (!ssh) {
const char *base;
char *ssh_dup;
diff --git a/connected.c b/connected.c
index bf1b12e..8e3e4b1 100644
--- a/connected.c
+++ b/connected.c
@@ -4,10 +4,6 @@
#include "connected.h"
#include "transport.h"
-int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
-{
- return check_everything_connected_with_transport(fn, quiet, cb_data, NULL);
-}
/*
* If we feed all the commits we want to verify to this command
*
@@ -19,22 +15,27 @@ int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
*
* Returns 0 if everything is connected, non-zero otherwise.
*/
-static int check_everything_connected_real(sha1_iterate_fn fn,
- int quiet,
- void *cb_data,
- struct transport *transport,
- const char *shallow_file)
+int check_connected(sha1_iterate_fn fn, void *cb_data,
+ struct check_connected_options *opt)
{
struct child_process rev_list = CHILD_PROCESS_INIT;
- const char *argv[9];
+ struct check_connected_options defaults = CHECK_CONNECTED_INIT;
char commit[41];
unsigned char sha1[20];
- int err = 0, ac = 0;
+ int err = 0;
struct packed_git *new_pack = NULL;
+ struct transport *transport;
size_t base_len;
- if (fn(cb_data, sha1))
+ if (!opt)
+ opt = &defaults;
+ transport = opt->transport;
+
+ if (fn(cb_data, sha1)) {
+ if (opt->err_fd)
+ close(opt->err_fd);
return err;
+ }
if (transport && transport->smart_options &&
transport->smart_options->self_contained_and_connected &&
@@ -47,24 +48,28 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
strbuf_release(&idx_file);
}
- if (shallow_file) {
- argv[ac++] = "--shallow-file";
- argv[ac++] = shallow_file;
+ if (opt->shallow_file) {
+ argv_array_push(&rev_list.args, "--shallow-file");
+ argv_array_push(&rev_list.args, opt->shallow_file);
}
- argv[ac++] = "rev-list";
- argv[ac++] = "--objects";
- argv[ac++] = "--stdin";
- argv[ac++] = "--not";
- argv[ac++] = "--all";
- if (quiet)
- argv[ac++] = "--quiet";
- argv[ac] = NULL;
+ argv_array_push(&rev_list.args,"rev-list");
+ argv_array_push(&rev_list.args, "--objects");
+ argv_array_push(&rev_list.args, "--stdin");
+ argv_array_push(&rev_list.args, "--not");
+ argv_array_push(&rev_list.args, "--all");
+ argv_array_push(&rev_list.args, "--quiet");
+ if (opt->progress)
+ argv_array_pushf(&rev_list.args, "--progress=%s",
+ _("Checking connectivity"));
- rev_list.argv = argv;
rev_list.git_cmd = 1;
rev_list.in = -1;
rev_list.no_stdout = 1;
- rev_list.no_stderr = quiet;
+ if (opt->err_fd)
+ rev_list.err = opt->err_fd;
+ else
+ rev_list.no_stderr = opt->quiet;
+
if (start_command(&rev_list))
return error(_("Could not run 'git rev-list'"));
@@ -98,19 +103,3 @@ static int check_everything_connected_real(sha1_iterate_fn fn,
sigchain_pop(SIGPIPE);
return finish_command(&rev_list) || err;
}
-
-int check_everything_connected_with_transport(sha1_iterate_fn fn,
- int quiet,
- void *cb_data,
- struct transport *transport)
-{
- return check_everything_connected_real(fn, quiet, cb_data,
- transport, NULL);
-}
-
-int check_shallow_connected(sha1_iterate_fn fn, int quiet, void *cb_data,
- const char *shallow_file)
-{
- return check_everything_connected_real(fn, quiet, cb_data,
- NULL, shallow_file);
-}
diff --git a/connected.h b/connected.h
index 071d408..afa48cc 100644
--- a/connected.h
+++ b/connected.h
@@ -11,17 +11,42 @@ struct transport;
typedef int (*sha1_iterate_fn)(void *, unsigned char [20]);
/*
+ * Named-arguments struct for check_connected. All arguments are
+ * optional, and can be left to defaults as set by CHECK_CONNECTED_INIT.
+ */
+struct check_connected_options {
+ /* Avoid printing any errors to stderr. */
+ int quiet;
+
+ /* --shallow-file to pass to rev-list sub-process */
+ const char *shallow_file;
+
+ /* Transport whose objects we are checking, if available. */
+ struct transport *transport;
+
+ /*
+ * If non-zero, send error messages to this descriptor rather
+ * than stderr. The descriptor is closed before check_connected
+ * returns.
+ */
+ int err_fd;
+
+ /* If non-zero, show progress as we traverse the objects. */
+ int progress;
+};
+
+#define CHECK_CONNECTED_INIT { 0 }
+
+/*
* Make sure that our object store has all the commits necessary to
* connect the ancestry chain to some of our existing refs, and all
* the trees and blobs that these commits use.
*
* Return 0 if Ok, non zero otherwise (i.e. some missing objects)
+ *
+ * If "opt" is NULL, behaves as if CHECK_CONNECTED_INIT was passed.
*/
-extern int check_everything_connected(sha1_iterate_fn, int quiet, void *cb_data);
-extern int check_shallow_connected(sha1_iterate_fn, int quiet, void *cb_data,
- const char *shallow_file);
-extern int check_everything_connected_with_transport(sha1_iterate_fn, int quiet,
- void *cb_data,
- struct transport *transport);
+int check_connected(sha1_iterate_fn fn, void *cb_data,
+ struct check_connected_options *opt);
#endif /* CONNECTED_H */
diff --git a/contrib/coccinelle/README b/contrib/coccinelle/README
new file mode 100644
index 0000000..9c2f887
--- /dev/null
+++ b/contrib/coccinelle/README
@@ -0,0 +1,2 @@
+This directory provides examples of Coccinelle (http://coccinelle.lip6.fr/)
+semantic patches that might be useful to developers.
diff --git a/contrib/coccinelle/object_id.cocci b/contrib/coccinelle/object_id.cocci
new file mode 100644
index 0000000..8ccdbb5
--- /dev/null
+++ b/contrib/coccinelle/object_id.cocci
@@ -0,0 +1,95 @@
+@@
+expression E1;
+@@
+- is_null_sha1(E1.hash)
++ is_null_oid(&E1)
+
+@@
+expression E1;
+@@
+- is_null_sha1(E1->hash)
++ is_null_oid(E1)
+
+@@
+expression E1;
+@@
+- sha1_to_hex(E1.hash)
++ oid_to_hex(&E1)
+
+@@
+expression E1;
+@@
+- sha1_to_hex(E1->hash)
++ oid_to_hex(E1)
+
+@@
+expression E1;
+@@
+- sha1_to_hex_r(E1.hash)
++ oid_to_hex_r(&E1)
+
+@@
+expression E1;
+@@
+- sha1_to_hex_r(E1->hash)
++ oid_to_hex_r(E1)
+
+@@
+expression E1;
+@@
+- hashclr(E1.hash)
++ oidclr(&E1)
+
+@@
+expression E1;
+@@
+- hashclr(E1->hash)
++ oidclr(E1)
+
+@@
+expression E1, E2;
+@@
+- hashcmp(E1.hash, E2.hash)
++ oidcmp(&E1, &E2)
+
+@@
+expression E1, E2;
+@@
+- hashcmp(E1->hash, E2->hash)
++ oidcmp(E1, E2)
+
+@@
+expression E1, E2;
+@@
+- hashcmp(E1->hash, E2.hash)
++ oidcmp(E1, &E2)
+
+@@
+expression E1, E2;
+@@
+- hashcmp(E1.hash, E2->hash)
++ oidcmp(&E1, E2)
+
+@@
+expression E1, E2;
+@@
+- hashcpy(E1.hash, E2.hash)
++ oidcpy(&E1, &E2)
+
+@@
+expression E1, E2;
+@@
+- hashcpy(E1->hash, E2->hash)
++ oidcpy(E1, E2)
+
+@@
+expression E1, E2;
+@@
+- hashcpy(E1->hash, E2.hash)
++ oidcpy(E1, &E2)
+
+@@
+expression E1, E2;
+@@
+- hashcpy(E1.hash, E2->hash)
++ oidcpy(&E1, E2)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 3402475..9c8f738 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -803,6 +803,50 @@ __git_find_on_cmdline ()
done
}
+# Echo the value of an option set on the command line or config
+#
+# $1: short option name
+# $2: long option name including =
+# $3: list of possible values
+# $4: config string (optional)
+#
+# example:
+# result="$(__git_get_option_value "-d" "--do-something=" \
+# "yes no" "core.doSomething")"
+#
+# result is then either empty (no option set) or "yes" or "no"
+#
+# __git_get_option_value requires 3 arguments
+__git_get_option_value ()
+{
+ local c short_opt long_opt val
+ local result= values config_key word
+
+ short_opt="$1"
+ long_opt="$2"
+ values="$3"
+ config_key="$4"
+
+ ((c = $cword - 1))
+ while [ $c -ge 0 ]; do
+ word="${words[c]}"
+ for val in $values; do
+ if [ "$short_opt$val" = "$word" ] ||
+ [ "$long_opt$val" = "$word" ]; then
+ result="$val"
+ break 2
+ fi
+ done
+ ((c--))
+ done
+
+ if [ -n "$config_key" ] && [ -z "$result" ]; then
+ result="$(git --git-dir="$(__gitdir)" config "$config_key")"
+ fi
+
+ echo "$result"
+}
+
__git_has_doubledash ()
{
local c=1
@@ -964,8 +1008,8 @@ _git_branch ()
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
- -d|-m) only_local_ref="y" ;;
- -r) has_r="y" ;;
+ -d|--delete|-m|--move) only_local_ref="y" ;;
+ -r|--remotes) has_r="y" ;;
esac
((c++))
done
@@ -979,7 +1023,7 @@ _git_branch ()
--color --no-color --verbose --abbrev= --no-abbrev
--track --no-track --contains --merged --no-merged
--set-upstream-to= --edit-description --list
- --unset-upstream
+ --unset-upstream --delete --move --remotes
"
;;
*)
@@ -1092,12 +1136,15 @@ _git_clone ()
--depth
--single-branch
--branch
+ --recurse-submodules
"
return
;;
esac
}
+__git_untracked_file_modes="all no normal"
+
_git_commit ()
{
case "$prev" in
@@ -1119,7 +1166,7 @@ _git_commit ()
return
;;
--untracked-files=*)
- __gitcomp "all no normal" "" "${cur##--untracked-files=}"
+ __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
return
;;
--*)
@@ -1158,6 +1205,8 @@ _git_describe ()
__git_diff_algorithms="myers minimal patience histogram"
+__git_diff_submodule_formats="log short"
+
__git_diff_common_options="--stat --numstat --shortstat --summary
--patch-with-stat --name-only --name-status --color
--no-color --color-words --no-renames --check
@@ -1173,6 +1222,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
--dirstat --dirstat= --dirstat-by-file
--dirstat-by-file= --cumulative
--diff-algorithm=
+ --submodule --submodule=
"
_git_diff ()
@@ -1184,6 +1234,10 @@ _git_diff ()
__gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
return
;;
+ --submodule=*)
+ __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+ return
+ ;;
--*)
__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
--base --ours --theirs --no-index
@@ -1447,6 +1501,14 @@ _git_log ()
__gitcomp "full short no" "" "${cur##--decorate=}"
return
;;
+ --diff-algorithm=*)
+ __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
+ return
+ ;;
+ --submodule=*)
+ __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+ return
+ ;;
--*)
__gitcomp "
$__git_log_common_options
@@ -1780,6 +1842,56 @@ _git_stage ()
_git_add
}
+_git_status ()
+{
+ local complete_opt
+ local untracked_state
+
+ case "$cur" in
+ --ignore-submodules=*)
+ __gitcomp "none untracked dirty all" "" "${cur##--ignore-submodules=}"
+ return
+ ;;
+ --untracked-files=*)
+ __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
+ return
+ ;;
+ --column=*)
+ __gitcomp "
+ always never auto column row plain dense nodense
+ " "" "${cur##--column=}"
+ return
+ ;;
+ --*)
+ __gitcomp "
+ --short --branch --porcelain --long --verbose
+ --untracked-files= --ignore-submodules= --ignored
+ --column= --no-column
+ "
+ return
+ ;;
+ esac
+
+ untracked_state="$(__git_get_option_value "-u" "--untracked-files=" \
+ "$__git_untracked_file_modes" "status.showUntrackedFiles")"
+
+ case "$untracked_state" in
+ no)
+ # --ignored option does not matter
+ complete_opt=
+ ;;
+ all|normal|*)
+ complete_opt="--cached --directory --no-empty-directory --others"
+
+ if [ -n "$(__git_find_on_cmdline "--ignored")" ]; then
+ complete_opt="$complete_opt --ignored --exclude=*"
+ fi
+ ;;
+ esac
+
+ __git_complete_index_file "$complete_opt"
+}
+
__git_config_get_set_variables ()
{
local prevword word config_file= c=$cword
@@ -2085,6 +2197,7 @@ _git_config ()
format.attach
format.cc
format.coverLetter
+ format.from
format.headers
format.numbered
format.pretty
@@ -2359,6 +2472,10 @@ _git_show ()
__gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
return
;;
+ --submodule=*)
+ __gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
+ return
+ ;;
--*)
__gitcomp "--pretty= --format= --abbrev-commit --oneline
--show-signature
@@ -2595,6 +2712,32 @@ _git_whatchanged ()
_git_log
}
+_git_worktree ()
+{
+ local subcommands="add list lock prune unlock"
+ local subcommand="$(__git_find_on_cmdline "$subcommands")"
+ if [ -z "$subcommand" ]; then
+ __gitcomp "$subcommands"
+ else
+ case "$subcommand,$cur" in
+ add,--*)
+ __gitcomp "--detach"
+ ;;
+ list,--*)
+ __gitcomp "--porcelain"
+ ;;
+ lock,--*)
+ __gitcomp "--reason"
+ ;;
+ prune,--*)
+ __gitcomp "--dry-run --expire --verbose"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+}
+
__git_main ()
{
local i c=1 command __git_dir
diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl
index 95438e1..d60b431 100755
--- a/contrib/fast-import/import-tars.perl
+++ b/contrib/fast-import/import-tars.perl
@@ -96,18 +96,21 @@ foreach my $tar_file (@ARGV)
$mtime = oct $mtime;
next if $typeflag == 5; # directory
- print FI "blob\n", "mark :$next_mark\n";
- if ($typeflag == 2) { # symbolic link
- print FI "data ", length($linkname), "\n", $linkname;
- $mode = 0120000;
- } else {
- print FI "data $size\n";
- while ($size > 0 && read(I, $_, 512) == 512) {
- print FI substr($_, 0, $size);
- $size -= 512;
+ if ($typeflag != 1) { # handle hard links later
+ print FI "blob\n", "mark :$next_mark\n";
+ if ($typeflag == 2) { # symbolic link
+ print FI "data ", length($linkname), "\n",
+ $linkname;
+ $mode = 0120000;
+ } else {
+ print FI "data $size\n";
+ while ($size > 0 && read(I, $_, 512) == 512) {
+ print FI substr($_, 0, $size);
+ $size -= 512;
+ }
}
+ print FI "\n";
}
- print FI "\n";
my $path;
if ($prefix) {
@@ -115,7 +118,13 @@ foreach my $tar_file (@ARGV)
} else {
$path = "$name";
}
- $files{$path} = [$next_mark++, $mode];
+
+ if ($typeflag == 1) { # hard link
+ $linkname = "$prefix/$linkname" if $prefix;
+ $files{$path} = [ $files{$linkname}->[0], $mode ];
+ } else {
+ $files{$path} = [$next_mark++, $mode];
+ }
$author_time = $mtime if $mtime > $author_time;
$path =~ m,^([^/]+)/,;
diff --git a/contrib/git-jump/README b/contrib/git-jump/README
index 1cebc32..225e3f0 100644
--- a/contrib/git-jump/README
+++ b/contrib/git-jump/README
@@ -29,7 +29,7 @@ Obviously this trivial case isn't that interesting; you could just open
`foo.c` yourself. But when you have many changes scattered across a
project, you can use the editor's support to "jump" from point to point.
-Git-jump can generate three types of interesting lists:
+Git-jump can generate four types of interesting lists:
1. The beginning of any diff hunks.
@@ -37,6 +37,8 @@ Git-jump can generate three types of interesting lists:
3. Any grep matches.
+ 4. Any whitespace errors detected by `git diff --check`.
+
Using git-jump
--------------
@@ -83,7 +85,7 @@ complete list of files and line numbers for each match.
Limitations
-----------
-This scripts was written and tested with vim. Given that the quickfix
+This script was written and tested with vim. Given that the quickfix
format is the same as what gcc produces, I expect emacs users have a
similar feature for iterating through the list, but I know nothing about
how to activate it.
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump
index dc90cd6..427f206 100755
--- a/contrib/git-jump/git-jump
+++ b/contrib/git-jump/git-jump
@@ -12,6 +12,8 @@ diff: elements are diff hunks. Arguments are given to diff.
merge: elements are merge conflicts. Arguments are ignored.
grep: elements are grep hits. Arguments are given to grep.
+
+ws: elements are whitespace errors. Arguments are given to diff --check.
EOF
}
@@ -25,7 +27,7 @@ mode_diff() {
perl -ne '
if (m{^\+\+\+ (.*)}) { $file = $1; next }
defined($file) or next;
- if (m/^@@ .*\+(\d+)/) { $line = $1; next }
+ if (m/^@@ .*?\+(\d+)/) { $line = $1; next }
defined($line) or next;
if (/^ /) { $line++; next }
if (/^[-+]\s*(.*)/) {
@@ -55,6 +57,10 @@ mode_grep() {
'
}
+mode_ws() {
+ git diff --check "$@"
+}
+
if test $# -lt 1; then
usage >&2
exit 1
diff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile
index 92baa3b..52b84ba 100644
--- a/contrib/persistent-https/Makefile
+++ b/contrib/persistent-https/Makefile
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-BUILD_LABEL=$(shell date +"%s")
+BUILD_LABEL=$(shell cut -d" " -f3 ../../GIT-VERSION-FILE)
TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz
all: git-remote-persistent-https git-remote-persistent-https--proxy \
@@ -25,8 +25,10 @@ git-remote-persistent-http: git-remote-persistent-https
ln -f -s git-remote-persistent-https git-remote-persistent-http
git-remote-persistent-https:
+ case $$(go version) in \
+ "go version go"1.[0-5].*) EQ=" " ;; *) EQ="=" ;; esac && \
go build -o git-remote-persistent-https \
- -ldflags "-X main._BUILD_EMBED_LABEL $(BUILD_LABEL)"
+ -ldflags "-X main._BUILD_EMBED_LABEL$${EQ}$(BUILD_LABEL)"
clean:
rm -f git-remote-persistent-http* *.tar.gz
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 7a39b30..dec085a 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -4,8 +4,9 @@
#
# Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
#
-if [ $# -eq 0 ]; then
- set -- -h
+if test $# -eq 0
+then
+ set -- -h
fi
OPTS_SPEC="\
git subtree add --prefix=<prefix> <commit>
@@ -48,89 +49,144 @@ squash=
message=
prefix=
-debug()
-{
- if [ -n "$debug" ]; then
+debug () {
+ if test -n "$debug"
+ then
printf "%s\n" "$*" >&2
fi
}
-say()
-{
- if [ -z "$quiet" ]; then
+say () {
+ if test -z "$quiet"
+ then
printf "%s\n" "$*" >&2
fi
}
-progress()
-{
- if [ -z "$quiet" ]; then
+progress () {
+ if test -z "$quiet"
+ then
printf "%s\r" "$*" >&2
fi
}
-assert()
-{
- if "$@"; then
- :
- else
+assert () {
+ if ! "$@"
+ then
die "assertion failed: " "$@"
fi
}
-#echo "Options: $*"
-
-while [ $# -gt 0 ]; do
+while test $# -gt 0
+do
opt="$1"
shift
+
case "$opt" in
- -q) quiet=1 ;;
- -d) debug=1 ;;
- --annotate) annotate="$1"; shift ;;
- --no-annotate) annotate= ;;
- -b) branch="$1"; shift ;;
- -P) prefix="${1%/}"; shift ;;
- -m) message="$1"; shift ;;
- --no-prefix) prefix= ;;
- --onto) onto="$1"; shift ;;
- --no-onto) onto= ;;
- --rejoin) rejoin=1 ;;
- --no-rejoin) rejoin= ;;
- --ignore-joins) ignore_joins=1 ;;
- --no-ignore-joins) ignore_joins= ;;
- --squash) squash=1 ;;
- --no-squash) squash= ;;
- --) break ;;
- *) die "Unexpected option: $opt" ;;
+ -q)
+ quiet=1
+ ;;
+ -d)
+ debug=1
+ ;;
+ --annotate)
+ annotate="$1"
+ shift
+ ;;
+ --no-annotate)
+ annotate=
+ ;;
+ -b)
+ branch="$1"
+ shift
+ ;;
+ -P)
+ prefix="${1%/}"
+ shift
+ ;;
+ -m)
+ message="$1"
+ shift
+ ;;
+ --no-prefix)
+ prefix=
+ ;;
+ --onto)
+ onto="$1"
+ shift
+ ;;
+ --no-onto)
+ onto=
+ ;;
+ --rejoin)
+ rejoin=1
+ ;;
+ --no-rejoin)
+ rejoin=
+ ;;
+ --ignore-joins)
+ ignore_joins=1
+ ;;
+ --no-ignore-joins)
+ ignore_joins=
+ ;;
+ --squash)
+ squash=1
+ ;;
+ --no-squash)
+ squash=
+ ;;
+ --)
+ break
+ ;;
+ *)
+ die "Unexpected option: $opt"
+ ;;
esac
done
command="$1"
shift
+
case "$command" in
- add|merge|pull) default= ;;
- split|push) default="--default HEAD" ;;
- *) die "Unknown command '$command'" ;;
+add|merge|pull)
+ default=
+ ;;
+split|push)
+ default="--default HEAD"
+ ;;
+*)
+ die "Unknown command '$command'"
+ ;;
esac
-if [ -z "$prefix" ]; then
+if test -z "$prefix"
+then
die "You must provide the --prefix option."
fi
case "$command" in
- add) [ -e "$prefix" ] &&
- die "prefix '$prefix' already exists." ;;
- *) [ -e "$prefix" ] ||
- die "'$prefix' does not exist; use 'git subtree add'" ;;
+add)
+ test -e "$prefix" &&
+ die "prefix '$prefix' already exists."
+ ;;
+*)
+ test -e "$prefix" ||
+ die "'$prefix' does not exist; use 'git subtree add'"
+ ;;
esac
dir="$(dirname "$prefix/.")"
-if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then
+if test "$command" != "pull" &&
+ test "$command" != "add" &&
+ test "$command" != "push"
+then
revs=$(git rev-parse $default --revs-only "$@") || exit $?
- dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $?
- if [ -n "$dirs" ]; then
+ dirs=$(git rev-parse --no-revs --no-flags "$@") || exit $?
+ if test -n "$dirs"
+ then
die "Error: Use --prefix instead of bare filenames."
fi
fi
@@ -142,78 +198,82 @@ debug "dir: {$dir}"
debug "opts: {$*}"
debug
-cache_setup()
-{
+cache_setup () {
cachedir="$GIT_DIR/subtree-cache/$$"
- rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir"
- mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir"
- mkdir -p "$cachedir/notree" || die "Can't create new cachedir: $cachedir/notree"
+ rm -rf "$cachedir" ||
+ die "Can't delete old cachedir: $cachedir"
+ mkdir -p "$cachedir" ||
+ die "Can't create new cachedir: $cachedir"
+ mkdir -p "$cachedir/notree" ||
+ die "Can't create new cachedir: $cachedir/notree"
debug "Using cachedir: $cachedir" >&2
}
-cache_get()
-{
- for oldrev in $*; do
- if [ -r "$cachedir/$oldrev" ]; then
+cache_get () {
+ for oldrev in "$@"
+ do
+ if test -r "$cachedir/$oldrev"
+ then
read newrev <"$cachedir/$oldrev"
echo $newrev
fi
done
}
-cache_miss()
-{
- for oldrev in $*; do
- if [ ! -r "$cachedir/$oldrev" ]; then
+cache_miss () {
+ for oldrev in "$@"
+ do
+ if ! test -r "$cachedir/$oldrev"
+ then
echo $oldrev
fi
done
}
-check_parents()
-{
- missed=$(cache_miss $*)
- for miss in $missed; do
- if [ ! -r "$cachedir/notree/$miss" ]; then
+check_parents () {
+ missed=$(cache_miss "$@")
+ for miss in $missed
+ do
+ if ! test -r "$cachedir/notree/$miss"
+ then
debug " incorrect order: $miss"
fi
done
}
-set_notree()
-{
+set_notree () {
echo "1" > "$cachedir/notree/$1"
}
-cache_set()
-{
+cache_set () {
oldrev="$1"
newrev="$2"
- if [ "$oldrev" != "latest_old" \
- -a "$oldrev" != "latest_new" \
- -a -e "$cachedir/$oldrev" ]; then
+ if test "$oldrev" != "latest_old" &&
+ test "$oldrev" != "latest_new" &&
+ test -e "$cachedir/$oldrev"
+ then
die "cache for $oldrev already exists!"
fi
echo "$newrev" >"$cachedir/$oldrev"
}
-rev_exists()
-{
- if git rev-parse "$1" >/dev/null 2>&1; then
+rev_exists () {
+ if git rev-parse "$1" >/dev/null 2>&1
+ then
return 0
else
return 1
fi
}
-rev_is_descendant_of_branch()
-{
+rev_is_descendant_of_branch () {
newrev="$1"
branch="$2"
- branch_hash=$(git rev-parse $branch)
- match=$(git rev-list -1 $branch_hash ^$newrev)
+ branch_hash=$(git rev-parse "$branch")
+ match=$(git rev-list -1 "$branch_hash" "^$newrev")
- if [ -z "$match" ]; then
+ if test -z "$match"
+ then
return 0
else
return 1
@@ -223,15 +283,14 @@ rev_is_descendant_of_branch()
# if a commit doesn't have a parent, this might not work. But we only want
# to remove the parent from the rev-list, and since it doesn't exist, it won't
# be there anyway, so do nothing in that case.
-try_remove_previous()
-{
- if rev_exists "$1^"; then
+try_remove_previous () {
+ if rev_exists "$1^"
+ then
echo "^$1^"
fi
}
-find_latest_squash()
-{
+find_latest_squash () {
debug "Looking for latest squash ($dir)..."
dir="$1"
sq=
@@ -239,37 +298,43 @@ find_latest_squash()
sub=
git log --grep="^git-subtree-dir: $dir/*\$" \
--pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
- while read a b junk; do
+ while read a b junk
+ do
debug "$a $b $junk"
debug "{{$sq/$main/$sub}}"
case "$a" in
- START) sq="$b" ;;
- git-subtree-mainline:) main="$b" ;;
- git-subtree-split:)
- sub="$(git rev-parse "$b^0")" ||
- die "could not rev-parse split hash $b from commit $sq"
- ;;
- END)
- if [ -n "$sub" ]; then
- if [ -n "$main" ]; then
- # a rejoin commit?
- # Pretend its sub was a squash.
- sq="$sub"
- fi
- debug "Squash found: $sq $sub"
- echo "$sq" "$sub"
- break
+ START)
+ sq="$b"
+ ;;
+ git-subtree-mainline:)
+ main="$b"
+ ;;
+ git-subtree-split:)
+ sub="$(git rev-parse "$b^0")" ||
+ die "could not rev-parse split hash $b from commit $sq"
+ ;;
+ END)
+ if test -n "$sub"
+ then
+ if test -n "$main"
+ then
+ # a rejoin commit?
+ # Pretend its sub was a squash.
+ sq="$sub"
fi
- sq=
- main=
- sub=
- ;;
+ debug "Squash found: $sq $sub"
+ echo "$sq" "$sub"
+ break
+ fi
+ sq=
+ main=
+ sub=
+ ;;
esac
done
}
-find_existing_splits()
-{
+find_existing_splits () {
debug "Looking for prior splits..."
dir="$1"
revs="$2"
@@ -277,37 +342,43 @@ find_existing_splits()
sub=
git log --grep="^git-subtree-dir: $dir/*\$" \
--pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
- while read a b junk; do
+ while read a b junk
+ do
case "$a" in
- START) sq="$b" ;;
- git-subtree-mainline:) main="$b" ;;
- git-subtree-split:)
- sub="$(git rev-parse "$b^0")" ||
- die "could not rev-parse split hash $b from commit $sq"
- ;;
- END)
- debug " Main is: '$main'"
- if [ -z "$main" -a -n "$sub" ]; then
- # squash commits refer to a subtree
- debug " Squash: $sq from $sub"
- cache_set "$sq" "$sub"
- fi
- if [ -n "$main" -a -n "$sub" ]; then
- debug " Prior: $main -> $sub"
- cache_set $main $sub
- cache_set $sub $sub
- try_remove_previous "$main"
- try_remove_previous "$sub"
- fi
- main=
- sub=
- ;;
+ START)
+ sq="$b"
+ ;;
+ git-subtree-mainline:)
+ main="$b"
+ ;;
+ git-subtree-split:)
+ sub="$(git rev-parse "$b^0")" ||
+ die "could not rev-parse split hash $b from commit $sq"
+ ;;
+ END)
+ debug " Main is: '$main'"
+ if test -z "$main" -a -n "$sub"
+ then
+ # squash commits refer to a subtree
+ debug " Squash: $sq from $sub"
+ cache_set "$sq" "$sub"
+ fi
+ if test -n "$main" -a -n "$sub"
+ then
+ debug " Prior: $main -> $sub"
+ cache_set $main $sub
+ cache_set $sub $sub
+ try_remove_previous "$main"
+ try_remove_previous "$sub"
+ fi
+ main=
+ sub=
+ ;;
esac
done
}
-copy_commit()
-{
+copy_commit () {
# We're going to set some environment vars here, so
# do it in a subshell to get rid of them safely later
debug copy_commit "{$1}" "{$2}" "{$3}"
@@ -325,66 +396,69 @@ copy_commit()
GIT_COMMITTER_NAME \
GIT_COMMITTER_EMAIL \
GIT_COMMITTER_DATE
- (printf "%s" "$annotate"; cat ) |
+ (
+ printf "%s" "$annotate"
+ cat
+ ) |
git commit-tree "$2" $3 # reads the rest of stdin
) || die "Can't copy commit $1"
}
-add_msg()
-{
+add_msg () {
dir="$1"
latest_old="$2"
latest_new="$3"
- if [ -n "$message" ]; then
+ if test -n "$message"
+ then
commit_message="$message"
else
commit_message="Add '$dir/' from commit '$latest_new'"
fi
cat <<-EOF
$commit_message
-
+
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
git-subtree-split: $latest_new
EOF
}
-add_squashed_msg()
-{
- if [ -n "$message" ]; then
+add_squashed_msg () {
+ if test -n "$message"
+ then
echo "$message"
else
echo "Merge commit '$1' as '$2'"
fi
}
-rejoin_msg()
-{
+rejoin_msg () {
dir="$1"
latest_old="$2"
latest_new="$3"
- if [ -n "$message" ]; then
+ if test -n "$message"
+ then
commit_message="$message"
else
commit_message="Split '$dir/' into commit '$latest_new'"
fi
cat <<-EOF
$commit_message
-
+
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
git-subtree-split: $latest_new
EOF
}
-squash_msg()
-{
+squash_msg () {
dir="$1"
oldsub="$2"
newsub="$3"
newsub_short=$(git rev-parse --short "$newsub")
-
- if [ -n "$oldsub" ]; then
+
+ if test -n "$oldsub"
+ then
oldsub_short=$(git rev-parse --short "$oldsub")
echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
echo
@@ -393,41 +467,41 @@ squash_msg()
else
echo "Squashed '$dir/' content from commit $newsub_short"
fi
-
+
echo
echo "git-subtree-dir: $dir"
echo "git-subtree-split: $newsub"
}
-toptree_for_commit()
-{
+toptree_for_commit () {
commit="$1"
git log -1 --pretty=format:'%T' "$commit" -- || exit $?
}
-subtree_for_commit()
-{
+subtree_for_commit () {
commit="$1"
dir="$2"
git ls-tree "$commit" -- "$dir" |
- while read mode type tree name; do
- assert [ "$name" = "$dir" ]
- assert [ "$type" = "tree" -o "$type" = "commit" ]
- [ "$type" = "commit" ] && continue # ignore submodules
+ while read mode type tree name
+ do
+ assert test "$name" = "$dir"
+ assert test "$type" = "tree" -o "$type" = "commit"
+ test "$type" = "commit" && continue # ignore submodules
echo $tree
break
done
}
-tree_changed()
-{
+tree_changed () {
tree=$1
shift
- if [ $# -ne 1 ]; then
+ if test $# -ne 1
+ then
return 0 # weird parents, consider it changed
else
ptree=$(toptree_for_commit $1)
- if [ "$ptree" != "$tree" ]; then
+ if test "$ptree" != "$tree"
+ then
return 0 # changed
else
return 1 # not changed
@@ -435,118 +509,127 @@ tree_changed()
fi
}
-new_squash_commit()
-{
+new_squash_commit () {
old="$1"
oldsub="$2"
newsub="$3"
tree=$(toptree_for_commit $newsub) || exit $?
- if [ -n "$old" ]; then
- squash_msg "$dir" "$oldsub" "$newsub" |
- git commit-tree "$tree" -p "$old" || exit $?
+ if test -n "$old"
+ then
+ squash_msg "$dir" "$oldsub" "$newsub" |
+ git commit-tree "$tree" -p "$old" || exit $?
else
squash_msg "$dir" "" "$newsub" |
- git commit-tree "$tree" || exit $?
+ git commit-tree "$tree" || exit $?
fi
}
-copy_or_skip()
-{
+copy_or_skip () {
rev="$1"
tree="$2"
newparents="$3"
- assert [ -n "$tree" ]
+ assert test -n "$tree"
identical=
nonidentical=
p=
gotparents=
- for parent in $newparents; do
+ for parent in $newparents
+ do
ptree=$(toptree_for_commit $parent) || exit $?
- [ -z "$ptree" ] && continue
- if [ "$ptree" = "$tree" ]; then
+ test -z "$ptree" && continue
+ if test "$ptree" = "$tree"
+ then
# an identical parent could be used in place of this rev.
identical="$parent"
else
nonidentical="$parent"
fi
-
+
# sometimes both old parents map to the same newparent;
# eliminate duplicates
is_new=1
- for gp in $gotparents; do
- if [ "$gp" = "$parent" ]; then
+ for gp in $gotparents
+ do
+ if test "$gp" = "$parent"
+ then
is_new=
break
fi
done
- if [ -n "$is_new" ]; then
+ if test -n "$is_new"
+ then
gotparents="$gotparents $parent"
p="$p -p $parent"
fi
done
copycommit=
- if [ -n "$identical" ] && [ -n "$nonidentical" ]; then
+ if test -n "$identical" && test -n "$nonidentical"
+ then
extras=$(git rev-list --count $identical..$nonidentical)
- if [ "$extras" -ne 0 ]; then
+ if test "$extras" -ne 0
+ then
# we need to preserve history along the other branch
copycommit=1
fi
fi
- if [ -n "$identical" ] && [ -z "$copycommit" ]; then
+ if test -n "$identical" && test -z "$copycommit"
+ then
echo $identical
else
- copy_commit $rev $tree "$p" || exit $?
+ copy_commit "$rev" "$tree" "$p" || exit $?
fi
}
-ensure_clean()
-{
- if ! git diff-index HEAD --exit-code --quiet 2>&1; then
+ensure_clean () {
+ if ! git diff-index HEAD --exit-code --quiet 2>&1
+ then
die "Working tree has modifications. Cannot add."
fi
- if ! git diff-index --cached HEAD --exit-code --quiet 2>&1; then
+ if ! git diff-index --cached HEAD --exit-code --quiet 2>&1
+ then
die "Index has modifications. Cannot add."
fi
}
-ensure_valid_ref_format()
-{
+ensure_valid_ref_format () {
git check-ref-format "refs/heads/$1" ||
- die "'$1' does not look like a ref"
+ die "'$1' does not look like a ref"
}
-cmd_add()
-{
- if [ -e "$dir" ]; then
+cmd_add () {
+ if test -e "$dir"
+ then
die "'$dir' already exists. Cannot add."
fi
ensure_clean
-
- if [ $# -eq 1 ]; then
- git rev-parse -q --verify "$1^{commit}" >/dev/null ||
- die "'$1' does not refer to a commit"
-
- "cmd_add_commit" "$@"
- elif [ $# -eq 2 ]; then
- # Technically we could accept a refspec here but we're
- # just going to turn around and add FETCH_HEAD under the
- # specified directory. Allowing a refspec might be
- # misleading because we won't do anything with any other
- # branches fetched via the refspec.
- ensure_valid_ref_format "$2"
-
- "cmd_add_repository" "$@"
+
+ if test $# -eq 1
+ then
+ git rev-parse -q --verify "$1^{commit}" >/dev/null ||
+ die "'$1' does not refer to a commit"
+
+ cmd_add_commit "$@"
+
+ elif test $# -eq 2
+ then
+ # Technically we could accept a refspec here but we're
+ # just going to turn around and add FETCH_HEAD under the
+ # specified directory. Allowing a refspec might be
+ # misleading because we won't do anything with any other
+ # branches fetched via the refspec.
+ ensure_valid_ref_format "$2"
+
+ cmd_add_repository "$@"
else
- say "error: parameters were '$@'"
- die "Provide either a commit or a repository and commit."
+ say "error: parameters were '$@'"
+ die "Provide either a commit or a repository and commit."
fi
}
-cmd_add_repository()
-{
+cmd_add_repository () {
echo "git fetch" "$@"
repository=$1
refspec=$2
@@ -556,60 +639,63 @@ cmd_add_repository()
cmd_add_commit "$@"
}
-cmd_add_commit()
-{
+cmd_add_commit () {
revs=$(git rev-parse $default --revs-only "$@") || exit $?
set -- $revs
rev="$1"
-
+
debug "Adding $dir as '$rev'..."
git read-tree --prefix="$dir" $rev || exit $?
git checkout -- "$dir" || exit $?
tree=$(git write-tree) || exit $?
-
+
headrev=$(git rev-parse HEAD) || exit $?
- if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
+ if test -n "$headrev" && test "$headrev" != "$rev"
+ then
headp="-p $headrev"
else
headp=
fi
-
- if [ -n "$squash" ]; then
+
+ if test -n "$squash"
+ then
rev=$(new_squash_commit "" "" "$rev") || exit $?
commit=$(add_squashed_msg "$rev" "$dir" |
- git commit-tree $tree $headp -p "$rev") || exit $?
+ git commit-tree "$tree" $headp -p "$rev") || exit $?
else
revp=$(peel_committish "$rev") &&
- commit=$(add_msg "$dir" "$headrev" "$rev" |
- git commit-tree $tree $headp -p "$revp") || exit $?
+ commit=$(add_msg "$dir" $headrev "$rev" |
+ git commit-tree "$tree" $headp -p "$revp") || exit $?
fi
git reset "$commit" || exit $?
-
+
say "Added dir '$dir'"
}
-cmd_split()
-{
+cmd_split () {
debug "Splitting $dir..."
cache_setup || exit $?
-
- if [ -n "$onto" ]; then
+
+ if test -n "$onto"
+ then
debug "Reading history for --onto=$onto..."
git rev-list $onto |
- while read rev; do
+ while read rev
+ do
# the 'onto' history is already just the subdir, so
# any parent we find there can be used verbatim
debug " cache: $rev"
- cache_set $rev $rev
+ cache_set "$rev" "$rev"
done
fi
-
- if [ -n "$ignore_joins" ]; then
+
+ if test -n "$ignore_joins"
+ then
unrevs=
else
unrevs="$(find_existing_splits "$dir" "$revs")"
fi
-
+
# We can't restrict rev-list to only $dir here, because some of our
# parents have the $dir contents the root, and those won't match.
# (and rev-list --follow doesn't seem to solve this)
@@ -618,12 +704,14 @@ cmd_split()
revcount=0
createcount=0
eval "$grl" |
- while read rev parents; do
+ while read rev parents
+ do
revcount=$(($revcount + 1))
progress "$revcount/$revmax ($createcount)"
debug "Processing commit: $rev"
- exists=$(cache_get $rev)
- if [ -n "$exists" ]; then
+ exists=$(cache_get "$rev")
+ if test -n "$exists"
+ then
debug " prior: $exists"
continue
fi
@@ -631,76 +719,89 @@ cmd_split()
debug " parents: $parents"
newparents=$(cache_get $parents)
debug " newparents: $newparents"
-
- tree=$(subtree_for_commit $rev "$dir")
+
+ tree=$(subtree_for_commit "$rev" "$dir")
debug " tree is: $tree"
check_parents $parents
-
+
# ugly. is there no better way to tell if this is a subtree
# vs. a mainline commit? Does it matter?
- if [ -z $tree ]; then
- set_notree $rev
- if [ -n "$newparents" ]; then
- cache_set $rev $rev
+ if test -z "$tree"
+ then
+ set_notree "$rev"
+ if test -n "$newparents"
+ then
+ cache_set "$rev" "$rev"
fi
continue
fi
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
debug " newrev is: $newrev"
- cache_set $rev $newrev
- cache_set latest_new $newrev
- cache_set latest_old $rev
+ cache_set "$rev" "$newrev"
+ cache_set latest_new "$newrev"
+ cache_set latest_old "$rev"
done || exit $?
+
latest_new=$(cache_get latest_new)
- if [ -z "$latest_new" ]; then
+ if test -z "$latest_new"
+ then
die "No new revisions were found"
fi
-
- if [ -n "$rejoin" ]; then
+
+ if test -n "$rejoin"
+ then
debug "Merging split branch into HEAD..."
latest_old=$(cache_get latest_old)
git merge -s ours \
- -m "$(rejoin_msg "$dir" $latest_old $latest_new)" \
- $latest_new >&2 || exit $?
- fi
- if [ -n "$branch" ]; then
- if rev_exists "refs/heads/$branch"; then
- if ! rev_is_descendant_of_branch $latest_new $branch; then
+ --allow-unrelated-histories \
+ -m "$(rejoin_msg "$dir" "$latest_old" "$latest_new")" \
+ "$latest_new" >&2 || exit $?
+ fi
+ if test -n "$branch"
+ then
+ if rev_exists "refs/heads/$branch"
+ then
+ if ! rev_is_descendant_of_branch "$latest_new" "$branch"
+ then
die "Branch '$branch' is not an ancestor of commit '$latest_new'."
fi
action='Updated'
else
action='Created'
fi
- git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $?
+ git update-ref -m 'subtree split' \
+ "refs/heads/$branch" "$latest_new" || exit $?
say "$action branch '$branch'"
fi
- echo $latest_new
+ echo "$latest_new"
exit 0
}
-cmd_merge()
-{
+cmd_merge () {
revs=$(git rev-parse $default --revs-only "$@") || exit $?
ensure_clean
-
+
set -- $revs
- if [ $# -ne 1 ]; then
+ if test $# -ne 1
+ then
die "You must provide exactly one revision. Got: '$revs'"
fi
rev="$1"
-
- if [ -n "$squash" ]; then
+
+ if test -n "$squash"
+ then
first_split="$(find_latest_squash "$dir")"
- if [ -z "$first_split" ]; then
+ if test -z "$first_split"
+ then
die "Can't squash-merge: '$dir' was never added."
fi
set $first_split
old=$1
sub=$2
- if [ "$sub" = "$rev" ]; then
+ if test "$sub" = "$rev"
+ then
say "Subtree is already at commit $rev."
exit 0
fi
@@ -710,25 +811,29 @@ cmd_merge()
fi
version=$(git version)
- if [ "$version" \< "git version 1.7" ]; then
- if [ -n "$message" ]; then
- git merge -s subtree --message="$message" $rev
+ if test "$version" \< "git version 1.7"
+ then
+ if test -n "$message"
+ then
+ git merge -s subtree --message="$message" "$rev"
else
- git merge -s subtree $rev
+ git merge -s subtree "$rev"
fi
else
- if [ -n "$message" ]; then
- git merge -Xsubtree="$prefix" --message="$message" $rev
+ if test -n "$message"
+ then
+ git merge -Xsubtree="$prefix" \
+ --message="$message" "$rev"
else
git merge -Xsubtree="$prefix" $rev
fi
fi
}
-cmd_pull()
-{
- if [ $# -ne 2 ]; then
- die "You must provide <repository> <ref>"
+cmd_pull () {
+ if test $# -ne 2
+ then
+ die "You must provide <repository> <ref>"
fi
ensure_clean
ensure_valid_ref_format "$2"
@@ -738,20 +843,21 @@ cmd_pull()
cmd_merge "$@"
}
-cmd_push()
-{
- if [ $# -ne 2 ]; then
- die "You must provide <repository> <ref>"
+cmd_push () {
+ if test $# -ne 2
+ then
+ die "You must provide <repository> <ref>"
fi
ensure_valid_ref_format "$2"
- if [ -e "$dir" ]; then
- repository=$1
- refspec=$2
- echo "git push using: " $repository $refspec
- localrev=$(git subtree split --prefix="$prefix") || die
- git push "$repository" $localrev:refs/heads/$refspec
+ if test -e "$dir"
+ then
+ repository=$1
+ refspec=$2
+ echo "git push using: " "$repository" "$refspec"
+ localrev=$(git subtree split --prefix="$prefix") || die
+ git push "$repository" "$localrev":"refs/heads/$refspec"
else
- die "'$dir' must already exist. Try 'git subtree add'."
+ die "'$dir' must already exist. Try 'git subtree add'."
fi
}
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 3bf96a9..9751cfe 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -16,16 +16,16 @@ export TEST_DIRECTORY
subtree_test_create_repo()
{
- test_create_repo "$1"
+ test_create_repo "$1" &&
(
- cd $1
+ cd "$1" &&
git config log.date relative
)
}
create()
{
- echo "$1" >"$1"
+ echo "$1" >"$1" &&
git add "$1"
}
@@ -71,12 +71,12 @@ join_commits()
}
test_create_commit() (
- repo=$1
- commit=$2
- cd "$repo"
- mkdir -p $(dirname "$commit") \
+ repo=$1 &&
+ commit=$2 &&
+ cd "$repo" &&
+ mkdir -p "$(dirname "$commit")" \
|| error "Could not create directory for commit"
- echo "$commit" >"$commit"
+ echo "$commit" >"$commit" &&
git add "$commit" || error "Could not add commit"
git commit -m "$commit" || error "Could not commit"
)
@@ -347,6 +347,22 @@ test_expect_success 'split sub dir/ with --rejoin' '
'
next_test
+test_expect_success 'split sub dir/ with --rejoin from scratch' '
+ subtree_test_create_repo "$subtree_test_count" &&
+ test_create_commit "$subtree_test_count" main1 &&
+ (
+ cd "$subtree_test_count" &&
+ mkdir "sub dir" &&
+ echo file >"sub dir"/file &&
+ git add "sub dir/file" &&
+ git commit -m"sub dir file" &&
+ split_hash=$(git subtree split --prefix="sub dir" --rejoin) &&
+ git subtree split --prefix="sub dir" --rejoin &&
+ check_equal "$(last_commit_message)" "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
+ )
+ '
+
+next_test
test_expect_success 'split sub dir/ with --rejoin and --message' '
subtree_test_create_repo "$subtree_test_count" &&
subtree_test_create_repo "$subtree_test_count/sub proj" &&
diff --git a/convert.c b/convert.c
index b1614bf..67d69b5 100644
--- a/convert.c
+++ b/convert.c
@@ -176,7 +176,9 @@ static enum eol output_eol(enum crlf_action crlf_action)
return EOL_LF;
case CRLF_UNDEFINED:
case CRLF_AUTO_CRLF:
+ return EOL_CRLF;
case CRLF_AUTO_INPUT:
+ return EOL_LF;
case CRLF_TEXT:
case CRLF_AUTO:
/* fall through */
@@ -254,17 +256,15 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
if (crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
if (convert_is_binary(len, &stats))
return 0;
-
- if (crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
- /*
- * If the file in the index has any CR in it, do not convert.
- * This is the new safer autocrlf handling.
- */
- if (has_cr_in_index(path))
- return 0;
- }
+ /*
+ * If the file in the index has any CR in it, do not convert.
+ * This is the new safer autocrlf handling.
+ */
+ if (checksafe == SAFE_CRLF_RENORMALIZE)
+ checksafe = SAFE_CRLF_FALSE;
+ else if (has_cr_in_index(path))
+ return 0;
}
-
check_safe_crlf(path, crlf_action, &stats, checksafe);
/* Optimization: No CRLF? Nothing to convert, regardless. */
@@ -320,12 +320,10 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
return 0;
if (crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
- if (crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
- /* If we have any CR or CRLF line endings, we do not touch it */
- /* This is the new safer autocrlf-handling */
- if (stats.lonecr || stats.crlf )
- return 0;
- }
+ /* If we have any CR or CRLF line endings, we do not touch it */
+ /* This is the new safer autocrlf-handling */
+ if (stats.lonecr || stats.crlf )
+ return 0;
if (convert_is_binary(len, &stats))
return 0;
@@ -786,7 +784,11 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
ca->drv = git_path_check_convert(ccheck + 2);
if (ca->crlf_action != CRLF_BINARY) {
enum eol eol_attr = git_path_check_eol(ccheck + 3);
- if (eol_attr == EOL_LF)
+ if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_LF)
+ ca->crlf_action = CRLF_AUTO_INPUT;
+ else if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_CRLF)
+ ca->crlf_action = CRLF_AUTO_CRLF;
+ else if (eol_attr == EOL_LF)
ca->crlf_action = CRLF_TEXT_INPUT;
else if (eol_attr == EOL_CRLF)
ca->crlf_action = CRLF_TEXT_CRLF;
@@ -845,9 +847,9 @@ const char *get_convert_attr_ascii(const char *path)
case CRLF_AUTO:
return "text=auto";
case CRLF_AUTO_CRLF:
- return "text=auto eol=crlf"; /* This is not supported yet */
+ return "text=auto eol=crlf";
case CRLF_AUTO_INPUT:
- return "text=auto eol=lf"; /* This is not supported yet */
+ return "text=auto eol=lf";
}
return "";
}
@@ -949,7 +951,7 @@ int renormalize_buffer(const char *path, const char *src, size_t len, struct str
src = dst->buf;
len = dst->len;
}
- return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_FALSE);
+ return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_RENORMALIZE);
}
/*****************************************************************
diff --git a/convert.h b/convert.h
index ccf436b..82871a1 100644
--- a/convert.h
+++ b/convert.h
@@ -7,7 +7,8 @@
enum safe_crlf {
SAFE_CRLF_FALSE = 0,
SAFE_CRLF_FAIL = 1,
- SAFE_CRLF_WARN = 2
+ SAFE_CRLF_WARN = 2,
+ SAFE_CRLF_RENORMALIZE = 3
};
extern enum safe_crlf safe_crlf;
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 1f14d56..1e5f16a 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -257,7 +257,7 @@ static void init_socket_directory(const char *path)
free(path_copy);
}
-int main(int argc, const char **argv)
+int cmd_main(int argc, const char **argv)
{
const char *socket_path;
int ignore_sighup = 0;
diff --git a/credential-cache.c b/credential-cache.c
index 86e21de..cc8a6ee 100644
--- a/credential-cache.c
+++ b/credential-cache.c
@@ -83,7 +83,7 @@ static void do_cache(const char *socket, const char *action, int timeout,
strbuf_release(&buf);
}
-int main(int argc, const char **argv)
+int cmd_main(int argc, const char **argv)
{
char *socket_path = NULL;
int timeout = 900;
diff --git a/credential-store.c b/credential-store.c
index 5714167..55ca1b1 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -142,7 +142,7 @@ static void lookup_credential(const struct string_list *fns, struct credential *
return; /* Found credential */
}
-int main(int argc, char **argv)
+int cmd_main(int argc, const char **argv)
{
const char * const usage[] = {
"git credential-store [<options>] <action>",
diff --git a/daemon.c b/daemon.c
index 46dddac..425aad0 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,6 +1,5 @@
#include "cache.h"
#include "pkt-line.h"
-#include "exec_cmd.h"
#include "run-command.h"
#include "strbuf.h"
#include "string-list.h"
@@ -32,7 +31,7 @@ static const char daemon_usage[] =
" [<directory>...]";
/* List of acceptable pathname prefixes */
-static char **ok_paths;
+static const char **ok_paths;
static int strict_paths;
/* If this is set, git-daemon-export-ok is not required */
@@ -240,7 +239,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
}
if ( ok_paths && *ok_paths ) {
- char **pp;
+ const char **pp;
int pathlen = strlen(path);
/* The validation is done on the paths after enter_repo
@@ -673,9 +672,11 @@ static void set_keep_alive(int sockfd)
{
int ka = 1;
- if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)
- logerror("unable to set SO_KEEPALIVE on socket: %s",
- strerror(errno));
+ if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) {
+ if (errno != ENOTSOCK)
+ logerror("unable to set SO_KEEPALIVE on socket: %s",
+ strerror(errno));
+ }
}
static int execute(void)
@@ -1192,7 +1193,7 @@ static int serve(struct string_list *listen_addr, int listen_port,
return service_loop(&socklist);
}
-int main(int argc, char **argv)
+int cmd_main(int argc, const char **argv)
{
int listen_port = 0;
struct string_list listen_addr = STRING_LIST_INIT_NODUP;
@@ -1202,12 +1203,8 @@ int main(int argc, char **argv)
struct credentials *cred = NULL;
int i;
- git_setup_gettext();
-
- git_extract_argv0_path(argv[0]);
-
for (i = 1; i < argc; i++) {
- char *arg = argv[i];
+ const char *arg = argv[i];
const char *v;
if (skip_prefix(arg, "--listen=", &v)) {
@@ -1381,8 +1378,7 @@ int main(int argc, char **argv)
if (detach) {
if (daemonize())
die("--detach not supported on this platform");
- } else
- sanitize_stdfds();
+ }
if (pid_file)
write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
diff --git a/date.c b/date.c
index 4c7aa9b..a996331 100644
--- a/date.c
+++ b/date.c
@@ -177,6 +177,12 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
struct tm *tm;
static struct strbuf timebuf = STRBUF_INIT;
+ if (mode->type == DATE_UNIX) {
+ strbuf_reset(&timebuf);
+ strbuf_addf(&timebuf, "%lu", time);
+ return timebuf.buf;
+ }
+
if (mode->local)
tz = local_tzoffset(time);
@@ -792,6 +798,8 @@ static enum date_mode_type parse_date_type(const char *format, const char **end)
return DATE_NORMAL;
if (skip_prefix(format, "raw", end))
return DATE_RAW;
+ if (skip_prefix(format, "unix", end))
+ return DATE_UNIX;
if (skip_prefix(format, "format", end))
return DATE_STRFTIME;
diff --git a/diff.c b/diff.c
index fa78fc1..b43d3dd 100644
--- a/diff.c
+++ b/diff.c
@@ -1933,8 +1933,8 @@ static void show_dirstat(struct diff_options *options)
name = p->two->path ? p->two->path : p->one->path;
- if (p->one->sha1_valid && p->two->sha1_valid)
- content_changed = hashcmp(p->one->sha1, p->two->sha1);
+ if (p->one->oid_valid && p->two->oid_valid)
+ content_changed = oidcmp(&p->one->oid, &p->two->oid);
else
content_changed = 1;
@@ -2306,7 +2306,8 @@ static void builtin_diff(const char *name_a,
const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
show_submodule_summary(o->file, one->path ? one->path : two->path,
line_prefix,
- one->sha1, two->sha1, two->dirty_submodule,
+ one->oid.hash, two->oid.hash,
+ two->dirty_submodule,
meta, del, add, reset);
return;
}
@@ -2384,7 +2385,7 @@ static void builtin_diff(const char *name_a,
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 (!oidcmp(&one->oid, &two->oid)) {
if (must_show_header)
fprintf(o->file, "%s", header.buf);
goto free_ab_and_return;
@@ -2505,7 +2506,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
return;
}
- same_contents = !hashcmp(one->sha1, two->sha1);
+ same_contents = !oidcmp(&one->oid, &two->oid);
if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
data->is_binary = 1;
@@ -2638,8 +2639,8 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
{
if (mode) {
spec->mode = canon_mode(mode);
- hashcpy(spec->sha1, sha1);
- spec->sha1_valid = sha1_valid;
+ hashcpy(spec->oid.hash, sha1);
+ spec->oid_valid = sha1_valid;
}
}
@@ -2682,6 +2683,13 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
return 0;
+ /*
+ * Similarly, if we'd have to convert the file contents anyway, that
+ * makes the optimization not worthwhile.
+ */
+ if (!want_file && would_convert_to_git(name))
+ return 0;
+
len = strlen(name);
pos = cache_name_pos(name, len);
if (pos < 0)
@@ -2721,7 +2729,8 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
if (s->dirty_submodule)
dirty = "-dirty";
- strbuf_addf(&buf, "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
+ strbuf_addf(&buf, "Subproject commit %s%s\n",
+ oid_to_hex(&s->oid), dirty);
s->size = buf.len;
if (size_only) {
s->data = NULL;
@@ -2764,8 +2773,8 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
if (S_ISGITLINK(s->mode))
return diff_populate_gitlink(s, size_only);
- if (!s->sha1_valid ||
- reuse_worktree_file(s->path, s->sha1, 0)) {
+ if (!s->oid_valid ||
+ reuse_worktree_file(s->path, s->oid.hash, 0)) {
struct strbuf buf = STRBUF_INIT;
struct stat st;
int fd;
@@ -2822,9 +2831,10 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
else {
enum object_type type;
if (size_only || (flags & CHECK_BINARY)) {
- type = sha1_object_info(s->sha1, &s->size);
+ type = sha1_object_info(s->oid.hash, &s->size);
if (type < 0)
- die("unable to read %s", sha1_to_hex(s->sha1));
+ die("unable to read %s",
+ oid_to_hex(&s->oid));
if (size_only)
return 0;
if (s->size > big_file_threshold && s->is_binary == -1) {
@@ -2832,9 +2842,9 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
return 0;
}
}
- s->data = read_sha1_file(s->sha1, &type, &s->size);
+ s->data = read_sha1_file(s->oid.hash, &type, &s->size);
if (!s->data)
- die("unable to read %s", sha1_to_hex(s->sha1));
+ die("unable to read %s", oid_to_hex(&s->oid));
s->should_free = 1;
}
return 0;
@@ -2863,7 +2873,7 @@ void diff_free_filespec_data(struct diff_filespec *s)
static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
void *blob,
unsigned long size,
- const unsigned char *sha1,
+ const struct object_id *oid,
int mode)
{
int fd;
@@ -2888,7 +2898,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
die_errno("unable to write temp-file");
close_tempfile(&temp->tempfile);
temp->name = get_tempfile_path(&temp->tempfile);
- sha1_to_hex_r(temp->hex, sha1);
+ oid_to_hex_r(temp->hex, oid);
xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
strbuf_release(&buf);
strbuf_release(&template);
@@ -2912,8 +2922,8 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
}
if (!S_ISGITLINK(one->mode) &&
- (!one->sha1_valid ||
- reuse_worktree_file(name, one->sha1, 1))) {
+ (!one->oid_valid ||
+ reuse_worktree_file(name, one->oid.hash, 1))) {
struct stat st;
if (lstat(name, &st) < 0) {
if (errno == ENOENT)
@@ -2925,19 +2935,19 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
if (strbuf_readlink(&sb, name, st.st_size) < 0)
die_errno("readlink(%s)", name);
prep_temp_blob(name, temp, sb.buf, sb.len,
- (one->sha1_valid ?
- one->sha1 : null_sha1),
- (one->sha1_valid ?
+ (one->oid_valid ?
+ &one->oid : &null_oid),
+ (one->oid_valid ?
one->mode : S_IFLNK));
strbuf_release(&sb);
}
else {
/* we can borrow from the file in the work tree */
temp->name = name;
- if (!one->sha1_valid)
+ if (!one->oid_valid)
sha1_to_hex_r(temp->hex, null_sha1);
else
- sha1_to_hex_r(temp->hex, one->sha1);
+ sha1_to_hex_r(temp->hex, one->oid.hash);
/* Even though we may sometimes borrow the
* contents from the work tree, we always want
* one->mode. mode is trustworthy even when
@@ -2952,7 +2962,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
if (diff_populate_filespec(one, 0))
die("cannot read data blob for %s", one->path);
prep_temp_blob(name, temp, one->data, one->size,
- one->sha1, one->mode);
+ &one->oid, one->mode);
}
return temp;
}
@@ -3065,7 +3075,7 @@ static void fill_metainfo(struct strbuf *msg,
default:
*must_show_header = 0;
}
- if (one && two && hashcmp(one->sha1, two->sha1)) {
+ if (one && two && oidcmp(&one->oid, &two->oid)) {
int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
if (DIFF_OPT_TST(o, BINARY)) {
@@ -3075,8 +3085,8 @@ static void fill_metainfo(struct strbuf *msg,
abbrev = 40;
}
strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
- find_unique_abbrev(one->sha1, abbrev));
- strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
+ find_unique_abbrev(one->oid.hash, abbrev));
+ strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
if (one->mode == two->mode)
strbuf_addf(msg, " %06o", one->mode);
strbuf_addf(msg, "%s\n", reset);
@@ -3131,20 +3141,20 @@ static void run_diff_cmd(const char *pgm,
static void diff_fill_sha1_info(struct diff_filespec *one)
{
if (DIFF_FILE_VALID(one)) {
- if (!one->sha1_valid) {
+ if (!one->oid_valid) {
struct stat st;
if (one->is_stdin) {
- hashcpy(one->sha1, null_sha1);
+ oidclr(&one->oid);
return;
}
if (lstat(one->path, &st) < 0)
die_errno("stat '%s'", one->path);
- if (index_path(one->sha1, one->path, &st, 0))
+ if (index_path(one->oid.hash, one->path, &st, 0))
die("cannot hash %s", one->path);
}
}
else
- hashclr(one->sha1);
+ oidclr(&one->oid);
}
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
@@ -3977,6 +3987,8 @@ int diff_opt_parse(struct diff_options *options,
if (!options->file)
die_errno("Could not open '%s'", path);
options->close_file = 1;
+ if (options->use_color != GIT_COLOR_ALWAYS)
+ options->use_color = GIT_COLOR_NEVER;
return argcount;
} else
return 0;
@@ -4118,8 +4130,9 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
fprintf(opt->file, "%s", diff_line_prefix(opt));
if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
- diff_unique_abbrev(p->one->sha1, opt->abbrev));
- fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
+ diff_unique_abbrev(p->one->oid.hash, opt->abbrev));
+ fprintf(opt->file, "%s ",
+ diff_unique_abbrev(p->two->oid.hash, opt->abbrev));
}
if (p->score) {
fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
@@ -4168,11 +4181,11 @@ int diff_unmodified_pair(struct diff_filepair *p)
/* both are valid and point at the same path. that is, we are
* dealing with a change.
*/
- if (one->sha1_valid && two->sha1_valid &&
- !hashcmp(one->sha1, two->sha1) &&
+ if (one->oid_valid && two->oid_valid &&
+ !oidcmp(&one->oid, &two->oid) &&
!one->dirty_submodule && !two->dirty_submodule)
return 1; /* no change */
- if (!one->sha1_valid && !two->sha1_valid)
+ if (!one->oid_valid && !two->oid_valid)
return 1; /* both look at the same file on the filesystem. */
return 0;
}
@@ -4233,7 +4246,7 @@ void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
s->path,
DIFF_FILE_VALID(s) ? "valid" : "invalid",
s->mode,
- s->sha1_valid ? sha1_to_hex(s->sha1) : "");
+ s->oid_valid ? oid_to_hex(&s->oid) : "");
fprintf(stderr, "queue[%d] %s size %lu\n",
x, one ? one : "",
s->size);
@@ -4303,11 +4316,11 @@ static void diff_resolve_rename_copy(void)
else
p->status = DIFF_STATUS_RENAMED;
}
- else if (hashcmp(p->one->sha1, p->two->sha1) ||
+ else if (oidcmp(&p->one->oid, &p->two->oid) ||
p->one->mode != p->two->mode ||
p->one->dirty_submodule ||
p->two->dirty_submodule ||
- is_null_sha1(p->one->sha1))
+ is_null_oid(&p->one->oid))
p->status = DIFF_STATUS_MODIFIED;
else {
/* This is a "no-change" entry and should not
@@ -4523,8 +4536,10 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
if (diff_filespec_is_binary(p->one) ||
diff_filespec_is_binary(p->two)) {
- git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
- git_SHA1_Update(&ctx, sha1_to_hex(p->two->sha1), 40);
+ git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
+ 40);
+ git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
+ 40);
continue;
}
@@ -4816,7 +4831,7 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
*/
if (!DIFF_FILE_VALID(p->one) || /* (1) */
!DIFF_FILE_VALID(p->two) ||
- (p->one->sha1_valid && p->two->sha1_valid) ||
+ (p->one->oid_valid && p->two->oid_valid) ||
(p->one->mode != p->two->mode) ||
diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
@@ -5112,8 +5127,9 @@ size_t fill_textconv(struct userdiff_driver *driver,
if (!driver->textconv)
die("BUG: fill_textconv called with non-textconv driver");
- if (driver->textconv_cache && df->sha1_valid) {
- *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+ if (driver->textconv_cache && df->oid_valid) {
+ *outbuf = notes_cache_get(driver->textconv_cache,
+ df->oid.hash,
&size);
if (*outbuf)
return size;
@@ -5123,9 +5139,9 @@ size_t fill_textconv(struct userdiff_driver *driver,
if (!*outbuf)
die("unable to read files to diff");
- if (driver->textconv_cache && df->sha1_valid) {
+ if (driver->textconv_cache && df->oid_valid) {
/* ignore errors, as we might be in a readonly repository */
- notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
+ notes_cache_put(driver->textconv_cache, df->oid.hash, *outbuf,
size);
/*
* we could save up changes and flush them all at the end,
diff --git a/diffcore-break.c b/diffcore-break.c
index 5473493..881a74f 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -57,8 +57,8 @@ static int should_break(struct diff_filespec *src,
return 1; /* even their types are different */
}
- if (src->sha1_valid && dst->sha1_valid &&
- !hashcmp(src->sha1, dst->sha1))
+ if (src->oid_valid && dst->oid_valid &&
+ !oidcmp(&src->oid, &dst->oid))
return 0; /* they are the same */
if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 7f03eb5..73d003a 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -60,7 +60,8 @@ static int add_rename_dst(struct diff_filespec *two)
memmove(rename_dst + first + 1, rename_dst + first,
(rename_dst_nr - first - 1) * sizeof(*rename_dst));
rename_dst[first].two = alloc_filespec(two->path);
- fill_filespec(rename_dst[first].two, two->sha1, two->sha1_valid, two->mode);
+ fill_filespec(rename_dst[first].two, two->oid.hash, two->oid_valid,
+ two->mode);
rename_dst[first].pair = NULL;
return 0;
}
@@ -260,12 +261,13 @@ struct file_similarity {
static unsigned int hash_filespec(struct diff_filespec *filespec)
{
- if (!filespec->sha1_valid) {
+ if (!filespec->oid_valid) {
if (diff_populate_filespec(filespec, 0))
return 0;
- hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1);
+ hash_sha1_file(filespec->data, filespec->size, "blob",
+ filespec->oid.hash);
}
- return sha1hash(filespec->sha1);
+ return sha1hash(filespec->oid.hash);
}
static int find_identical_files(struct hashmap *srcs,
@@ -287,7 +289,7 @@ static int find_identical_files(struct hashmap *srcs,
struct diff_filespec *source = p->filespec;
/* False hash collision? */
- if (hashcmp(source->sha1, target->sha1))
+ if (oidcmp(&source->oid, &target->oid))
continue;
/* Non-regular files? If so, the modes must match! */
if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) {
@@ -466,7 +468,7 @@ void diffcore_rename(struct diff_options *options)
strcmp(options->single_follow, p->two->path))
continue; /* not interested */
else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
- is_empty_blob_sha1(p->two->sha1))
+ is_empty_blob_sha1(p->two->oid.hash))
continue;
else if (add_rename_dst(p->two) < 0) {
warning("skipping rename detection, detected"
@@ -476,7 +478,7 @@ void diffcore_rename(struct diff_options *options)
}
}
else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
- is_empty_blob_sha1(p->one->sha1))
+ is_empty_blob_sha1(p->one->oid.hash))
continue;
else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
/*
@@ -539,7 +541,7 @@ void diffcore_rename(struct diff_options *options)
rename_dst_nr * rename_src_nr, 50, 1);
}
- mx = xcalloc(st_mult(num_create, NUM_CANDIDATE_PER_DST), sizeof(*mx));
+ mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_create), sizeof(*mx));
for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
struct diff_filespec *two = rename_dst[i].two;
struct diff_score *m;
diff --git a/diffcore.h b/diffcore.h
index 33ea2de..c11b846 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -25,7 +25,7 @@
struct userdiff_driver;
struct diff_filespec {
- unsigned char sha1[20];
+ struct object_id oid;
char *path;
void *data;
void *cnt_data;
@@ -33,7 +33,7 @@ struct diff_filespec {
int count; /* Reference count */
int rename_used; /* Count of rename users */
unsigned short mode; /* file mode */
- unsigned sha1_valid : 1; /* if true, use sha1 and trust mode;
+ unsigned oid_valid : 1; /* if true, use oid and trust mode;
* if false, use the name and read from
* the filesystem.
*/
diff --git a/dir-iterator.c b/dir-iterator.c
new file mode 100644
index 0000000..34182a9
--- /dev/null
+++ b/dir-iterator.c
@@ -0,0 +1,202 @@
+#include "cache.h"
+#include "dir.h"
+#include "iterator.h"
+#include "dir-iterator.h"
+
+struct dir_iterator_level {
+ int initialized;
+
+ DIR *dir;
+
+ /*
+ * The length of the directory part of path at this level
+ * (including a trailing '/'):
+ */
+ size_t prefix_len;
+
+ /*
+ * The last action that has been taken with the current entry
+ * (needed for directories, which have to be included in the
+ * iteration and also iterated into):
+ */
+ enum {
+ DIR_STATE_ITER,
+ DIR_STATE_RECURSE
+ } dir_state;
+};
+
+/*
+ * The full data structure used to manage the internal directory
+ * iteration state. It includes members that are not part of the
+ * public interface.
+ */
+struct dir_iterator_int {
+ struct dir_iterator base;
+
+ /*
+ * The number of levels currently on the stack. This is always
+ * at least 1, because when it becomes zero the iteration is
+ * ended and this struct is freed.
+ */
+ size_t levels_nr;
+
+ /* The number of levels that have been allocated on the stack */
+ size_t levels_alloc;
+
+ /*
+ * A stack of levels. levels[0] is the uppermost directory
+ * that will be included in this iteration.
+ */
+ struct dir_iterator_level *levels;
+};
+
+int dir_iterator_advance(struct dir_iterator *dir_iterator)
+{
+ struct dir_iterator_int *iter =
+ (struct dir_iterator_int *)dir_iterator;
+
+ while (1) {
+ struct dir_iterator_level *level =
+ &iter->levels[iter->levels_nr - 1];
+ struct dirent *de;
+
+ if (!level->initialized) {
+ /*
+ * Note: dir_iterator_begin() ensures that
+ * path is not the empty string.
+ */
+ if (!is_dir_sep(iter->base.path.buf[iter->base.path.len - 1]))
+ strbuf_addch(&iter->base.path, '/');
+ level->prefix_len = iter->base.path.len;
+
+ level->dir = opendir(iter->base.path.buf);
+ if (!level->dir && errno != ENOENT) {
+ warning("error opening directory %s: %s",
+ iter->base.path.buf, strerror(errno));
+ /* Popping the level is handled below */
+ }
+
+ level->initialized = 1;
+ } else if (S_ISDIR(iter->base.st.st_mode)) {
+ if (level->dir_state == DIR_STATE_ITER) {
+ /*
+ * The directory was just iterated
+ * over; now prepare to iterate into
+ * it.
+ */
+ level->dir_state = DIR_STATE_RECURSE;
+ ALLOC_GROW(iter->levels, iter->levels_nr + 1,
+ iter->levels_alloc);
+ level = &iter->levels[iter->levels_nr++];
+ level->initialized = 0;
+ continue;
+ } else {
+ /*
+ * The directory has already been
+ * iterated over and iterated into;
+ * we're done with it.
+ */
+ }
+ }
+
+ if (!level->dir) {
+ /*
+ * This level is exhausted (or wasn't opened
+ * successfully); pop up a level.
+ */
+ if (--iter->levels_nr == 0)
+ return dir_iterator_abort(dir_iterator);
+
+ continue;
+ }
+
+ /*
+ * Loop until we find an entry that we can give back
+ * to the caller:
+ */
+ while (1) {
+ strbuf_setlen(&iter->base.path, level->prefix_len);
+ errno = 0;
+ de = readdir(level->dir);
+
+ if (!de) {
+ /* This level is exhausted; pop up a level. */
+ if (errno) {
+ warning("error reading directory %s: %s",
+ iter->base.path.buf, strerror(errno));
+ } else if (closedir(level->dir))
+ warning("error closing directory %s: %s",
+ iter->base.path.buf, strerror(errno));
+
+ level->dir = NULL;
+ if (--iter->levels_nr == 0)
+ return dir_iterator_abort(dir_iterator);
+ break;
+ }
+
+ if (is_dot_or_dotdot(de->d_name))
+ continue;
+
+ strbuf_addstr(&iter->base.path, de->d_name);
+ if (lstat(iter->base.path.buf, &iter->base.st) < 0) {
+ if (errno != ENOENT)
+ warning("error reading path '%s': %s",
+ iter->base.path.buf,
+ strerror(errno));
+ continue;
+ }
+
+ /*
+ * We have to set these each time because
+ * the path strbuf might have been realloc()ed.
+ */
+ iter->base.relative_path =
+ iter->base.path.buf + iter->levels[0].prefix_len;
+ iter->base.basename =
+ iter->base.path.buf + level->prefix_len;
+ level->dir_state = DIR_STATE_ITER;
+
+ return ITER_OK;
+ }
+ }
+}
+
+int dir_iterator_abort(struct dir_iterator *dir_iterator)
+{
+ struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator;
+
+ for (; iter->levels_nr; iter->levels_nr--) {
+ struct dir_iterator_level *level =
+ &iter->levels[iter->levels_nr - 1];
+
+ if (level->dir && closedir(level->dir)) {
+ strbuf_setlen(&iter->base.path, level->prefix_len);
+ warning("error closing directory %s: %s",
+ iter->base.path.buf, strerror(errno));
+ }
+ }
+
+ free(iter->levels);
+ strbuf_release(&iter->base.path);
+ free(iter);
+ return ITER_DONE;
+}
+
+struct dir_iterator *dir_iterator_begin(const char *path)
+{
+ struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter));
+ struct dir_iterator *dir_iterator = &iter->base;
+
+ if (!path || !*path)
+ die("BUG: empty path passed to dir_iterator_begin()");
+
+ strbuf_init(&iter->base.path, PATH_MAX);
+ strbuf_addstr(&iter->base.path, path);
+
+ ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
+
+ iter->levels_nr = 1;
+ iter->levels[0].initialized = 0;
+
+ return dir_iterator;
+}
diff --git a/dir-iterator.h b/dir-iterator.h
new file mode 100644
index 0000000..27739e6
--- /dev/null
+++ b/dir-iterator.h
@@ -0,0 +1,87 @@
+#ifndef DIR_ITERATOR_H
+#define DIR_ITERATOR_H
+
+/*
+ * Iterate over a directory tree.
+ *
+ * Iterate over a directory tree, recursively, including paths of all
+ * types and hidden paths. Skip "." and ".." entries and don't follow
+ * symlinks except for the original path.
+ *
+ * Every time dir_iterator_advance() is called, update the members of
+ * the dir_iterator structure to reflect the next path in the
+ * iteration. The order that paths are iterated over within a
+ * directory is undefined, but directory paths are always iterated
+ * over before the subdirectory contents.
+ *
+ * A typical iteration looks like this:
+ *
+ * int ok;
+ * struct iterator *iter = dir_iterator_begin(path);
+ *
+ * while ((ok = dir_iterator_advance(iter)) == ITER_OK) {
+ * if (want_to_stop_iteration()) {
+ * ok = dir_iterator_abort(iter);
+ * break;
+ * }
+ *
+ * // Access information about the current path:
+ * if (S_ISDIR(iter->st.st_mode))
+ * printf("%s is a directory\n", iter->relative_path);
+ * }
+ *
+ * if (ok != ITER_DONE)
+ * handle_error();
+ *
+ * Callers are allowed to modify iter->path while they are working,
+ * but they must restore it to its original contents before calling
+ * dir_iterator_advance() again.
+ */
+
+struct dir_iterator {
+ /* The current path: */
+ struct strbuf path;
+
+ /*
+ * The current path relative to the starting path. This part
+ * of the path always uses "/" characters to separate path
+ * components:
+ */
+ const char *relative_path;
+
+ /* The current basename: */
+ const char *basename;
+
+ /* The result of calling lstat() on path: */
+ struct stat st;
+};
+
+/*
+ * Start a directory iteration over path. Return a dir_iterator that
+ * holds the internal state of the iteration.
+ *
+ * The iteration includes all paths under path, not including path
+ * itself and not including "." or ".." entries.
+ *
+ * path is the starting directory. An internal copy will be made.
+ */
+struct dir_iterator *dir_iterator_begin(const char *path);
+
+/*
+ * Advance the iterator to the first or next item and return ITER_OK.
+ * If the iteration is exhausted, free the dir_iterator and any
+ * resources associated with it and return ITER_DONE. On error, free
+ * dir_iterator and associated resources and return ITER_ERROR. It is
+ * a bug to use iterator or call this function again after it has
+ * returned ITER_DONE or ITER_ERROR.
+ */
+int dir_iterator_advance(struct dir_iterator *iterator);
+
+/*
+ * End the iteration before it has been exhausted. Free the
+ * dir_iterator and any associated resources and return ITER_DONE. On
+ * error, free the dir_iterator and return ITER_ERROR.
+ */
+int dir_iterator_abort(struct dir_iterator *iterator);
+
+#endif
diff --git a/dir.c b/dir.c
index 6172b34..0ea235f 100644
--- a/dir.c
+++ b/dir.c
@@ -2364,7 +2364,7 @@ void write_untracked_extension(struct strbuf *out, struct untracked_cache *untra
varint_len = encode_varint(untracked->ident.len, varbuf);
strbuf_add(out, varbuf, varint_len);
- strbuf_add(out, untracked->ident.buf, untracked->ident.len);
+ strbuf_addbuf(out, &untracked->ident);
strbuf_add(out, ouc, ouc_size(len));
free(ouc);
diff --git a/dir.h b/dir.h
index bfde698..da1a858 100644
--- a/dir.h
+++ b/dir.h
@@ -262,9 +262,32 @@ extern int is_empty_dir(const char *dir);
extern void setup_standard_excludes(struct dir_struct *dir);
+
+/* Constants for remove_dir_recursively: */
+
+/*
+ * If a non-directory is found within path, stop and return an error.
+ * (In this case some empty directories might already have been
+ * removed.)
+ */
#define REMOVE_DIR_EMPTY_ONLY 01
+
+/*
+ * If any Git work trees are found within path, skip them without
+ * considering it an error.
+ */
#define REMOVE_DIR_KEEP_NESTED_GIT 02
+
+/* Remove the contents of path, but leave path itself. */
#define REMOVE_DIR_KEEP_TOPLEVEL 04
+
+/*
+ * Remove path and its contents, recursively. flags is a combination
+ * of the above REMOVE_DIR_* constants. Return 0 on success.
+ *
+ * This function uses path as temporary scratch space, but restores it
+ * before returning.
+ */
extern int remove_dir_recursively(struct strbuf *path, int flag);
/* tries to remove the path with empty directories along it, ignores ENOENT */
diff --git a/fast-import.c b/fast-import.c
index c504ef7..bf53ac9 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -164,8 +164,8 @@ Format of STDIN stream:
#include "refs.h"
#include "csum-file.h"
#include "quote.h"
-#include "exec_cmd.h"
#include "dir.h"
+#include "run-command.h"
#define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -282,6 +282,7 @@ struct recent_command {
/* Configured limits on output */
static unsigned long max_depth = 10;
static off_t max_packsize;
+static int unpack_limit = 100;
static int force_update;
static int pack_compression_level = Z_DEFAULT_COMPRESSION;
static int pack_compression_seen;
@@ -300,7 +301,7 @@ static int failure;
static FILE *pack_edges;
static unsigned int show_stats = 1;
static int global_argc;
-static char **global_argv;
+static const char **global_argv;
/* Memory pools */
static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -596,6 +597,33 @@ static struct object_entry *insert_object(unsigned char *sha1)
return e;
}
+static void invalidate_pack_id(unsigned int id)
+{
+ unsigned int h;
+ unsigned long lu;
+ struct tag *t;
+
+ for (h = 0; h < ARRAY_SIZE(object_table); h++) {
+ struct object_entry *e;
+
+ for (e = object_table[h]; e; e = e->next)
+ if (e->pack_id == id)
+ e->pack_id = MAX_PACK_ID;
+ }
+
+ for (lu = 0; lu < branch_table_sz; lu++) {
+ struct branch *b;
+
+ for (b = branch_table[lu]; b; b = b->table_next_branch)
+ if (b->pack_id == id)
+ b->pack_id = MAX_PACK_ID;
+ }
+
+ for (t = first_tag; t; t = t->next_tag)
+ if (t->pack_id == id)
+ t->pack_id = MAX_PACK_ID;
+}
+
static unsigned int hc_str(const char *s, size_t len)
{
unsigned int r = 0;
@@ -951,6 +979,23 @@ static void unkeep_all_packs(void)
}
}
+static int loosen_small_pack(const struct packed_git *p)
+{
+ struct child_process unpack = CHILD_PROCESS_INIT;
+
+ if (lseek(p->pack_fd, 0, SEEK_SET) < 0)
+ die_errno("Failed seeking to start of '%s'", p->pack_name);
+
+ unpack.in = p->pack_fd;
+ unpack.git_cmd = 1;
+ unpack.stdout_to_stderr = 1;
+ argv_array_push(&unpack.args, "unpack-objects");
+ if (!show_stats)
+ argv_array_push(&unpack.args, "-q");
+
+ return run_command(&unpack);
+}
+
static void end_packfile(void)
{
static int running;
@@ -973,6 +1018,14 @@ static void end_packfile(void)
fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
pack_data->pack_name, object_count,
cur_pack_sha1, pack_size);
+
+ if (object_count <= unpack_limit) {
+ if (!loosen_small_pack(pack_data)) {
+ invalidate_pack_id(pack_id);
+ goto discard_pack;
+ }
+ }
+
close(pack_data->pack_fd);
idx_name = keep_pack(create_index());
@@ -1003,6 +1056,7 @@ static void end_packfile(void)
pack_id++;
}
else {
+discard_pack:
close(pack_data->pack_fd);
unlink_or_warn(pack_data->pack_name);
}
@@ -3320,6 +3374,7 @@ static void parse_option(const char *option)
static void git_pack_config(void)
{
int indexversion_value;
+ int limit;
unsigned long packsizelimit_value;
if (!git_config_get_ulong("pack.depth", &max_depth)) {
@@ -3344,6 +3399,11 @@ static void git_pack_config(void)
if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
max_packsize = packsizelimit_value;
+ if (!git_config_get_int("fastimport.unpacklimit", &limit))
+ unpack_limit = limit;
+ else if (!git_config_get_int("transfer.unpacklimit", &limit))
+ unpack_limit = limit;
+
git_config(git_default_config, NULL);
}
@@ -3384,14 +3444,10 @@ static void parse_argv(void)
read_marks();
}
-int main(int argc, char **argv)
+int cmd_main(int argc, const char **argv)
{
unsigned int i;
- git_extract_argv0_path(argv[0]);
-
- git_setup_gettext();
-
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(fast_import_usage);
diff --git a/fetch-pack.c b/fetch-pack.c
index b501d5c..85e77af 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -243,16 +243,21 @@ static void insert_one_alternate_ref(const struct ref *ref, void *unused)
#define INITIAL_FLUSH 16
#define PIPESAFE_FLUSH 32
-#define LARGE_FLUSH 1024
+#define LARGE_FLUSH 16384
static int next_flush(struct fetch_pack_args *args, int count)
{
- int flush_limit = args->stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
-
- if (count < flush_limit)
- count <<= 1;
- else
- count += flush_limit;
+ if (args->stateless_rpc) {
+ if (count < LARGE_FLUSH)
+ count <<= 1;
+ else
+ count = count * 11 / 10;
+ } else {
+ if (count < PIPESAFE_FLUSH)
+ count <<= 1;
+ else
+ count += PIPESAFE_FLUSH;
+ }
return count;
}
diff --git a/fsck.c b/fsck.c
index 0531545..c9cf3de 100644
--- a/fsck.c
+++ b/fsck.c
@@ -9,6 +9,7 @@
#include "refs.h"
#include "utf8.h"
#include "sha1-array.h"
+#include "decorate.h"
#define FSCK_FATAL -1
#define FSCK_INFO -2
@@ -290,35 +291,87 @@ static int report(struct fsck_options *options, struct object *object,
va_start(ap, fmt);
strbuf_vaddf(&sb, fmt, ap);
- result = options->error_func(object, msg_type, sb.buf);
+ result = options->error_func(options, object, msg_type, sb.buf);
strbuf_release(&sb);
va_end(ap);
return result;
}
+static char *get_object_name(struct fsck_options *options, struct object *obj)
+{
+ if (!options->object_names)
+ return NULL;
+ return lookup_decoration(options->object_names, obj);
+}
+
+static void put_object_name(struct fsck_options *options, struct object *obj,
+ const char *fmt, ...)
+{
+ va_list ap;
+ struct strbuf buf = STRBUF_INIT;
+ char *existing;
+
+ if (!options->object_names)
+ return;
+ existing = lookup_decoration(options->object_names, obj);
+ if (existing)
+ return;
+ va_start(ap, fmt);
+ strbuf_vaddf(&buf, fmt, ap);
+ add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
+ va_end(ap);
+}
+
+static const char *describe_object(struct fsck_options *o, struct object *obj)
+{
+ static struct strbuf buf = STRBUF_INIT;
+ char *name;
+
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, oid_to_hex(&obj->oid));
+ if (o->object_names && (name = lookup_decoration(o->object_names, obj)))
+ strbuf_addf(&buf, " (%s)", name);
+
+ return buf.buf;
+}
+
static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
{
struct tree_desc desc;
struct name_entry entry;
int res = 0;
+ const char *name;
if (parse_tree(tree))
return -1;
+ name = get_object_name(options, &tree->object);
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
+ struct object *obj;
int result;
if (S_ISGITLINK(entry.mode))
continue;
- if (S_ISDIR(entry.mode))
- result = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);
- else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
- result = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);
+
+ if (S_ISDIR(entry.mode)) {
+ obj = &lookup_tree(entry.oid->hash)->object;
+ if (name)
+ put_object_name(options, obj, "%s%s/", name,
+ entry.path);
+ result = options->walk(obj, OBJ_TREE, data, options);
+ }
+ else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
+ obj = &lookup_blob(entry.oid->hash)->object;
+ if (name)
+ put_object_name(options, obj, "%s%s", name,
+ entry.path);
+ result = options->walk(obj, OBJ_BLOB, data, options);
+ }
else {
result = error("in tree %s: entry %s has bad mode %.6o",
- oid_to_hex(&tree->object.oid), entry.path, entry.mode);
+ describe_object(options, &tree->object), entry.path, entry.mode);
}
if (result < 0)
return result;
@@ -330,20 +383,55 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)
{
+ int counter = 0, generation = 0, name_prefix_len = 0;
struct commit_list *parents;
int res;
int result;
+ const char *name;
if (parse_commit(commit))
return -1;
+ name = get_object_name(options, &commit->object);
+ if (name)
+ put_object_name(options, &commit->tree->object, "%s:", name);
+
result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);
if (result < 0)
return result;
res = result;
parents = commit->parents;
+ if (name && parents) {
+ int len = strlen(name), power;
+
+ if (len && name[len - 1] == '^') {
+ generation = 1;
+ name_prefix_len = len - 1;
+ }
+ else { /* parse ~<generation> suffix */
+ for (generation = 0, power = 1;
+ len && isdigit(name[len - 1]);
+ power *= 10)
+ generation += power * (name[--len] - '0');
+ if (power > 1 && len && name[len - 1] == '~')
+ name_prefix_len = len - 1;
+ }
+ }
+
while (parents) {
+ if (name) {
+ struct object *obj = &parents->item->object;
+
+ if (++counter > 1)
+ put_object_name(options, obj, "%s^%d",
+ name, counter);
+ else if (generation > 0)
+ put_object_name(options, obj, "%.*s~%d",
+ name_prefix_len, name, generation + 1);
+ else
+ put_object_name(options, obj, "%s^", name);
+ }
result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
if (result < 0)
return result;
@@ -356,8 +444,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
{
+ char *name = get_object_name(options, &tag->object);
+
if (parse_tag(tag))
return -1;
+ if (name)
+ put_object_name(options, tag->tagged, "%s", name);
return options->walk(tag->tagged, OBJ_ANY, data, options);
}
@@ -375,7 +467,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
case OBJ_TAG:
return fsck_walk_tag((struct tag *)obj, data, options);
default:
- error("Unknown object type for %s", oid_to_hex(&obj->oid));
+ error("Unknown object type for %s", describe_object(options, obj));
return -1;
}
}
@@ -818,12 +910,13 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
obj->type);
}
-int fsck_error_function(struct object *obj, int msg_type, const char *message)
+int fsck_error_function(struct fsck_options *o,
+ struct object *obj, int msg_type, const char *message)
{
if (msg_type == FSCK_WARN) {
- warning("object %s: %s", oid_to_hex(&obj->oid), message);
+ warning("object %s: %s", describe_object(o, obj), message);
return 0;
}
- error("object %s: %s", oid_to_hex(&obj->oid), message);
+ error("object %s: %s", describe_object(o, obj), message);
return 1;
}
diff --git a/fsck.h b/fsck.h
index dded84b..1891c18 100644
--- a/fsck.h
+++ b/fsck.h
@@ -23,9 +23,11 @@ int is_valid_msg_type(const char *msg_id, const char *msg_type);
typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options);
/* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
-typedef int (*fsck_error)(struct object *obj, int type, const char *message);
+typedef int (*fsck_error)(struct fsck_options *o,
+ struct object *obj, int type, const char *message);
-int fsck_error_function(struct object *obj, int type, const char *message);
+int fsck_error_function(struct fsck_options *o,
+ struct object *obj, int type, const char *message);
struct fsck_options {
fsck_walk_func walk;
@@ -33,6 +35,7 @@ struct fsck_options {
unsigned strict:1;
int *msg_type;
struct sha1_array *skiplist;
+ struct decoration *object_names;
};
#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }
diff --git a/git-bisect.sh b/git-bisect.sh
index 5d1cb00..ae3cb01 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -33,7 +33,6 @@ Please use "git help bisect" to get the full man page.'
OPTIONS_SPEC=
. git-sh-setup
-. git-sh-i18n
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
@@ -275,10 +274,11 @@ bisect_state() {
check_and_set_terms $state
case "$#,$state" in
0,*)
- die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
+ die "Please call 'bisect_state' with at least one argument." ;;
1,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip)
- rev=$(git rev-parse --verify $(bisect_head)) ||
- die "$(gettext "Bad rev input: $(bisect_head)")"
+ bisected_head=$(bisect_head)
+ rev=$(git rev-parse --verify "$bisected_head") ||
+ die "$(eval_gettext "Bad rev input: \$bisected_head")"
bisect_write "$state" "$rev"
check_expected_revs "$rev" ;;
2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
diff --git a/git-compat-util.h b/git-compat-util.h
index 49d4029..590bfdd 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -474,6 +474,23 @@ static inline int skip_prefix(const char *str, const char *prefix,
}
/*
+ * Like skip_prefix, but promises never to read past "len" bytes of the input
+ * buffer, and returns the remaining number of bytes in "out" via "outlen".
+ */
+static inline int skip_prefix_mem(const char *buf, size_t len,
+ const char *prefix,
+ const char **out, size_t *outlen)
+{
+ size_t prefix_len = strlen(prefix);
+ if (prefix_len <= len && !memcmp(buf, prefix, prefix_len)) {
+ *out = buf + prefix_len;
+ *outlen = len - prefix_len;
+ return 1;
+ }
+ return 0;
+}
+
+/*
* If buf ends with suffix, return 1 and subtract the length of the suffix
* from *len. Otherwise, return 0 and leave *len untouched.
*/
@@ -1045,3 +1062,5 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);
#endif
#endif
+
+extern int cmd_main(int, const char **);
diff --git a/git-difftool.perl b/git-difftool.perl
index ebd13ba..a5790d0 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -37,14 +37,6 @@ USAGE
exit($exitcode);
}
-sub find_worktree
-{
- # Git->repository->wc_path() does not honor changes to the working
- # tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'
- # config variable.
- return Git::command_oneline('rev-parse', '--show-toplevel');
-}
-
sub print_tool_help
{
# See the comment at the bottom of file_diff() for the reason behind
@@ -67,14 +59,14 @@ sub exit_cleanup
sub use_wt_file
{
- my ($repo, $workdir, $file, $sha1) = @_;
+ my ($workdir, $file, $sha1) = @_;
my $null_sha1 = '0' x 40;
if (-l "$workdir/$file" || ! -e _) {
return (0, $null_sha1);
}
- my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
+ my $wt_sha1 = Git::command_oneline('hash-object', "$workdir/$file");
my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
return ($use, $wt_sha1);
}
@@ -83,20 +75,17 @@ sub changed_files
{
my ($repo_path, $index, $worktree) = @_;
$ENV{GIT_INDEX_FILE} = $index;
- $ENV{GIT_WORK_TREE} = $worktree;
- my $must_unset_git_dir = 0;
- if (not defined($ENV{GIT_DIR})) {
- $must_unset_git_dir = 1;
- $ENV{GIT_DIR} = $repo_path;
- }
- my @refreshargs = qw/update-index --really-refresh -q --unmerged/;
- my @gitargs = qw/diff-files --name-only -z/;
+ my @gitargs = ('--git-dir', $repo_path, '--work-tree', $worktree);
+ my @refreshargs = (
+ @gitargs, 'update-index',
+ '--really-refresh', '-q', '--unmerged');
try {
Git::command_oneline(@refreshargs);
} catch Git::Error::Command with {};
- my $line = Git::command_oneline(@gitargs);
+ my @diffargs = (@gitargs, 'diff-files', '--name-only', '-z');
+ my $line = Git::command_oneline(@diffargs);
my @files;
if (defined $line) {
@files = split('\0', $line);
@@ -105,26 +94,15 @@ sub changed_files
}
delete($ENV{GIT_INDEX_FILE});
- delete($ENV{GIT_WORK_TREE});
- delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
return map { $_ => 1 } @files;
}
sub setup_dir_diff
{
- my ($repo, $workdir, $symlinks) = @_;
-
- # Run the diff; exit immediately if no diff found
- # 'Repository' and 'WorkingCopy' must be explicitly set to insure that
- # if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used
- # by Git->repository->command*.
- my $repo_path = $repo->repo_path();
- my %repo_args = (Repository => $repo_path, WorkingCopy => $workdir);
- my $diffrepo = Git->repository(%repo_args);
-
+ my ($workdir, $symlinks) = @_;
my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV);
- my $diffrtn = $diffrepo->command_oneline(@gitargs);
+ my $diffrtn = Git::command_oneline(@gitargs);
exit(0) unless defined($diffrtn);
# Build index info for left and right sides of the diff
@@ -176,12 +154,12 @@ EOF
if ($lmode eq $symlink_mode) {
$symlink{$src_path}{left} =
- $diffrepo->command_oneline('show', "$lsha1");
+ Git::command_oneline('show', $lsha1);
}
if ($rmode eq $symlink_mode) {
$symlink{$dst_path}{right} =
- $diffrepo->command_oneline('show', "$rsha1");
+ Git::command_oneline('show', $rsha1);
}
if ($lmode ne $null_mode and $status !~ /^C/) {
@@ -193,8 +171,8 @@ EOF
if ($working_tree_dups{$dst_path}++) {
next;
}
- my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
- $dst_path, $rsha1);
+ my ($use, $wt_sha1) =
+ use_wt_file($workdir, $dst_path, $rsha1);
if ($use) {
push @working_tree, $dst_path;
$wtindex .= "$rmode $wt_sha1\t$dst_path\0";
@@ -211,44 +189,34 @@ EOF
mkpath($ldir) or exit_cleanup($tmpdir, 1);
mkpath($rdir) or exit_cleanup($tmpdir, 1);
- # If $GIT_DIR is not set prior to calling 'git update-index' and
- # 'git checkout-index', then those commands will fail if difftool
- # is called from a directory other than the repo root.
- my $must_unset_git_dir = 0;
- if (not defined($ENV{GIT_DIR})) {
- $must_unset_git_dir = 1;
- $ENV{GIT_DIR} = $repo_path;
- }
-
# Populate the left and right directories based on each index file
my ($inpipe, $ctx);
$ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
($inpipe, $ctx) =
- $repo->command_input_pipe(qw(update-index -z --index-info));
+ Git::command_input_pipe('update-index', '-z', '--index-info');
print($inpipe $lindex);
- $repo->command_close_pipe($inpipe, $ctx);
+ Git::command_close_pipe($inpipe, $ctx);
my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
exit_cleanup($tmpdir, $rc) if $rc != 0;
$ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
($inpipe, $ctx) =
- $repo->command_input_pipe(qw(update-index -z --index-info));
+ Git::command_input_pipe('update-index', '-z', '--index-info');
print($inpipe $rindex);
- $repo->command_close_pipe($inpipe, $ctx);
+ Git::command_close_pipe($inpipe, $ctx);
$rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
exit_cleanup($tmpdir, $rc) if $rc != 0;
$ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex";
($inpipe, $ctx) =
- $repo->command_input_pipe(qw(update-index --info-only -z --index-info));
+ Git::command_input_pipe('update-index', '--info-only', '-z', '--index-info');
print($inpipe $wtindex);
- $repo->command_close_pipe($inpipe, $ctx);
+ Git::command_close_pipe($inpipe, $ctx);
# If $GIT_DIR was explicitly set just for the update/checkout
# commands, then it should be unset before continuing.
- delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
delete($ENV{GIT_INDEX_FILE});
# Changes in the working tree need special treatment since they are
@@ -415,9 +383,9 @@ sub dir_diff
my $rc;
my $error = 0;
my $repo = Git->repository();
- my $workdir = find_worktree();
- my ($a, $b, $tmpdir, @worktree) =
- setup_dir_diff($repo, $workdir, $symlinks);
+ my $repo_path = $repo->repo_path();
+ my $workdir = $repo->wc_path();
+ my ($a, $b, $tmpdir, @worktree) = setup_dir_diff($workdir, $symlinks);
if (defined($extcmd)) {
$rc = system($extcmd, $a, $b);
@@ -443,10 +411,10 @@ sub dir_diff
next if ! -f "$b/$file";
if (!$indices_loaded) {
- %wt_modified = changed_files($repo->repo_path(),
- "$tmpdir/wtindex", "$workdir");
- %tmp_modified = changed_files($repo->repo_path(),
- "$tmpdir/wtindex", "$b");
+ %wt_modified = changed_files(
+ $repo_path, "$tmpdir/wtindex", $workdir);
+ %tmp_modified = changed_files(
+ $repo_path, "$tmpdir/wtindex", $b);
$indices_loaded = 1;
}
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index dc2fd1b..308eafd 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -5,14 +5,11 @@
# Resolve two or more trees.
#
+. git-sh-setup
+
LF='
'
-die () {
- echo >&2 "$*"
- exit 1
-}
-
# The first parameters up to -- are merge bases; the rest are heads.
bases= head= remotes= sep_seen=
for arg
@@ -46,7 +43,7 @@ esac
if ! git diff-index --quiet --cached HEAD --
then
- echo "Error: Your local changes to the following files would be overwritten by merge"
+ gettextln "Error: Your local changes to the following files would be overwritten by merge"
git diff-index --cached --name-only HEAD -- | sed -e 's/^/ /'
exit 2
fi
@@ -61,8 +58,8 @@ do
# We allow only last one to have a hand-resolvable
# conflicts. Last round failed and we still had
# a head to merge.
- echo "Automated merge did not work."
- echo "Should not be doing an Octopus."
+ gettextln "Automated merge did not work."
+ gettextln "Should not be doing an Octopus."
exit 2
esac
@@ -73,11 +70,11 @@ do
eval pretty_name=\${GITHEAD_$SHA1_UP:-$pretty_name}
fi
common=$(git merge-base --all $SHA1 $MRC) ||
- die "Unable to find common commit with $pretty_name"
+ die "$(eval_gettext "Unable to find common commit with \$pretty_name")"
case "$LF$common$LF" in
*"$LF$SHA1$LF"*)
- echo "Already up-to-date with $pretty_name"
+ eval_gettextln "Already up-to-date with \$pretty_name"
continue
;;
esac
@@ -89,7 +86,7 @@ do
# tree as the intermediate result of the merge.
# We still need to count this as part of the parent set.
- echo "Fast-forwarding to: $pretty_name"
+ eval_gettextln "Fast-forwarding to: \$pretty_name"
git read-tree -u -m $head $SHA1 || exit
MRC=$SHA1 MRT=$(git write-tree)
continue
@@ -97,12 +94,12 @@ do
NON_FF_MERGE=1
- echo "Trying simple merge with $pretty_name"
+ eval_gettextln "Trying simple merge with \$pretty_name"
git read-tree -u -m --aggressive $common $MRT $SHA1 || exit 2
next=$(git write-tree 2>/dev/null)
if test $? -ne 0
then
- echo "Simple merge did not work, trying automatic merge."
+ gettextln "Simple merge did not work, trying automatic merge."
git-merge-index -o git-merge-one-file -a ||
OCTOPUS_FAILURE=1
next=$(git write-tree 2>/dev/null)
diff --git a/git-p4.py b/git-p4.py
index b123aa2..ac6f4c1 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -2274,7 +2274,7 @@ class P4Sync(Command, P4UserMap):
self.useClientSpec_from_options = False
self.clientSpecDirs = None
self.tempBranches = []
- self.tempBranchLocation = "git-p4-tmp"
+ self.tempBranchLocation = "refs/git-p4-tmp"
self.largeFileSystem = None
if gitConfig('git-p4.largeFileSystem'):
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 4f499d2..e2da524 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -121,14 +121,14 @@ mark_action_done () {
sed -e 1q < "$todo" >> "$done"
sed -e 1d < "$todo" >> "$todo".new
mv -f "$todo".new "$todo"
- new_count=$(git stripspace --strip-comments <"$done" | wc -l)
+ new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
echo $new_count >"$msgnum"
total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
echo $total >"$end"
if test "$last_count" != "$new_count"
then
last_count=$new_count
- printf "Rebasing (%d/%d)\r" $new_count $total
+ eval_gettext "Rebasing (\$new_count/\$total)"; printf "\r"
test -z "$verbose" || echo
fi
}
@@ -144,29 +144,28 @@ reschedule_last_action () {
}
append_todo_help () {
- git stripspace --comment-lines >>"$todo" <<\EOF
-
+ gettext "
Commands:
p, pick = use commit
r, reword = use commit, but edit the commit message
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
- f, fixup = like "squash", but discard this commit's log message
+ f, fixup = like \"squash\", but discard this commit's log message
x, exec = run command (the rest of the line) using shell
d, drop = remove commit
These lines can be re-ordered; they are executed from top to bottom.
+" | git stripspace --comment-lines >>"$todo"
-EOF
if test $(get_missing_commit_check_level) = error
then
- git stripspace --comment-lines >>"$todo" <<\EOF
+ gettext "
Do not remove any line. Use 'drop' explicitly to remove a commit.
-EOF
+" | git stripspace --comment-lines >>"$todo"
else
- git stripspace --comment-lines >>"$todo" <<\EOF
+ gettext "
If you remove a line here THAT COMMIT WILL BE LOST.
-EOF
+" | git stripspace --comment-lines >>"$todo"
fi
}
@@ -200,13 +199,14 @@ exit_with_patch () {
make_patch $1
git rev-parse --verify HEAD > "$amend"
gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
- warn "You can amend the commit now, with"
- warn
- warn " git commit --amend $gpg_sign_opt_quoted"
- warn
- warn "Once you are satisfied with your changes, run"
- warn
- warn " git rebase --continue"
+ warn "$(eval_gettext "\
+You can amend the commit now, with
+
+ git commit --amend \$gpg_sign_opt_quoted
+
+Once you are satisfied with your changes, run
+
+ git rebase --continue")"
warn
exit $2
}
@@ -222,10 +222,12 @@ has_action () {
}
is_empty_commit() {
- tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null ||
- die "$1: not a commit that can be picked")
- ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null ||
- ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904)
+ tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null) || {
+ sha1=$1
+ die "$(eval_gettext "\$sha1: not a commit that can be picked")"
+ }
+ ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null) ||
+ ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
test "$tree" = "$ptree"
}
@@ -261,7 +263,7 @@ pick_one () {
case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
case "$force_rebase" in '') ;; ?*) ff= ;; esac
- output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
+ output git rev-parse --verify $sha1 || die "$(eval_gettext "Invalid commit name: \$sha1")"
if is_empty_commit "$sha1"
then
@@ -303,7 +305,7 @@ pick_one_preserving_merges () {
git rev-parse HEAD > "$rewritten"/$current_commit
done <"$state_dir"/current-commit
rm "$state_dir"/current-commit ||
- die "Cannot write current commit's replacement sha1"
+ die "$(gettext "Cannot write current commit's replacement sha1")"
fi
fi
@@ -355,9 +357,9 @@ pick_one_preserving_merges () {
done
case $fast_forward in
t)
- output warn "Fast-forward to $sha1"
+ output warn "$(eval_gettext "Fast-forward to \$sha1")"
output git reset --hard $sha1 ||
- die "Cannot fast-forward to $sha1"
+ die "$(eval_gettext "Cannot fast-forward to \$sha1")"
;;
f)
first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
@@ -366,12 +368,12 @@ pick_one_preserving_merges () {
then
# detach HEAD to current parent
output git checkout $first_parent 2> /dev/null ||
- die "Cannot move HEAD to $first_parent"
+ die "$(eval_gettext "Cannot move HEAD to \$first_parent")"
fi
case "$new_parents" in
' '*' '*)
- test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
+ test "a$1" = a-n && die "$(eval_gettext "Refusing to squash a merge: \$sha1")"
# redo merge
author_script_content=$(get_author_ident_from_commit $sha1)
@@ -385,7 +387,7 @@ pick_one_preserving_merges () {
$merge_args $strategy_args -m "$msg_content" $new_parents'
then
printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
- die_with_patch $sha1 "Error redoing merge $sha1"
+ die_with_patch $sha1 "$(eval_gettext "Error redoing merge \$sha1")"
fi
echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
;;
@@ -393,19 +395,59 @@ pick_one_preserving_merges () {
output eval git cherry-pick \
${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
"$strategy_args" "$@" ||
- die_with_patch $sha1 "Could not pick $sha1"
+ die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
;;
esac
;;
esac
}
-nth_string () {
- case "$1" in
- *1[0-9]|*[04-9]) echo "$1"th;;
- *1) echo "$1"st;;
- *2) echo "$1"nd;;
- *3) echo "$1"rd;;
+this_nth_commit_message () {
+ n=$1
+ case "$n" in
+ 1) gettext "This is the 1st commit message:";;
+ 2) gettext "This is the 2nd commit message:";;
+ 3) gettext "This is the 3rd commit message:";;
+ 4) gettext "This is the 4th commit message:";;
+ 5) gettext "This is the 5th commit message:";;
+ 6) gettext "This is the 6th commit message:";;
+ 7) gettext "This is the 7th commit message:";;
+ 8) gettext "This is the 8th commit message:";;
+ 9) gettext "This is the 9th commit message:";;
+ 10) gettext "This is the 10th commit message:";;
+ # TRANSLATORS: if the language you are translating into
+ # doesn't allow you to compose a sentence in this fashion,
+ # consider translating as if this and the following few strings
+ # were "This is the commit message ${n}:"
+ *1[0-9]|*[04-9]) eval_gettext "This is the \${n}th commit message:";;
+ *1) eval_gettext "This is the \${n}st commit message:";;
+ *2) eval_gettext "This is the \${n}nd commit message:";;
+ *3) eval_gettext "This is the \${n}rd commit message:";;
+ *) eval_gettext "This is the commit message \${n}:";;
+ esac
+}
+skip_nth_commit_message () {
+ n=$1
+ case "$n" in
+ 1) gettext "The 1st commit message will be skipped:";;
+ 2) gettext "The 2nd commit message will be skipped:";;
+ 3) gettext "The 3rd commit message will be skipped:";;
+ 4) gettext "The 4th commit message will be skipped:";;
+ 5) gettext "The 5th commit message will be skipped:";;
+ 6) gettext "The 6th commit message will be skipped:";;
+ 7) gettext "The 7th commit message will be skipped:";;
+ 8) gettext "The 8th commit message will be skipped:";;
+ 9) gettext "The 9th commit message will be skipped:";;
+ 10) gettext "The 10th commit message will be skipped:";;
+ # TRANSLATORS: if the language you are translating into
+ # doesn't allow you to compose a sentence in this fashion,
+ # consider translating as if this and the following few strings
+ # were "The commit message ${n} will be skipped:"
+ *1[0-9]|*[04-9]) eval_gettext "The \${n}th commit message will be skipped:";;
+ *1) eval_gettext "The \${n}st commit message will be skipped:";;
+ *2) eval_gettext "The \${n}nd commit message will be skipped:";;
+ *3) eval_gettext "The \${n}rd commit message will be skipped:";;
+ *) eval_gettext "The commit message \${n} will be skipped:";;
esac
}
@@ -413,20 +455,23 @@ update_squash_messages () {
if test -f "$squash_msg"; then
mv "$squash_msg" "$squash_msg".bak || exit
count=$(($(sed -n \
- -e "1s/^. This is a combination of \(.*\) commits\./\1/p" \
+ -e "1s/^$comment_char.*\([0-9][0-9]*\).*/\1/p" \
-e "q" < "$squash_msg".bak)+1))
{
- printf '%s\n' "$comment_char This is a combination of $count commits."
+ printf '%s\n' "$comment_char $(eval_ngettext \
+ "This is a combination of \$count commit." \
+ "This is a combination of \$count commits." \
+ $count)"
sed -e 1d -e '2,/^./{
/^$/d
}' <"$squash_msg".bak
} >"$squash_msg"
else
- commit_message HEAD > "$fixup_msg" || die "Cannot write $fixup_msg"
+ commit_message HEAD > "$fixup_msg" || die "$(gettext "Cannot write \$fixup_msg")"
count=2
{
- printf '%s\n' "$comment_char This is a combination of 2 commits."
- printf '%s\n' "$comment_char The first commit's message is:"
+ printf '%s\n' "$comment_char $(gettext "This is a combination of 2 commits.")"
+ printf '%s\n' "$comment_char $(gettext "This is the 1st commit message:")"
echo
cat "$fixup_msg"
} >"$squash_msg"
@@ -435,13 +480,13 @@ update_squash_messages () {
squash)
rm -f "$fixup_msg"
echo
- printf '%s\n' "$comment_char This is the $(nth_string $count) commit message:"
+ printf '%s\n' "$comment_char $(this_nth_commit_message $count)"
echo
commit_message $2
;;
fixup)
echo
- printf '%s\n' "$comment_char The $(nth_string $count) commit message will be skipped:"
+ printf '%s\n' "$comment_char $(skip_nth_commit_message $count)"
echo
# Change the space after the comment character to TAB:
commit_message $2 | git stripspace --comment-lines | sed -e 's/ / /'
@@ -460,12 +505,14 @@ peek_next_command () {
# messages, effectively causing the combined commit to be used as the
# new basis for any further squash/fixups. Args: sha1 rest
die_failed_squash() {
+ sha1=$1
+ rest=$2
mv "$squash_msg" "$msg" || exit
rm -f "$fixup_msg"
cp "$msg" "$GIT_DIR"/MERGE_MSG || exit
warn
- warn "Could not apply $1... $2"
- die_with_patch $1 ""
+ warn "$(eval_gettext "Could not apply \$sha1... \$rest")"
+ die_with_patch $sha1 ""
}
flush_rewritten_pending() {
@@ -489,6 +536,8 @@ record_in_rewritten() {
}
do_pick () {
+ sha1=$1
+ rest=$2
if test "$(git rev-parse HEAD)" = "$squash_onto"
then
# Set the correct commit message and author info on the
@@ -500,15 +549,15 @@ do_pick () {
# resolve before manually running git commit --amend then git
# rebase --continue.
git commit --allow-empty --allow-empty-message --amend \
- --no-post-rewrite -n -q -C $1 &&
- pick_one -n $1 &&
+ --no-post-rewrite -n -q -C $sha1 &&
+ pick_one -n $sha1 &&
git commit --allow-empty --allow-empty-message \
- --amend --no-post-rewrite -n -q -C $1 \
+ --amend --no-post-rewrite -n -q -C $sha1 \
${gpg_sign_opt:+"$gpg_sign_opt"} ||
- die_with_patch $1 "Could not apply $1... $2"
+ die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
else
- pick_one $1 ||
- die_with_patch $1 "Could not apply $1... $2"
+ pick_one $sha1 ||
+ die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
fi
}
@@ -536,10 +585,11 @@ do_next () {
mark_action_done
do_pick $sha1 "$rest"
git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} || {
- warn "Could not amend commit after successfully picking $sha1... $rest"
- warn "This is most likely due to an empty commit message, or the pre-commit hook"
- warn "failed. If the pre-commit hook failed, you may need to resolve the issue before"
- warn "you are able to reword the commit."
+ warn "$(eval_gettext "\
+Could not amend commit after successfully picking \$sha1... \$rest
+This is most likely due to an empty commit message, or the pre-commit hook
+failed. If the pre-commit hook failed, you may need to resolve the issue before
+you are able to reword the commit.")"
exit_with_patch $sha1 1
}
record_in_rewritten $sha1
@@ -550,7 +600,7 @@ do_next () {
mark_action_done
do_pick $sha1 "$rest"
sha1_abbrev=$(git rev-parse --short $sha1)
- warn "Stopped at $sha1_abbrev... $rest"
+ warn "$(eval_gettext "Stopped at \$sha1_abbrev... \$rest")"
exit_with_patch $sha1 0
;;
squash|s|fixup|f)
@@ -565,7 +615,7 @@ do_next () {
comment_for_reflog $squash_style
test -f "$done" && has_action "$done" ||
- die "Cannot '$squash_style' without a previous commit"
+ die "$(eval_gettext "Cannot '\$squash_style' without a previous commit")"
mark_action_done
update_squash_messages $squash_style $sha1
@@ -607,7 +657,7 @@ do_next () {
x|"exec")
read -r command rest < "$todo"
mark_action_done
- printf 'Executing: %s\n' "$rest"
+ eval_gettextln "Executing: \$rest"
"${SHELL:-@SHELL_PATH@}" -c "$rest" # Actual execution
status=$?
# Run in subshell because require_clean_work_tree can die.
@@ -615,13 +665,14 @@ do_next () {
(require_clean_work_tree "rebase" 2>/dev/null) || dirty=t
if test "$status" -ne 0
then
- warn "Execution failed: $rest"
+ warn "$(eval_gettext "Execution failed: \$rest")"
test "$dirty" = f ||
- warn "and made changes to the index and/or the working tree"
+ warn "$(gettext "and made changes to the index and/or the working tree")"
- warn "You can fix the problem, and then run"
- warn
- warn " git rebase --continue"
+ warn "$(gettext "\
+You can fix the problem, and then run
+
+ git rebase --continue")"
warn
if test $status -eq 127 # command not found
then
@@ -630,18 +681,20 @@ do_next () {
exit "$status"
elif test "$dirty" = t
then
- warn "Execution succeeded: $rest"
- warn "but left changes to the index and/or the working tree"
- warn "Commit or stash your changes, and then run"
- warn
- warn " git rebase --continue"
+ # TRANSLATORS: after these lines is a command to be issued by the user
+ warn "$(eval_gettext "\
+Execution succeeded: \$rest
+but left changes to the index and/or the working tree
+Commit or stash your changes, and then run
+
+ git rebase --continue")"
warn
exit 1
fi
;;
*)
- warn "Unknown command: $command $sha1 $rest"
- fixtodo="Please fix this using 'git rebase --edit-todo'."
+ warn "$(eval_gettext "Unknown command: \$command \$sha1 \$rest")"
+ fixtodo="$(gettext "Please fix this using 'git rebase --edit-todo'.")"
if git rev-parse --verify -q "$sha1" >/dev/null
then
die_with_patch $sha1 "$fixtodo"
@@ -676,7 +729,7 @@ do_next () {
"$hook" rebase < "$rewritten_list"
true # we don't care if this hook failed
fi &&
- warn "Successfully rebased and updated $head_name."
+ warn "$(eval_gettext "Successfully rebased and updated \$head_name.")"
return 1 # not failure; just to break the do_rest loop
}
@@ -723,7 +776,7 @@ skip_unnecessary_picks () {
record_in_rewritten "$onto"
;;
esac ||
- die "Could not skip unnecessary pick commands"
+ die "$(gettext "Could not skip unnecessary pick commands")"
}
transform_todo_ids () {
@@ -881,9 +934,9 @@ check_commit_sha () {
if test $badsha -ne 0
then
line="$(sed -n -e "${2}p" "$3")"
- warn "Warning: the SHA-1 is missing or isn't" \
- "a commit in the following line:"
- warn " - $line"
+ warn "$(eval_gettext "\
+Warning: the SHA-1 is missing or isn't a commit in the following line:
+ - \$line")"
warn
fi
@@ -914,9 +967,9 @@ check_bad_cmd_and_sha () {
;;
*)
line="$(sed -n -e "${lineno}p" "$1")"
- warn "Warning: the command isn't recognized" \
- "in the following line:"
- warn " - $line"
+ warn "$(eval_gettext "\
+Warning: the command isn't recognized in the following line:
+ - \$line")"
warn
retval=1
;;
@@ -953,7 +1006,7 @@ warn_lines () {
# Switch to the branch in $into and notify it in the reflog
checkout_onto () {
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
- output git checkout $onto || die_abort "could not detach HEAD"
+ output git checkout $onto || die_abort "$(gettext "could not detach HEAD")"
git update-ref ORIG_HEAD $orig_head
}
@@ -991,28 +1044,26 @@ check_todo_list () {
then
test "$check_level" = error && raise_error=t
- warn "Warning: some commits may have been dropped" \
- "accidentally."
- warn "Dropped commits (newer to older):"
+ warn "$(gettext "\
+Warning: some commits may have been dropped accidentally.
+Dropped commits (newer to older):")"
# Make the list user-friendly and display
opt="--no-walk=sorted --format=oneline --abbrev-commit --stdin"
git rev-list $opt <"$todo".miss | warn_lines
- warn "To avoid this message, use \"drop\" to" \
- "explicitly remove a commit."
- warn
- warn "Use 'git config rebase.missingCommitsCheck' to change" \
- "the level of warnings."
- warn "The possible behaviours are: ignore, warn, error."
+ warn "$(gettext "\
+To avoid this message, use \"drop\" to explicitly remove a commit.
+
+Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
+The possible behaviours are: ignore, warn, error.")"
warn
fi
;;
ignore)
;;
*)
- warn "Unrecognized setting $check_level for option" \
- "rebase.missingCommitsCheck. Ignoring."
+ warn "$(eval_gettext "Unrecognized setting \$check_level for option rebase.missingCommitsCheck. Ignoring.")"
;;
esac
@@ -1029,8 +1080,8 @@ check_todo_list () {
# placed before the commit of the next action
checkout_onto
- warn "You can fix this with 'git rebase --edit-todo'."
- die "Or you can abort the rebase with 'git rebase --abort'."
+ warn "$(gettext "You can fix this with 'git rebase --edit-todo'.")"
+ die "$(gettext "Or you can abort the rebase with 'git rebase --abort'.")"
fi
}
@@ -1054,41 +1105,43 @@ continue)
test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
- die "Could not remove CHERRY_PICK_HEAD"
+ die "$(gettext "Could not remove CHERRY_PICK_HEAD")"
else
if ! test -f "$author_script"
then
gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
- die "You have staged changes in your working tree. If these changes are meant to be
+ die "$(eval_gettext "\
+You have staged changes in your working tree.
+If these changes are meant to be
squashed into the previous commit, run:
- git commit --amend $gpg_sign_opt_quoted
+ git commit --amend \$gpg_sign_opt_quoted
If they are meant to go into a new commit, run:
- git commit $gpg_sign_opt_quoted
+ git commit \$gpg_sign_opt_quoted
In both case, once you're done, continue with:
git rebase --continue
-"
+")"
fi
. "$author_script" ||
- die "Error trying to find the author identity to amend commit"
+ die "$(gettext "Error trying to find the author identity to amend commit")"
if test -f "$amend"
then
current_head=$(git rev-parse --verify HEAD)
test "$current_head" = $(cat "$amend") ||
- die "\
-You have uncommitted changes in your working tree. Please, commit them
-first and then run 'git rebase --continue' again."
+ die "$(gettext "\
+You have uncommitted changes in your working tree. Please commit them
+first and then run 'git rebase --continue' again.")"
do_with_author git commit --amend --no-verify -F "$msg" -e \
${gpg_sign_opt:+"$gpg_sign_opt"} ||
- die "Could not commit staged changes."
+ die "$(gettext "Could not commit staged changes.")"
else
do_with_author git commit --no-verify -F "$msg" -e \
${gpg_sign_opt:+"$gpg_sign_opt"} ||
- die "Could not commit staged changes."
+ die "$(gettext "Could not commit staged changes.")"
fi
fi
@@ -1112,16 +1165,15 @@ edit-todo)
mv -f "$todo".new "$todo"
collapse_todo_ids
append_todo_help
- git stripspace --comment-lines >>"$todo" <<\EOF
-
+ gettext "
You are editing the todo file of an ongoing interactive rebase.
To continue rebase after editing, run:
git rebase --continue
-EOF
+" | git stripspace --comment-lines >>"$todo"
git_sequence_editor "$todo" ||
- die "Could not execute editor"
+ die "$(gettext "Could not execute editor")"
expand_todo_ids
exit
@@ -1129,7 +1181,7 @@ EOF
esac
git var GIT_COMMITTER_IDENT >/dev/null ||
- die "You need to set your committer info first"
+ die "$(gettext "You need to set your committer info first")"
comment_for_reflog start
@@ -1137,15 +1189,15 @@ if test ! -z "$switch_to"
then
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
output git checkout "$switch_to" -- ||
- die "Could not checkout $switch_to"
+ die "$(eval_gettext "Could not checkout \$switch_to")"
comment_for_reflog start
fi
-orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?"
-mkdir -p "$state_dir" || die "Could not create temporary $state_dir"
+orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
+mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
-: > "$state_dir"/interactive || die "Could not mark as interactive"
+: > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
write_basic_state
if test t = "$preserve_merges"
then
@@ -1155,12 +1207,12 @@ then
for c in $(git merge-base --all $orig_head $upstream)
do
echo $onto > "$rewritten"/$c ||
- die "Could not init rewritten commits"
+ die "$(gettext "Could not init rewritten commits")"
done
else
mkdir "$rewritten" &&
echo $onto > "$rewritten"/root ||
- die "Could not init rewritten commits"
+ die "$(gettext "Could not init rewritten commits")"
fi
# No cherry-pick because our first pass is to determine
# parents to rewrite and skipping dropped commits would
@@ -1259,18 +1311,20 @@ todocount=${todocount##* }
cat >>"$todo" <<EOF
-$comment_char Rebase $shortrevisions onto $shortonto ($todocount command(s))
+$comment_char $(eval_ngettext \
+ "Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \
+ "Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \
+ "$todocount")
EOF
append_todo_help
-git stripspace --comment-lines >>"$todo" <<\EOF
-
+gettext "
However, if you remove everything, the rebase will be aborted.
-EOF
+" | git stripspace --comment-lines >>"$todo"
if test -z "$keep_empty"
then
- printf '%s\n' "$comment_char Note that empty commits are commented out" >>"$todo"
+ printf '%s\n' "$comment_char $(gettext "Note that empty commits are commented out")" >>"$todo"
fi
@@ -1280,7 +1334,7 @@ has_action "$todo" ||
cp "$todo" "$todo".backup
collapse_todo_ids
git_sequence_editor "$todo" ||
- die_abort "Could not execute editor"
+ die_abort "$(gettext "Could not execute editor")"
has_action "$todo" ||
return 2
diff --git a/git-rebase.sh b/git-rebase.sh
index 44ede36..04f6e44 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -45,7 +45,6 @@ skip! skip current patch and continue
edit-todo! edit the todo list during an interactive rebase
"
. git-sh-setup
-. git-sh-i18n
set_reflog_action rebase
require_work_tree_exists
cd_to_toplevel
@@ -154,7 +153,7 @@ move_to_original_branch () {
git symbolic-ref \
-m "rebase finished: returning to $head_name" \
HEAD $head_name ||
- die "$(gettext "Could not move back to $head_name")"
+ die "$(eval_gettext "Could not move back to \$head_name")"
;;
esac
}
@@ -449,7 +448,7 @@ then
then
. git-parse-remote
error_on_missing_default_upstream "rebase" "rebase" \
- "against" "git rebase <branch>"
+ "against" "git rebase $(gettext '<branch>')"
fi
test "$fork_point" = auto && fork_point=t
diff --git a/git-send-email.perl b/git-send-email.perl
index 6958785..da81be4 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -621,6 +621,8 @@ if (@rev_list_opts) {
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
}
+@files = handle_backup_files(@files);
+
if ($validate) {
foreach my $f (@files) {
unless (-p $f) {
@@ -1727,6 +1729,44 @@ sub validate_patch {
return;
}
+sub handle_backup {
+ my ($last, $lastlen, $file, $known_suffix) = @_;
+ my ($suffix, $skip);
+
+ $skip = 0;
+ if (defined $last &&
+ ($lastlen < length($file)) &&
+ (substr($file, 0, $lastlen) eq $last) &&
+ ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) {
+ if (defined $known_suffix && $suffix eq $known_suffix) {
+ print "Skipping $file with backup suffix '$known_suffix'.\n";
+ $skip = 1;
+ } else {
+ my $answer = ask("Do you really want to send $file? (y|N): ",
+ valid_re => qr/^(?:y|n)/i,
+ default => 'n');
+ $skip = ($answer ne 'y');
+ if ($skip) {
+ $known_suffix = $suffix;
+ }
+ }
+ }
+ return ($skip, $known_suffix);
+}
+
+sub handle_backup_files {
+ my @file = @_;
+ my ($last, $lastlen, $known_suffix, $skip, @result);
+ for my $file (@file) {
+ ($skip, $known_suffix) = handle_backup($last, $lastlen,
+ $file, $known_suffix);
+ push @result, $file unless $skip;
+ $last = $file;
+ $lastlen = length($file);
+ }
+ return @result;
+}
+
sub file_has_nonascii {
my $fn = shift;
open(my $fh, '<', $fn)
diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh
index e6c3116..1ef1889 100644
--- a/git-sh-i18n.sh
+++ b/git-sh-i18n.sh
@@ -53,6 +53,13 @@ gettext_without_eval_gettext)
git sh-i18n--envsubst "$1"
)
}
+
+ eval_ngettext () {
+ ngettext "$1" "$2" "$3" | (
+ export PATH $(git sh-i18n--envsubst --variables "$2");
+ git sh-i18n--envsubst "$2"
+ )
+ }
;;
poison)
# Emit garbage so that tests that incorrectly rely on translatable
@@ -64,6 +71,10 @@ poison)
eval_gettext () {
printf "%s" "# GETTEXT POISON #"
}
+
+ eval_ngettext () {
+ printf "%s" "# GETTEXT POISON #"
+ }
;;
*)
gettext () {
@@ -76,6 +87,13 @@ poison)
git sh-i18n--envsubst "$1"
)
}
+
+ eval_ngettext () {
+ (test "$3" = 1 && printf "%s" "$1" || printf "%s" "$2") | (
+ export PATH $(git sh-i18n--envsubst --variables "$2");
+ git sh-i18n--envsubst "$2"
+ )
+ }
;;
esac
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 85db5f1..a8a4576 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -2,6 +2,9 @@
# to set up some variables pointing at the normal git directories and
# a few helper shell functions.
+# Source git-sh-i18n for gettext support.
+. git-sh-i18n
+
# Having this variable in your environment would break scripts because
# you would cause "cd" to be taken to unexpected places. If you
# like CDPATH, define it for your interactive shell sessions without
@@ -83,16 +86,16 @@ if test -n "$OPTIONS_SPEC"; then
else
dashless=$(basename -- "$0" | sed -e 's/-/ /')
usage() {
- die "usage: $dashless $USAGE"
+ die "$(eval_gettext "usage: \$dashless \$USAGE")"
}
if [ -z "$LONG_USAGE" ]
then
- LONG_USAGE="usage: $dashless $USAGE"
+ LONG_USAGE="$(eval_gettext "usage: \$dashless \$USAGE")"
else
- LONG_USAGE="usage: $dashless $USAGE
+ LONG_USAGE="$(eval_gettext "usage: \$dashless \$USAGE
-$LONG_USAGE"
+$LONG_USAGE")"
fi
case "$1" in
@@ -160,9 +163,11 @@ git_pager() {
else
GIT_PAGER=cat
fi
- : "${LESS=-FRX}"
- : "${LV=-c}"
- export LESS LV
+ for vardef in @@PAGER_ENV@@
+ do
+ var=${vardef%%=*}
+ eval ": \"\${$vardef}\" && export $var"
+ done
eval "$GIT_PAGER" '"$@"'
}
@@ -182,7 +187,7 @@ is_bare_repository () {
cd_to_toplevel () {
cdup=$(git rev-parse --show-toplevel) &&
cd "$cdup" || {
- echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
+ gettextln "Cannot chdir to \$cdup, the toplevel of the working tree" >&2
exit 1
}
}
@@ -190,13 +195,16 @@ cd_to_toplevel () {
require_work_tree_exists () {
if test "z$(git rev-parse --is-bare-repository)" != zfalse
then
- die "fatal: $0 cannot be used without a working tree."
+ program_name=$0
+ die "$(gettext "fatal: \$program_name cannot be used without a working tree.")"
fi
}
require_work_tree () {
- test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true ||
- die "fatal: $0 cannot be used without a working tree."
+ test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true || {
+ program_name=$0
+ die "$(gettext "fatal: \$program_name cannot be used without a working tree.")"
+ }
}
require_clean_work_tree () {
@@ -206,24 +214,49 @@ require_clean_work_tree () {
if ! git diff-files --quiet --ignore-submodules
then
- echo >&2 "Cannot $1: You have unstaged changes."
+ action=$1
+ case "$action" in
+ rebase)
+ gettextln "Cannot rebase: You have unstaged changes." >&2
+ ;;
+ "rewrite branches")
+ gettextln "Cannot rewrite branches: You have unstaged changes." >&2
+ ;;
+ "pull with rebase")
+ gettextln "Cannot pull with rebase: You have unstaged changes." >&2
+ ;;
+ *)
+ eval_gettextln "Cannot \$action: You have unstaged changes." >&2
+ ;;
+ esac
err=1
fi
if ! git diff-index --cached --quiet --ignore-submodules HEAD --
then
- if [ $err = 0 ]
+ if test $err = 0
then
- echo >&2 "Cannot $1: Your index contains uncommitted changes."
+ action=$1
+ case "$action" in
+ rebase)
+ gettextln "Cannot rebase: Your index contains uncommitted changes." >&2
+ ;;
+ "pull with rebase")
+ gettextln "Cannot pull with rebase: Your index contains uncommitted changes." >&2
+ ;;
+ *)
+ eval_gettextln "Cannot \$action: Your index contains uncommitted changes." >&2
+ ;;
+ esac
else
- echo >&2 "Additionally, your index contains uncommitted changes."
+ gettextln "Additionally, your index contains uncommitted changes." >&2
fi
err=1
fi
- if [ $err = 1 ]
+ if test $err = 1
then
- test -n "$2" && echo >&2 "$2"
+ test -n "$2" && echo "$2" >&2
exit 1
fi
}
@@ -336,12 +369,12 @@ git_dir_init () {
then
test -z "$(git rev-parse --show-cdup)" || {
exit=$?
- echo >&2 "You need to run this command from the toplevel of the working tree."
+ gettextln "You need to run this command from the toplevel of the working tree." >&2
exit $exit
}
fi
test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || {
- echo >&2 "Unable to determine absolute path of git directory"
+ gettextln "Unable to determine absolute path of git directory" >&2
exit 1
}
: "${GIT_OBJECT_DIRECTORY="$(git rev-parse --git-path objects)"}"
diff --git a/git-stash.sh b/git-stash.sh
index c7509e8..22fb8bc 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -15,7 +15,6 @@ SUBDIRECTORY_OK=Yes
OPTIONS_SPEC=
START_DIR=$(pwd)
. git-sh-setup
-. git-sh-i18n
require_work_tree
cd_to_toplevel
diff --git a/git-submodule.sh b/git-submodule.sh
index 7fe8a51..b57f87d 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -9,14 +9,13 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...]
or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
- or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]
+ or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...]
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: $dashless [--quiet] foreach [--recursive] <command>
or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
OPTIONS_SPEC=
SUBDIRECTORY_OK=Yes
. git-sh-setup
-. git-sh-i18n
. git-parse-remote
require_work_tree
wt_prefix=$(git rev-parse --show-prefix)
@@ -50,7 +49,7 @@ die_if_unmatched ()
{
if test "$1" = "#unmatched"
then
- exit 1
+ exit ${2:-1}
fi
}
@@ -240,14 +239,15 @@ Use -f if you really want to add it." >&2
then
if test -z "$force"
then
- echo >&2 "$(eval_gettext "A git directory for '\$sm_name' is found locally with remote(s):")"
+ eval_gettextln >&2 "A git directory for '\$sm_name' is found locally with remote(s):"
GIT_DIR=".git/modules/$sm_name" GIT_WORK_TREE=. git remote -v | grep '(fetch)' | sed -e s,^," ", -e s,' (fetch)',, >&2
- echo >&2 "$(eval_gettext "If you want to reuse this local git directory instead of cloning again from")"
- echo >&2 " $realrepo"
- echo >&2 "$(eval_gettext "use the '--force' option. If the local git directory is not the correct repo")"
- die "$(eval_gettext "or you are unsure what this means choose another name with the '--name' option.")"
+ die "$(eval_gettextln "\
+If you want to reuse this local git directory instead of cloning again from
+ \$realrepo
+use the '--force' option. If the local git directory is not the correct repo
+or you are unsure what this means choose another name with the '--name' option.")"
else
- echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
+ eval_gettextln "Reactivating local git directory for submodule '\$sm_name'."
fi
fi
git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${depth:+"$depth"} || exit
@@ -312,11 +312,11 @@ cmd_foreach()
{
git submodule--helper list --prefix "$wt_prefix" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
if test -e "$sm_path"/.git
then
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
@@ -379,8 +379,6 @@ cmd_init()
#
# Unregister submodules from .git/config and remove their work tree
#
-# $@ = requested paths (use '.' to deinit all submodules)
-#
cmd_deinit()
{
# parse $args after "submodule ... deinit".
@@ -423,11 +421,11 @@ cmd_deinit()
{
git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
@@ -438,8 +436,9 @@ cmd_deinit()
# Protect submodules containing a .git directory
if test -d "$sm_path/.git"
then
- echo >&2 "$(eval_gettext "Submodule work tree '\$displaypath' contains a .git directory")"
- die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
+ die "$(eval_gettext "\
+Submodule work tree '\$displaypath' contains a .git directory
+(use 'rm -rf' if you really want to remove it including all of its history)")"
fi
if test -z "$force"
@@ -480,7 +479,8 @@ fetch_in_submodule () (
'')
git fetch ;;
*)
- git fetch $(get_default_remote) "$2" ;;
+ shift
+ git fetch $(get_default_remote) "$@" ;;
esac
)
@@ -530,6 +530,12 @@ cmd_update()
--checkout)
update="checkout"
;;
+ --recommend-shallow)
+ recommend_shallow="--recommend-shallow"
+ ;;
+ --no-recommend-shallow)
+ recommend_shallow="--no-recommend-shallow"
+ ;;
--depth)
case "$2" in '') usage ;; esac
depth="--depth=$2"
@@ -572,17 +578,17 @@ cmd_update()
${update:+--update "$update"} \
${reference:+--reference "$reference"} \
${depth:+--depth "$depth"} \
+ ${recommend_shallow:+"$recommend_shallow"} \
${jobs:+$jobs} \
- "$@" || echo "#unmatched"
+ "$@" || echo "#unmatched" $?
} | {
err=
while read mode sha1 stage just_cloned sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
- branch=$(get_submodule_config "$name" branch master)
if ! test -z "$update"
then
update_module=$update
@@ -608,16 +614,17 @@ cmd_update()
if test -n "$remote"
then
+ branch=$(git submodule--helper remote-branch "$sm_path")
if test -z "$nofetch"
then
# Fetch remote before determining tracking $sha1
- (sanitize_submodule_env; cd "$sm_path" && git-fetch) ||
+ fetch_in_submodule "$sm_path" $depth ||
die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
fi
remote_name=$(sanitize_submodule_env; cd "$sm_path" && get_default_remote)
sha1=$(sanitize_submodule_env; cd "$sm_path" &&
git rev-parse --verify "${remote_name}/${branch}") ||
- die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")"
+ die "$(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")"
fi
if test "$subsha1" != "$sha1" || test -n "$force"
@@ -634,14 +641,14 @@ cmd_update()
# Run fetch only if $sha1 isn't present or it
# is not reachable from a ref.
is_tip_reachable "$sm_path" "$sha1" ||
- fetch_in_submodule "$sm_path" ||
+ fetch_in_submodule "$sm_path" $depth ||
die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
# Now we tried the usual fetch, but $sha1 may
# not be reachable from any of the refs
is_tip_reachable "$sm_path" "$sha1" ||
- fetch_in_submodule "$sm_path" "$sha1" ||
- die "$(eval_gettext "Fetched in submodule path '\$displaypath', but it did not contain $sha1. Direct fetching of that commit failed.")"
+ fetch_in_submodule "$sm_path" $depth "$sha1" ||
+ die "$(eval_gettext "Fetched in submodule path '\$displaypath', but it did not contain \$sha1. Direct fetching of that commit failed.")"
fi
must_die_on_failure=
@@ -698,7 +705,7 @@ cmd_update()
if test $res -gt 0
then
die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
- if test $res -eq 1
+ if test $res -ne 2
then
err="${err};$die_msg"
continue
@@ -986,11 +993,11 @@ cmd_status()
{
git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
@@ -1067,11 +1074,11 @@ cmd_sync()
cd_to_toplevel
{
git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched"
+ echo "#unmatched" $?
} |
while read mode sha1 stage sm_path
do
- die_if_unmatched "$mode"
+ die_if_unmatched "$mode" "$sha1"
name=$(git submodule--helper name "$sm_path")
url=$(git config -f .gitmodules --get submodule."$name".url)
diff --git a/git-svn.perl b/git-svn.perl
index 05eced0..4d41d22 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -339,7 +339,7 @@ if ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) {
die "failed to open $ENV{GIT_DIR}: $!\n";
$ENV{GIT_DIR} = $1 if <$fh> =~ /^gitdir: (.+)$/;
}
-} else {
+} elsif ($cmd) {
my ($git_dir, $cdup);
git_cmd_try {
$git_dir = command_oneline([qw/rev-parse --git-dir/]);
@@ -356,7 +356,7 @@ if ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) {
my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
-read_git_config(\%opts);
+read_git_config(\%opts) if $ENV{GIT_DIR};
if ($cmd && ($cmd eq 'log' || $cmd eq 'blame')) {
Getopt::Long::Configure('pass_through');
}
@@ -507,7 +507,10 @@ sub init_subdir {
sub cmd_clone {
my ($url, $path) = @_;
- if (!defined $path &&
+ if (!$url) {
+ die "SVN repository location required ",
+ "as a command-line argument\n";
+ } elsif (!defined $path &&
(defined $_trunk || @_branches || @_tags ||
defined $_stdlayout) &&
$url !~ m#^[a-z\+]+://#) {
diff --git a/git.c b/git.c
index 968a8a4..0f1937f 100644
--- a/git.c
+++ b/git.c
@@ -609,48 +609,15 @@ 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)
+int cmd_main(int argc, const char **argv)
{
- const char **argv = (const char **) av;
const char *cmd;
int done_help = 0;
- cmd = git_extract_argv0_path(argv[0]);
+ cmd = argv[0];
if (!cmd)
cmd = "git-help";
- /*
- * Always open file descriptors 0/1/2 to avoid clobbering files
- * in die(). It also avoids messing up when the pipes are dup'ed
- * onto stdin/stdout/stderr in the child processes we spawn.
- */
- sanitize_stdfds();
-
- restore_sigpipe_to_default();
-
- git_setup_gettext();
-
trace_command_performance(argv);
/*
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2fddf75..33d701d 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2090,7 +2090,7 @@ sub format_ref_marker {
-href => href(
action=>$dest_action,
hash=>$dest
- )}, $name);
+ )}, esc_html($name));
$markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
$link . "</span>";
diff --git a/gpg-interface.c b/gpg-interface.c
index c4b1e8c..08356f9 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -3,6 +3,7 @@
#include "strbuf.h"
#include "gpg-interface.h"
#include "sigchain.h"
+#include "tempfile.h"
static char *configured_signing_key;
static const char *gpg_program = "gpg";
@@ -150,42 +151,30 @@ const char *get_signing_key(void)
int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
{
struct child_process gpg = CHILD_PROCESS_INIT;
- const char *args[4];
- ssize_t len;
+ int ret;
size_t i, j, bottom;
+ struct strbuf gpg_status = STRBUF_INIT;
- gpg.argv = args;
- gpg.in = -1;
- gpg.out = -1;
- args[0] = gpg_program;
- args[1] = "-bsau";
- args[2] = signing_key;
- args[3] = NULL;
+ argv_array_pushl(&gpg.args,
+ gpg_program,
+ "--status-fd=2",
+ "-bsau", signing_key,
+ NULL);
- if (start_command(&gpg))
- return error(_("could not run gpg."));
+ bottom = signature->len;
/*
* When the username signingkey is bad, program could be terminated
* because gpg exits without reading and then write gets SIGPIPE.
*/
sigchain_push(SIGPIPE, SIG_IGN);
-
- if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
- close(gpg.in);
- close(gpg.out);
- finish_command(&gpg);
- return error(_("gpg did not accept the data"));
- }
- close(gpg.in);
-
- bottom = signature->len;
- len = strbuf_read(signature, gpg.out, 1024);
- close(gpg.out);
-
+ ret = pipe_command(&gpg, buffer->buf, buffer->len,
+ signature, 1024, &gpg_status, 0);
sigchain_pop(SIGPIPE);
- if (finish_command(&gpg) || !len || len < 0)
+ ret |= !strstr(gpg_status.buf, "\n[GNUPG:] SIG_CREATED ");
+ strbuf_release(&gpg_status);
+ if (ret)
return error(_("gpg failed to sign the data"));
/* Strip CR from the line endings, in case we are on Windows. */
@@ -210,50 +199,38 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
struct strbuf *gpg_output, struct strbuf *gpg_status)
{
struct child_process gpg = CHILD_PROCESS_INIT;
- const char *args_gpg[] = {NULL, "--status-fd=1", "--verify", "FILE", "-", NULL};
- char path[PATH_MAX];
+ static struct tempfile temp;
int fd, ret;
struct strbuf buf = STRBUF_INIT;
- struct strbuf *pbuf = &buf;
- args_gpg[0] = gpg_program;
- fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
+ fd = mks_tempfile_t(&temp, ".git_vtag_tmpXXXXXX");
if (fd < 0)
- return error_errno(_("could not create temporary file '%s'"), path);
- if (write_in_full(fd, signature, signature_size) < 0)
- return error_errno(_("failed writing detached signature to '%s'"), path);
+ return error_errno(_("could not create temporary file"));
+ if (write_in_full(fd, signature, signature_size) < 0) {
+ error_errno(_("failed writing detached signature to '%s'"),
+ temp.filename.buf);
+ delete_tempfile(&temp);
+ return -1;
+ }
close(fd);
- gpg.argv = args_gpg;
- gpg.in = -1;
- gpg.out = -1;
- if (gpg_output)
- gpg.err = -1;
- args_gpg[3] = path;
- if (start_command(&gpg)) {
- unlink(path);
- return error(_("could not run gpg."));
- }
+ argv_array_pushl(&gpg.args,
+ gpg_program,
+ "--status-fd=1",
+ "--verify", temp.filename.buf, "-",
+ NULL);
- sigchain_push(SIGPIPE, SIG_IGN);
- write_in_full(gpg.in, payload, payload_size);
- close(gpg.in);
+ if (!gpg_status)
+ gpg_status = &buf;
- if (gpg_output) {
- strbuf_read(gpg_output, gpg.err, 0);
- close(gpg.err);
- }
- if (gpg_status)
- pbuf = gpg_status;
- strbuf_read(pbuf, gpg.out, 0);
- close(gpg.out);
-
- ret = finish_command(&gpg);
+ sigchain_push(SIGPIPE, SIG_IGN);
+ ret = pipe_command(&gpg, payload, payload_size,
+ gpg_status, 0, gpg_output, 0);
sigchain_pop(SIGPIPE);
- unlink_or_warn(path);
+ delete_tempfile(&temp);
- ret |= !strstr(pbuf->buf, "\n[GNUPG:] GOODSIG ");
+ ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
strbuf_release(&buf); /* no matter it was used or not */
return ret;
diff --git a/graph.c b/graph.c
index ad766fa..dd17201 100644
--- a/graph.c
+++ b/graph.c
@@ -17,8 +17,8 @@
static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
/*
- * Print a strbuf to stdout. If the graph is non-NULL, all lines but the
- * first will be prefixed with the graph output.
+ * Print a strbuf. If the graph is non-NULL, all lines but the first will be
+ * prefixed with the graph output.
*
* If the strbuf ends with a newline, the output will end after this
* newline. A new graph line will not be printed after the final newline.
@@ -1200,9 +1200,10 @@ void graph_show_commit(struct git_graph *graph)
while (!shown_commit_line && !graph_is_commit_finished(graph)) {
shown_commit_line = graph_next_line(graph, &msgbuf);
- fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+ fwrite(msgbuf.buf, sizeof(char), msgbuf.len,
+ graph->revs->diffopt.file);
if (!shown_commit_line)
- putchar('\n');
+ putc('\n', graph->revs->diffopt.file);
strbuf_setlen(&msgbuf, 0);
}
@@ -1217,7 +1218,7 @@ void graph_show_oneline(struct git_graph *graph)
return;
graph_next_line(graph, &msgbuf);
- fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+ fwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);
strbuf_release(&msgbuf);
}
@@ -1229,7 +1230,7 @@ void graph_show_padding(struct git_graph *graph)
return;
graph_padding_line(graph, &msgbuf);
- fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+ fwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);
strbuf_release(&msgbuf);
}
@@ -1246,12 +1247,13 @@ int graph_show_remainder(struct git_graph *graph)
for (;;) {
graph_next_line(graph, &msgbuf);
- fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+ fwrite(msgbuf.buf, sizeof(char), msgbuf.len,
+ graph->revs->diffopt.file);
strbuf_setlen(&msgbuf, 0);
shown = 1;
if (!graph_is_commit_finished(graph))
- putchar('\n');
+ putc('\n', graph->revs->diffopt.file);
else
break;
}
@@ -1266,7 +1268,8 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
char *p;
if (!graph) {
- fwrite(sb->buf, sizeof(char), sb->len, stdout);
+ fwrite(sb->buf, sizeof(char), sb->len,
+ graph->revs->diffopt.file);
return;
}
@@ -1284,7 +1287,7 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
} else {
len = (sb->buf + sb->len) - p;
}
- fwrite(p, sizeof(char), len, stdout);
+ fwrite(p, sizeof(char), len, graph->revs->diffopt.file);
if (next_p && *next_p != '\0')
graph_show_oneline(graph);
p = next_p;
@@ -1304,7 +1307,8 @@ void graph_show_commit_msg(struct git_graph *graph,
* CMIT_FMT_USERFORMAT are already missing a terminating
* newline. All of the other formats should have it.
*/
- fwrite(sb->buf, sizeof(char), sb->len, stdout);
+ fwrite(sb->buf, sizeof(char), sb->len,
+ graph->revs->diffopt.file);
return;
}
@@ -1325,7 +1329,7 @@ void graph_show_commit_msg(struct git_graph *graph,
* new line.
*/
if (!newline_terminated)
- putchar('\n');
+ putc('\n', graph->revs->diffopt.file);
graph_show_remainder(graph);
@@ -1333,6 +1337,6 @@ void graph_show_commit_msg(struct git_graph *graph,
* If sb ends with a newline, our output should too.
*/
if (newline_terminated)
- putchar('\n');
+ putc('\n', graph->revs->diffopt.file);
}
}
diff --git a/grep.c b/grep.c
index 394c856..d7d00b8 100644
--- a/grep.c
+++ b/grep.c
@@ -163,17 +163,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
color_set(opt->color_sep, def->color_sep);
}
-void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
-{
- if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
- grep_set_pattern_type_option(pattern_type, opt);
- else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
- grep_set_pattern_type_option(opt->pattern_type_option, opt);
- else if (opt->extended_regexp_option)
- grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
-}
-
-void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
{
switch (pattern_type) {
case GREP_PATTERN_TYPE_UNSPECIFIED:
@@ -205,6 +195,16 @@ void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct gr
}
}
+void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+{
+ if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
+ grep_set_pattern_type_option(pattern_type, opt);
+ else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
+ grep_set_pattern_type_option(opt->pattern_type_option, opt);
+ else if (opt->extended_regexp_option)
+ grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
+}
+
static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
const char *origin, int no,
enum grep_pat_token t,
@@ -693,10 +693,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
for (p = opt->header_list; p; p = p->next) {
if (p->token != GREP_PATTERN_HEAD)
- die("bug: a non-header pattern in grep header list.");
+ die("BUG: a non-header pattern in grep header list.");
if (p->field < GREP_HEADER_FIELD_MIN ||
GREP_HEADER_FIELD_MAX <= p->field)
- die("bug: unknown header field %d", p->field);
+ die("BUG: unknown header field %d", p->field);
compile_regexp(p, opt);
}
@@ -709,7 +709,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
h = compile_pattern_atom(&pp);
if (!h || pp != p->next)
- die("bug: malformed header expr");
+ die("BUG: malformed header expr");
if (!header_group[p->field]) {
header_group[p->field] = h;
continue;
@@ -1514,7 +1514,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
case GREP_BINARY_TEXT:
break;
default:
- die("bug: unknown binary handling mode");
+ die("BUG: unknown binary handling mode");
}
}
diff --git a/grep.h b/grep.h
index cee4357..5856a23 100644
--- a/grep.h
+++ b/grep.h
@@ -145,7 +145,6 @@ struct grep_opt {
extern void init_grep_defaults(void);
extern int grep_config(const char *var, const char *value, void *);
extern void grep_init(struct grep_opt *, const char *prefix);
-void grep_set_pattern_type_option(enum grep_pattern_type, struct grep_opt *opt);
void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
diff --git a/hex.c b/hex.c
index 0519f85..9619b67 100644
--- a/hex.c
+++ b/hex.c
@@ -77,6 +77,11 @@ char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
return buffer;
}
+char *oid_to_hex_r(char *buffer, const struct object_id *oid)
+{
+ return sha1_to_hex_r(buffer, oid->hash);
+}
+
char *sha1_to_hex(const unsigned char *sha1)
{
static int bufno;
diff --git a/http-backend.c b/http-backend.c
index 2148814..0d59499 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -632,7 +632,7 @@ static struct service_cmd {
{"POST", "/git-receive-pack$", service_rpc}
};
-int main(int argc, char **argv)
+int cmd_main(int argc, const char **argv)
{
char *method = getenv("REQUEST_METHOD");
char *dir;
@@ -640,9 +640,6 @@ int main(int argc, char **argv)
char *cmd_arg = NULL;
int i;
- git_setup_gettext();
-
- git_extract_argv0_path(argv[0]);
set_die_routine(die_webcgi);
set_die_is_recursing_routine(die_webcgi_recursing);
diff --git a/http-fetch.c b/http-fetch.c
index ba3ea10..3b556d6 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -6,7 +6,7 @@
static const ch