Merge branch 'jt/namespaced-ls-refs-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 5 Feb 2019 22:26:15 +0000 (14:26 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 5 Feb 2019 22:26:15 +0000 (14:26 -0800)
Fix namespace support in protocol v2.

* jt/namespaced-ls-refs-fix:
  ls-refs: filter refs using namespace-stripped name

205 files changed:
.gitignore
Documentation/Makefile
Documentation/RelNotes/2.21.0.txt
Documentation/config/rebase.txt
Documentation/config/worktree.txt
Documentation/diff-options.txt
Documentation/git-cat-file.txt
Documentation/git-column.txt
Documentation/git-for-each-ref.txt
Documentation/git-gc.txt
Documentation/git-init.txt
Documentation/git-quiltimport.txt
Documentation/git-rebase.txt
Documentation/git-show-ref.txt
Documentation/git-status.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-oid-array.txt
Documentation/technical/commit-graph-format.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/partial-clone.txt
Documentation/technical/protocol-v2.txt
Makefile
banned.h
bisect.c
builtin/add.c
builtin/archive.c
builtin/clone.c
builtin/commit-graph.c
builtin/commit.c
builtin/config.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/init-db.c
builtin/log.c
builtin/ls-files.c
builtin/merge-recursive.c
builtin/merge-tree.c
builtin/pack-objects.c
builtin/push.c
builtin/rebase--interactive.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/repack.c
builtin/rev-list.c
builtin/send-pack.c
builtin/stripspace.c
builtin/submodule--helper.c
builtin/worktree.c
cache-tree.c
cache.h
commit-graph.c
commit-graph.h
commit-reach.c
commit-reach.h
commit.c
commit.h
compat/mingw.c
compat/obstack.c
compat/obstack.h
compat/regex/regcomp.c
config.mak.dev
config.mak.uname
connect.c
contrib/coccinelle/object_id.cocci
contrib/coccinelle/the_repository.pending.cocci [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/completion/git-completion.zsh
contrib/hooks/multimail/CHANGES
contrib/hooks/multimail/CONTRIBUTING.rst
contrib/hooks/multimail/README.Git
contrib/hooks/multimail/README.rst [moved from contrib/hooks/multimail/README with 95% similarity]
contrib/hooks/multimail/doc/gitolite.rst
contrib/hooks/multimail/git_multimail.py
contrib/hooks/multimail/migrate-mailhook-config
contrib/hooks/multimail/post-receive.example
convert.c
credential-cache--daemon.c
delta-islands.c
diff.c
environment.c
fetch-pack.c
fsck.c
fuzz-commit-graph.c [new file with mode: 0644]
git-compat-util.h
git-legacy-rebase.sh
git-p4.py
git-quiltimport.sh
git-rebase--common.sh
git.c
hash.h
hex.c
http-push.c
http.c
http.h
imap-send.c
list-objects-filter-options.c
list-objects-filter-options.h
list-objects-filter.c
list-objects.c
log-tree.c
ls-refs.c
match-trees.c
notes.c
object-store.h
object.c
packfile.c
packfile.h
path.h
pkt-line.c
pkt-line.h
pretty.c
pretty.h
read-cache.c
ref-filter.c
remote-curl.c
remote.c
repository.c
repository.h
revision.c
send-pack.c
sequencer.c
sequencer.h
serve.c
setup.c
sha1-array.c
sha1-array.h
sha1-file.c
sha1-name.c
sha256/block/sha256.c [new file with mode: 0644]
sha256/block/sha256.h [new file with mode: 0644]
sha256/gcrypt.h [new file with mode: 0644]
shallow.c
sideband.c
sideband.h
streaming.c
string-list.c
submodule.c
submodule.h
t/README
t/helper/test-hash-speed.c [new file with mode: 0644]
t/helper/test-hash.c [new file with mode: 0644]
t/helper/test-ref-store.c
t/helper/test-repository.c
t/helper/test-sha1.c
t/helper/test-sha256.c [new file with mode: 0644]
t/helper/test-sigchain.c
t/helper/test-submodule-nested-repo-config.c
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-git-daemon.sh
t/lib-git-p4.sh
t/lib-git-svn.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-submodule-update.sh
t/t0015-hash.sh [new file with mode: 0755]
t/t0025-crlf-renormalize.sh
t/t0030-stripspace.sh
t/t0061-run-command.sh
t/t0410-partial-clone.sh
t/t2028-worktree-move.sh
t/t3418-rebase-continue.sh
t/t3502-cherry-pick-merge.sh
t/t3506-cherry-pick-ff.sh
t/t3510-cherry-pick-sequence.sh
t/t4015-diff-whitespace.sh
t/t4205-log-pretty-formats.sh
t/t5318-commit-graph.sh
t/t5403-post-checkout-hook.sh
t/t5500-fetch-pack.sh
t/t5512-ls-remote.sh
t/t5526-fetch-submodules.sh
t/t5537-fetch-shallow.sh
t/t5570-git-daemon.sh
t/t5580-clone-push-unc.sh
t/t5581-http-curl-verbose.sh [new file with mode: 0755]
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5703-upload-pack-ref-in-want.sh
t/t6006-rev-list-format.sh
t/t6042-merge-rename-corner-cases.sh
t/t6112-rev-list-filters-objects.sh
t/t6300-for-each-ref.sh
t/t7400-submodule-basic.sh
t/t7412-submodule-absorbgitdirs.sh
t/t9807-git-p4-submit.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
transport-helper.c
transport.c
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
unpack-trees.c
upload-pack.c
url.c
userdiff.c
walker.c

index 0d77ea5..8bcf153 100644 (file)
@@ -1,3 +1,4 @@
+/fuzz-commit-graph
 /fuzz_corpora
 /fuzz-pack-headers
 /fuzz-pack-idx
index d5d936e..26a2342 100644 (file)
@@ -36,14 +36,19 @@ MAN7_TXT += gittutorial-2.txt
 MAN7_TXT += gittutorial.txt
 MAN7_TXT += gitworkflows.txt
 
+ifdef MAN_FILTER
+MAN_TXT = $(filter $(MAN_FILTER),$(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT))
+else
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
+MAN_FILTER = $(MAN_TXT)
+endif
+
 MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT))
 MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
 GIT_MAN_REF = master
 
 OBSOLETE_HTML += everyday.html
 OBSOLETE_HTML += git-remote-helpers.html
-DOC_HTML = $(MAN_HTML) $(OBSOLETE_HTML)
 
 ARTICLES += howto-index
 ARTICLES += git-tools
@@ -89,11 +94,13 @@ TECH_DOCS += technical/trivial-merge
 SP_ARTICLES += $(TECH_DOCS)
 SP_ARTICLES += technical/api-index
 
-DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
+ARTICLES_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
+HTML_FILTER ?= $(ARTICLES_HTML) $(OBSOLETE_HTML)
+DOC_HTML = $(MAN_HTML) $(filter $(HTML_FILTER),$(ARTICLES_HTML) $(OBSOLETE_HTML))
 
-DOC_MAN1 = $(patsubst %.txt,%.1,$(MAN1_TXT))
-DOC_MAN5 = $(patsubst %.txt,%.5,$(MAN5_TXT))
-DOC_MAN7 = $(patsubst %.txt,%.7,$(MAN7_TXT))
+DOC_MAN1 = $(patsubst %.txt,%.1,$(filter $(MAN_FILTER),$(MAN1_TXT)))
+DOC_MAN5 = $(patsubst %.txt,%.5,$(filter $(MAN_FILTER),$(MAN5_TXT)))
+DOC_MAN7 = $(patsubst %.txt,%.7,$(filter $(MAN_FILTER),$(MAN7_TXT)))
 
 prefix ?= $(HOME)
 bindir ?= $(prefix)/bin
@@ -457,4 +464,9 @@ print-man1:
 lint-docs::
        $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl
 
+ifeq ($(wildcard po/Makefile),po/Makefile)
+doc-l10n install-l10n::
+       $(MAKE) -C po $@
+endif
+
 .PHONY: FORCE
index 71e437a..4596d9e 100644 (file)
@@ -29,6 +29,36 @@ UI, Workflows & Features
    which gives it the same degree of noisy-ness as the case in which
    the command checks out a branch.
 
+ * "git quiltimport" learned "--keep-non-patch" option.
+
+ * "git worktree remove" and "git worktree move" refused to work when
+   there is a submodule involved.  This has been loosened to ignore
+   uninitialized submodules.
+
+ * "git cherry-pick -m1" was forbidden when picking a non-merge
+   commit, even though there _is_ parent number 1 for such a commit.
+   This was done to avoid mistakes back when "cherry-pick" was about
+   picking a single commit, but is no longer useful with "cherry-pick"
+   that can pick a range of commits.  Now the "-m$num" option is
+   allowed when picking any commit, as long as $num names an existing
+   parent of the commit.
+
+ * Update "git multimail" from the upstream.
+
+ * "git p4" update.
+
+ * The "--format=<placeholder>" option of for-each-ref, branch and tag
+   learned to show a few more traits of objects that can be learned by
+   the object_info API.
+
+ * "git rebase -i" learned to re-execute a command given with 'exec'
+   to run after it failed the last time.
+
+ * "git diff --color-moved-ws" updates.
+
+ * Custom userformat "log --format" learned %S atom that stands for
+   the tip the traversal reached the commit from, i.e. --source.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -38,6 +68,39 @@ Performance, Internal Implementation, Development Support etc.
  * More codepaths become aware of working with in-core repository
    instance other than the default "the_repository".
 
+ * The "strncat()" function is now among the banned functions.
+
+ * Portability updates for the HPE NonStop platform.
+
+ * Earlier we added "-Wformat-security" to developer builds, assuming
+   that "-Wall" (which includes "-Wformat" which in turn is required
+   to use "-Wformat-security") is always in effect.  This is not true
+   when config.mak.autogen is in use, unfortunately.  This has been
+   fixed by unconditionally adding "-Wall" to developer builds.
+
+ * The loose object cache used to optimize existence look-up has been
+   updated.
+
+ * Flaky tests can now be repeatedly run under load with the
+   "--stress" option.
+   (merge fb7d1e3ac8 sg/stress-test later to maint).
+
+ * Documentation/Makefile is getting prepared for manpage
+   localization.
+
+ * "git fetch-pack" now can talk the version 2 protocol.
+
+ * sha-256 hash has been added and plumbed through the code to allow
+   building Git with the "NewHash".
+
+ * Debugging help for http transport.
+
+ * "git fetch --deepen=<more>" has been corrected to work over v2
+   protocol.
+
+ * The code to walk tree objects has been taught that we may be
+   working with object names that are not computed with SHA-1.
+
 
 Fixes since v2.20
 -----------------
@@ -97,6 +160,67 @@ Fixes since v2.20
    the --text option is in effect, that is).
    (merge e0e7cb8080 tb/log-G-binary later to maint).
 
+ * "git submodule update" ought to use a single job unless asked, but
+   by mistake used multiple jobs, which has been fixed.
+   (merge e3a9d1aca9 sb/submodule-fetchjobs-default-to-one later to maint).
+
+ * "git stripspace" should be usable outside a git repository, but
+   under the "-s" or "-c" mode, it didn't.
+   (merge 957da75802 jn/stripspace-wo-repository later to maint).
+
+ * Some of the documentation pages formatted incorrectly with
+   Asciidoctor, which have been fixed.
+   (merge b62eb1d2f4 ma/asciidoctor later to maint).
+
+ * The core.worktree setting in a submodule repository should not be
+   pointing at a directory when the submodule loses its working tree
+   (e.g. getting deinit'ed), but the code did not properly maintain
+   this invariant.
+
+ * With zsh, "git cmd path<TAB>" was completed to "git cmd path name"
+   when the completed path has a special character like SP in it,
+   without any attempt to keep "path name" a single filename.  This
+   has been fixed to complete it to "git cmd path\ name" just like
+   Bash completion does.
+
+ * The test suite tried to see if it is run under bash, but the check
+   itself failed under some other implementations of shell (notably
+   under NetBSD).  This has been corrected.
+   (merge 54ea72f09c sg/test-bash-version-fix later to maint).
+
+ * "git gc" and "git repack" did not close the open packfiles that
+   they found unneeded before removing them, which didn't work on a
+   platform incapable of removing an open file.  This has been
+   corrected.
+   (merge 5bdece0d70 js/gc-repack-close-before-remove later to maint).
+
+ * The code to drive GIT_EXTERNAL_DIFF command relied on the string
+   returned from getenv() to be non-volatile, which is not true, that
+   has been corrected.
+   (merge 6776a84dae kg/external-diff-save-env later to maint).
+
+ * There were many places the code relied on the string returned from
+   getenv() to be non-volatile, which is not true, that have been
+   corrected.
+   (merge 0da0e9268b jk/save-getenv-result later to maint).
+
+ * The v2 upload-pack protocol implementation failed to honor
+   hidden-ref configuration, which has been corrected.
+   (merge e20b4192a3 jk/proto-v2-hidden-refs-fix later to maint).
+
+ * "git fetch --recurse-submodules" may not fetch the necessary commit
+   that is bound to the superproject, which is getting corrected.
+   (merge be76c21282 sb/submodule-recursive-fetch-gets-the-tip later to maint).
+
+ * "git rebase" internally runs "checkout" to switch between branches,
+   and the command used to call the post-checkout hook, but the
+   reimplementation stopped doing so, which is getting fixed.
+
+ * "git add -e" got confused when the change it wants to let the user
+   edit is smaller than the previous change that was left over in a
+   temporary file.
+   (merge fa6f225e01 js/add-e-clear-patch-before-stating later to maint).
+
  * Code cleanup, docfix, build fix, etc.
    (merge 89ba9a79ae hb/t0061-dot-in-path-fix later to maint).
    (merge d173e799ea sb/diff-color-moved-config-option-fixup later to maint).
@@ -106,3 +230,13 @@ Fixes since v2.20
    (merge 112ea42663 fd/gitweb-snapshot-conf-doc-fix later to maint).
    (merge 1cadad6f65 tb/use-common-win32-pathfuncs-on-cygwin later to maint).
    (merge 57e9dcaa65 km/rebase-doc-typofix later to maint).
+   (merge b8b4cb27e6 ds/gc-doc-typofix later to maint).
+   (merge 3b3357626e nd/style-opening-brace later to maint).
+   (merge b4583d5595 es/doc-worktree-guessremote-config later to maint).
+   (merge cce99cd8c6 ds/commit-graph-assert-missing-parents later to maint).
+   (merge 0650614982 cy/completion-typofix later to maint).
+   (merge 6881925ef5 rs/sha1-file-close-mapped-file-on-error later to maint).
+   (merge bd8d6f0def en/show-ref-doc-fix later to maint).
+   (merge 1747125e2c cc/parial-clone-doc-typofix later to maint).
+   (merge e01378753d cc/fetch-error-message-fix later to maint).
+   (merge 54e8c11215 jk/remote-insteadof-cleanup later to maint).
index f079bf6..331d250 100644 (file)
@@ -64,3 +64,8 @@ instead of:
 -------------------------------------------
 +
 Defaults to false.
+
+rebase.rescheduleFailedExec::
+       Automatically reschedule `exec` commands that failed. This only makes
+       sense in interactive mode (or when an `--exec` option was provided).
+       This is the same as specifying the `--reschedule-failed-exec` option.
index b853798..048e349 100644 (file)
@@ -1,6 +1,6 @@
 worktree.guessRemote::
-       With `add`, if no branch argument, and neither of `-b` nor
-       `-B` nor `--detach` are given, the command defaults to
+       If no branch is specified and neither `-b` nor `-B` nor
+       `--detach` is used, then `git worktree add` defaults to
        creating a new branch from HEAD.  If `worktree.guessRemote` is
        set to true, `worktree add` tries to find a remote-tracking
        branch whose name uniquely matches the new branch name.  If
index b94d332..554a340 100644 (file)
@@ -293,8 +293,12 @@ dimmed-zebra::
        `dimmed_zebra` is a deprecated synonym.
 --
 
+--no-color-moved::
+       Turn off move detection. This can be used to override configuration
+       settings. It is the same as `--color-moved=no`.
+
 --color-moved-ws=<modes>::
-       This configures how white spaces are ignored when performing the
+       This configures how whitespace is ignored when performing the
        move detection for `--color-moved`.
 ifdef::git-diff[]
        It can be set by the `diff.colorMovedWS` configuration setting.
@@ -302,6 +306,8 @@ endif::git-diff[]
        These modes can be given as a comma separated list:
 +
 --
+no::
+       Do not ignore whitespace when performing move detection.
 ignore-space-at-eol::
        Ignore changes in whitespace at EOL.
 ignore-space-change::
@@ -312,12 +318,17 @@ ignore-all-space::
        Ignore whitespace when comparing lines. This ignores differences
        even if one line has whitespace where the other line has none.
 allow-indentation-change::
-       Initially ignore any white spaces in the move detection, then
+       Initially ignore any whitespace in the move detection, then
        group the moved code blocks only into a block if the change in
        whitespace is the same per line. This is incompatible with the
        other modes.
 --
 
+--no-color-moved-ws::
+       Do not ignore whitespace when performing move detection. This can be
+       used to override configuration settings. It is the same as
+       `--color-moved-ws=no`.
+
 --word-diff[=<mode>]::
        Show a word diff, using the <mode> to delimit changed words.
        By default, words are delimited by whitespace; see
index 7401333..9a2e9cd 100644 (file)
@@ -23,8 +23,8 @@ In the second form, a list of objects (separated by linefeeds) is provided on
 stdin, and the SHA-1, type, and size of each object is printed on stdout. The
 output format can be overridden using the optional `<format>` argument. If
 either `--textconv` or `--filters` was specified, the input is expected to
-list the object names followed by the path name, separated by a single white
-space, so that the appropriate drivers can be determined.
+list the object names followed by the path name, separated by a single
+whitespace, so that the appropriate drivers can be determined.
 
 OPTIONS
 -------
@@ -79,7 +79,7 @@ OPTIONS
        Print object information and contents for each object provided
        on stdin.  May not be combined with any other options or arguments
        except `--textconv` or `--filters`, in which case the input lines
-       also need to specify the path, separated by white space.  See the
+       also need to specify the path, separated by whitespace.  See the
        section `BATCH OUTPUT` below for details.
 
 --batch-check::
@@ -87,7 +87,7 @@ OPTIONS
        Print object information for each object provided on stdin.  May
        not be combined with any other options or arguments except
        `--textconv` or `--filters`, in which case the input lines also
-       need to specify the path, separated by white space.  See the
+       need to specify the path, separated by whitespace.  See the
        section `BATCH OUTPUT` below for details.
 
 --batch-all-objects::
index 763afab..f58e9c4 100644 (file)
@@ -47,7 +47,7 @@ OPTIONS
        The number of spaces between columns. One space by default.
 
 EXAMPLES
-------
+--------
 
 Format data by columns:
 ------------
index 901faef..774cecc 100644 (file)
@@ -128,13 +128,18 @@ objecttype::
 
 objectsize::
        The size of the object (the same as 'git cat-file -s' reports).
-
+       Append `:disk` to get the size, in bytes, that the object takes up on
+       disk. See the note about on-disk sizes in the `CAVEATS` section below.
 objectname::
        The object name (aka SHA-1).
        For a non-ambiguous abbreviation of the object name append `:short`.
        For an abbreviation of the object name with desired length append
        `:short=<length>`, where the minimum length is MINIMUM_ABBREV. The
        length may be exceeded to ensure unique object names.
+deltabase::
+       This expands to the object name of the delta base for the
+       given object, if it is stored as a delta.  Otherwise it
+       expands to the null object name (all zeroes).
 
 upstream::
        The name of a local ref which can be considered ``upstream''
@@ -361,6 +366,20 @@ This prints the authorname, if present.
 git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Authored by: %(authorname)%(end)"
 ------------
 
+CAVEATS
+-------
+
+Note that the sizes of objects on disk are reported accurately, but care
+should be taken in drawing conclusions about which refs or objects are
+responsible for disk usage. The size of a packed non-delta object may be
+much larger than the size of objects which delta against it, but the
+choice of which object is the base and which is the delta is arbitrary
+and is subject to change during a repack.
+
+Note also that multiple copies of an object may be present in the object
+database; in this case, it is undefined which copy's size or delta base
+will be reported.
+
 SEE ALSO
 --------
 linkgit:git-show-ref[1]
index c20ee6c..a744249 100644 (file)
@@ -137,7 +137,7 @@ The optional configuration variable `gc.packRefs` determines if
 it within all non-bare repos or it can be set to a boolean value.
 This defaults to true.
 
-The optional configuration variable `gc.commitGraph` determines if
+The optional configuration variable `gc.writeCommitGraph` determines if
 'git gc' should run 'git commit-graph write'. This can be set to a
 boolean value. This defaults to false.
 
index 3c5a67f..057076c 100644 (file)
@@ -38,8 +38,6 @@ the repository to another place if --separate-git-dir is given).
 OPTIONS
 -------
 
---
-
 -q::
 --quiet::
 
@@ -111,8 +109,6 @@ into it.
 If you provide a 'directory', the command is run inside it. If this directory
 does not exist, it will be created.
 
---
-
 TEMPLATE DIRECTORY
 ------------------
 
index 8cf952b..70562dc 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git quiltimport' [--dry-run | -n] [--author <author>] [--patches <dir>]
-               [--series <file>]
+               [--series <file>] [--keep-non-patch]
 
 
 DESCRIPTION
@@ -56,6 +56,9 @@ The default for the series file is <patches>/series
 or the value of the `$QUILT_SERIES` environment
 variable.
 
+--keep-non-patch::
+       Pass `-b` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index d284155..4dd5853 100644 (file)
@@ -462,6 +462,12 @@ without an explicit `--interactive`.
 +
 See also INCOMPATIBLE OPTIONS below.
 
+-y <cmd>::
+       This is the same as passing `--reschedule-failed-exec` before
+       `-x <cmd>`, i.e. it appends the specified `exec` command and
+       turns on the mode where failed `exec` commands are automatically
+       rescheduled.
+
 --root::
        Rebase all commits reachable from <branch>, instead of
        limiting them with an <upstream>.  This allows you to rebase
@@ -501,6 +507,11 @@ See also INCOMPATIBLE OPTIONS below.
        with care: the final stash application after a successful
        rebase might result in non-trivial conflicts.
 
+--reschedule-failed-exec::
+--no-reschedule-failed-exec::
+       Automatically reschedule `exec` commands that failed. This only makes
+       sense in interactive mode (or when an `--exec` option was provided).
+
 INCOMPATIBLE OPTIONS
 --------------------
 
index d28e615..ab4d271 100644 (file)
@@ -37,8 +37,8 @@ OPTIONS
 
        Show the HEAD reference, even if it would normally be filtered out.
 
---tags::
 --heads::
+--tags::
 
        Limit to "refs/heads" and "refs/tags", respectively.  These options
        are not mutually exclusive; when given both, references stored in
index d9f422d..861d821 100644 (file)
@@ -197,31 +197,33 @@ codes can be interpreted as follows:
 Ignored files are not listed, unless `--ignored` option is in effect,
 in which case `XY` are `!!`.
 
-    X          Y     Meaning
-    -------------------------------------------------
-            [AMD]   not updated
-    M        [ MD]   updated in index
-    A        [ MD]   added to index
-    D                deleted from index
-    R        [ MD]   renamed in index
-    C        [ MD]   copied in index
-    [MARC]           index and work tree matches
-    [ MARC]     M    work tree changed since index
-    [ MARC]     D    deleted in work tree
-    [ D]        R    renamed in work tree
-    [ D]        C    copied in work tree
-    -------------------------------------------------
-    D           D    unmerged, both deleted
-    A           U    unmerged, added by us
-    U           D    unmerged, deleted by them
-    U           A    unmerged, added by them
-    D           U    unmerged, deleted by us
-    A           A    unmerged, both added
-    U           U    unmerged, both modified
-    -------------------------------------------------
-    ?           ?    untracked
-    !           !    ignored
-    -------------------------------------------------
+....
+X          Y     Meaning
+-------------------------------------------------
+        [AMD]   not updated
+M        [ MD]   updated in index
+A        [ MD]   added to index
+D                deleted from index
+R        [ MD]   renamed in index
+C        [ MD]   copied in index
+[MARC]           index and work tree matches
+[ MARC]     M    work tree changed since index
+[ MARC]     D    deleted in work tree
+[ D]        R    renamed in work tree
+[ D]        C    copied in work tree
+-------------------------------------------------
+D           D    unmerged, both deleted
+A           U    unmerged, added by us
+U           D    unmerged, deleted by them
+U           A    unmerged, added by them
+D           U    unmerged, deleted by us
+A           A    unmerged, both added
+U           U    unmerged, both modified
+-------------------------------------------------
+?           ?    untracked
+!           !    ignored
+-------------------------------------------------
+....
 
 Submodules have more state and instead report
                M    the submodule has a different HEAD than
@@ -281,14 +283,16 @@ don't recognize.
 If `--branch` is given, a series of header lines are printed with
 information about the current branch.
 
-    Line                                     Notes
-    ------------------------------------------------------------
-    # branch.oid <commit> | (initial)        Current commit.
-    # branch.head <branch> | (detached)      Current branch.
-    # branch.upstream <upstream_branch>      If upstream is set.
-    # branch.ab +<ahead> -<behind>           If upstream is set and
-                                            the commit is present.
-    ------------------------------------------------------------
+....
+Line                                     Notes
+------------------------------------------------------------
+# branch.oid <commit> | (initial)        Current commit.
+# branch.head <branch> | (detached)      Current branch.
+# branch.upstream <upstream_branch>      If upstream is set.
+# branch.ab +<ahead> -<behind>           If upstream is set and
+                                        the commit is present.
+------------------------------------------------------------
+....
 
 ### Changed Tracked Entries
 
@@ -306,56 +310,60 @@ Renamed or copied entries have the following format:
 
     2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path><sep><origPath>
 
-    Field       Meaning
-    --------------------------------------------------------
-    <XY>        A 2 character field containing the staged and
-               unstaged XY values described in the short format,
-               with unchanged indicated by a "." rather than
-               a space.
-    <sub>       A 4 character field describing the submodule state.
-               "N..." when the entry is not a submodule.
-               "S<c><m><u>" when the entry is a submodule.
-               <c> is "C" if the commit changed; otherwise ".".
-               <m> is "M" if it has tracked changes; otherwise ".".
-               <u> is "U" if there are untracked changes; otherwise ".".
-    <mH>        The octal file mode in HEAD.
-    <mI>        The octal file mode in the index.
-    <mW>        The octal file mode in the worktree.
-    <hH>        The object name in HEAD.
-    <hI>        The object name in the index.
-    <X><score>  The rename or copy score (denoting the percentage
-               of similarity between the source and target of the
-               move or copy). For example "R100" or "C75".
-    <path>      The pathname.  In a renamed/copied entry, this
-               is the target path.
-    <sep>       When the `-z` option is used, the 2 pathnames are separated
-               with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
-               byte separates them.
-    <origPath>  The pathname in the commit at HEAD or in the index.
-               This is only present in a renamed/copied entry, and
-               tells where the renamed/copied contents came from.
-    --------------------------------------------------------
+....
+Field       Meaning
+--------------------------------------------------------
+<XY>        A 2 character field containing the staged and
+           unstaged XY values described in the short format,
+           with unchanged indicated by a "." rather than
+           a space.
+<sub>       A 4 character field describing the submodule state.
+           "N..." when the entry is not a submodule.
+           "S<c><m><u>" when the entry is a submodule.
+           <c> is "C" if the commit changed; otherwise ".".
+           <m> is "M" if it has tracked changes; otherwise ".".
+           <u> is "U" if there are untracked changes; otherwise ".".
+<mH>        The octal file mode in HEAD.
+<mI>        The octal file mode in the index.
+<mW>        The octal file mode in the worktree.
+<hH>        The object name in HEAD.
+<hI>        The object name in the index.
+<X><score>  The rename or copy score (denoting the percentage
+           of similarity between the source and target of the
+           move or copy). For example "R100" or "C75".
+<path>      The pathname.  In a renamed/copied entry, this
+           is the target path.
+<sep>       When the `-z` option is used, the 2 pathnames are separated
+           with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
+           byte separates them.
+<origPath>  The pathname in the commit at HEAD or in the index.
+           This is only present in a renamed/copied entry, and
+           tells where the renamed/copied contents came from.
+--------------------------------------------------------
+....
 
 Unmerged entries have the following format; the first character is
 a "u" to distinguish from ordinary changed entries.
 
     u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
 
-    Field       Meaning
-    --------------------------------------------------------
-    <XY>        A 2 character field describing the conflict type
-               as described in the short format.
-    <sub>       A 4 character field describing the submodule state
-               as described above.
-    <m1>        The octal file mode in stage 1.
-    <m2>        The octal file mode in stage 2.
-    <m3>        The octal file mode in stage 3.
-    <mW>        The octal file mode in the worktree.
-    <h1>        The object name in stage 1.
-    <h2>        The object name in stage 2.
-    <h3>        The object name in stage 3.
-    <path>      The pathname.
-    --------------------------------------------------------
+....
+Field       Meaning
+--------------------------------------------------------
+<XY>        A 2 character field describing the conflict type
+           as described in the short format.
+<sub>       A 4 character field describing the submodule state
+           as described above.
+<m1>        The octal file mode in stage 1.
+<m2>        The octal file mode in stage 2.
+<m3>        The octal file mode in stage 3.
+<mW>        The octal file mode in the worktree.
+<h1>        The object name in stage 1.
+<h2>        The object name in stage 2.
+<h3>        The object name in stage 3.
+<path>      The pathname.
+--------------------------------------------------------
+....
 
 ### Other Items
 
index 417b638..de69531 100644 (file)
@@ -134,6 +134,8 @@ The placeholders are:
 - '%cI': committer date, strict ISO 8601 format
 - '%d': ref names, like the --decorate option of linkgit:git-log[1]
 - '%D': ref names without the " (", ")" wrapping.
+- '%S': ref name given on the command line by which the commit was reached
+  (like `git log --source`), only works with `git log`
 - '%e': encoding
 - '%s': subject
 - '%f': sanitized subject line, suitable for a filename
index bab5f50..91b3a72 100644 (file)
@@ -13,8 +13,6 @@ has a line that matches `<pattern>`), unless otherwise noted.
 Note that these are applied before commit
 ordering and formatting options, such as `--reverse`.
 
---
-
 -<number>::
 -n <number>::
 --max-count=<number>::
@@ -308,8 +306,6 @@ ifdef::git-rev-list[]
        `<header>` text will be printed with each progress update.
 endif::git-rev-list[]
 
---
-
 History Simplification
 ~~~~~~~~~~~~~~~~~~~~~~
 
@@ -734,8 +730,13 @@ specification contained in <path>.
 +
 The form '--filter=tree:<depth>' omits all blobs and trees whose depth
 from the root tree is >= <depth> (minimum depth if an object is located
-at multiple depths in the commits traversed). Currently, only <depth>=0
-is supported, which omits all blobs and trees.
+at multiple depths in the commits traversed). <depth>=0 will not include
+any trees or blobs unless included explicitly in the command-line (or
+standard input when --stdin is used). <depth>=1 will include only the
+tree and blobs which are referenced directly by a commit reachable from
+<commit> or an explicitly-given object. <depth>=2 is like <depth>=1
+while also including trees and blobs one more level removed from an
+explicitly-given commit or tree.
 
 --no-filter::
        Turn off any previous `--filter=` argument.
index 9febfb1..c97428c 100644 (file)
@@ -48,6 +48,11 @@ Functions
        is not sorted, this function has the side effect of sorting
        it.
 
+`oid_array_filter`::
+       Apply the callback function `want` to each entry in the array,
+       retaining only the entries for which the function returns true.
+       Preserve the order of the entries that are retained.
+
 Examples
 --------
 
index cc0474b..16452a0 100644 (file)
@@ -76,7 +76,7 @@ CHUNK DATA:
       of the ith commit. Stores value 0x7000000 if no parent in that
       position. If there are more than two parents, the second value
       has its most-significant bit on and the other bits store an array
-      position into the Large Edge List chunk.
+      position into the Extra Edge List chunk.
     * The next 8 bytes store the generation number of the commit and
       the commit time in seconds since EPOCH. The generation number
       uses the higher 30 bits of the first 4 bytes, while the commit
@@ -84,7 +84,7 @@ CHUNK DATA:
       2 bits of the lowest byte, storing the 33rd and 34th bit of the
       commit time.
 
-  Large Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
+  Extra Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
       This list of 4-byte values store the second through nth parents for
       all octopus merges. The second parent value in the commit data stores
       an array position within this list along with the most-significant bit
index 6ac774d..7a2375a 100644 (file)
@@ -22,6 +22,16 @@ protocol-common.txt. When the grammar indicate `PKT-LINE(...)`, unless
 otherwise noted the usual pkt-line LF rules apply: the sender SHOULD
 include a LF, but the receiver MUST NOT complain if it is not present.
 
+An error packet is a special pkt-line that contains an error string.
+
+----
+  error-line     =  PKT-LINE("ERR" SP explanation-text)
+----
+
+Throughout the protocol, where `PKT-LINE(...)` is expected, an error packet MAY
+be sent. Once this packet is sent by a client or a server, the data transfer
+process defined in this protocol is terminated.
+
 Transports
 ----------
 There are three transports over which the packfile protocol is
@@ -89,13 +99,6 @@ process on the server side over the Git protocol is this:
      "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
      nc -v example.com 9418
 
-If the server refuses the request for some reasons, it could abort
-gracefully with an error message.
-
-----
-  error-line     =  PKT-LINE("ERR" SP explanation-text)
-----
-
 
 SSH Transport
 -------------
@@ -398,12 +401,11 @@ from the client).
 Then the server will start sending its packfile data.
 
 ----
-  server-response = *ack_multi ack / nak / error-line
+  server-response = *ack_multi ack / nak
   ack_multi       = PKT-LINE("ACK" SP obj-id ack_status)
   ack_status      = "continue" / "common" / "ready"
   ack             = PKT-LINE("ACK" SP obj-id)
   nak             = PKT-LINE("NAK")
-  error-line     =  PKT-LINE("ERR" SP explanation-text)
 ----
 
 A simple clone may look like this (with no 'have' lines):
index 1ef66bd..896c7b3 100644 (file)
@@ -114,7 +114,7 @@ their "<name>.pack" and "<name>.idx" files.
   that it has, either because the local repository has that object in one of
   its promisor packfiles, or because another promisor object refers to it.
 +
-When Git encounters a missing object, Git can see if it a promisor object
+When Git encounters a missing object, Git can see if it is a promisor object
 and handle it appropriately.  If not, Git can report a corruption.
 +
 This means that there is no need for the client to explicitly maintain an
index 09e4e02..ead85ce 100644 (file)
@@ -296,7 +296,13 @@ included in the client's request:
        Request that various objects from the packfile be omitted
        using one of several filtering techniques. These are intended
        for use with partial clone and partial fetch operations. See
-       `rev-list` for possible "filter-spec" values.
+       `rev-list` for possible "filter-spec" values. When communicating
+       with other processes, senders SHOULD translate scaled integers
+       (e.g. "1k") into a fully-expanded form (e.g. "1024") to aid
+       interoperability with older receivers that may not understand
+       newly-invented scaling suffixes. However, receivers SHOULD
+       accept the following suffixes: 'k', 'm', and 'g' for 1024,
+       1048576, and 1073741824, respectively.
 
 If the 'ref-in-want' feature is advertised, the following argument can
 be included in the client's request as well as the potential addition of
@@ -307,6 +313,16 @@ the 'wanted-refs' section in the server's response as explained below.
        particular ref, where <ref> is the full name of a ref on the
        server.
 
+If the 'sideband-all' feature is advertised, the following argument can be
+included in the client's request:
+
+    sideband-all
+       Instruct the server to send the whole response multiplexed, not just
+       the packfile section. All non-flush and non-delim PKT-LINE in the
+       response (not only in the packfile section) will then start with a byte
+       indicating its sideband (1, 2, or 3), and the server may send "0005\2"
+       (a PKT-LINE of sideband 2 with no payload) as a keepalive packet.
+
 The response of `fetch` is broken into a number of sections separated by
 delimiter packets (0001), with each section beginning with its section
 header.
index 1a44c81..28ee179 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -186,6 +186,12 @@ all::
 # in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
 # wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
 #
+# Define BLK_SHA256 to use the built-in SHA-256 routines.
+#
+# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt.
+#
+# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL.
+#
 # Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
 #
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
@@ -684,6 +690,7 @@ SCRIPTS = $(SCRIPT_SH_INS) \
 
 ETAGS_TARGET = TAGS
 
+FUZZ_OBJS += fuzz-commit-graph.o
 FUZZ_OBJS += fuzz-pack-headers.o
 FUZZ_OBJS += fuzz-pack-idx.o
 
@@ -724,7 +731,9 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-genrandom.o
+TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -747,6 +756,7 @@ TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
+TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-strcmp-offset.o
 TEST_BUILTINS_OBJS += test-string-list.o
@@ -1646,6 +1656,19 @@ endif
 endif
 endif
 
+ifdef OPENSSL_SHA256
+       EXTLIBS += $(LIB_4_CRYPTO)
+       BASIC_CFLAGS += -DSHA256_OPENSSL
+else
+ifdef GCRYPT_SHA256
+       BASIC_CFLAGS += -DSHA256_GCRYPT
+       EXTLIBS += -lgcrypt
+else
+       LIB_OBJS += sha256/block/sha256.o
+       BASIC_CFLAGS += -DSHA256_BLK
+endif
+endif
+
 ifdef SHA1_MAX_BLOCK_SIZE
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
@@ -3103,7 +3126,7 @@ cover_db_html: cover_db
 # An example command to build against libFuzzer from LLVM 4.0.0:
 #
 # make CC=clang CXX=clang++ \
-#      FUZZ_CXXFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
+#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
 #      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
 #      fuzz-all
 #
index 28f5937..447af24 100644 (file)
--- a/banned.h
+++ b/banned.h
@@ -16,6 +16,8 @@
 #define strcat(x,y) BANNED(strcat)
 #undef strncpy
 #define strncpy(x,y,n) BANNED(strncpy)
+#undef strncat
+#define strncat(x,y,n) BANNED(strncat)
 
 #undef sprintf
 #undef vsprintf
index 4c1b80b..6bf5211 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -558,7 +558,8 @@ struct commit_list *filter_skipped(struct commit_list *list,
  * is increased by one between each call, but that should not matter
  * for this application.
  */
-static unsigned get_prn(unsigned count) {
+static unsigned get_prn(unsigned count)
+{
        count = count * 1103515245 + 12345;
        return (count/65536) % PRN_MODULO;
 }
index 12247b4..7c2a7c5 100644 (file)
@@ -137,7 +137,7 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
                        continue; /* do not touch non blobs */
                if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
                        continue;
-               retval |= add_file_to_cache(ce->name, flags | HASH_RENORMALIZE);
+               retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE);
        }
 
        return retval;
@@ -239,7 +239,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
        rev.diffopt.use_color = 0;
        rev.diffopt.flags.ignore_dirty_submodules = 1;
-       out = open(file, O_CREAT | O_WRONLY, 0666);
+       out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (out < 0)
                die(_("Could not open '%s' for writing."), file);
        rev.diffopt.file = xfdopen(out, "w");
index d245523..45d1166 100644 (file)
@@ -27,10 +27,10 @@ static int run_remote_archiver(int argc, const char **argv,
                               const char *remote, const char *exec,
                               const char *name_hint)
 {
-       char *buf;
        int fd[2], i, rv;
        struct transport *transport;
        struct remote *_remote;
+       struct packet_reader reader;
 
        _remote = remote_get(remote);
        if (!_remote->url[0])
@@ -53,18 +53,19 @@ static int run_remote_archiver(int argc, const char **argv,
                packet_write_fmt(fd[1], "argument %s\n", argv[i]);
        packet_flush(fd[1]);
 
-       buf = packet_read_line(fd[0], NULL);
-       if (!buf)
+       packet_reader_init(&reader, fd[0], NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
+       if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
                die(_("git archive: expected ACK/NAK, got a flush packet"));
-       if (strcmp(buf, "ACK")) {
-               if (starts_with(buf, "NACK "))
-                       die(_("git archive: NACK %s"), buf + 5);
-               if (starts_with(buf, "ERR "))
-                       die(_("remote error: %s"), buf + 4);
+       if (strcmp(reader.line, "ACK")) {
+               if (starts_with(reader.line, "NACK "))
+                       die(_("git archive: NACK %s"), reader.line + 5);
                die(_("git archive: protocol error"));
        }
 
-       if (packet_read_line(fd[0], NULL))
+       if (packet_reader_read(&reader) != PACKET_READ_FLUSH)
                die(_("git archive: expected a flush"));
 
        /* Now, start reading from fd[0] and spit it out to stdout */
index 7c7f98c..739de68 100644 (file)
@@ -1136,9 +1136,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                     option_upload_pack);
 
        if (filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&filter_options,
+                                               &expanded_filter_spec);
                transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                                    filter_options.filter_spec);
+                                    expanded_filter_spec.buf);
                transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+               strbuf_release(&expanded_filter_spec);
        }
 
        if (transport->smart_options && !deepen && !filter_options.choice)
index c02a3f1..4ae5027 100644 (file)
@@ -110,8 +110,8 @@ static int graph_read(int argc, const char **argv)
                printf(" oid_lookup");
        if (graph->chunk_commit_data)
                printf(" commit_metadata");
-       if (graph->chunk_large_edges)
-               printf(" large_edges");
+       if (graph->chunk_extra_edges)
+               printf(" extra_edges");
        printf("\n");
 
        UNLEAK(graph);
index 004b816..7d2e0b6 100644 (file)
@@ -351,7 +351,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to create temporary index"));
 
-               old_index_env = getenv(INDEX_ENVIRONMENT);
+               old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
                setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
 
                if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
@@ -361,6 +361,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                        setenv(INDEX_ENVIRONMENT, old_index_env, 1);
                else
                        unsetenv(INDEX_ENVIRONMENT);
+               FREE_AND_NULL(old_index_env);
 
                discard_cache();
                read_cache_from(get_lock_file_path(&index_lock));
index 84385ef..98d65bc 100644 (file)
@@ -164,7 +164,8 @@ static NORETURN void usage_builtin_config(void)
        usage_with_options(builtin_config_usage, builtin_config_options);
 }
 
-static void check_argc(int argc, int min, int max) {
+static void check_argc(int argc, int min, int max)
+{
        if (argc >= min && argc <= max)
                return;
        if (min == max)
@@ -598,7 +599,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        int nongit = !startup_info->have_repository;
        char *value;
 
-       given_config_source.file = getenv(CONFIG_ENVIRONMENT);
+       given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
        argc = parse_options(argc, argv, prefix, builtin_config_options,
                             builtin_config_usage,
index 63e69a5..153a2bd 100644 (file)
@@ -55,6 +55,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        struct oid_array shallow = OID_ARRAY_INIT;
        struct string_list deepen_not = STRING_LIST_INIT_DUP;
        struct packet_reader reader;
+       enum protocol_version version;
 
        fetch_if_missing = 0;
 
@@ -217,11 +218,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
-       switch (discover_version(&reader)) {
+       version = discover_version(&reader);
+       switch (version) {
        case protocol_v2:
-               die("support for protocol v2 not implemented yet");
+               get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL);
+               break;
        case protocol_v1:
        case protocol_v0:
                get_remote_heads(&reader, &ref, 0, NULL, &shallow);
@@ -231,7 +235,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        }
 
        ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
-                        &shallow, pack_lockfile_ptr, protocol_v0);
+                        &shallow, pack_lockfile_ptr, version);
        if (pack_lockfile) {
                printf("lock %s\n", pack_lockfile);
                fflush(stdout);
index e014032..61a4430 100644 (file)
@@ -763,9 +763,6 @@ static int update_local_ref(struct ref *ref,
                        what = _("[new ref]");
                }
 
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref(msg, ref, 0);
                format_display(display, r ? '!' : '*', what,
                               r ? _("unable to update local ref") : NULL,
@@ -779,9 +776,6 @@ static int update_local_ref(struct ref *ref,
                strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "..");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("fast-forward", ref, 1);
                format_display(display, r ? '!' : ' ', quickref.buf,
                               r ? _("unable to update local ref") : NULL,
@@ -794,9 +788,6 @@ static int update_local_ref(struct ref *ref,
                strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "...");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("forced-update", ref, 1);
                format_display(display, r ? '!' : '+', quickref.buf,
                               r ? _("unable to update local ref") : _("forced update"),
@@ -892,6 +883,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                ref->force = rm->peer_ref->force;
                        }
 
+                       if (recurse_submodules != RECURSE_SUBMODULES_OFF)
+                               check_for_new_submodule_commits(&rm->old_oid);
 
                        if (!strcmp(rm->name, "HEAD")) {
                                kind = "";
@@ -1172,6 +1165,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
 static struct transport *prepare_transport(struct remote *remote, int deepen)
 {
        struct transport *transport;
+
        transport = transport_get(remote, NULL);
        transport_set_verbosity(transport, verbosity, progress);
        transport->family = family;
@@ -1191,9 +1185,13 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        if (filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&filter_options,
+                                               &expanded_filter_spec);
                set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                          filter_options.filter_spec);
+                          expanded_filter_spec.buf);
                set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+               strbuf_release(&expanded_filter_spec);
        }
        if (negotiation_tip.nr) {
                if (transport->smart_options)
@@ -1478,7 +1476,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
         */
        if (strcmp(remote->name, repository_format_partial_clone)) {
                if (filter_options.choice)
-                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+                       die(_("--filter can only be used with the remote "
+                             "configured in extensions.partialclone"));
                return;
        }
 
@@ -1646,7 +1645,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                result = fetch_one(remote, argc, argv, prune_tags_ok);
        } else {
                if (filter_options.choice)
-                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+                       die(_("--filter can only be used with the remote "
+                             "configured in extensions.partialclone"));
                /* TODO should this also die if we have a previous partial-clone? */
                result = fetch_multiple(&list);
        }
index bf5ddff..9c4c11d 100644 (file)
@@ -401,7 +401,8 @@ out:
        if (obj->type == OBJ_TREE)
                free_tree_buffer((struct tree *)obj);
        if (obj->type == OBJ_COMMIT)
-               free_commit_buffer((struct commit *)obj);
+               free_commit_buffer(the_repository->parsed_objects,
+                                  (struct commit *)obj);
        return err;
 }
 
index 7696017..020f725 100644 (file)
@@ -659,8 +659,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
 
        report_garbage = report_pack_garbage;
        reprepare_packed_git(the_repository);
-       if (pack_garbage.nr > 0)
+       if (pack_garbage.nr > 0) {
+               close_all_packs(the_repository->objects);
                clean_pack_garbage();
+       }
 
        if (gc_write_commit_graph)
                write_commit_graph_reachable(get_object_directory(), 0,
index 4748195..dd52ea9 100644 (file)
@@ -404,7 +404,10 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
                          const struct object_id *oid,
                          const char *filename, const char *path)
 {
-       struct repository submodule;
+       struct repository subrepo;
+       const struct submodule *sub = submodule_from_path(superproject,
+                                                         &null_oid, path);
+
        int hit;
 
        /*
@@ -420,12 +423,12 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
                return 0;
        }
 
-       if (repo_submodule_init(&submodule, superproject, path)) {
+       if (repo_submodule_init(&subrepo, superproject, sub)) {
                grep_read_unlock();
                return 0;
        }
 
-       repo_read_gitmodules(&submodule);
+       repo_read_gitmodules(&subrepo);
 
        /*
         * NEEDSWORK: This adds the submodule's object directory to the list of
@@ -437,7 +440,7 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
         * store is no longer global and instead is a member of the repository
         * object.
         */
-       add_to_alternates_memory(submodule.objects->odb->path);
+       add_to_alternates_memory(subrepo.objects->odb->path);
        grep_read_unlock();
 
        if (oid) {
@@ -462,14 +465,14 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
 
                init_tree_desc(&tree, data, size);
                hit = grep_tree(opt, pathspec, &tree, &base, base.len,
-                               object->type == OBJ_COMMIT, &submodule);
+                               object->type == OBJ_COMMIT, &subrepo);
                strbuf_release(&base);
                free(data);
        } else {
-               hit = grep_cache(opt, &submodule, pathspec, 1);
+               hit = grep_cache(opt, &subrepo, pathspec, 1);
        }
 
-       repo_clear(&submodule);
+       repo_clear(&subrepo);
        return hit;
 }
 
@@ -567,7 +570,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                strbuf_add(base, entry.path, te_len);
 
                if (S_ISREG(entry.mode)) {
-                       hit |= grep_oid(opt, entry.oid, base->buf, tn_len,
+                       hit |= grep_oid(opt, &entry.oid, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                } else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
@@ -575,10 +578,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        void *data;
                        unsigned long size;
 
-                       data = lock_and_read_oid_file(entry.oid, &type, &size);
+                       data = lock_and_read_oid_file(&entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
-                                   oid_to_hex(entry.oid));
+                                   oid_to_hex(&entry.oid));
 
                        strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
@@ -586,7 +589,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                                         check_attr, repo);
                        free(data);
                } else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
-                       hit |= grep_submodule(opt, repo, pathspec, entry.oid,
+                       hit |= grep_submodule(opt, repo, pathspec, &entry.oid,
                                              base->buf, base->buf + tn_len);
                }
 
index 41faffd..93eff76 100644 (file)
@@ -542,8 +542,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
         * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
         * without --bare.  Catch the error early.
         */
-       git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT));
+       work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT));
        if ((!git_dir || is_bare_repository_cfg == 1) && work_tree)
                die(_("%s (or --work-tree=<directory>) not allowed without "
                          "specifying %s (or --git-dir=<directory>)"),
@@ -582,6 +582,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        }
 
        UNLEAK(real_git_dir);
+       UNLEAK(git_dir);
+       UNLEAK(work_tree);
 
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, flags);
index 3e145fe..c91a59d 100644 (file)
@@ -203,7 +203,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
            rev->diffopt.filter || rev->diffopt.flags.follow_renames)
                rev->always_show_header = 0;
 
-       if (source) {
+       if (source || w.source) {
                init_revision_sources(&revision_sources);
                rev->sources = &revision_sources;
        }
@@ -397,7 +397,8 @@ static int cmd_log_walk(struct rev_info *rev)
                         * We may show a given commit multiple times when
                         * walking the reflogs.
                         */
-                       free_commit_buffer(commit);
+                       free_commit_buffer(the_repository->parsed_objects,
+                                          commit);
                        free_commit_list(commit->parents);
                        commit->parents = NULL;
                }
@@ -1940,7 +1941,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                    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);
+               free_commit_buffer(the_repository->parsed_objects,
+                                  commit);
 
                /* We put one extra blank line between formatted
                 * patches and this flag is used by log-tree code
index 7e0dcaa..cde87cb 100644 (file)
@@ -206,17 +206,19 @@ static void show_files(struct repository *repo, struct dir_struct *dir);
 static void show_submodule(struct repository *superproject,
                           struct dir_struct *dir, const char *path)
 {
-       struct repository submodule;
+       struct repository subrepo;
+       const struct submodule *sub = submodule_from_path(superproject,
+                                                         &null_oid, path);
 
-       if (repo_submodule_init(&submodule, superproject, path))
+       if (repo_submodule_init(&subrepo, superproject, sub))
                return;
 
-       if (repo_read_index(&submodule) < 0)
+       if (repo_read_index(&subrepo) < 0)
                die("index file corrupt");
 
-       show_files(&submodule, dir);
+       show_files(&subrepo, dir);
 
-       repo_clear(&submodule);
+       repo_clear(&subrepo);
 }
 
 static void show_ce(struct repository *repo, struct dir_struct *dir,
index 9b2f707..7545136 100644 (file)
@@ -7,16 +7,16 @@
 static const char builtin_merge_recursive_usage[] =
        "git %s <base>... -- <head> <remote> ...";
 
-static const char *better_branch_name(const char *branch)
+static char *better_branch_name(const char *branch)
 {
        static char githead_env[8 + GIT_MAX_HEXSZ + 1];
        char *name;
 
        if (strlen(branch) != the_hash_algo->hexsz)
-               return branch;
+               return xstrdup(branch);
        xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
        name = getenv(githead_env);
-       return name ? name : branch;
+       return xstrdup(name ? name : branch);
 }
 
 int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
@@ -26,6 +26,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        int i, failed;
        struct object_id h1, h2;
        struct merge_options o;
+       char *better1, *better2;
        struct commit *result;
 
        init_merge_options(&o);
@@ -70,13 +71,17 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        if (get_oid(o.branch2, &h2))
                die(_("could not resolve ref '%s'"), o.branch2);
 
-       o.branch1 = better_branch_name(o.branch1);
-       o.branch2 = better_branch_name(o.branch2);
+       o.branch1 = better1 = better_branch_name(o.branch1);
+       o.branch2 = better2 = better_branch_name(o.branch2);
 
        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);
+
+       free(better1);
+       free(better2);
+
        if (failed < 0)
                return 128; /* die() error code */
        return failed;
index 4984b7e..5541ad0 100644 (file)
@@ -154,15 +154,15 @@ static void show_result(void)
 /* An empty entry never compares same, not even to another empty entry */
 static int same_entry(struct name_entry *a, struct name_entry *b)
 {
-       return  a->oid &&
-               b->oid &&
-               oideq(a->oid, b->oid) &&
+       return  !is_null_oid(&a->oid) &&
+               !is_null_oid(&b->oid) &&
+               oideq(&a->oid, &b->oid) &&
                a->mode == b->mode;
 }
 
 static int both_empty(struct name_entry *a, struct name_entry *b)
 {
-       return !(a->oid || b->oid);
+       return is_null_oid(&a->oid) && is_null_oid(&b->oid);
 }
 
 static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path)
@@ -178,7 +178,7 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const stru
 
 static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
 {
-       char *path = xmallocz(traverse_path_len(info, n));
+       char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
        return make_traverse_path(path, info, n);
 }
 
@@ -192,8 +192,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
                return;
 
        path = traverse_path(info, result);
-       orig = create_entry(2, ours->mode, ours->oid, path);
-       final = create_entry(0, result->mode, result->oid, path);
+       orig = create_entry(2, ours->mode, &ours->oid, path);
+       final = create_entry(0, result->mode, &result->oid, path);
 
        final->link = orig;
 
@@ -217,7 +217,7 @@ static void unresolved_directory(const struct traverse_info *info,
 
        newbase = traverse_path(info, p);
 
-#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->oid : NULL)
+#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)
        buf0 = fill_tree_descriptor(t + 0, ENTRY_OID(n + 0));
        buf1 = fill_tree_descriptor(t + 1, ENTRY_OID(n + 1));
        buf2 = fill_tree_descriptor(t + 2, ENTRY_OID(n + 2));
@@ -243,7 +243,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info
                path = entry->path;
        else
                path = traverse_path(info, n);
-       link = create_entry(stage, n->mode, n->oid, path);
+       link = create_entry(stage, n->mode, &n->oid, path);
        link->link = entry;
        return link;
 }
@@ -318,7 +318,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s
        }
 
        if (same_entry(entry+0, entry+1)) {
-               if (entry[2].oid && !S_ISDIR(entry[2].mode)) {
+               if (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {
                        /* We did not touch, they modified -- take theirs */
                        resolve(info, entry+1, entry+2);
                        return mask;
index 889df2c..0a70d04 100644 (file)
@@ -1334,7 +1334,7 @@ static void add_pbase_object(struct tree_desc *tree,
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
-                       add_object_entry(entry.oid,
+                       add_object_entry(&entry.oid,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
@@ -1345,7 +1345,7 @@ static void add_pbase_object(struct tree_desc *tree,
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
 
-                       tree = pbase_tree_get(entry.oid);
+                       tree = pbase_tree_get(&entry.oid);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
index ee1e842..021dd3b 100644 (file)
@@ -143,7 +143,9 @@ static int push_url_of_remote(struct remote *remote, const char ***url_p)
        return remote->url_nr;
 }
 
-static NORETURN int die_push_simple(struct branch *branch, struct remote *remote) {
+static NORETURN int die_push_simple(struct branch *branch,
+                                   struct remote *remote)
+{
        /*
         * There's no point in using shorten_unambiguous_ref here,
         * as the ambiguity would be on the remote side, not what
index dd2a55a..c2c2d51 100644 (file)
@@ -193,6 +193,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
                OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")),
                OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")),
                OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto),
+               OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
+                        N_("automatically re-schedule any `exec` that fails")),
                OPT_END()
        };
 
index 00de703..774264b 100644 (file)
@@ -104,6 +104,7 @@ struct rebase_options {
        int rebase_merges, rebase_cousins;
        char *strategy, *strategy_opts;
        struct strbuf git_format_patch_opt;
+       int reschedule_failed_exec;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -415,6 +416,8 @@ static int run_specific_rebase(struct rebase_options *opts)
                        argv_array_push(&child.args, opts->gpg_sign_opt);
                if (opts->signoff)
                        argv_array_push(&child.args, "--signoff");
+               if (opts->reschedule_failed_exec)
+                       argv_array_push(&child.args, "--reschedule-failed-exec");
 
                status = run_command(&child);
                goto finished_rebase;
@@ -530,6 +533,7 @@ finished_rebase:
 
 #define RESET_HEAD_DETACH (1<<0)
 #define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
 
 static int reset_head(struct object_id *oid, const char *action,
                      const char *switch_to_branch, unsigned flags,
@@ -537,6 +541,7 @@ static int reset_head(struct object_id *oid, const char *action,
 {
        unsigned detach_head = flags & RESET_HEAD_DETACH;
        unsigned reset_hard = flags & RESET_HEAD_HARD;
+       unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        struct object_id head_oid;
        struct tree_desc desc[2] = { { NULL }, { NULL } };
        struct lock_file lock = LOCK_INIT;
@@ -636,6 +641,10 @@ static int reset_head(struct object_id *oid, const char *action,
                        ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
                                         UPDATE_REFS_MSG_ON_ERR);
        }
+       if (run_hook)
+               run_hook_le(NULL, "post-checkout",
+                           oid_to_hex(orig ? orig : &null_oid),
+                           oid_to_hex(oid), "1", NULL);
 
 leave_reset_head:
        strbuf_release(&msg);
@@ -674,6 +683,11 @@ static int rebase_config(const char *var, const char *value, void *data)
                return 0;
        }
 
+       if (!strcmp(var, "rebase.reschedulefailedexec")) {
+               opts->reschedule_failed_exec = git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, data);
 }
 
@@ -746,6 +760,23 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
        return 0;
 }
 
+struct opt_y {
+       struct string_list *list;
+       struct rebase_options *options;
+};
+
+static int parse_opt_y(const struct option *opt, const char *arg, int unset)
+{
+       struct opt_y *o = opt->value;
+
+       if (unset || !arg)
+               return -1;
+
+       o->options->reschedule_failed_exec = 1;
+       string_list_append(o->list, arg);
+       return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
        struct branch *current_branch = branch_get(NULL);
@@ -826,6 +857,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
+       struct opt_y opt_y = { .list = &exec, .options = &options };
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
@@ -903,6 +935,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
                                N_("add exec lines after each commit of the "
                                   "editable list")),
+               { OPTION_CALLBACK, 'y', NULL, &opt_y, N_("<cmd>"),
+                       N_("same as --reschedule-failed-exec -x <cmd>"),
+                       PARSE_OPT_NONEG, parse_opt_y },
                OPT_BOOL(0, "allow-empty-message",
                         &options.allow_empty_message,
                         N_("allow rebasing commits with empty messages")),
@@ -920,6 +955,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                   "strategy")),
                OPT_BOOL(0, "root", &options.root,
                         N_("rebase all reachable commits up to the root(s)")),
+               OPT_BOOL(0, "reschedule-failed-exec",
+                        &options.reschedule_failed_exec,
+                        N_("automatically re-schedule any `exec` that fails")),
                OPT_END(),
        };
        int i;
@@ -1216,6 +1254,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                break;
        }
 
+       if (options.reschedule_failed_exec && !is_interactive(&options))
+               die(_("--reschedule-failed-exec requires an interactive rebase"));
+
        if (options.git_am_opts.argc) {
                /* all am options except -q are compatible only with --am */
                for (i = options.git_am_opts.argc - 1; i >= 0; i--)
@@ -1241,7 +1282,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                options.flags |= REBASE_FORCE;
        }
 
-       if (options.type == REBASE_PRESERVE_MERGES)
+       if (options.type == REBASE_PRESERVE_MERGES) {
                /*
                 * Note: incompatibility with --signoff handled in signoff block above
                 * Note: incompatibility with --interactive is just a strong warning;
@@ -1251,6 +1292,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        die(_("error: cannot combine '--preserve-merges' with "
                              "'--rebase-merges'"));
 
+               if (options.reschedule_failed_exec)
+                       die(_("error: cannot combine '--preserve-merges' with "
+                             "'--reschedule-failed-exec'"));
+       }
+
        if (options.rebase_merges) {
                if (strategy_options.nr)
                        die(_("error: cannot combine '--rebase-merges' with "
@@ -1466,7 +1512,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                            getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
                                            options.switch_to);
                                if (reset_head(&oid, "checkout",
-                                              options.head_name, 0,
+                                              options.head_name,
+                                              RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
                                               NULL, buf.buf) < 0) {
                                        ret = !!error(_("could not switch to "
                                                        "%s"),
@@ -1540,7 +1587,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        strbuf_addf(&msg, "%s: checkout %s",
                    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
        if (reset_head(&options.onto->object.oid, "checkout", NULL,
-                      RESET_HEAD_DETACH, NULL, msg.buf))
+                      RESET_HEAD_DETACH | RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
+                      NULL, msg.buf))
                die(_("Could not detach HEAD"));
        strbuf_release(&msg);
 
index 33187bd..d58b775 100644 (file)
@@ -1569,30 +1569,29 @@ static void queue_commands_from_cert(struct command **tail,
        }
 }
 
-static struct command *read_head_info(struct oid_array *shallow)
+static struct command *read_head_info(struct packet_reader *reader,
+                                     struct oid_array *shallow)
 {
        struct command *commands = NULL;
        struct command **p = &commands;
        for (;;) {
-               char *line;
-               int len, linelen;
+               int linelen;
 
-               line = packet_read_line(0, &len);
-               if (!line)
+               if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
 
-               if (len > 8 && starts_with(line, "shallow ")) {
+               if (reader->pktlen > 8 && starts_with(reader->line, "shallow ")) {
                        struct object_id oid;
-                       if (get_oid_hex(line + 8, &oid))
+                       if (get_oid_hex(reader->line + 8, &oid))
                                die("protocol error: expected shallow sha, got '%s'",
-                                   line + 8);
+                                   reader->line + 8);
                        oid_array_append(shallow, &oid);
                        continue;
                }
 
-               linelen = strlen(line);
-               if (linelen < len) {
-                       const char *feature_list = line + linelen + 1;
+               linelen = strlen(reader->line);
+               if (linelen < reader->pktlen) {
+                       const char *feature_list = reader->line + linelen + 1;
                        if (parse_feature_request(feature_list, "report-status"))
                                report_status = 1;
                        if (parse_feature_request(feature_list, "side-band-64k"))
@@ -1607,28 +1606,32 @@ static struct command *read_head_info(struct oid_array *shallow)
                                use_push_options = 1;
                }
 
-               if (!strcmp(line, "push-cert")) {
+               if (!strcmp(reader->line, "push-cert")) {
                        int true_flush = 0;
-                       char certbuf[1024];
+                       int saved_options = reader->options;
+                       reader->options &= ~PACKET_READ_CHOMP_NEWLINE;
 
                        for (;;) {
-                               len = packet_read(0, NULL, NULL,
-                                                 certbuf, sizeof(certbuf), 0);
-                               if (!len) {
+                               packet_reader_read(reader);
+                               if (reader->status == PACKET_READ_FLUSH) {
                                        true_flush = 1;
                                        break;
                                }
-                               if (!strcmp(certbuf, "push-cert-end\n"))
+                               if (reader->status != PACKET_READ_NORMAL) {
+                                       die("protocol error: got an unexpected packet");
+                               }
+                               if (!strcmp(reader->line, "push-cert-end\n"))
                                        break; /* end of cert */
-                               strbuf_addstr(&push_cert, certbuf);
+                               strbuf_addstr(&push_cert, reader->line);
                        }
+                       reader->options = saved_options;
 
                        if (true_flush)
                                break;
                        continue;
                }
 
-               p = queue_command(p, line, linelen);
+               p = queue_command(p, reader->line, linelen);
        }
 
        if (push_cert.len)
@@ -1637,18 +1640,14 @@ static struct command *read_head_info(struct oid_array *shallow)
        return commands;
 }
 
-static void read_push_options(struct string_list *options)
+static void read_push_options(struct packet_reader *reader,
+                             struct string_list *options)
 {
        while (1) {
-               char *line;
-               int len;
-
-               line = packet_read_line(0, &len);
-
-               if (!line)
+               if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                        break;
 
-               string_list_append(options, line);
+               string_list_append(options, reader->line);
        }
 }
 
@@ -1924,6 +1923,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        struct oid_array shallow = OID_ARRAY_INIT;
        struct oid_array ref = OID_ARRAY_INIT;
        struct shallow_info si;
+       struct packet_reader reader;
 
        struct option options[] = {
                OPT__QUIET(&quiet, N_("quiet")),
@@ -1986,12 +1986,16 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        if (advertise_refs)
                return 0;
 
-       if ((commands = read_head_info(&shallow)) != NULL) {
+       packet_reader_init(&reader, 0, NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
+       if ((commands = read_head_info(&reader, &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);
+                       read_push_options(&reader, &push_options);
                if (!check_cert_push_options(&push_options)) {
                        struct command *cmd;
                        for (cmd = commands; cmd; cmd = cmd->next)
index 64a8df4..1f1010e 100644 (file)
@@ -94,8 +94,8 @@ static int tree_is_complete(const struct object_id *oid)
        init_tree_desc(&desc, tree->buffer, tree->size);
        complete = 1;
        while (tree_entry(&desc, &entry)) {
-               if (!has_sha1_file(entry.oid->hash) ||
-                   (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid))) {
+               if (!has_sha1_file(entry.oid.hash) ||
+                   (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
                        tree->object.flags |= INCOMPLETE;
                        complete = 0;
                }
index 2a1c7b2..67f8978 100644 (file)
@@ -419,6 +419,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (!names.nr && !po_args.quiet)
                printf_ln(_("Nothing new to pack."));
 
+       close_all_packs(the_repository->objects);
+
        /*
         * Ok we have prepared all new packfiles.
         * First see if there are packs of the same name and if so
index 51e9e12..14ef659 100644 (file)
@@ -197,7 +197,8 @@ static void finish_commit(struct commit *commit, void *data)
                free_commit_list(commit->parents);
                commit->parents = NULL;
        }
-       free_commit_buffer(commit);
+       free_commit_buffer(the_repository->parsed_objects,
+                          commit);
 }
 
 static inline void finish_object__ma(struct object *obj)
index 8e3c749..098ebf2 100644 (file)
@@ -250,7 +250,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
-                          PACKET_READ_GENTLE_ON_EOF);
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
 
        switch (discover_version(&reader)) {
        case protocol_v2:
index bdf0328..be33eb8 100644 (file)
@@ -30,6 +30,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
        enum stripspace_mode mode = STRIP_DEFAULT;
+       int nongit;
 
        const struct option options[] = {
                OPT_CMDMODE('s', "strip-comments", &mode,
@@ -46,7 +47,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
                usage_with_options(stripspace_usage, options);
 
        if (mode == STRIP_COMMENTS || mode == COMMENT_LINES) {
-               setup_git_directory_gently(NULL);
+               setup_git_directory_gently(&nongit);
                git_config(git_default_config, NULL);
        }
 
index b45514b..0e140f1 100644 (file)
@@ -1131,6 +1131,8 @@ static void deinit_submodule(const char *path, const char *prefix,
                if (!(flags & OPT_QUIET))
                        printf(format, displaypath);
 
+               submodule_unset_core_worktree(sub);
+
                strbuf_release(&sb_rm);
        }
 
@@ -1552,7 +1554,7 @@ struct submodule_update_clone {
 #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
        SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
        NULL, NULL, NULL, \
-       NULL, 0, 0, 0, NULL, 0, 0, 0}
+       NULL, 0, 0, 0, NULL, 0, 0, 1}
 
 
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@ -2046,7 +2048,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
        struct repository subrepo;
 
        if (argc != 2)
-               BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
+               BUG("submodule--helper ensure-core-worktree <path>");
 
        path = argv[1];
 
@@ -2054,7 +2056,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
        if (!sub)
                BUG("We could get the submodule handle before?");
 
-       if (repo_submodule_init(&subrepo, the_repository, path))
+       if (repo_submodule_init(&subrepo, the_repository, sub))
                die(_("could not get a repository handle for submodule '%s'"), path);
 
        if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
index 5e84026..3f9907f 100644 (file)
@@ -9,6 +9,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "sigchain.h"
+#include "submodule.h"
 #include "refs.h"
 #include "utf8.h"
 #include "worktree.h"
@@ -724,20 +725,36 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
 static void validate_no_submodules(const struct worktree *wt)
 {
        struct index_state istate = { NULL };
+       struct strbuf path = STRBUF_INIT;
        int i, found_submodules = 0;
 
-       if (read_index_from(&istate, worktree_git_path(wt, "index"),
-                           get_worktree_git_dir(wt)) > 0) {
+       if (is_directory(worktree_git_path(wt, "modules"))) {
+               /*
+                * There could be false positives, e.g. the "modules"
+                * directory exists but is empty. But it's a rare case and
+                * this simpler check is probably good enough for now.
+                */
+               found_submodules = 1;
+       } else if (read_index_from(&istate, worktree_git_path(wt, "index"),
+                                  get_worktree_git_dir(wt)) > 0) {
                for (i = 0; i < istate.cache_nr; i++) {
                        struct cache_entry *ce = istate.cache[i];
+                       int err;
 
-                       if (S_ISGITLINK(ce->ce_mode)) {
-                               found_submodules = 1;
-                               break;
-                       }
+                       if (!S_ISGITLINK(ce->ce_mode))
+                               continue;
+
+                       strbuf_reset(&path);
+                       strbuf_addf(&path, "%s/%s", wt->path, ce->name);
+                       if (!is_submodule_populated_gently(path.buf, &err))
+                               continue;
+
+                       found_submodules = 1;
+                       break;
                }
        }
        discard_index(&istate);
+       strbuf_release(&path);
 
        if (found_submodules)
                die(_("working trees containing submodules cannot be moved or removed"));
index eabb8fb..c4b8a1f 100644 (file)
@@ -675,7 +675,7 @@ static void prime_cache_tree_rec(struct repository *r,
                        cnt++;
                else {
                        struct cache_tree_sub *sub;
-                       struct tree *subtree = lookup_tree(r, entry.oid);
+                       struct tree *subtree = lookup_tree(r, &entry.oid);
                        if (!subtree->object.parsed)
                                parse_tree(subtree);
                        sub = cache_tree_sub(it, entry.path);
@@ -724,7 +724,7 @@ int cache_tree_matches_traversal(struct cache_tree *root,
 
        it = find_cache_tree_from_traversal(root, info);
        it = cache_tree_find(it, ent->path);
-       if (it && it->entry_count > 0 && oideq(ent->oid, &it->oid))
+       if (it && it->entry_count > 0 && oideq(&ent->oid, &it->oid))
                return it->entry_count;
        return 0;
 }
diff --git a/cache.h b/cache.h
index 49713cc..038e376 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -45,10 +45,20 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long);
 /* The length in bytes and in hex digits of an object name (SHA-1 value). */
 #define GIT_SHA1_RAWSZ 20
 #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
 
 /* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
 
 struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@ -745,6 +755,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
 #define ADD_CACHE_JUST_APPEND 8                /* Append only; tree.c::read_tree() */
 #define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
 #define ADD_CACHE_KEEP_CACHE_TREE 32   /* Do not invalidate cache-tree */
+#define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 
@@ -1028,16 +1039,12 @@ extern const struct object_id null_oid;
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
        /*
-        * This is a temporary optimization hack. By asserting the size here,
-        * we let the compiler know that it's always going to be 20, which lets
-        * it turn this fixed-size memcmp into a few inline instructions.
-        *
-        * This will need to be extended or ripped out when we learn about
-        * hashes of different sizes.
+        * Teach the compiler that there are only two possibilities of hash size
+        * here, so that it can optimize for this case as much as possible.
         */
-       if (the_hash_algo->rawsz != 20)
-               BUG("hash size not yet supported by hashcmp");
-       return memcmp(sha1, sha2, the_hash_algo->rawsz);
+       if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
+               return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 }
 
 static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@ -1047,7 +1054,13 @@ static inline int oidcmp(const struct object_id *oid1, const struct object_id *o
 
 static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 {
-       return !hashcmp(sha1, sha2);
+       /*
+        * We write this here instead of deferring to hashcmp so that the
+        * compiler can properly inline it and avoid calling memcmp.
+        */
+       if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
+               return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 }
 
 static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
@@ -1072,7 +1085,7 @@ static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
 
 static inline void oidcpy(struct object_id *dst, const struct object_id *src)
 {
-       hashcpy(dst->hash, src->hash);
+       memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
 }
 
 static inline struct object_id *oiddup(const struct object_id *src)
@@ -1365,9 +1378,9 @@ extern int get_oid_hex(const char *hex, struct object_id *sha1);
 extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 
 /*
- * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
+ * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
  * and writes the NUL-terminated output to the buffer `out`, which must be at
- * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
+ * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
  * convenience.
  *
  * The non-`_r` variant returns a static buffer, but uses a ring of 4
@@ -1375,10 +1388,13 @@ extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  *
  *   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 */
+char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
+char *sha1_to_hex_r(char *out, const unsigned char *sha1);
+char *oid_to_hex_r(char *out, const struct object_id *oid);
+char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);      /* static buffer result! */
+char *sha1_to_hex(const unsigned char *sha1);                                          /* same static buffer */
+char *hash_to_hex(const unsigned char *hash);                                          /* same static buffer */
+char *oid_to_hex(const struct object_id *oid);                                         /* same static buffer */
 
 /*
  * Parse a 40-character hexadecimal object ID starting from hex, updating the
index 5c8fb4b..47e9be0 100644 (file)
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
-#define GRAPH_CHUNKID_LARGEEDGES 0x45444745 /* "EDGE" */
+#define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
 
-#define GRAPH_DATA_WIDTH 36
+#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
 
 #define GRAPH_VERSION_1 0x1
 #define GRAPH_VERSION GRAPH_VERSION_1
 
-#define GRAPH_OID_VERSION_SHA1 1
-#define GRAPH_OID_LEN_SHA1 GIT_SHA1_RAWSZ
-#define GRAPH_OID_VERSION GRAPH_OID_VERSION_SHA1
-#define GRAPH_OID_LEN GRAPH_OID_LEN_SHA1
-
-#define GRAPH_OCTOPUS_EDGES_NEEDED 0x80000000
-#define GRAPH_PARENT_MISSING 0x7fffffff
+#define GRAPH_EXTRA_EDGES_NEEDED 0x80000000
 #define GRAPH_EDGE_LAST_MASK 0x7fffffff
 #define GRAPH_PARENT_NONE 0x70000000
 
 #define GRAPH_FANOUT_SIZE (4 * 256)
 #define GRAPH_CHUNKLOOKUP_WIDTH 12
 #define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
-                       + GRAPH_FANOUT_SIZE + GRAPH_OID_LEN)
+                       + GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
 
 char *get_commit_graph_filename(const char *obj_dir)
 {
        return xstrfmt("%s/info/commit-graph", obj_dir);
 }
 
+static uint8_t oid_version(void)
+{
+       return 1;
+}
+
 static struct commit_graph *alloc_commit_graph(void)
 {
        struct commit_graph *g = xcalloc(1, sizeof(*g));
@@ -84,16 +83,10 @@ static int commit_graph_compatible(struct repository *r)
 struct commit_graph *load_commit_graph_one(const char *graph_file)
 {
        void *graph_map;
-       const unsigned char *data, *chunk_lookup;
        size_t graph_size;
        struct stat st;
-       uint32_t i;
-       struct commit_graph *graph;
+       struct commit_graph *ret;
        int fd = git_open(graph_file);
-       uint64_t last_chunk_offset;
-       uint32_t last_chunk_id;
-       uint32_t graph_signature;
-       unsigned char graph_version, hash_version;
 
        if (fd < 0)
                return NULL;
@@ -108,32 +101,60 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
                die(_("graph file %s is too small"), graph_file);
        }
        graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       ret = parse_commit_graph(graph_map, fd, graph_size);
+
+       if (!ret) {
+               munmap(graph_map, graph_size);
+               close(fd);
+               exit(1);
+       }
+
+       return ret;
+}
+
+struct commit_graph *parse_commit_graph(void *graph_map, int fd,
+                                       size_t graph_size)
+{
+       const unsigned char *data, *chunk_lookup;
+       uint32_t i;
+       struct commit_graph *graph;
+       uint64_t last_chunk_offset;
+       uint32_t last_chunk_id;
+       uint32_t graph_signature;
+       unsigned char graph_version, hash_version;
+
+       if (!graph_map)
+               return NULL;
+
+       if (graph_size < GRAPH_MIN_SIZE)
+               return NULL;
+
        data = (const unsigned char *)graph_map;
 
        graph_signature = get_be32(data);
        if (graph_signature != GRAPH_SIGNATURE) {
                error(_("graph signature %X does not match signature %X"),
                      graph_signature, GRAPH_SIGNATURE);
-               goto cleanup_fail;
+               return NULL;
        }
 
        graph_version = *(unsigned char*)(data + 4);
        if (graph_version != GRAPH_VERSION) {
                error(_("graph version %X does not match version %X"),
                      graph_version, GRAPH_VERSION);
-               goto cleanup_fail;
+               return NULL;
        }
 
        hash_version = *(unsigned char*)(data + 5);
-       if (hash_version != GRAPH_OID_VERSION) {
+       if (hash_version != oid_version()) {
                error(_("hash version %X does not match version %X"),
-                     hash_version, GRAPH_OID_VERSION);
-               goto cleanup_fail;
+                     hash_version, oid_version());
+               return NULL;
        }
 
        graph = alloc_commit_graph();
 
-       graph->hash_len = GRAPH_OID_LEN;
+       graph->hash_len = the_hash_algo->rawsz;
        graph->num_chunks = *(unsigned char*)(data + 6);
        graph->graph_fd = fd;
        graph->data = graph_map;
@@ -143,16 +164,27 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
        last_chunk_offset = 8;
        chunk_lookup = data + 8;
        for (i = 0; i < graph->num_chunks; i++) {
-               uint32_t chunk_id = get_be32(chunk_lookup + 0);
-               uint64_t chunk_offset = get_be64(chunk_lookup + 4);
+               uint32_t chunk_id;
+               uint64_t chunk_offset;
                int chunk_repeated = 0;
 
+               if (data + graph_size - chunk_lookup <
+                   GRAPH_CHUNKLOOKUP_WIDTH) {
+                       error(_("chunk lookup table entry missing; graph file may be incomplete"));
+                       free(graph);
+                       return NULL;
+               }
+
+               chunk_id = get_be32(chunk_lookup + 0);
+               chunk_offset = get_be64(chunk_lookup + 4);
+
                chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
 
-               if (chunk_offset > graph_size - GIT_MAX_RAWSZ) {
+               if (chunk_offset > graph_size - the_hash_algo->rawsz) {
                        error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
                              (uint32_t)chunk_offset);
-                       goto cleanup_fail;
+                       free(graph);
+                       return NULL;
                }
 
                switch (chunk_id) {
@@ -177,17 +209,18 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
                                graph->chunk_commit_data = data + chunk_offset;
                        break;
 
-               case GRAPH_CHUNKID_LARGEEDGES:
-                       if (graph->chunk_large_edges)
+               case GRAPH_CHUNKID_EXTRAEDGES:
+                       if (graph->chunk_extra_edges)
                                chunk_repeated = 1;
                        else
-                               graph->chunk_large_edges = data + chunk_offset;
+                               graph->chunk_extra_edges = data + chunk_offset;
                        break;
                }
 
                if (chunk_repeated) {
                        error(_("chunk id %08x appears multiple times"), chunk_id);
-                       goto cleanup_fail;
+                       free(graph);
+                       return NULL;
                }
 
                if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
@@ -201,11 +234,6 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
        }
 
        return graph;
-
-cleanup_fail:
-       munmap(graph_map, graph_size);
-       close(fd);
-       exit(1);
 }
 
 static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
@@ -289,7 +317,8 @@ static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t
                            g->chunk_oid_lookup, g->hash_len, pos);
 }
 
-static struct commit_list **insert_parent_or_die(struct commit_graph *g,
+static struct commit_list **insert_parent_or_die(struct repository *r,
+                                                struct commit_graph *g,
                                                 uint64_t pos,
                                                 struct commit_list **pptr)
 {
@@ -300,7 +329,7 @@ static struct commit_list **insert_parent_or_die(struct commit_graph *g,
                die("invalid parent position %"PRIu64, pos);
 
        hashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * pos);
-       c = lookup_commit(the_repository, &oid);
+       c = lookup_commit(r, &oid);
        if (!c)
                die(_("could not find commit %s"), oid_to_hex(&oid));
        c->graph_pos = pos;
@@ -314,7 +343,9 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
        item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
 }
 
-static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t pos)
+static int fill_commit_in_graph(struct repository *r,
+                               struct commit *item,
+                               struct commit_graph *g, uint32_t pos)
 {
        uint32_t edge_value;
        uint32_t *parent_data_ptr;
@@ -338,21 +369,21 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        edge_value = get_be32(commit_data + g->hash_len);
        if (edge_value == GRAPH_PARENT_NONE)
                return 1;
-       pptr = insert_parent_or_die(g, edge_value, pptr);
+       pptr = insert_parent_or_die(r, g, edge_value, pptr);
 
        edge_value = get_be32(commit_data + g->hash_len + 4);
        if (edge_value == GRAPH_PARENT_NONE)
                return 1;
-       if (!(edge_value & GRAPH_OCTOPUS_EDGES_NEEDED)) {
-               pptr = insert_parent_or_die(g, edge_value, pptr);
+       if (!(edge_value & GRAPH_EXTRA_EDGES_NEEDED)) {
+               pptr = insert_parent_or_die(r, g, edge_value, pptr);
                return 1;
        }
 
-       parent_data_ptr = (uint32_t*)(g->chunk_large_edges +
+       parent_data_ptr = (uint32_t*)(g->chunk_extra_edges +
                          4 * (uint64_t)(edge_value & GRAPH_EDGE_LAST_MASK));
        do {
                edge_value = get_be32(parent_data_ptr);
-               pptr = insert_parent_or_die(g,
+               pptr = insert_parent_or_die(r, g,
                                            edge_value & GRAPH_EDGE_LAST_MASK,
                                            pptr);
                parent_data_ptr++;
@@ -371,7 +402,9 @@ static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        }
 }
 
-static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item)
+static int parse_commit_in_graph_one(struct repository *r,
+                                    struct commit_graph *g,
+                                    struct commit *item)
 {
        uint32_t pos;
 
@@ -379,7 +412,7 @@ static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item
                return 1;
 
        if (find_commit_in_graph(item, g, &pos))
-               return fill_commit_in_graph(item, g, pos);
+               return fill_commit_in_graph(r, item, g, pos);
 
        return 0;
 }
@@ -388,7 +421,7 @@ int parse_commit_in_graph(struct repository *r, struct commit *item)
 {
        if (!prepare_commit_graph(r))
                return 0;
-       return parse_commit_in_graph_one(r->objects->commit_graph, item);
+       return parse_commit_in_graph_one(r, r->objects->commit_graph, item);
 }
 
 void load_commit_graph_info(struct repository *r, struct commit *item)
@@ -400,19 +433,22 @@ void load_commit_graph_info(struct repository *r, struct commit *item)
                fill_commit_graph_info(item, r->objects->commit_graph, pos);
 }
 
-static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *c)
+static struct tree *load_tree_for_commit(struct repository *r,
+                                        struct commit_graph *g,
+                                        struct commit *c)
 {
        struct object_id oid;
        const unsigned char *commit_data = g->chunk_commit_data +
                                           GRAPH_DATA_WIDTH * (c->graph_pos);
 
        hashcpy(oid.hash, commit_data);
-       c->maybe_tree = lookup_tree(the_repository, &oid);
+       c->maybe_tree = lookup_tree(r, &oid);
 
        return c->maybe_tree;
 }
 
-static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g,
+static struct tree *get_commit_tree_in_graph_one(struct repository *r,
+                                                struct commit_graph *g,
                                                 const struct commit *c)
 {
        if (c->maybe_tree)
@@ -420,17 +456,19 @@ static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g,
        if (c->graph_pos == COMMIT_NOT_FROM_GRAPH)
                BUG("get_commit_tree_in_graph_one called from non-commit-graph commit");
 
-       return load_tree_for_commit(g, (struct commit *)c);
+       return load_tree_for_commit(r, g, (struct commit *)c);
 }
 
 struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit *c)
 {
-       return get_commit_tree_in_graph_one(r->objects->commit_graph, c);
+       return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
 }
 
 static void write_graph_chunk_fanout(struct hashfile *f,
                                     struct commit **commits,
-                                    int nr_commits)
+                                    int nr_commits,
+                                    struct progress *progress,
+                                    uint64_t *progress_cnt)
 {
        int i, count = 0;
        struct commit **list = commits;
@@ -444,6 +482,7 @@ static void write_graph_chunk_fanout(struct hashfile *f,
                while (count < nr_commits) {
                        if ((*list)->object.oid.hash[0] != i)
                                break;
+                       display_progress(progress, ++*progress_cnt);
                        count++;
                        list++;
                }
@@ -453,12 +492,16 @@ static void write_graph_chunk_fanout(struct hashfile *f,
 }
 
 static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits)
+                                  struct commit **commits, int nr_commits,
+                                  struct progress *progress,
+                                  uint64_t *progress_cnt)
 {
        struct commit **list = commits;
        int count;
-       for (count = 0; count < nr_commits; count++, list++)
+       for (count = 0; count < nr_commits; count++, list++) {
+               display_progress(progress, ++*progress_cnt);
                hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
+       }
 }
 
 static const unsigned char *commit_to_sha1(size_t index, void *table)
@@ -468,7 +511,9 @@ static const unsigned char *commit_to_sha1(size_t index, void *table)
 }
 
 static void write_graph_chunk_data(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits)
+                                  struct commit **commits, int nr_commits,
+                                  struct progress *progress,
+                                  uint64_t *progress_cnt)
 {
        struct commit **list = commits;
        struct commit **last = commits + nr_commits;
@@ -478,6 +523,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                struct commit_list *parent;
                int edge_value;
                uint32_t packedDate[2];
+               display_progress(progress, ++*progress_cnt);
 
                parse_commit(*list);
                hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
@@ -493,7 +539,9 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                                              commit_to_sha1);
 
                        if (edge_value < 0)
-                               edge_value = GRAPH_PARENT_MISSING;
+                               BUG("missing parent %s for commit %s",
+                                   oid_to_hex(&parent->item->object.oid),
+                                   oid_to_hex(&(*list)->object.oid));
                }
 
                hashwrite_be32(f, edge_value);
@@ -504,19 +552,21 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                if (!parent)
                        edge_value = GRAPH_PARENT_NONE;
                else if (parent->next)
-                       edge_value = GRAPH_OCTOPUS_EDGES_NEEDED | num_extra_edges;
+                       edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
                                              commits,
                                              nr_commits,
                                              commit_to_sha1);
                        if (edge_value < 0)
-                               edge_value = GRAPH_PARENT_MISSING;
+                               BUG("missing parent %s for commit %s",
+                                   oid_to_hex(&parent->item->object.oid),
+                                   oid_to_hex(&(*list)->object.oid));
                }
 
                hashwrite_be32(f, edge_value);
 
-               if (edge_value & GRAPH_OCTOPUS_EDGES_NEEDED) {
+               if (edge_value & GRAPH_EXTRA_EDGES_NEEDED) {
                        do {
                                num_extra_edges++;
                                parent = parent->next;
@@ -537,9 +587,11 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
        }
 }
 
-static void write_graph_chunk_large_edges(struct hashfile *f,
+static void write_graph_chunk_extra_edges(struct hashfile *f,
                                          struct commit **commits,
-                                         int nr_commits)
+                                         int nr_commits,
+                                         struct progress *progress,
+                                         uint64_t *progress_cnt)
 {
        struct commit **list = commits;
        struct commit **last = commits + nr_commits;
@@ -547,6 +599,9 @@ static void write_graph_chunk_large_edges(struct hashfile *f,
 
        while (list < last) {
                int num_parents = 0;
+
+               display_progress(progress, ++*progress_cnt);
+
                for (parent = (*list)->parents; num_parents < 3 && parent;
                     parent = parent->next)
                        num_parents++;
@@ -564,7 +619,9 @@ static void write_graph_chunk_large_edges(struct hashfile *f,
                                                  commit_to_sha1);
 
                        if (edge_value < 0)
-                               edge_value = GRAPH_PARENT_MISSING;
+                               BUG("missing parent %s for commit %s",
+                                   oid_to_hex(&parent->item->object.oid),
+                                   oid_to_hex(&(*list)->object.oid));
                        else if (!parent->next)
                                edge_value |= GRAPH_LAST_EDGE;
 
@@ -638,15 +695,15 @@ static void add_missing_parents(struct packed_oid_list *oids, struct commit *com
 
 static void close_reachable(struct packed_oid_list *oids, int report_progress)
 {
-       int i, j;
+       int i;
        struct commit *commit;
        struct progress *progress = NULL;
 
        if (report_progress)
                progress = start_delayed_progress(
-                       _("Loading known commits in commit graph"), j = 0);
+                       _("Loading known commits in commit graph"), oids->nr);
        for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, ++j);
+               display_progress(progress, i + 1);
                commit = lookup_commit(the_repository, &oids->list[i]);
                if (commit)
                        commit->object.flags |= UNINTERESTING;
@@ -660,9 +717,9 @@ static void close_reachable(struct packed_oid_list *oids, int report_progress)
         */
        if (report_progress)
                progress = start_delayed_progress(
-                       _("Expanding reachable commits in commit graph"), j = 0);
+                       _("Expanding reachable commits in commit graph"), oids->nr);
        for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, ++j);
+               display_progress(progress, i + 1);
                commit = lookup_commit(the_repository, &oids->list[i]);
 
                if (commit && !parse_commit(commit))
@@ -672,9 +729,9 @@ static void close_reachable(struct packed_oid_list *oids, int report_progress)
 
        if (report_progress)
                progress = start_delayed_progress(
-                       _("Clearing commit marks in commit graph"), j = 0);
+                       _("Clearing commit marks in commit graph"), oids->nr);
        for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, ++j);
+               display_progress(progress, i + 1);
                commit = lookup_commit(the_repository, &oids->list[i]);
 
                if (commit)
@@ -768,12 +825,17 @@ void write_commit_graph(const char *obj_dir,
        int num_extra_edges;
        struct commit_list *parent;
        struct progress *progress = NULL;
+       const unsigned hashsz = the_hash_algo->rawsz;
+       uint64_t progress_cnt = 0;
+       struct strbuf progress_title = STRBUF_INIT;
+       unsigned long approx_nr_objects;
 
        if (!commit_graph_compatible(the_repository))
                return;
 
        oids.nr = 0;
-       oids.alloc = approximate_object_count() / 32;
+       approx_nr_objects = approximate_object_count();
+       oids.alloc = approx_nr_objects / 32;
        oids.progress = NULL;
        oids.progress_done = 0;
 
@@ -803,8 +865,12 @@ void write_commit_graph(const char *obj_dir,
                strbuf_addf(&packname, "%s/pack/", obj_dir);
                dirlen = packname.len;
                if (report_progress) {
-                       oids.progress = start_delayed_progress(
-                               _("Finding commits for commit graph"), 0);
+                       strbuf_addf(&progress_title,
+                                   Q_("Finding commits for commit graph in %d pack",
+                                      "Finding commits for commit graph in %d packs",
+                                      pack_indexes->nr),
+                                   pack_indexes->nr);
+                       oids.progress = start_delayed_progress(progress_title.buf, 0);
                        oids.progress_done = 0;
                }
                for (i = 0; i < pack_indexes->nr; i++) {
@@ -816,19 +882,26 @@ void write_commit_graph(const char *obj_dir,
                                die(_("error adding pack %s"), packname.buf);
                        if (open_pack_index(p))
                                die(_("error opening index for %s"), packname.buf);
-                       for_each_object_in_pack(p, add_packed_commits, &oids, 0);
+                       for_each_object_in_pack(p, add_packed_commits, &oids,
+                                               FOR_EACH_OBJECT_PACK_ORDER);
                        close_pack(p);
                        free(p);
                }
                stop_progress(&oids.progress);
+               strbuf_reset(&progress_title);
                strbuf_release(&packname);
        }
 
        if (commit_hex) {
-               if (report_progress)
-                       progress = start_delayed_progress(
-                               _("Finding commits for commit graph"),
-                               commit_hex->nr);
+               if (report_progress) {
+                       strbuf_addf(&progress_title,
+                                   Q_("Finding commits for commit graph from %d ref",
+                                      "Finding commits for commit graph from %d refs",
+                                      commit_hex->nr),
+                                   commit_hex->nr);
+                       progress = start_delayed_progress(progress_title.buf,
+                                                         commit_hex->nr);
+               }
                for (i = 0; i < commit_hex->nr; i++) {
                        const char *end;
                        struct object_id oid;
@@ -848,27 +921,38 @@ void write_commit_graph(const char *obj_dir,
                        }
                }
                stop_progress(&progress);
+               strbuf_reset(&progress_title);
        }
 
        if (!pack_indexes && !commit_hex) {
                if (report_progress)
                        oids.progress = start_delayed_progress(
-                               _("Finding commits for commit graph"), 0);
-               for_each_packed_object(add_packed_commits, &oids, 0);
+                               _("Finding commits for commit graph among packed objects"),
+                               approx_nr_objects);
+               for_each_packed_object(add_packed_commits, &oids,
+                                      FOR_EACH_OBJECT_PACK_ORDER);
+               if (oids.progress_done < approx_nr_objects)
+                       display_progress(oids.progress, approx_nr_objects);
                stop_progress(&oids.progress);
        }
 
        close_reachable(&oids, report_progress);
 
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Counting distinct commits in commit graph"),
+                       oids.nr);
+       display_progress(progress, 0); /* TODO: Measure QSORT() progress */
        QSORT(oids.list, oids.nr, commit_compare);
-
        count_distinct = 1;
        for (i = 1; i < oids.nr; i++) {
+               display_progress(progress, i + 1);
                if (!oideq(&oids.list[i - 1], &oids.list[i]))
                        count_distinct++;
        }
+       stop_progress(&progress);
 
-       if (count_distinct >= GRAPH_PARENT_MISSING)
+       if (count_distinct >= GRAPH_EDGE_LAST_MASK)
                die(_("the commit graph format cannot write %d commits"), count_distinct);
 
        commits.nr = 0;
@@ -876,8 +960,13 @@ void write_commit_graph(const char *obj_dir,
        ALLOC_ARRAY(commits.list, commits.alloc);
 
        num_extra_edges = 0;
+       if (report_progress)
+               progress = start_delayed_progress(
+                       _("Finding extra edges in commit graph"),
+                       oids.nr);
        for (i = 0; i < oids.nr; i++) {
                int num_parents = 0;
+               display_progress(progress, i + 1);
                if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
                        continue;
 
@@ -894,8 +983,9 @@ void write_commit_graph(const char *obj_dir,
                commits.nr++;
        }
        num_chunks = num_extra_edges ? 4 : 3;
+       stop_progress(&progress);
 
-       if (commits.nr >= GRAPH_PARENT_MISSING)
+       if (commits.nr >= GRAPH_EDGE_LAST_MASK)
                die(_("too many commits to write graph"));
 
        compute_generation_numbers(&commits, report_progress);
@@ -913,7 +1003,7 @@ void write_commit_graph(const char *obj_dir,
        hashwrite_be32(f, GRAPH_SIGNATURE);
 
        hashwrite_u8(f, GRAPH_VERSION);
-       hashwrite_u8(f, GRAPH_OID_VERSION);
+       hashwrite_u8(f, oid_version());
        hashwrite_u8(f, num_chunks);
        hashwrite_u8(f, 0); /* unused padding byte */
 
@@ -921,15 +1011,15 @@ void write_commit_graph(const char *obj_dir,
        chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
        chunk_ids[2] = GRAPH_CHUNKID_DATA;
        if (num_extra_edges)
-               chunk_ids[3] = GRAPH_CHUNKID_LARGEEDGES;
+               chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
        else
                chunk_ids[3] = 0;
        chunk_ids[4] = 0;
 
        chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
        chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
-       chunk_offsets[2] = chunk_offsets[1] + GRAPH_OID_LEN * commits.nr;
-       chunk_offsets[3] = chunk_offsets[2] + (GRAPH_OID_LEN + 16) * commits.nr;
+       chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
+       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
        chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
 
        for (i = 0; i <= num_chunks; i++) {
@@ -941,10 +1031,23 @@ void write_commit_graph(const char *obj_dir,
                hashwrite(f, chunk_write, 12);
        }
 
-       write_graph_chunk_fanout(f, commits.list, commits.nr);
-       write_graph_chunk_oids(f, GRAPH_OID_LEN, commits.list, commits.nr);
-       write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
-       write_graph_chunk_large_edges(f, commits.list, commits.nr);
+       if (report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Writing out commit graph in %d pass",
+                              "Writing out commit graph in %d passes",
+                              num_chunks),
+                           num_chunks);
+               progress = start_delayed_progress(
+                       progress_title.buf,
+                       num_chunks * commits.nr);
+       }
+       write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
+       write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
+       write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
+       if (num_extra_edges)
+               write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
+       stop_progress(&progress);
+       strbuf_release(&progress_title);
 
        close_commit_graph(the_repository);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
@@ -1029,7 +1132,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
                }
 
                graph_commit = lookup_commit(r, &cur_oid);
-               if (!parse_commit_in_graph_one(g, graph_commit))
+               if (!parse_commit_in_graph_one(r, g, graph_commit))
                        graph_report("failed to parse %s from commit-graph",
                                     oid_to_hex(&cur_oid));
        }
@@ -1065,7 +1168,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
                        continue;
                }
 
-               if (!oideq(&get_commit_tree_in_graph_one(g, graph_commit)->object.oid,
+               if (!oideq(&get_commit_tree_in_graph_one(r, g, graph_commit)->object.oid,
                           get_commit_tree_oid(odb_commit)))
                        graph_report("root tree OID for commit %s in commit-graph is %s != %s",
                                     oid_to_hex(&cur_oid),
index 9db40b4..096d8ba 100644 (file)
@@ -49,11 +49,14 @@ struct commit_graph {
        const uint32_t *chunk_oid_fanout;
        const unsigned char *chunk_oid_lookup;
        const unsigned char *chunk_commit_data;
-       const unsigned char *chunk_large_edges;
+       const unsigned char *chunk_extra_edges;
 };
 
 struct commit_graph *load_commit_graph_one(const char *graph_file);
 
+struct commit_graph *parse_commit_graph(void *graph_map, int fd,
+                                       size_t graph_size);
+
 /*
  * Return 1 if and only if the repository has a commit-graph
  * file and generation numbers are computed in that file.
index d5a39de..3ea1747 100644 (file)
@@ -30,7 +30,8 @@ static int queue_has_nonstale(struct prio_queue *queue)
 }
 
 /* all input commits in one and twos[] must have been parsed! */
-static struct commit_list *paint_down_to_common(struct commit *one, int n,
+static struct commit_list *paint_down_to_common(struct repository *r,
+                                               struct commit *one, int n,
                                                struct commit **twos,
                                                int min_generation)
 {
@@ -83,7 +84,7 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n,
                        parents = parents->next;
                        if ((p->object.flags & flags) == flags)
                                continue;
-                       if (parse_commit(p))
+                       if (repo_parse_commit(r, p))
                                return NULL;
                        p->object.flags |= flags;
                        prio_queue_put(&queue, p);
@@ -94,7 +95,9 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n,
        return result;
 }
 
-static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
+static struct commit_list *merge_bases_many(struct repository *r,
+                                           struct commit *one, int n,
+                                           struct commit **twos)
 {
        struct commit_list *list = NULL;
        struct commit_list *result = NULL;
@@ -109,14 +112,14 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
                        return commit_list_insert(one, &result);
        }
 
-       if (parse_commit(one))
+       if (repo_parse_commit(r, one))
                return NULL;
        for (i = 0; i < n; i++) {
-               if (parse_commit(twos[i]))
+               if (repo_parse_commit(r, twos[i]))
                        return NULL;
        }
 
-       list = paint_down_to_common(one, n, twos, 0);
+       list = paint_down_to_common(r, one, n, twos, 0);
 
        while (list) {
                struct commit *commit = pop_commit(&list);
@@ -153,7 +156,7 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
        return ret;
 }
 
-static int remove_redundant(struct commit **array, int cnt)
+static int remove_redundant(struct repository *r, struct commit **array, int cnt)
 {
        /*
         * Some commit in the array may be an ancestor of
@@ -171,7 +174,7 @@ static int remove_redundant(struct commit **array, int cnt)
        ALLOC_ARRAY(filled_index, cnt - 1);
 
        for (i = 0; i < cnt; i++)
-               parse_commit(array[i]);
+               repo_parse_commit(r, array[i]);
        for (i = 0; i < cnt; i++) {
                struct commit_list *common;
                uint32_t min_generation = array[i]->generation;
@@ -187,8 +190,8 @@ static int remove_redundant(struct commit **array, int cnt)
                        if (array[j]->generation < min_generation)
                                min_generation = array[j]->generation;
                }
-               common = paint_down_to_common(array[i], filled, work,
-                                             min_generation);
+               common = paint_down_to_common(r, array[i], filled,
+                                             work, min_generation);
                if (array[i]->object.flags & PARENT2)
                        redundant[i] = 1;
                for (j = 0; j < filled; j++)
@@ -213,7 +216,8 @@ static int remove_redundant(struct commit **array, int cnt)
        return filled;
 }
 
-static struct commit_list *get_merge_bases_many_0(struct commit *one,
+static struct commit_list *get_merge_bases_many_0(struct repository *r,
+                                                 struct commit *one,
                                                  int n,
                                                  struct commit **twos,
                                                  int cleanup)
@@ -223,7 +227,7 @@ static struct commit_list *get_merge_bases_many_0(struct commit *one,
        struct commit_list *result;
        int cnt, i;
 
-       result = merge_bases_many(one, n, twos);
+       result = merge_bases_many(r, one, n, twos);
        for (i = 0; i < n; i++) {
                if (one == twos[i])
                        return result;
@@ -246,7 +250,7 @@ static struct commit_list *get_merge_bases_many_0(struct commit *one,
        clear_commit_marks(one, all_flags);
        clear_commit_marks_many(n, twos, all_flags);
 
-       cnt = remove_redundant(rslt, cnt);
+       cnt = remove_redundant(r, rslt, cnt);
        result = NULL;
        for (i = 0; i < cnt; i++)
                commit_list_insert_by_date(rslt[i], &result);
@@ -254,23 +258,27 @@ static struct commit_list *get_merge_bases_many_0(struct commit *one,
        return result;
 }
 
-struct commit_list *get_merge_bases_many(struct commit *one,
-                                        int n,
-                                        struct commit **twos)
+struct commit_list *repo_get_merge_bases_many(struct repository *r,
+                                             struct commit *one,
+                                             int n,
+                                             struct commit **twos)
 {
-       return get_merge_bases_many_0(one, n, twos, 1);
+       return get_merge_bases_many_0(r, one, n, twos, 1);
 }
 
-struct commit_list *get_merge_bases_many_dirty(struct commit *one,
-                                              int n,
-                                              struct commit **twos)
+struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
+                                                   struct commit *one,
+                                                   int n,
+                                                   struct commit **twos)
 {
-       return get_merge_bases_many_0(one, n, twos, 0);
+       return get_merge_bases_many_0(r, one, n, twos, 0);
 }
 
-struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
+struct commit_list *repo_get_merge_bases(struct repository *r,
+                                        struct commit *one,
+                                        struct commit *two)
 {
-       return get_merge_bases_many_0(one, 1, &two, 1);
+       return get_merge_bases_many_0(r, one, 1, &two, 1);
 }
 
 /*
@@ -304,16 +312,17 @@ int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
 /*
  * Is "commit" an ancestor of one of the "references"?
  */
-int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference)
+int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
+                            int nr_reference, struct commit **reference)
 {
        struct commit_list *bases;
        int ret = 0, i;
        uint32_t min_generation = GENERATION_NUMBER_INFINITY;
 
-       if (parse_commit(commit))
+       if (repo_parse_commit(r, commit))
                return ret;
        for (i = 0; i < nr_reference; i++) {
-               if (parse_commit(reference[i]))
+               if (repo_parse_commit(r, reference[i]))
                        return ret;
                if (reference[i]->generation < min_generation)
                        min_generation = reference[i]->generation;
@@ -322,7 +331,9 @@ int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit *
        if (commit->generation > min_generation)
                return ret;
 
-       bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
+       bases = paint_down_to_common(r, commit,
+                                    nr_reference, reference,
+                                    commit->generation);
        if (commit->object.flags & PARENT2)
                ret = 1;
        clear_commit_marks(commit, all_flags);
@@ -334,9 +345,11 @@ int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit *
 /*
  * Is "commit" an ancestor of (i.e. reachable from) the "reference"?
  */
-int in_merge_bases(struct commit *commit, struct commit *reference)
+int repo_in_merge_bases(struct repository *r,
+                       struct commit *commit,
+                       struct commit *reference)
 {
-       return in_merge_bases_many(commit, 1, &reference);
+       return repo_in_merge_bases_many(r, commit, 1, &reference);
 }
 
 struct commit_list *reduce_heads(struct commit_list *heads)
@@ -365,7 +378,7 @@ struct commit_list *reduce_heads(struct commit_list *heads)
                        p->item->object.flags &= ~STALE;
                }
        }
-       num_head = remove_redundant(array, num_head);
+       num_head = remove_redundant(the_repository, array, num_head);
        for (i = 0; i < num_head; i++)
                tail = &commit_list_insert(array[i], tail)->next;
        free(array);
index fb8082a..99a43e8 100644 (file)
@@ -9,21 +9,35 @@ struct ref_filter;
 struct object_id;
 struct object_array;
 
-struct commit_list *get_merge_bases_many(struct commit *one,
-                                        int n,
-                                        struct commit **twos);
-struct commit_list *get_merge_bases_many_dirty(struct commit *one,
-                                              int n,
-                                              struct commit **twos);
-struct commit_list *get_merge_bases(struct commit *one, struct commit *two);
-struct commit_list *get_octopus_merge_bases(struct commit_list *in);
-
+struct commit_list *repo_get_merge_bases(struct repository *r,
+                                        struct commit *rev1,
+                                        struct commit *rev2);
+struct commit_list *repo_get_merge_bases_many(struct repository *r,
+                                             struct commit *one, int n,
+                                             struct commit **twos);
 /* To be used only when object flags after this call no longer matter */
-struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos);
+struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
+                                                   struct commit *one, int n,
+                                                   struct commit **twos);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define get_merge_bases(r1, r2)           repo_get_merge_bases(the_repository, r1, r2)
+#define get_merge_bases_many(one, n, two) repo_get_merge_bases_many(the_repository, one, n, two)
+#define get_merge_bases_many_dirty(one, n, twos) repo_get_merge_bases_many_dirty(the_repository, one, n, twos)
+#endif
+
+struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
 int is_descendant_of(struct commit *commit, struct commit_list *with_commit);
-int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference);
-int in_merge_bases(struct commit *commit, struct commit *reference);
+int repo_in_merge_bases(struct repository *r,
+                       struct commit *commit,
+                       struct commit *reference);
+int repo_in_merge_bases_many(struct repository *r,
+                            struct commit *commit,
+                            int nr_reference, struct commit **reference);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define in_merge_bases(c1, c2) repo_in_merge_bases(the_repository, c1, c2)
+#define in_merge_bases_many(c1, n, cs) repo_in_merge_bases_many(the_repository, c1, n, cs)
+#endif
 
 /*
  * Takes a list of commits and returns a new list where those
index d13a7bc..a5333c7 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -299,13 +299,15 @@ const void *get_cached_commit_buffer(struct repository *r, const struct commit *
        return v->buffer;
 }
 
-const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
+const void *repo_get_commit_buffer(struct repository *r,
+                                  const struct commit *commit,
+                                  unsigned long *sizep)
 {
-       const void *ret = get_cached_commit_buffer(the_repository, commit, sizep);
+       const void *ret = get_cached_commit_buffer(r, commit, sizep);
        if (!ret) {
                enum object_type type;
                unsigned long size;
-               ret = read_object_file(&commit->object.oid, &type, &size);
+               ret = repo_read_object_file(r, &commit->object.oid, &type, &size);
                if (!ret)
                        die("cannot read commit object %s",
                            oid_to_hex(&commit->object.oid));
@@ -318,18 +320,20 @@ const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
        return ret;
 }
 
-void unuse_commit_buffer(const struct commit *commit, const void *buffer)
+void repo_unuse_commit_buffer(struct repository *r,
+                             const struct commit *commit,
+                             const void *buffer)
 {
        struct commit_buffer *v = buffer_slab_peek(
-               the_repository->parsed_objects->buffer_slab, commit);
+               r->parsed_objects->buffer_slab, commit);
        if (!(v && v->buffer == buffer))
                free((void *)buffer);
 }
 
-void free_commit_buffer(struct commit *commit)
+void free_commit_buffer(struct parsed_object_pool *pool, struct commit *commit)
 {
        struct commit_buffer *v = buffer_slab_peek(
-               the_repository->parsed_objects->buffer_slab, commit);
+               pool->buffer_slab, commit);
        if (v) {
                FREE_AND_NULL(v->buffer);
                v->size = 0;
@@ -352,13 +356,12 @@ struct object_id *get_commit_tree_oid(const struct commit *commit)
        return &get_commit_tree(commit)->object.oid;
 }
 
-void release_commit_memory(struct commit *c)
+void release_commit_memory(struct parsed_object_pool *pool, struct commit *c)
 {
        c->maybe_tree = NULL;
        c->index = 0;
-       free_commit_buffer(c);
+       free_commit_buffer(pool, c);
        free_commit_list(c->parents);
-       /* TODO: what about commit->util? */
 
        c->object.parsed = 0;
 }
@@ -445,7 +448,10 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        return 0;
 }
 
-int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph)
+int repo_parse_commit_internal(struct repository *r,
+                              struct commit *item,
+                              int quiet_on_missing,
+                              int use_commit_graph)
 {
        enum object_type type;
        void *buffer;
@@ -456,9 +462,9 @@ int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_com
                return -1;
        if (item->object.parsed)
                return 0;
-       if (use_commit_graph && parse_commit_in_graph(the_repository, item))
+       if (use_commit_graph && parse_commit_in_graph(r, item))
                return 0;
-       buffer = read_object_file(&item->object.oid, &type, &size);
+       buffer = repo_read_object_file(r, &item->object.oid, &type, &size);
        if (!buffer)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
@@ -469,18 +475,19 @@ int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_com
                             oid_to_hex(&item->object.oid));
        }
 
-       ret = parse_commit_buffer(the_repository, item, buffer, size, 0);
+       ret = parse_commit_buffer(r, item, buffer, size, 0);
        if (save_commit_buffer && !ret) {
-               set_commit_buffer(the_repository, item, buffer, size);
+               set_commit_buffer(r, item, buffer, size);
                return 0;
        }
        free(buffer);
        return ret;
 }
 
-int parse_commit_gently(struct commit *item, int quiet_on_missing)
+int repo_parse_commit_gently(struct repository *r,
+                            struct commit *item, int quiet_on_missing)
 {
-       return parse_commit_internal(item, quiet_on_missing, 1);
+       return repo_parse_commit_internal(r, item, quiet_on_missing, 1);
 }
 
 void parse_commit_or_die(struct commit *item)
index 9866453..42728c2 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -80,12 +80,21 @@ struct commit *lookup_commit_reference_by_name(const char *name);
 struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name);
 
 int parse_commit_buffer(struct repository *r, struct commit *item, const void *buffer, unsigned long size, int check_graph);
-int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph);
-int parse_commit_gently(struct commit *item, int quiet_on_missing);
-static inline int parse_commit(struct commit *item)
+int repo_parse_commit_internal(struct repository *r, struct commit *item,
+                              int quiet_on_missing, int use_commit_graph);
+int repo_parse_commit_gently(struct repository *r,
+                            struct commit *item,
+                            int quiet_on_missing);
+static inline int repo_parse_commit(struct repository *r, struct commit *item)
 {
-       return parse_commit_gently(item, 0);
+       return repo_parse_commit_gently(r, item, 0);
 }
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define parse_commit_internal(item, quiet, use) repo_parse_commit_internal(the_repository, item, quiet, use)
+#define parse_commit_gently(item, quiet) repo_parse_commit_gently(the_repository, item, quiet)
+#define parse_commit(item) repo_parse_commit(the_repository, item)
+#endif
+
 void parse_commit_or_die(struct commit *item);
 
 struct buffer_slab;
@@ -109,7 +118,12 @@ const void *get_cached_commit_buffer(struct repository *, const struct commit *,
  * from disk. The resulting memory should not be modified, and must be given
  * to unuse_commit_buffer when the caller is done.
  */
-const void *get_commit_buffer(const struct commit *, unsigned long *size);
+const void *repo_get_commit_buffer(struct repository *r,
+                                  const struct commit *,
+                                  unsigned long *size);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define get_commit_buffer(c, s) repo_get_commit_buffer(the_repository, c, s)
+#endif
 
 /*
  * Tell the commit subsytem that we are done with a particular commit buffer.
@@ -117,12 +131,17 @@ const void *get_commit_buffer(const struct commit *, unsigned long *size);
  * from an earlier call to get_commit_buffer.  The buffer may or may not be
  * freed by this call; callers should not access the memory afterwards.
  */
-void unuse_commit_buffer(const struct commit *, const void *buffer);
+void repo_unuse_commit_buffer(struct repository *r,
+                             const struct commit *,
+                             const void *buffer);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define unuse_commit_buffer(c, b) repo_unuse_commit_buffer(the_repository, c, b)
+#endif
 
 /*
  * Free any cached object buffer associated with the commit.
  */
-void free_commit_buffer(struct commit *);
+void free_commit_buffer(struct parsed_object_pool *pool, struct commit *);
 
 struct tree *get_commit_tree(const struct commit *);
 struct object_id *get_commit_tree_oid(const struct commit *);
@@ -131,7 +150,7 @@ struct object_id *get_commit_tree_oid(const struct commit *);
  * Release memory related to a commit, including the parent list and
  * any cached object buffer.
  */
-void release_commit_memory(struct commit *c);
+void release_commit_memory(struct parsed_object_pool *pool, struct commit *c);
 
 /*
  * Disassociate any cached object buffer from the commit, but do not free it.
@@ -162,6 +181,14 @@ extern int has_non_ascii(const char *text);
 extern const char *logmsg_reencode(const struct commit *commit,
                                   char **commit_encoding,
                                   const char *output_encoding);
+const char *repo_logmsg_reencode(struct repository *r,
+                                const struct commit *commit,
+                                char **commit_encoding,
+                                const char *output_encoding);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define logmsg_reencode(c, enc, out) repo_logmsg_reencode(the_repository, c, enc, out)
+#endif
+
 extern const char *skip_blank_lines(const char *msg);
 
 /** Removes the first commit from a list sorted by date, and adds all
index b459e1a..0af8684 100644 (file)
@@ -7,6 +7,7 @@
 #include "../cache.h"
 #include "win32/lazyload.h"
 #include "../config.h"
+#include "dir.h"
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -1031,7 +1032,7 @@ char *mingw_getcwd(char *pointer, int len)
  * See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
  * https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
  */
-static const char *quote_arg(const char *arg)
+static const char *quote_arg_msvc(const char *arg)
 {
        /* count chars to quote */
        int len = 0, n = 0;
@@ -1086,6 +1087,37 @@ static const char *quote_arg(const char *arg)
        return q;
 }
 
+#include "quote.h"
+
+static const char *quote_arg_msys2(const char *arg)
+{
+       struct strbuf buf = STRBUF_INIT;
+       const char *p2 = arg, *p;
+
+       for (p = arg; *p; p++) {
+               int ws = isspace(*p);
+               if (!ws && *p != '\\' && *p != '"' && *p != '{')
+                       continue;
+               if (!buf.len)
+                       strbuf_addch(&buf, '"');
+               if (p != p2)
+                       strbuf_add(&buf, p2, p - p2);
+               if (!ws && *p != '{')
+                       strbuf_addch(&buf, '\\');
+               p2 = p;
+       }
+
+       if (p == arg)
+               strbuf_addch(&buf, '"');
+       else if (!buf.len)
+               return arg;
+       else
+               strbuf_add(&buf, p2, p - p2),
+
+       strbuf_addch(&buf, '"');
+       return strbuf_detach(&buf, 0);
+}
+
 static const char *parse_interpreter(const char *cmd)
 {
        static char buf[100];
@@ -1317,6 +1349,47 @@ struct pinfo_t {
 static struct pinfo_t *pinfo = NULL;
 CRITICAL_SECTION pinfo_cs;
 
+/* Used to match and chomp off path components */
+static inline int match_last_path_component(const char *path, size_t *len,
+                                           const char *component)
+{
+       size_t component_len = strlen(component);
+       if (*len < component_len + 1 ||
+           !is_dir_sep(path[*len - component_len - 1]) ||
+           fspathncmp(path + *len - component_len, component, component_len))
+               return 0;
+       *len -= component_len + 1;
+       /* chomp off repeated dir separators */
+       while (*len > 0 && is_dir_sep(path[*len - 1]))
+               (*len)--;
+       return 1;
+}
+
+static int is_msys2_sh(const char *cmd)
+{
+       if (cmd && !strcmp(cmd, "sh")) {
+               static int ret = -1;
+               char *p;
+
+               if (ret >= 0)
+                       return ret;
+
+               p = path_lookup(cmd, 0);
+               if (!p)
+                       ret = 0;
+               else {
+                       size_t len = strlen(p);
+
+                       ret = match_last_path_component(p, &len, "sh.exe") &&
+                               match_last_path_component(p, &len, "bin") &&
+                               match_last_path_component(p, &len, "usr");
+                       free(p);
+               }
+               return ret;
+       }
+       return 0;
+}
+
 static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
@@ -1328,6 +1401,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
        unsigned flags = CREATE_UNICODE_ENVIRONMENT;
        BOOL ret;
        HANDLE cons;
+       const char *(*quote_arg)(const char *arg) =
+               is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
 
        do_unset_environment_variables();
 
index 4d1d95b..27cd5c1 100644 (file)
@@ -112,15 +112,15 @@ compat_symbol (libc, _obstack_compat, _obstack, GLIBC_2_0);
 
 # define CALL_CHUNKFUN(h, size) \
   (((h) -> use_extra_arg) \
-   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
-   : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))
+   ? (*(h)->chunkfun.extra) ((h)->extra_arg, (size)) \
+   : (*(h)->chunkfun.plain) ((size)))
 
 # define CALL_FREEFUN(h, old_chunk) \
   do { \
     if ((h) -> use_extra_arg) \
-      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+      (*(h)->freefun.extra) ((h)->extra_arg, (old_chunk)); \
     else \
-      (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
+      (*(h)->freefun.plain) ((old_chunk)); \
   } while (0)
 
 \f
@@ -159,8 +159,8 @@ _obstack_begin (struct obstack *h,
       size = 4096 - extra;
     }
 
-  h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
-  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunkfun.plain = chunkfun;
+  h->freefun.plain = freefun;
   h->chunk_size = size;
   h->alignment_mask = alignment - 1;
   h->use_extra_arg = 0;
@@ -206,8 +206,9 @@ _obstack_begin_1 (struct obstack *h, int size, int alignment,
       size = 4096 - extra;
     }
 
-  h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
-  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
+  h->chunkfun.extra = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
+  h->freefun.extra = (void (*) (void *, struct _obstack_chunk *)) freefun;
+
   h->chunk_size = size;
   h->alignment_mask = alignment - 1;
   h->extra_arg = arg;
index 6bc24b7..ced94d0 100644 (file)
@@ -160,11 +160,15 @@ struct obstack            /* control current object in current chunk */
     void *tempptr;
   } temp;                      /* Temporary for some macros.  */
   int   alignment_mask;                /* Mask of alignment for each object. */
-  /* These prototypes vary based on `use_extra_arg', and we use
-     casts to the prototypeless function type in all assignments,
-     but having prototypes here quiets -Wstrict-prototypes.  */
-  struct _obstack_chunk *(*chunkfun) (void *, long);
-  void (*freefun) (void *, struct _obstack_chunk *);
+  /* These prototypes vary based on `use_extra_arg'. */
+  union {
+    void *(*plain) (long);
+    struct _obstack_chunk *(*extra) (void *, long);
+  } chunkfun;
+  union {
+    void (*plain) (void *);
+    void (*extra) (void *, struct _obstack_chunk *);
+  } freefun;
   void *extra_arg;             /* first arg for chunk alloc/dealloc funcs */
   unsigned use_extra_arg:1;    /* chunk alloc/dealloc funcs take extra arg */
   unsigned maybe_empty_object:1;/* There is a possibility that the current
@@ -235,10 +239,10 @@ extern void (*obstack_alloc_failed_handler) (void);
                    (void (*) (void *, void *)) (freefun), (arg))
 
 #define obstack_chunkfun(h, newchunkfun) \
-  ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
+  ((h)->chunkfun.extra = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))
 
 #define obstack_freefun(h, newfreefun) \
-  ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
+  ((h)->freefun.extra = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))
 
 #define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar))
 
index 51cd60b..c0d8388 100644 (file)
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#if defined __TANDEM
+ /* This is currently duplicated from git-compat-utils.h */
+# ifdef NO_INTPTR_T
+ typedef long intptr_t;
+ typedef unsigned long uintptr_t;
+# endif
+#endif
+
 static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
                                          size_t length, reg_syntax_t syntax);
 static void re_compile_fastmap_iter (regex_t *bufp,
index bbeeff4..7354fe1 100644 (file)
@@ -6,6 +6,7 @@ CFLAGS += -pedantic
 # don't warn for each N_ use
 CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
 endif
+CFLAGS += -Wall
 CFLAGS += -Wdeclaration-after-statement
 CFLAGS += -Wformat-security
 CFLAGS += -Wno-format-zero-length
index e391431..7b36a1d 100644 (file)
@@ -442,26 +442,43 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        # INLINE='' would just replace one set of warnings with another and
        # still not compile in c89 mode, due to non-const array initializations.
        CC = cc -c99
+       # Build down-rev compatible objects that don't use our new getopt_long.
+       ifeq ($(uname_R).$(uname_V),J06.21)
+               CC += -WRVU=J06.20
+       endif
+       ifeq ($(uname_R).$(uname_V),L17.02)
+               CC += -WRVU=L16.05
+       endif
        # Disable all optimization, seems to result in bad code, with -O or -O2
        # or even -O1 (default), /usr/local/libexec/git-core/git-pack-objects
        # abends on "git push". Needs more investigation.
-       CFLAGS = -g -O0
+       CFLAGS = -g -O0 -Winline
        # We'd want it to be here.
        prefix = /usr/local
-       # Our's are in ${prefix}/bin (perl might also be in /usr/bin/perl).
-       PERL_PATH = ${prefix}/bin/perl
-       PYTHON_PATH = ${prefix}/bin/python
-
+       # perl and python must be in /usr/bin on NonStop - supplied by HPE
+       # with operating system in that managed directory.
+       PERL_PATH = /usr/bin/perl
+       PYTHON_PATH = /usr/bin/python
+       # The current /usr/coreutils/rm at lowest support level does not work
+       # with the git test structure. Long paths as in
+       # 'trash directory...' cause rm to terminate prematurely without fully
+       # removing the directory at OS releases J06.21 and L17.02.
+       # Default to the older rm until those two releases are deprecated.
+       RM = /bin/rm -f
        # As detected by './configure'.
        # Missdetected, hence commented out, see below.
        #NO_CURL = YesPlease
        # Added manually, see above.
+       NEEDS_SSL_WITH_CURL = YesPlease
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
+       HAVE_DEV_TTY = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
        HAVE_STRINGS_H = YesPlease
        NEEDS_LIBICONV = YesPlease
        NEEDS_LIBINTL_BEFORE_LIBICONV = YesPlease
        NO_SYS_SELECT_H = UnfortunatelyYes
        NO_D_TYPE_IN_DIRENT = YesPlease
+       NO_GETTEXT = YesPlease
        NO_HSTRERROR = YesPlease
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
@@ -471,7 +488,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        NO_MKDTEMP = YesPlease
        # Currently libiconv-1.9.1.
        OLD_ICONV = UnfortunatelyYes
-       NO_REGEX = YesPlease
+       NO_REGEX = NeedsStartEnd
        NO_PTHREADS = UnfortunatelyYes
 
        # Not detected (nor checked for) by './configure'.
index 24281b6..4813f00 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -296,7 +296,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
        struct ref **orig_list = list;
        int len = 0;
        enum get_remote_heads_state state = EXPECTING_FIRST_REF;
-       const char *arg;
 
        *list = NULL;
 
@@ -306,8 +305,6 @@ struct ref **get_remote_heads(struct packet_reader *reader,
                        die_initial_contact(1);
                case PACKET_READ_NORMAL:
                        len = reader->pktlen;
-                       if (len > 4 && skip_prefix(reader->line, "ERR ", &arg))
-                               die(_("remote error: %s"), arg);
                        break;
                case PACKET_READ_FLUSH:
                        state = EXPECTING_DONE;
index 6a7cf3e..3e536a9 100644 (file)
@@ -86,36 +86,6 @@ struct object_id OID;
 - hashcmp(OID.hash, OIDPTR->hash)
 + oidcmp(&OID, OIDPTR)
 
-@@
-struct object_id OID1, OID2;
-@@
-- hashcpy(OID1.hash, OID2.hash)
-+ oidcpy(&OID1, &OID2)
-
-@@
-identifier f != oidcpy;
-struct object_id *OIDPTR1;
-struct object_id *OIDPTR2;
-@@
-  f(...) {<...
-- hashcpy(OIDPTR1->hash, OIDPTR2->hash)
-+ oidcpy(OIDPTR1, OIDPTR2)
-  ...>}
-
-@@
-struct object_id *OIDPTR;
-struct object_id OID;
-@@
-- hashcpy(OIDPTR->hash, OID.hash)
-+ oidcpy(OIDPTR, &OID)
-
-@@
-struct object_id *OIDPTR;
-struct object_id OID;
-@@
-- hashcpy(OID.hash, OIDPTR->hash)
-+ oidcpy(&OID, OIDPTR)
-
 @@
 struct object_id *OIDPTR1;
 struct object_id *OIDPTR2;
diff --git a/contrib/coccinelle/the_repository.pending.cocci b/contrib/coccinelle/the_repository.pending.cocci
new file mode 100644 (file)
index 0000000..2ee702e
--- /dev/null
@@ -0,0 +1,144 @@
+// This file is used for the ongoing refactoring of
+// bringing the index or repository struct in all of
+// our code base.
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- read_object_file(
++ repo_read_object_file(the_repository,
+  E, F, G)
+
+@@
+expression E;
+@@
+- has_sha1_file(
++ repo_has_sha1_file(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+@@
+- has_sha1_file_with_flags(
++ repo_has_sha1_file_with_flags(the_repository,
+  E)
+
+@@
+expression E;
+@@
+- has_object_file(
++ repo_has_object_file(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+@@
+- has_object_file_with_flags(
++ repo_has_object_file_with_flags(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- parse_commit_internal(
++ repo_parse_commit_internal(the_repository,
+  E, F, G)
+
+@@
+expression E;
+expression F;
+@@
+- parse_commit_gently(
++ repo_parse_commit_gently(the_repository,
+  E, F)
+
+@@
+expression E;
+@@
+- parse_commit(
++ repo_parse_commit(the_repository,
+  E)
+
+@@
+expression E;
+expression F;
+@@
+- get_merge_bases(
++ repo_get_merge_bases(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- get_merge_bases_many(
++ repo_get_merge_bases_many(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- get_merge_bases_many_dirty(
++ repo_get_merge_bases_many_dirty(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+@@
+- in_merge_bases(
++ repo_in_merge_bases(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- in_merge_bases_many(
++ repo_in_merge_bases_many(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+@@
+- get_commit_buffer(
++ repo_get_commit_buffer(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+@@
+- unuse_commit_buffer(
++ repo_unuse_commit_buffer(the_repository,
+  E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- logmsg_reencode(
++ repo_logmsg_reencode(the_repository,
+  E, F, G);
+
+@@
+expression E;
+expression F;
+expression G;
+expression H;
+@@
+- format_commit_message(
++ repo_format_commit_message(the_repository,
+  E, F, G, H);
index 9e8ec95..499e56f 100644 (file)
@@ -438,7 +438,7 @@ __gitcomp_nl ()
 # Callers must take care of providing only paths that match the current path
 # to be completed and adding any prefix path components, if necessary.
 # 1: List of newline-separated matching paths, complete with all prefix
-#    path componens.
+#    path components.
 __gitcomp_file_direct ()
 {
        local IFS=$'\n'
@@ -855,7 +855,7 @@ __git_compute_merge_strategies ()
 
 __git_complete_revlist_file ()
 {
-       local pfx ls ref cur_="$cur"
+       local dequoted_word pfx ls ref cur_="$cur"
        case "$cur_" in
        *..?*:*)
                return
@@ -863,14 +863,18 @@ __git_complete_revlist_file ()
        ?*:*)
                ref="${cur_%%:*}"
                cur_="${cur_#*:}"
-               case "$cur_" in
+
+               __git_dequote "$cur_"
+
+               case "$dequoted_word" in
                ?*/*)
-                       pfx="${cur_%/*}"
-                       cur_="${cur_##*/}"
+                       pfx="${dequoted_word%/*}"
+                       cur_="${dequoted_word##*/}"
                        ls="$ref:$pfx"
                        pfx="$pfx/"
                        ;;
                *)
+                       cur_="$dequoted_word"
                        ls="$ref"
                        ;;
                esac
@@ -880,21 +884,10 @@ __git_complete_revlist_file ()
                *)   pfx="$ref:$pfx" ;;
                esac
 
-               __gitcomp_nl "$(__git ls-tree "$ls" \
-                               | sed '/^100... blob /{
-                                          s,^.*        ,,
-                                          s,$, ,
-                                      }
-                                      /^120000 blob /{
-                                          s,^.*        ,,
-                                          s,$, ,
-                                      }
-                                      /^040000 tree /{
-                                          s,^.*        ,,
-                                          s,$,/,
-                                      }
-                                      s/^.*    //')" \
-                       "$pfx" "$cur_" ""
+               __gitcomp_file "$(__git ls-tree "$ls" \
+                               | sed 's/^.*    //
+                                      s/$//')" \
+                       "$pfx" "$cur_"
                ;;
        *...*)
                pfx="${cur_%...*}..."
@@ -2993,7 +2986,7 @@ if [[ -n ${ZSH_VERSION-} ]] &&
 
                local IFS=$'\n'
                compset -P '*[=:]'
-               compadd -Q -f -- ${=1} && _ret=0
+               compadd -f -- ${=1} && _ret=0
        }
 
        __gitcomp_file ()
@@ -3002,7 +2995,7 @@ if [[ -n ${ZSH_VERSION-} ]] &&
 
                local IFS=$'\n'
                compset -P '*[=:]'
-               compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
+               compadd -p "${2-}" -f -- ${=1} && _ret=0
        }
 
        _git ()
index 049d6b8..886bf95 100644 (file)
@@ -99,7 +99,7 @@ __gitcomp_file_direct ()
 
        local IFS=$'\n'
        compset -P '*[=:]'
-       compadd -Q -f -- ${=1} && _ret=0
+       compadd -f -- ${=1} && _ret=0
 }
 
 __gitcomp_file ()
@@ -108,7 +108,7 @@ __gitcomp_file ()
 
        local IFS=$'\n'
        compset -P '*[=:]'
-       compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
+       compadd -p "${2-}" -f -- ${=1} && _ret=0
 }
 
 __git_zsh_bash_func ()
index 2076cf9..35791fd 100644 (file)
@@ -1,3 +1,59 @@
+Release 1.5.0
+=============
+
+Backward-incompatible change
+----------------------------
+
+The name of classes for environment was misnamed as `*Environement`.
+It is now `*Environment`.
+
+New features
+------------
+
+* A Thread-Index header is now added to each email sent (except for
+  combined emails where it would not make sense), so that MS Outlook
+  properly groups messages by threads even though they have a
+  different subject line. Unfortunately, even adding this header the
+  threading still seems to be unreliable, but it is unclear whether
+  this is an issue on our side or on MS Outlook's side (see discussion
+  here: https://github.com/git-multimail/git-multimail/pull/194).
+
+* A new variable multimailhook.ExcludeMergeRevisions was added to send
+  notification emails only for non-merge commits.
+
+* For gitolite environment, it is now possible to specify the mail map
+  in a separate file in addition to gitolite.conf, using the variable
+  multimailhook.MailaddressMap.
+
+Internal changes
+----------------
+
+* The testsuite now uses GIT_PRINT_SHA1_ELLIPSIS where needed for
+  compatibility with recent Git versions. Only tests are affected.
+
+* We don't try to install pyflakes in the continuous integration job
+  for old Python versions where it's no longer available.
+
+* Stop using the deprecated cgi.escape in Python 3.
+
+* New flake8 warnings have been fixed.
+
+* Python 3.6 is now tested against on Travis-CI.
+
+* A bunch of lgtm.com warnings have been fixed.
+
+Bug fixes
+---------
+
+* SMTPMailer logs in only once now. It used to re-login for each email
+  sent which triggered errors for some SMTP servers.
+
+* migrate-mailhook-config was broken by internal refactoring, it
+  should now work again.
+
+This version was tested with Python 2.6 to 3.7. It was tested with Git
+1.7.10.406.gdc801, 2.15.1 and 2.20.1.98.gecbdaf0.
+
 Release 1.4.0
 =============
 
index da65570..de20a54 100644 (file)
@@ -4,9 +4,8 @@ Contributing
 git-multimail is an open-source project, built by volunteers. We would
 welcome your help!
 
-The current maintainers are Matthieu Moy
-<matthieu.moy@grenoble-inp.fr> and Michael Haggerty
-<mhagger@alum.mit.edu>.
+The current maintainers are `Matthieu Moy <http://matthieu-moy.fr>`__ and
+`Michael Haggerty <https://github.com/mhagger>`__.
 
 Please note that although a copy of git-multimail is distributed in
 the "contrib" section of the main Git project, development takes place
@@ -33,6 +32,29 @@ mailing list`_.
 Please CC emails regarding git-multimail to the maintainers so that we
 don't overlook them.
 
+Help needed: testers/maintainer for specific environments/OS
+------------------------------------------------------------
+
+The current maintainer uses and tests git-multimail on Linux with the
+Generic environment. More testers, or better contributors are needed
+to test git-multimail on other real-life setups:
+
+* Mac OS X, Windows: git-multimail is currently not supported on these
+  platforms. But since we have no external dependencies and try to
+  write code as portable as possible, it is possible that
+  git-multimail already runs there and if not, it is likely that it
+  could be ported easily.
+
+  Patches to improve support for Windows and OS X are welcome.
+  Ideally, there would be a sub-maintainer for each OS who would test
+  at least once before each release (around twice a year).
+
+* Gerrit, Stash, Gitolite environments: although the testsuite
+  contains tests for these environments, a tester/maintainer for each
+  environment would be welcome to test and report failure (or success)
+  on real-life environments periodically (here also, feedback before
+  each release would be highly appreciated).
+
 
 .. _`git-multimail repository on GitHub`: https://github.com/git-multimail/git-multimail
 .. _`Git mailing list`: git@vger.kernel.org
index 161b023..0444442 100644 (file)
@@ -6,10 +6,10 @@ website:
     https://github.com/git-multimail/git-multimail
 
 The version in this directory was obtained from the upstream project
-on August 17 2016 and consists of the "git-multimail" subdirectory from
+on January 07 2019 and consists of the "git-multimail" subdirectory from
 revision
 
-    07b1cb6bfd7be156c62e1afa17cae13b850a869f refs/tags/1.4.0
+    04e80e6c40be465cc62b6c246f0fcb8fd2cfd454 refs/tags/1.5.0
 
 Please see the README file in this directory for information about how
 to report bugs or contribute to git-multimail.
similarity index 95%
rename from contrib/hooks/multimail/README
rename to contrib/hooks/multimail/README.rst
index 5105373..7c0fc4a 100644 (file)
@@ -1,4 +1,4 @@
-git-multimail version 1.4.0
+git-multimail version 1.5.0
 ===========================
 
 .. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master
@@ -20,8 +20,8 @@ GPLv2 (see the COPYING file for details).
 
 Please note: although, as a convenience, git-multimail may be
 distributed along with the main Git project, development of
-git-multimail takes place in its own, separate project.  See section
-"Getting involved" below for more information.
+git-multimail takes place in its own, separate project.  Please, read
+`<CONTRIBUTING.rst>`__ for more information.
 
 
 By default, for each push received by the repository, git-multimail:
@@ -89,6 +89,10 @@ Requirements
   the multimailhook.mailer configuration variable below for how to
   configure git-multimail to send emails via an SMTP server.
 
+* git-multimail is currently tested only on Linux. It may or may not
+  work on other platforms such as Windows and Mac OS. See
+  `<CONTRIBUTING.rst>`__ to improve the situation.
+
 
 Invocation
 ----------
@@ -369,7 +373,7 @@ multimailhook.mailer
         unset, then the value of multimailhook.from is used.
 
       multimailhook.smtpServerTimeout
-        Timeout in seconds.
+        Timeout in seconds. Default is 10.
 
       multimailhook.smtpEncryption
         Set the security type. Allowed values: ``none``, ``ssl``, ``tls`` (starttls).
@@ -419,8 +423,20 @@ multimailhook.from, multimailhook.fromCommit, multimailhook.fromRefchange
     If config values are unset, the value of the From: header is
     determined as follows:
 
-    1. (gitolite environment only) Parse gitolite.conf, looking for a
-       block of comments that looks like this::
+    1. (gitolite environment only)
+       1.a) If ``multimailhook.MailaddressMap`` is set, and is a path
+       to an existing file (if relative, it is considered relative to
+       the place where ``gitolite.conf`` is located), then this file
+       should contain lines like::
+
+           username Firstname Lastname <email@example.com>
+
+       git-multimail will then look for a line where ``$GL_USER``
+       matches the ``username`` part, and use the rest of the line for
+       the ``From:`` header.
+
+       1.b) Parse gitolite.conf, looking for a block of comments that
+       looks like this::
 
            # BEGIN USER EMAILS
            # username Firstname Lastname <email@example.com>
@@ -436,6 +452,11 @@ multimailhook.from, multimailhook.fromCommit, multimailhook.fromRefchange
 
     3. Use the value of multimailhook.envelopeSender.
 
+multimailhook.MailaddressMap
+    (gitolite environment only)
+    File to look for a ``From:`` address based on the user doing the
+    push. Defaults to unset. See ``multimailhook.from`` for details.
+
 multimailhook.administrator
     The name and/or email address of the administrator of the Git
     repository; used in FOOTER_TEMPLATE.  Default is
@@ -484,6 +505,11 @@ multimailhook.maxCommitEmails
     mailbombing, for example on an initial push.  To disable commit
     emails limit, set this option to 0.  The default is 500.
 
+multimailhook.excludeMergeRevisions
+    When sending out revision emails, do not consider merge commits (the
+    functional equivalent of `rev-list --no-merges`).
+    The default is `false` (send merge commit emails).
+
 multimailhook.emailStrictUTF8
     If this boolean option is set to `true`, then the main part of the
     email body is forced to be valid UTF-8.  Any characters that are
index 00aedd9..5054833 100644 (file)
@@ -46,6 +46,15 @@ and add::
       config multimailhook.mailingList = # Where emails should be sent
       config multimailhook.from = # From address to use
 
+Note that by default, gitolite forbids ``<`` and ``>`` in variable
+values (for security/paranoia reasons, see
+`compensating for UNSAFE_PATT
+<http://gitolite.com/gitolite/git-config/index.html#compensating-for-unsafe95patt>`__
+in gitolite's documentation for explanations and a way to disable
+this). As a consequence, you will not be able to use ``First Last
+<First.Last@example.com>`` as recipient email, but specifying
+``First.Last@example.com`` alone works.
+
 Obviously, you can customize all parameters on a per-repository basis by
 adding these ``config multimailhook.*`` lines in the section
 corresponding to a repository or set of repositories.
index 73fdda6..8823399 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/env python
 
-__version__ = '1.4.0'
+__version__ = '1.5.0'
 
 # Copyright (c) 2015-2016 Matthieu Moy and others
 # Copyright (c) 2012-2014 Michael Haggerty and others
@@ -64,7 +64,9 @@ except ImportError:
     # Python < 2.6 do not have ssl, but that's OK if we don't use it.
     pass
 import time
-import cgi
+
+import uuid
+import base64
 
 PYTHON3 = sys.version_info >= (3, 0)
 
@@ -73,7 +75,7 @@ if sys.version_info <= (2, 5):
         for element in iterable:
             if not element:
                 return False
-            return True
+        return True
 
 
 def is_ascii(s):
@@ -108,6 +110,12 @@ if PYTHON3:
             return out.decode(sys.getdefaultencoding())
         except UnicodeEncodeError:
             return out.decode(ENCODING)
+
+    import html
+
+    def html_escape(s):
+        return html.escape(s)
+
 else:
     def is_string(s):
         try:
@@ -130,6 +138,10 @@ else:
     def next(it):
         return it.next()
 
+    import cgi
+
+    def html_escape(s):
+        return cgi.escape(s, True)
 
 try:
     from email.charset import Charset
@@ -190,6 +202,7 @@ Content-Transfer-Encoding: 8bit
 Message-ID: %(msgid)s
 From: %(fromaddr)s
 Reply-To: %(reply_to)s
+Thread-Index: %(thread_index)s
 X-Git-Host: %(fqdn)s
 X-Git-Repo: %(repo_shortname)s
 X-Git-Refname: %(refname)s
@@ -322,6 +335,7 @@ From: %(fromaddr)s
 Reply-To: %(reply_to)s
 In-Reply-To: %(reply_to_msgid)s
 References: %(reply_to_msgid)s
+Thread-Index: %(thread_index)s
 X-Git-Host: %(fqdn)s
 X-Git-Repo: %(repo_shortname)s
 X-Git-Refname: %(refname)s
@@ -763,6 +777,9 @@ class GitObject(object):
     def __eq__(self, other):
         return isinstance(other, GitObject) and self.sha1 == other.sha1
 
+    def __ne__(self, other):
+        return not self == other
+
     def __hash__(self):
         return hash(self.sha1)
 
@@ -852,7 +869,7 @@ class Change(object):
         if html_escape_val:
             for k in values:
                 if is_string(values[k]):
-                    values[k] = cgi.escape(values[k], True)
+                    values[k] = html_escape(values[k])
         for line in template.splitlines(True):
             yield line % values
 
@@ -909,7 +926,7 @@ class Change(object):
 
         raise NotImplementedError()
 
-    def generate_email_body(self):
+    def generate_email_body(self, push):
         """Generate the main part of the email body, a line at a time.
 
         The text in the body might be truncated after a specified
@@ -936,7 +953,7 @@ class Change(object):
             yield "<pre style='margin:0'>\n"
 
             for line in lines:
-                yield cgi.escape(line)
+                yield html_escape(line)
 
             yield '</pre>\n'
         else:
@@ -1011,7 +1028,7 @@ class Change(object):
                     fgcolor = '404040'
 
                 # Chop the trailing LF, we don't want it inside <pre>.
-                line = cgi.escape(line[:-1])
+                line = html_escape(line[:-1])
 
                 if bgcolor or fgcolor:
                     style = 'display:block; white-space:pre;'
@@ -1060,6 +1077,10 @@ class Revision(Change):
         self.author = read_git_output(['log', '--no-walk', '--format=%aN <%aE>', self.rev.sha1])
         self.recipients = self.environment.get_revision_recipients(self)
 
+        # -s is short for --no-patch, but -s works on older git's (e.g. 1.7)
+        self.parents = read_git_lines(['show', '-s', '--format=%P',
+                                      self.rev.sha1])[0].split()
+
         self.cc_recipients = ''
         if self.environment.get_scancommitforcc():
             self.cc_recipients = ', '.join(to.strip() for to in self._cc_recipients())
@@ -1090,6 +1111,7 @@ class Revision(Change):
             oneline = oneline[:max_subject_length - 6] + ' [...]'
 
         values['rev'] = self.rev.sha1
+        values['parents'] = ' '.join(self.parents)
         values['rev_short'] = self.rev.short
         values['change_type'] = self.change_type
         values['refname'] = self.refname
@@ -1097,6 +1119,7 @@ class Revision(Change):
         values['short_refname'] = self.reference_change.short_refname
         values['refname_type'] = self.reference_change.refname_type
         values['reply_to_msgid'] = self.reference_change.msgid
+        values['thread_index'] = self.reference_change.thread_index
         values['num'] = self.num
         values['tot'] = self.tot
         values['recipients'] = self.recipients
@@ -1244,6 +1267,23 @@ class ReferenceChange(Change):
             old=old, new=new, rev=rev,
             )
 
+    @staticmethod
+    def make_thread_index():
+        """Return a string appropriate for the Thread-Index header,
+        needed by MS Outlook to get threading right.
+
+        The format is (base64-encoded):
+        - 1 byte must be 1
+        - 5 bytes encode a date (hardcoded here)
+        - 16 bytes for a globally unique identifier
+
+        FIXME: Unfortunately, even with the Thread-Index field, MS
+        Outlook doesn't seem to do the threading reliably (see
+        https://github.com/git-multimail/git-multimail/pull/194).
+        """
+        thread_index = b'\x01\x00\x00\x12\x34\x56' + uuid.uuid4().bytes
+        return base64.standard_b64encode(thread_index).decode('ascii')
+
     def __init__(self, environment, refname, short_refname, old, new, rev):
         Change.__init__(self, environment)
         self.change_type = {
@@ -1257,6 +1297,7 @@ class ReferenceChange(Change):
         self.new = new
         self.rev = rev
         self.msgid = make_msgid()
+        self.thread_index = self.make_thread_index()
         self.diffopts = environment.diffopts
         self.graphopts = environment.graphopts
         self.logopts = environment.logopts
@@ -1276,6 +1317,7 @@ class ReferenceChange(Change):
         values['refname'] = self.refname
         values['short_refname'] = self.short_refname
         values['msgid'] = self.msgid
+        values['thread_index'] = self.thread_index
         values['recipients'] = self.recipients
         values['oldrev'] = str(self.old)
         values['oldrev_short'] = self.old.short
@@ -1941,6 +1983,9 @@ class Mailer(object):
     def __init__(self, environment):
         self.environment = environment
 
+    def close(self):
+        pass
+
     def send(self, lines, to_addrs):
         """Send an email consisting of lines.
 
@@ -2054,6 +2099,7 @@ class SMTPMailer(Mailer):
         self.username = smtpuser
         self.password = smtppass
         self.smtpcacerts = smtpcacerts
+        self.loggedin = False
         try:
             def call(klass, server, timeout):
                 try:
@@ -2130,20 +2176,30 @@ class SMTPMailer(Mailer):
                 % (self.smtpserver, sys.exc_info()[1]))
             sys.exit(1)
 
-    def __del__(self):
+    def close(self):
         if hasattr(self, 'smtp'):
             self.smtp.quit()
             del self.smtp
 
+    def __del__(self):
+        self.close()
+
     def send(self, lines, to_addrs):
         try:
             if self.username or self.password:
-                self.smtp.login(self.username, self.password)
+                if not self.loggedin:
+                    self.smtp.login(self.username, self.password)
+                    self.loggedin = True
             msg = ''.join(lines)
             # turn comma-separated list into Python list if needed.
             if is_string(to_addrs):
                 to_addrs = [email for (name, email) in getaddresses([to_addrs])]
             self.smtp.sendmail(self.envelopesender, to_addrs, msg)
+        except socket.timeout:
+            self.environment.get_logger().error(
+                '*** Error sending email ***\n'
+                '*** SMTP server timed out (timeout is %s)\n'
+                % self.smtpservertimeout)
         except smtplib.SMTPResponseException:
             err = sys.exc_info()[1]
             self.environment.get_logger().error(
@@ -2171,7 +2227,8 @@ class OutputMailer(Mailer):
 
     SEPARATOR = '=' * 75 + '\n'
 
-    def __init__(self, f):
+    def __init__(self, f, environment=None):
+        super(OutputMailer, self).__init__(environment=environment)
         self.f = f
 
     def send(self, lines, to_addrs):
@@ -2382,6 +2439,7 @@ class Environment(object):
         self.html_in_footer = False
         self.commitBrowseURL = None
         self.maxcommitemails = 500
+        self.excludemergerevisions = False
         self.diffopts = ['--stat', '--summary', '--find-copies-harder']
         self.graphopts = ['--oneline', '--decorate']
         self.logopts = []
@@ -2621,6 +2679,8 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):
 
         self.commitBrowseURL = config.get('commitBrowseURL')
 
+        self.excludemergerevisions = config.get('excludeMergeRevisions')
+
         maxcommitemails = config.get('maxcommitemails')
         if maxcommitemails is not None:
             try:
@@ -3152,7 +3212,10 @@ class GitoliteEnvironmentHighPrecMixin(Environment):
         return self.osenv.get('GL_USER', 'unknown user')
 
 
-class GitoliteEnvironmentLowPrecMixin(Environment):
+class GitoliteEnvironmentLowPrecMixin(
+        ConfigEnvironmentMixin,
+        Environment):
+
     def get_repo_shortname(self):
         # The gitolite environment variable $GL_REPO is a pretty good
         # repo_shortname (though it's probably not as good as a value
@@ -3162,6 +3225,16 @@ class GitoliteEnvironmentLowPrecMixin(Environment):
             super(GitoliteEnvironmentLowPrecMixin, self).get_repo_shortname()
             )
 
+    @staticmethod
+    def _compile_regex(re_template):
+        return (
+            re.compile(re_template % x)
+            for x in (
+                r'BEGIN\s+USER\s+EMAILS',
+                r'([^\s]+)\s+(.*)',
+                r'END\s+USER\s+EMAILS',
+                ))
+
     def get_fromaddr(self, change=None):
         GL_USER = self.osenv.get('GL_USER')
         if GL_USER is not None:
@@ -3174,18 +3247,42 @@ class GitoliteEnvironmentLowPrecMixin(Environment):
             GL_CONF = self.osenv.get(
                 'GL_CONF',
                 os.path.join(GL_ADMINDIR, 'conf', 'gitolite.conf'))
+
+            mailaddress_map = self.config.get('MailaddressMap')
+            # If relative, consider relative to GL_CONF:
+            if mailaddress_map:
+                mailaddress_map = os.path.join(os.path.dirname(GL_CONF),
+                                               mailaddress_map)
+                if os.path.isfile(mailaddress_map):
+                    f = open(mailaddress_map, 'rU')
+                    try:
+                        # Leading '#' is optional
+                        re_begin, re_user, re_end = self._compile_regex(
+                            r'^(?:\s*#)?\s*%s\s*$')
+                        for l in f:
+                            l = l.rstrip('\n')
+                            if re_begin.match(l) or re_end.match(l):
+                                continue  # Ignore these lines
+                            m = re_user.match(l)
+                            if m:
+                                if m.group(1) == GL_USER:
+                                    return m.group(2)
+                                else:
+                                    continue  # Not this user, but not an error
+                            raise ConfigurationException(
+                                "Syntax error in mail address map.\n"
+                                "Check file {}.\n"
+                                "Line: {}".format(mailaddress_map, l))
+
+                    finally:
+                        f.close()
+
             if os.path.isfile(GL_CONF):
                 f = open(GL_CONF, 'rU')
                 try:
                     in_user_emails_section = False
-                    re_template = r'^\s*#\s*%s\s*$'
-                    re_begin, re_user, re_end = (
-                        re.compile(re_template % x)
-                        for x in (
-                            r'BEGIN\s+USER\s+EMAILS',
-                            re.escape(GL_USER) + r'\s+(.*)',
-                            r'END\s+USER\s+EMAILS',
-                            ))
+                    re_begin, re_user, re_end = self._compile_regex(
+                        r'^\s*#\s*%s\s*$')
                     for l in f:
                         l = l.rstrip('\n')
                         if not in_user_emails_section:
@@ -3195,8 +3292,8 @@ class GitoliteEnvironmentLowPrecMixin(Environment):
                         if re_end.match(l):
                             break
                         m = re_user.match(l)
-                        if m:
-                            return m.group(1)
+                        if m and m.group(1) == GL_USER:
+                            return m.group(2)
                 finally:
                     f.close()
         return super(GitoliteEnvironmentLowPrecMixin, self).get_fromaddr(change)
@@ -3228,7 +3325,7 @@ class StashEnvironmentHighPrecMixin(Environment):
         self.__repo = repo
 
     def get_pusher(self):
-        return re.match('(.*?)\s*<', self.__user).group(1)
+        return re.match(r'(.*?)\s*<', self.__user).group(1)
 
     def get_pusher_email(self):
         return self.__user
@@ -3262,7 +3359,7 @@ class GerritEnvironmentHighPrecMixin(Environment):
             if self.__submitter.find('<') != -1:
                 # Submitter has a configured email, we transformed
                 # __submitter into an RFC 2822 string already.
-                return re.match('(.*?)\s*<', self.__submitter).group(1)
+                return re.match(r'(.*?)\s*<', self.__submitter).group(1)
             else:
                 # Submitter has no configured email, it's just his name.
                 return self.__submitter
@@ -3615,6 +3712,9 @@ class Push(object):
 
             for (num, sha1) in enumerate(sha1s):
                 rev = Revision(change, GitObject(sha1), num=num + 1, tot=len(sha1s))
+                if len(rev.parents) > 1 and change.environment.excludemergerevisions:
+                    # skipping a merge commit
+                    continue
                 if not rev.recipients and rev.cc_recipients:
                     change.environment.log_msg('*** Replacing Cc: with To:')
                     rev.recipients = rev.cc_recipients
@@ -3664,11 +3764,14 @@ def run_as_post_receive_hook(environment, mailer):
         changes.append(
             ReferenceChange.create(environment, oldrev, newrev, refname)
             )
-    if changes:
-        push = Push(environment, changes)
+    if not changes:
+        mailer.close()
+        return
+    push = Push(environment, changes)
+    try:
         push.send_emails(mailer, body_filter=environment.filter_body)
-    if hasattr(mailer, '__del__'):
-        mailer.__del__()
+    finally:
+        mailer.close()
 
 
 def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=False):
@@ -3687,10 +3790,14 @@ def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=
             refname,
             ),
         ]
+    if not changes:
+        mailer.close()
+        return
     push = Push(environment, changes, force_send)
-    push.send_emails(mailer, body_filter=environment.filter_body)
-    if hasattr(mailer, '__del__'):
-        mailer.__del__()
+    try:
+        push.send_emails(mailer, body_filter=environment.filter_body)
+    finally:
+        mailer.close()
 
 
 def check_ref_filter(environment):
@@ -3860,7 +3967,7 @@ def build_environment_klass(env_name):
         low_prec_mixin = known_env['lowprec']
         environment_mixins.append(low_prec_mixin)
     environment_mixins.append(Environment)
-    klass_name = env_name.capitalize() + 'Environement'
+    klass_name = env_name.capitalize() + 'Environment'
     environment_klass = type(
         klass_name,
         tuple(environment_mixins),
@@ -4057,21 +4164,21 @@ class Logger(object):
                 environment, 'git_multimail.error', environment.error_log_file, logging.ERROR)
             self.loggers.append(error_log_file)
 
-    def info(self, msg):
+    def info(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.info(msg)
+            l.info(msg, *args, **kwargs)
 
-    def debug(self, msg):
+    def debug(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.debug(msg)
+            l.debug(msg, *args, **kwargs)
 
-    def warning(self, msg):
+    def warning(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.warning(msg)
+            l.warning(msg, *args, **kwargs)
 
-    def error(self, msg):
+    def error(self, msg, *args, **kwargs):
         for l in self.loggers:
-            l.error(msg)
+            l.error(msg, *args, **kwargs)
 
 
 def main(args):
@@ -4189,7 +4296,7 @@ def main(args):
             show_env(environment, sys.stderr)
 
         if options.stdout or environment.stdout:
-            mailer = OutputMailer(sys.stdout)
+            mailer = OutputMailer(sys.stdout, environment)
         else:
             mailer = choose_mailer(config, environment)
 
@@ -4234,5 +4341,6 @@ def main(args):
             sys.stderr.write(msg)
         sys.exit(1)
 
+
 if __name__ == '__main__':
     main(sys.argv[1:])
index 992657b..241ba22 100755 (executable)
@@ -110,11 +110,12 @@ def is_section_empty(section, local):
 
     try:
         read_output(
-            ['git', 'config']
-            + local_option
-            ['--get-regexp', '^%s\.' % (section,)]
+            ['git', 'config'] +
+            local_option +
+            ['--get-regexp', '^%s\.' % (section,)]
             )
-    except CommandError, e:
+    except CommandError:
+        t, e, traceback = sys.exc_info()
         if e.retcode == 1:
             # This means that no settings were found.
             return True
@@ -188,7 +189,9 @@ def migrate_config(strict=False, retain=False, overwrite=False):
             sys.stderr.write(
                 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
                 )
-            new.set_recipients(name, old.get_recipients(name))
+            old_recipients = old.get_all(name, default=None)
+            old_recipients = ', '.join(o.strip() for o in old_recipients)
+            new.set_recipients(name, old_recipients)
 
     if strict:
         sys.stderr.write(
index 1ea113d..b9bb118 100755 (executable)
@@ -30,7 +30,6 @@ script's behavior could be changed or customized.
 """
 
 import sys
-import os
 
 # If necessary, add the path to the directory containing
 # git_multimail.py to the Python path as follows.  (This is not
@@ -86,6 +85,7 @@ mailer = git_multimail.choose_mailer(config, environment)
 
 # Use Python's smtplib to send emails.  Both arguments are required.
 #mailer = git_multimail.SMTPMailer(
+#    environment=environment,
 #    envelopesender='git-repo@example.com',
 #    # The smtpserver argument can also include a port number; e.g.,
 #    #     smtpserver='mail.example.com:25'
index 5f60c11..0d89ae7 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -778,7 +778,8 @@ static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
 
 static void handle_filter_error(const struct strbuf *filter_status,
                                struct cmd2process *entry,
-                               const unsigned int wanted_capability) {
+                               const unsigned int wanted_capability)
+{
        if (!strcmp(filter_status->buf, "error"))
                ; /* The filter signaled a problem with the file. */
        else if (!strcmp(filter_status->buf, "abort") && wanted_capability) {
index 4dfbc8c..ec1271f 100644 (file)
@@ -91,7 +91,8 @@ static timestamp_t check_expirations(void)
 }
 
 static int read_request(FILE *fh, struct credential *c,
-                       struct strbuf *action, int *timeout) {
+                       struct strbuf *action, int *timeout)
+{
        static struct strbuf item = STRBUF_INIT;
        const char *p;
 
index 191a930..2186bd0 100644 (file)
@@ -296,7 +296,7 @@ void resolve_tree_islands(struct repository *r,
                        if (S_ISGITLINK(entry.mode))
                                continue;
 
-                       obj = lookup_object(r, entry.oid->hash);
+                       obj = lookup_object(r, entry.oid.hash);
                        if (!obj)
                                continue;
 
diff --git a/diff.c b/diff.c
index f1e901a..e8c3e80 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -304,7 +304,9 @@ static unsigned parse_color_moved_ws(const char *arg)
                strbuf_addstr(&sb, i->string);
                strbuf_trim(&sb);
 
-               if (!strcmp(sb.buf, "ignore-space-change"))
+               if (!strcmp(sb.buf, "no"))
+                       ret = 0;
+               else if (!strcmp(sb.buf, "ignore-space-change"))
                        ret |= XDF_IGNORE_WHITESPACE_CHANGE;
                else if (!strcmp(sb.buf, "ignore-space-at-eol"))
                        ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
@@ -322,7 +324,7 @@ static unsigned parse_color_moved_ws(const char *arg)
 
        if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
            (ret & XDF_WHITESPACE_FLAGS)) {
-               error(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));
+               error(_("color-moved-ws: allow-indentation-change cannot be combined with other whitespace modes"));
                ret |= COLOR_MOVED_WS_ERROR;
        }
 
@@ -493,7 +495,7 @@ static const char *external_diff(void)
 
        if (done_preparing)
                return external_diff_cmd;
-       external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
+       external_diff_cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
        if (!external_diff_cmd)
                external_diff_cmd = external_diff_cmd_cfg;
        done_preparing = 1;
@@ -754,6 +756,8 @@ struct emitted_diff_symbol {
        const char *line;
        int len;
        int flags;
+       int indent_off;   /* Offset to first non-whitespace character */
+       int indent_width; /* The visual width of the indentation */
        enum diff_symbol s;
 };
 #define EMITTED_DIFF_SYMBOL_INIT {NULL}
@@ -784,44 +788,85 @@ struct moved_entry {
        struct moved_entry *next_line;
 };
 
-/**
- * The struct ws_delta holds white space differences between moved lines, i.e.
- * between '+' and '-' lines that have been detected to be a move.
- * The string contains the difference in leading white spaces, before the
- * rest of the line is compared using the white space config for move
- * coloring. The current_longer indicates if the first string in the
- * comparision is longer than the second.
- */
-struct ws_delta {
-       char *string;
-       unsigned int current_longer : 1;
-};
-#define WS_DELTA_INIT { NULL, 0 }
-
 struct moved_block {
        struct moved_entry *match;
-       struct ws_delta wsd;
+       int wsd; /* The whitespace delta of this block */
 };
 
 static void moved_block_clear(struct moved_block *b)
 {
-       FREE_AND_NULL(b->wsd.string);
-       b->match = NULL;
+       memset(b, 0, sizeof(*b));
 }
 
-static int compute_ws_delta(const struct emitted_diff_symbol *a,
-                            const struct emitted_diff_symbol *b,
-                            struct ws_delta *out)
+#define INDENT_BLANKLINE INT_MIN
+
+static void fill_es_indent_data(struct emitted_diff_symbol *es)
 {
-       const struct emitted_diff_symbol *longer =  a->len > b->len ? a : b;
-       const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
-       int d = longer->len - shorter->len;
+       unsigned int off = 0, i;
+       int width = 0, tab_width = es->flags & WS_TAB_WIDTH_MASK;
+       const char *s = es->line;
+       const int len = es->len;
+
+       /* skip any \v \f \r at start of indentation */
+       while (s[off] == '\f' || s[off] == '\v' ||
+              (s[off] == '\r' && off < len - 1))
+               off++;
+
+       /* calculate the visual width of indentation */
+       while(1) {
+               if (s[off] == ' ') {
+                       width++;
+                       off++;
+               } else if (s[off] == '\t') {
+                       width += tab_width - (width % tab_width);
+                       while (s[++off] == '\t')
+                               width += tab_width;
+               } else {
+                       break;
+               }
+       }
+
+       /* check if this line is blank */
+       for (i = off; i < len; i++)
+               if (!isspace(s[i]))
+                   break;
+
+       if (i == len) {
+               es->indent_width = INDENT_BLANKLINE;
+               es->indent_off = len;
+       } else {
+               es->indent_off = off;
+               es->indent_width = width;
+       }
+}
+
+static int compute_ws_delta(const struct emitted_diff_symbol *a,
+                           const struct emitted_diff_symbol *b,
+                           int *out)
+{
+       int a_len = a->len,
+           b_len = b->len,
+           a_off = a->indent_off,
+           a_width = a->indent_width,
+           b_off = b->indent_off,
+           b_width = b->indent_width;
+       int delta;
+
+       if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE) {
+               *out = INDENT_BLANKLINE;
+               return 1;
+       }
 
-       if (strncmp(longer->line + d, shorter->line, shorter->len))
+       if (a->s == DIFF_SYMBOL_PLUS)
+               delta = a_width - b_width;
+       else
+               delta = b_width - a_width;
+
+       if (a_len - a_off != b_len - b_off ||
+           memcmp(a->line + a_off, b->line + b_off, a_len - a_off))
                return 0;
 
-       out->string = xmemdupz(longer->line, d);
-       out->current_longer = (a == longer);
+       *out = delta;
 
        return 1;
 }
@@ -833,51 +878,53 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
                                 int n)
 {
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
-       int al = cur->es->len, cl = l->len;
+       int al = cur->es->len, bl = match->es->len, cl = l->len;
        const char *a = cur->es->line,
                   *b = match->es->line,
                   *c = l->line;
-
-       int wslen;
+       int a_off = cur->es->indent_off,
+           a_width = cur->es->indent_width,
+           c_off = l->indent_off,
+           c_width = l->indent_width;
+       int delta;
 
        /*
-        * We need to check if 'cur' is equal to 'match'.
-        * As those are from the same (+/-) side, we do not need to adjust for
-        * indent changes. However these were found using fuzzy matching
-        * so we do have to check if they are equal.
+        * We need to check if 'cur' is equal to 'match'.  As those
+        * are from the same (+/-) side, we do not need to adjust for
+        * indent changes. However these were found using fuzzy
+        * matching so we do have to check if they are equal. Here we
+        * just check the lengths. We delay calling memcmp() to check
+        * the contents until later as if the length comparison for a
+        * and c fails we can avoid the call all together.
         */
-       if (strcmp(a, b))
+       if (al != bl)
                return 1;
 
-       if (!pmb->wsd.string)
-               /*
-                * The white space delta is not active? This can happen
-                * when we exit early in this function.
-                */
-               return 1;
+       /* If 'l' and 'cur' are both blank then they match. */
+       if (a_width == INDENT_BLANKLINE && c_width == INDENT_BLANKLINE)
+               return 0;
 
        /*
-        * The indent changes of the block are known and stored in
-        * pmb->wsd; however we need to check if the indent changes of the
-        * current line are still the same as before.
-        *
-        * To do so we need to compare 'l' to 'cur', adjusting the
-        * one of them for the white spaces, depending which was longer.
+        * The indent changes of the block are known and stored in pmb->wsd;
+        * however we need to check if the indent changes of the current line
+        * match those of the current block and that the text of 'l' and 'cur'
+        * after the indentation match.
         */
+       if (cur->es->s == DIFF_SYMBOL_PLUS)
+               delta = a_width - c_width;
+       else
+               delta = c_width - a_width;
 
-       wslen = strlen(pmb->wsd.string);
-       if (pmb->wsd.current_longer) {
-               c += wslen;
-               cl -= wslen;
-       } else {
-               a += wslen;
-               al -= wslen;
-       }
-
-       if (al != cl || memcmp(a, c, al))
-               return 1;
+       /*
+        * If the previous lines of this block were all blank then set its
+        * whitespace delta.
+        */
+       if (pmb->wsd == INDENT_BLANKLINE)
+               pmb->wsd = delta;
 
-       return 0;
+       return !(delta == pmb->wsd && al - a_off == cl - c_off &&
+                !memcmp(a, b, al) && !
+                memcmp(a + a_off, c + c_off, al - a_off));
 }
 
 static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
@@ -943,6 +990,9 @@ static void add_lines_to_move_detection(struct diff_options *o,
                        continue;
                }
 
+               if (o->color_moved_ws_handling &
+                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+                       fill_es_indent_data(&o->emitted_symbols->buf[n]);
                key = prepare_entry(o, n);
                if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
                        prev_line->next_line = key;
@@ -1021,8 +1071,7 @@ static int shrink_potential_moved_blocks(struct moved_block *pmb,
 
                if (lp < pmb_nr && rp > -1 && lp < rp) {
                        pmb[lp] = pmb[rp];
-                       pmb[rp].match = NULL;
-                       pmb[rp].wsd.string = NULL;
+                       memset(&pmb[rp], 0, sizeof(pmb[rp]));
                        rp--;
                        lp++;
                }
@@ -1042,14 +1091,17 @@ static int shrink_potential_moved_blocks(struct moved_block *pmb,
  * The last block consists of the (n - block_length)'th line up to but not
  * including the nth line.
  *
+ * Returns 0 if the last block is empty or is unset by this function, non zero
+ * otherwise.
+ *
  * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
  * Think of a way to unify them.
  */
-static void adjust_last_block(struct diff_options *o, int n, int block_length)
+static int adjust_last_block(struct diff_options *o, int n, int block_length)
 {
        int i, alnum_count = 0;
        if (o->color_moved == COLOR_MOVED_PLAIN)
-               return;
+               return block_length;
        for (i = 1; i < block_length + 1; i++) {
                const char *c = o->emitted_symbols->buf[n - i].line;
                for (; *c; c++) {
@@ -1057,11 +1109,12 @@ static void adjust_last_block(struct diff_options *o, int n, int block_length)
                                continue;
                        alnum_count++;
                        if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
-                               return;
+                               return 1;
                }
        }
        for (i = 1; i < block_length + 1; i++)
                o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+       return 0;
 }
 
 /* Find blocks of moved code, delegate actual coloring decision to helper */
@@ -1071,7 +1124,7 @@ static void mark_color_as_moved(struct diff_options *o,
 {
        struct moved_block *pmb = NULL; /* potentially moved blocks */
        int pmb_nr = 0, pmb_alloc = 0;
-       int n, flipped_block = 1, block_length = 0;
+       int n, flipped_block = 0, block_length = 0;
 
 
        for (n = 0; n < o->emitted_symbols->nr; n++) {
@@ -1079,6 +1132,7 @@ static void mark_color_as_moved(struct diff_options *o,
                struct moved_entry *key;
                struct moved_entry *match = NULL;
                struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+               enum diff_symbol last_symbol = 0;
 
                switch (l->s) {
                case DIFF_SYMBOL_PLUS:
@@ -1094,7 +1148,7 @@ static void mark_color_as_moved(struct diff_options *o,
                        free(key);
                        break;
                default:
-                       flipped_block = 1;
+                       flipped_block = 0;
                }
 
                if (!match) {
@@ -1105,13 +1159,16 @@ static void mark_color_as_moved(struct diff_options *o,
                                moved_block_clear(&pmb[i]);
                        pmb_nr = 0;
                        block_length = 0;
+                       flipped_block = 0;
+                       last_symbol = l->s;
                        continue;
                }
 
-               l->flags |= DIFF_SYMBOL_MOVED_LINE;
-
-               if (o->color_moved == COLOR_MOVED_PLAIN)
+               if (o->color_moved == COLOR_MOVED_PLAIN) {
+                       last_symbol = l->s;
+                       l->flags |= DIFF_SYMBOL_MOVED_LINE;
                        continue;
+               }
 
                if (o->color_moved_ws_handling &
                    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
@@ -1134,21 +1191,27 @@ static void mark_color_as_moved(struct diff_options *o,
                                                             &pmb[pmb_nr].wsd))
                                                pmb[pmb_nr++].match = match;
                                } else {
-                                       pmb[pmb_nr].wsd.string = NULL;
+                                       pmb[pmb_nr].wsd = 0;
                                        pmb[pmb_nr++].match = match;
                                }
                        }
 
-                       flipped_block = (flipped_block + 1) % 2;
+                       if (adjust_last_block(o, n, block_length) &&
+                           pmb_nr && last_symbol != l->s)
+                               flipped_block = (flipped_block + 1) % 2;
+                       else
+                               flipped_block = 0;
 
-                       adjust_last_block(o, n, block_length);
                        block_length = 0;
                }
 
-               block_length++;
-
-               if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
-                       l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
+               if (pmb_nr) {
+                       block_length++;
+                       l->flags |= DIFF_SYMBOL_MOVED_LINE;
+                       if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
+                               l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
+               }
+               last_symbol = l->s;
        }
        adjust_last_block(o, n, block_length);
 
@@ -1492,7 +1555,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
 static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
                             const char *line, int len, unsigned flags)
 {
-       struct emitted_diff_symbol e = {line, len, flags, s};
+       struct emitted_diff_symbol e = {line, len, flags, 0, 0, s};
 
        if (o->emitted_symbols)
                append_emitted_diff_symbol(o, &e);
@@ -1641,7 +1704,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        strbuf_release(&msgbuf);
 }
 
-static struct diff_tempfile *claim_diff_tempfile(void) {
+static struct diff_tempfile *claim_diff_tempfile(void)
+{
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
                if (!diff_temp[i].name)
@@ -3480,7 +3544,7 @@ static void builtin_diff(const char *name_a,
                o->found_changes = 1;
        } else {
                /* Crazy xdl interfaces.. */
-               const char *diffopts = getenv("GIT_DIFF_OPTS");
+               const char *diffopts;
                const char *v;
                xpparam_t xpp;
                xdemitconf_t xecfg;
@@ -3523,12 +3587,15 @@ static void builtin_diff(const char *name_a,
                        xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
                if (pe)
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
+
+               diffopts = getenv("GIT_DIFF_OPTS");
                if (!diffopts)
                        ;
                else if (skip_prefix(diffopts, "--unified=", &v))
                        xecfg.ctxlen = strtoul(v, NULL, 10);
                else if (skip_prefix(diffopts, "-u", &v))
                        xecfg.ctxlen = strtoul(v, NULL, 10);
+
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
                if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
@@ -4823,7 +4890,8 @@ static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)
        return 0;
 }
 
-static void enable_patch_output(int *fmt) {
+static void enable_patch_output(int *fmt)
+{
        *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
        *fmt |= DIFF_FORMAT_PATCH;
 }
@@ -5040,6 +5108,8 @@ int diff_opt_parse(struct diff_options *options,
                if (cm < 0)
                        return error("bad --color-moved argument: %s", arg);
                options->color_moved = cm;
+       } else if (!strcmp(arg, "--no-color-moved-ws")) {
+               options->color_moved_ws_handling = 0;
        } else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
                unsigned cm = parse_color_moved_ws(arg);
                if (cm & COLOR_MOVED_WS_ERROR)
index 0e37741..89af47c 100644 (file)
@@ -107,7 +107,7 @@ char *git_work_tree_cfg;
 
 static char *git_namespace;
 
-static const char *super_prefix;
+static char *super_prefix;
 
 /*
  * Repository-local GIT_* environment variables; see cache.h for details.
@@ -240,7 +240,7 @@ const char *get_super_prefix(void)
 {
        static int initialized;
        if (!initialized) {
-               super_prefix = getenv(GIT_SUPER_PREFIX_ENVIRONMENT);
+               super_prefix = xstrdup_or_null(getenv(GIT_SUPER_PREFIX_ENVIRONMENT));
                initialized = 1;
        }
        return super_prefix;
index dd6700b..08b3b35 100644 (file)
@@ -135,38 +135,42 @@ enum ack_type {
        ACK_ready
 };
 
-static void consume_shallow_list(struct fetch_pack_args *args, int fd)
+static void consume_shallow_list(struct fetch_pack_args *args,
+                                struct packet_reader *reader)
 {
        if (args->stateless_rpc && args->deepen) {
                /* If we sent a depth we will get back "duplicate"
                 * shallow and unshallow commands every time there
                 * is a block of have lines exchanged.
                 */
-               char *line;
-               while ((line = packet_read_line(fd, NULL))) {
-                       if (starts_with(line, "shallow "))
+               while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+                       if (starts_with(reader->line, "shallow "))
                                continue;
-                       if (starts_with(line, "unshallow "))
+                       if (starts_with(reader->line, "unshallow "))
                                continue;
                        die(_("git fetch-pack: expected shallow list"));
                }
+               if (reader->status != PACKET_READ_FLUSH)
+                       die(_("git fetch-pack: expected a flush packet after shallow list"));
        }
 }
 
-static enum ack_type get_ack(int fd, struct object_id *result_oid)
+static enum ack_type get_ack(struct packet_reader *reader,
+                            struct object_id *result_oid)
 {
        int len;
-       char *line = packet_read_line(fd, &len);
        const char *arg;
 
-       if (!line)
+       if (packet_reader_read(reader) != PACKET_READ_NORMAL)
                die(_("git fetch-pack: expected ACK/NAK, got a flush packet"));
-       if (!strcmp(line, "NAK"))
+       len = reader->pktlen;
+
+       if (!strcmp(reader->line, "NAK"))
                return NAK;
-       if (skip_prefix(line, "ACK ", &arg)) {
+       if (skip_prefix(reader->line, "ACK ", &arg)) {
                if (!get_oid_hex(arg, result_oid)) {
                        arg += 40;
-                       len -= arg - line;
+                       len -= arg - reader->line;
                        if (len < 1)
                                return ACK;
                        if (strstr(arg, "continue"))
@@ -178,9 +182,7 @@ static enum ack_type get_ack(int fd, struct object_id *result_oid)
                        return ACK;
                }
        }
-       if (skip_prefix(line, "ERR ", &arg))
-               die(_("remote error: %s"), arg);
-       die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
+       die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line);
 }
 
 static void send_request(struct fetch_pack_args *args,
@@ -248,10 +250,15 @@ static int find_common(struct fetch_negotiator *negotiator,
        int got_ready = 0;
        struct strbuf req_buf = STRBUF_INIT;
        size_t state_len = 0;
+       struct packet_reader reader;
 
        if (args->stateless_rpc && multi_ack == 1)
                die(_("--stateless-rpc requires multi_ack_detailed"));
 
+       packet_reader_init(&reader, fd[0], NULL, 0,
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+
        if (!args->no_dependents) {
                mark_tips(negotiator, args->negotiation_tips);
                for_each_cached_alternate(negotiator, insert_one_alternate_object);
@@ -329,38 +336,42 @@ static int find_common(struct fetch_negotiator *negotiator,
                        packet_buf_write(&req_buf, "deepen-not %s", s->string);
                }
        }
-       if (server_supports_filtering && args->filter_options.choice)
+       if (server_supports_filtering && args->filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&args->filter_options,
+                                               &expanded_filter_spec);
                packet_buf_write(&req_buf, "filter %s",
-                                args->filter_options.filter_spec);
+                                expanded_filter_spec.buf);
+               strbuf_release(&expanded_filter_spec);
+       }
        packet_buf_flush(&req_buf);
        state_len = req_buf.len;
 
        if (args->deepen) {
-               char *line;
                const char *arg;
                struct object_id oid;
 
                send_request(args, fd[1], &req_buf);
-               while ((line = packet_read_line(fd[0], NULL))) {
-                       if (skip_prefix(line, "shallow ", &arg)) {
+               while (packet_reader_read(&reader) == PACKET_READ_NORMAL) {
+                       if (skip_prefix(reader.line, "shallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
-                                       die(_("invalid shallow line: %s"), line);
+                                       die(_("invalid shallow line: %s"), reader.line);
                                register_shallow(the_repository, &oid);
                                continue;
                        }
-                       if (skip_prefix(line, "unshallow ", &arg)) {
+                       if (skip_prefix(reader.line, "unshallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
-                                       die(_("invalid unshallow line: %s"), line);
+                                       die(_("invalid unshallow line: %s"), reader.line);
                                if (!lookup_object(the_repository, oid.hash))
-                                       die(_("object not found: %s"), line);
+                                       die(_("object not found: %s"), reader.line);
                                /* make sure that it is parsed as shallow */
                                if (!parse_object(the_repository, &oid))
-                                       die(_("error in object: %s"), line);
+                                       die(_("error in object: %s"), reader.line);
                                if (unregister_shallow(&oid))
-                                       die(_("no shallow found: %s"), line);
+                                       die(_("no shallow found: %s"), reader.line);
                                continue;
                        }
-                       die(_("expected shallow/unshallow, got %s"), line);
+                       die(_("expected shallow/unshallow, got %s"), reader.line);
                }
        } else if (!args->stateless_rpc)
                send_request(args, fd[1], &req_buf);
@@ -397,9 +408,9 @@ static int find_common(struct fetch_negotiator *negotiator,
                        if (!args->stateless_rpc && count == INITIAL_FLUSH)
                                continue;
 
-                       consume_shallow_list(args, fd[0]);
+                       consume_shallow_list(args, &reader);
                        do {
-                               ack = get_ack(fd[0], result_oid);
+                               ack = get_ack(&reader, result_oid);
                                if (ack)
                                        print_verbose(args, _("got %s %d %s"), "ack",
                                                      ack, oid_to_hex(result_oid));
@@ -469,9 +480,9 @@ done:
        strbuf_release(&req_buf);
 
        if (!got_ready || !no_done)
-               consume_shallow_list(args, fd[0]);
+               consume_shallow_list(args, &reader);
        while (flushes || multi_ack) {
-               int ack = get_ack(fd[0], result_oid);
+               int ack = get_ack(&reader, result_oid);
                if (ack) {
                        print_verbose(args, _("got %s (%d) %s"), "ack",
                                      ack, oid_to_hex(result_oid));
@@ -1007,6 +1018,8 @@ static void add_shallow_requests(struct strbuf *req_buf,
                        packet_buf_write(req_buf, "deepen-not %s", s->string);
                }
        }
+       if (args->deepen_relative)
+               packet_buf_write(req_buf, "deepen-relative\n");
 }
 
 static void add_wants(int no_dependents, const struct ref *wants, struct strbuf *req_buf)
@@ -1084,7 +1097,8 @@ static int add_haves(struct fetch_negotiator *negotiator,
 static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
                              const struct fetch_pack_args *args,
                              const struct ref *wants, struct oidset *common,
-                             int *haves_to_send, int *in_vain)
+                             int *haves_to_send, int *in_vain,
+                             int sideband_all)
 {
        int ret = 0;
        struct strbuf req_buf = STRBUF_INIT;
@@ -1110,6 +1124,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
                packet_buf_write(&req_buf, "include-tag");
        if (prefer_ofs_delta)
                packet_buf_write(&req_buf, "ofs-delta");
+       if (sideband_all)
+               packet_buf_write(&req_buf, "sideband-all");
 
        /* Add shallow-info and deepen request */
        if (server_supports_feature("fetch", "shallow", 0))
@@ -1120,9 +1136,13 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
        /* Add filter */
        if (server_supports_feature("fetch", "filter", 0) &&
            args->filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
                print_verbose(args, _("Server supports filter"));
+               expand_list_objects_filter_spec(&args->filter_options,
+                                               &expanded_filter_spec);
                packet_buf_write(&req_buf, "filter %s",
-                                args->filter_options.filter_spec);
+                                expanded_filter_spec.buf);
+               strbuf_release(&expanded_filter_spec);
        } else if (args->filter_options.choice) {
                warning("filtering not recognized by server, ignoring");
        }
@@ -1232,6 +1252,8 @@ static int process_acks(struct fetch_negotiator *negotiator,
 static void receive_shallow_info(struct fetch_pack_args *args,
                                 struct packet_reader *reader)
 {
+       int line_received = 0;
+
        process_section_header(reader, "shallow-info", 0);
        while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
                const char *arg;
@@ -1241,6 +1263,7 @@ static void receive_shallow_info(struct fetch_pack_args *args,
                        if (get_oid_hex(arg, &oid))
                                die(_("invalid shallow line: %s"), reader->line);
                        register_shallow(the_repository, &oid);
+                       line_received = 1;
                        continue;
                }
                if (skip_prefix(reader->line, "unshallow ", &arg)) {
@@ -1253,6 +1276,7 @@ static void receive_shallow_info(struct fetch_pack_args *args,
                                die(_("error in object: %s"), reader->line);
                        if (unregister_shallow(&oid))
                                die(_("no shallow found: %s"), reader->line);
+                       line_received = 1;
                        continue;
                }
                die(_("expected shallow/unshallow, got %s"), reader->line);
@@ -1262,8 +1286,11 @@ static void receive_shallow_info(struct fetch_pack_args *args,
            reader->status != PACKET_READ_DELIM)
                die(_("error processing shallow info: %d"), reader->status);
 
-       setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, NULL);
-       args->deepen = 1;
+       if (line_received) {
+               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
+                                       NULL);
+               args->deepen = 1;
+       }
 }
 
 static void receive_wanted_refs(struct packet_reader *reader,
@@ -1316,7 +1343,13 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
        struct fetch_negotiator negotiator;
        fetch_negotiator_init(&negotiator, negotiation_algorithm);
        packet_reader_init(&reader, fd[0], NULL, 0,
-                          PACKET_READ_CHOMP_NEWLINE);
+                          PACKET_READ_CHOMP_NEWLINE |
+                          PACKET_READ_DIE_ON_ERR_PACKET);
+       if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 1) &&
+           server_supports_feature("fetch", "sideband-all", 0)) {
+               reader.use_sideband = 1;
+               reader.me = "fetch-pack";
+       }
 
        while (state != FETCH_DONE) {
                switch (state) {
@@ -1350,7 +1383,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                case FETCH_SEND_REQUEST:
                        if (send_fetch_request(&negotiator, fd[1], args, ref,
                                               &common,
-                                              &haves_to_send, &in_vain))
+                                              &haves_to_send, &in_vain,
+                                              reader.use_sideband))
                                state = FETCH_GET_PACK;
                        else
                                state = FETCH_PROCESS_ACKS;
diff --git a/fsck.c b/fsck.c
index 68502ce..2260adb 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -410,14 +410,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
                        continue;
 
                if (S_ISDIR(entry.mode)) {
-                       obj = (struct object *)lookup_tree(the_repository, entry.oid);
+                       obj = (struct object *)lookup_tree(the_repository, &entry.oid);
                        if (name && obj)
                                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 = (struct object *)lookup_blob(the_repository, entry.oid);
+                       obj = (struct object *)lookup_blob(the_repository, &entry.oid);
                        if (name && obj)
                                put_object_name(options, obj, "%s%s", name,
                                        entry.path);
diff --git a/fuzz-commit-graph.c b/fuzz-commit-graph.c
new file mode 100644 (file)
index 0000000..cf790c9
--- /dev/null
@@ -0,0 +1,16 @@
+#include "commit-graph.h"
+
+struct commit_graph *parse_commit_graph(void *graph_map, int fd,
+                                       size_t graph_size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       struct commit_graph *g;
+
+       g = parse_commit_graph((void *)data, -1, size);
+       free(g);
+
+       return 0;
+}
index b8ace77..29a1990 100644 (file)
@@ -398,6 +398,19 @@ static inline char *git_find_last_dir_sep(const char *path)
 #define query_user_email() NULL
 #endif
 
+#ifdef __TANDEM
+#include <floss.h(floss_execl,floss_execlp,floss_execv,floss_execvp)>
+#include <floss.h(floss_getpwuid)>
+#ifndef NSIG
+/*
+ * NonStop NSE and NSX do not provide NSIG. SIGGUARDIAN(99) is the highest
+ * known, by detective work using kill -l as a list is all signals
+ * instead of signal.h where it should be.
+ */
+# define NSIG 100
+#endif
+#endif
+
 #if defined(__HP_cc) && (__HP_cc >= 61000)
 #define NORETURN __attribute__((noreturn))
 #define NORETURN_PTR
index b4c7dbf..3bb0682 100755 (executable)
@@ -26,6 +26,7 @@ f,force-rebase!    cherry-pick all commits, even if unchanged
 m,merge!           use merging strategies to rebase
 i,interactive!     let the user edit the list of commits to rebase
 x,exec=!           add exec lines after each commit of the editable list
+y=!                same as --reschedule-failed-exec -x
 k,keep-empty      preserve empty commits during rebase
 allow-empty-message allow rebasing commits with empty messages
 stat!              display a diffstat of what changed upstream
@@ -48,6 +49,7 @@ skip!              skip current patch and continue
 edit-todo!         edit the todo list during an interactive rebase
 quit!              abort but keep HEAD where it is
 show-current-patch! show the patch file being applied or merged
+reschedule-failed-exec automatically reschedule failed exec commands
 "
 . git-sh-setup
 set_reflog_action rebase
@@ -92,11 +94,14 @@ autosquash=
 keep_empty=
 allow_empty_message=--allow-empty-message
 signoff=
+reschedule_failed_exec=
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 case "$(git config --bool commit.gpgsign)" in
 true)  gpg_sign_opt=-S ;;
 *)     gpg_sign_opt= ;;
 esac
+test "$(git config --bool rebase.reschedulefailedexec)" = "true" &&
+reschedule_failed_exec=--reschedule-failed-exec
 . git-rebase--common
 
 read_basic_state () {
@@ -126,6 +131,8 @@ read_basic_state () {
                signoff="$(cat "$state_dir"/signoff)"
                force_rebase=t
        }
+       test -f "$state_dir"/reschedule-failed-exec &&
+               reschedule_failed_exec=t
 }
 
 finish_rebase () {
@@ -163,7 +170,8 @@ run_interactive () {
                "$allow_empty_message" "$autosquash" "$verbose" \
                "$force_rebase" "$onto_name" "$head_name" "$strategy" \
                "$strategy_opts" "$cmd" "$switch_to" \
-               "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff"
+               "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff" \
+               "$reschedule_failed_exec"
 }
 
 run_specific_rebase () {
@@ -255,6 +263,11 @@ do
                cmd="${cmd}exec ${1#--exec=}${LF}"
                test -z "$interactive_rebase" && interactive_rebase=implied
                ;;
+       -y*)
+               reschedule_failed_exec=--reschedule-failed-exec
+               cmd="${cmd}exec ${1#-y}${LF}"
+               test -z "$interactive_rebase" && interactive_rebase=implied
+               ;;
        --interactive)
                interactive_rebase=explicit
                ;;
@@ -378,6 +391,12 @@ do
        --gpg-sign=*)
                gpg_sign_opt="-S${1#--gpg-sign=}"
                ;;
+       --reschedule-failed-exec)
+               reschedule_failed_exec=--reschedule-failed-exec
+               ;;
+       --no-reschedule-failed-exec)
+               reschedule_failed_exec=
+               ;;
        --)
                shift
                break