diff options
175 files changed, 3946 insertions, 1142 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index d5d936e6a7..26a2342bea 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -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 diff --git a/Documentation/RelNotes/2.21.0.txt b/Documentation/RelNotes/2.21.0.txt index 71e437a66f..4596d9e01a 100644 --- a/Documentation/RelNotes/2.21.0.txt +++ b/Documentation/RelNotes/2.21.0.txt @@ -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). diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt index f079bf6b7e..331d250e04 100644 --- a/Documentation/config/rebase.txt +++ b/Documentation/config/rebase.txt @@ -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. diff --git a/Documentation/config/worktree.txt b/Documentation/config/worktree.txt index b853798fc2..048e349482 100644 --- a/Documentation/config/worktree.txt +++ b/Documentation/config/worktree.txt @@ -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 diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index b94d332f71..554a34080d 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -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 diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 74013335a1..9a2e9cdafb 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -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:: diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt index 763afabb6d..f58e9c43e6 100644 --- a/Documentation/git-column.txt +++ b/Documentation/git-column.txt @@ -47,7 +47,7 @@ OPTIONS The number of spaces between columns. One space by default. EXAMPLES ------- +-------- Format data by columns: ------------ diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 901faef1bf..774cecc7ed 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -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] diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index c20ee6c789..a7442499f6 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -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. diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index 3c5a67fb96..057076ca38 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -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 ------------------ diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt index 8cf952b4de..70562dc4c0 100644 --- a/Documentation/git-quiltimport.txt +++ b/Documentation/git-quiltimport.txt @@ -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 diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index d284155cf3..4dd5853d6e 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -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 -------------------- diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index d28e6154c6..ab4d271925 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -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 diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index d9f422d560..861d821d7f 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -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 diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 417b638cd8..de6953108c 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -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 diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index bab5f50b17..91b3a72bda 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -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. diff --git a/Documentation/technical/api-oid-array.txt b/Documentation/technical/api-oid-array.txt index 9febfb1d52..c97428c2c3 100644 --- a/Documentation/technical/api-oid-array.txt +++ b/Documentation/technical/api-oid-array.txt @@ -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 -------- diff --git a/Documentation/technical/partial-clone.txt b/Documentation/technical/partial-clone.txt index 1ef66bd788..896c7b3878 100644 --- a/Documentation/technical/partial-clone.txt +++ b/Documentation/technical/partial-clone.txt @@ -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 diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt index 09e4e0273f..292060a9dc 100644 --- a/Documentation/technical/protocol-v2.txt +++ b/Documentation/technical/protocol-v2.txt @@ -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 @@ -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). @@ -725,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 @@ -748,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 @@ -1647,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)" @@ -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 @@ -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; } diff --git a/builtin/add.c b/builtin/add.c index 12247b48fd..d461ba08b9 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -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"); diff --git a/builtin/clone.c b/builtin/clone.c index 7c7f98c72c..739de68820 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -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) diff --git a/builtin/commit.c b/builtin/commit.c index 004b816635..7d2e0b61e5 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -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)); diff --git a/builtin/config.c b/builtin/config.c index 84385ef165..98d65bc0ad 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -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, diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 63e69a5801..f6a513495e 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -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; @@ -219,9 +220,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) PACKET_READ_CHOMP_NEWLINE | PACKET_READ_GENTLE_ON_EOF); - 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 +234,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); diff --git a/builtin/fetch.c b/builtin/fetch.c index e0140327aa..61a443031d 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -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, ¤t->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, ¤t->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); } diff --git a/builtin/fsck.c b/builtin/fsck.c index bf5ddff43f..9c4c11d0ad 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -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; } diff --git a/builtin/gc.c b/builtin/gc.c index 7696017cd4..020f725acc 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -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, diff --git a/builtin/grep.c b/builtin/grep.c index 4748195ae1..dd52ea968b 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -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); } diff --git a/builtin/init-db.c b/builtin/init-db.c index 41faffd28d..93eff7618c 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -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); diff --git a/builtin/log.c b/builtin/log.c index 3e145fe502..c91a59dec1 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -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 diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 7e0dcaa359..cde87cbeeb 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -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, diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 9b2f707c29..7545136c2a 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -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; diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 4984b7e12e..5541ad091e 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -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; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 889df2c755..0a70d04604 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -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); diff --git a/builtin/push.c b/builtin/push.c index ee1e842027..021dd3b1e4 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -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 diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c index dd2a55ab1d..c2c2d51a3b 100644 --- a/builtin/rebase--interactive.c +++ b/builtin/rebase--interactive.c @@ -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() }; diff --git a/builtin/rebase.c b/builtin/rebase.c index 00de70365e..774264bae8 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -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); diff --git a/builtin/reflog.c b/builtin/reflog.c index 64a8df4f25..1f1010e2d9 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -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; } diff --git a/builtin/repack.c b/builtin/repack.c index 2a1c7b21c5..67f8978043 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -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 diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 51e9e1267e..14ef659c12 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -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) diff --git a/builtin/stripspace.c b/builtin/stripspace.c index bdf0328869..be33eb83c1 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -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); } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index b45514be31..0e140f176c 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -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)) { diff --git a/builtin/worktree.c b/builtin/worktree.c index 5e84026177..3f9907fcc9 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -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")); diff --git a/cache-tree.c b/cache-tree.c index eabb8fb654..c4b8a1fa16 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -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; } @@ -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]; @@ -1028,16 +1038,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 +1053,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 +1084,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 +1377,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 +1387,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 diff --git a/commit-graph.c b/commit-graph.c index 359e782dee..18bd2b6df7 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -23,18 +23,12 @@ #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */ #define GRAPH_CHUNKID_LARGEEDGES 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_EDGE_LAST_MASK 0x7fffffff #define GRAPH_PARENT_NONE 0x70000000 @@ -44,13 +38,18 @@ #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)); @@ -147,15 +146,15 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd, } 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); + 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; @@ -181,7 +180,7 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd, 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); free(graph); @@ -318,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) { @@ -329,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; @@ -343,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; @@ -367,13 +369,13 @@ 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); + pptr = insert_parent_or_die(r, g, edge_value, pptr); return 1; } @@ -381,7 +383,7 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin 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++; @@ -400,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; @@ -408,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; } @@ -417,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) @@ -429,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) @@ -449,12 +456,12 @@ 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, @@ -522,7 +529,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); @@ -540,7 +549,9 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len, 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); @@ -593,7 +604,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; @@ -797,6 +810,7 @@ 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; if (!commit_graph_compatible(the_repository)) return; @@ -897,7 +911,7 @@ void write_commit_graph(const char *obj_dir, count_distinct++; } - 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; @@ -924,7 +938,7 @@ void write_commit_graph(const char *obj_dir, } num_chunks = num_extra_edges ? 4 : 3; - 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); @@ -942,7 +956,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 */ @@ -957,8 +971,8 @@ void write_commit_graph(const char *obj_dir, 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++) { @@ -971,8 +985,8 @@ void write_commit_graph(const char *obj_dir, } 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_oids(f, hashsz, commits.list, commits.nr); + write_graph_chunk_data(f, hashsz, commits.list, commits.nr); write_graph_chunk_large_edges(f, commits.list, commits.nr); close_commit_graph(the_repository); @@ -1058,7 +1072,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)); } @@ -1094,7 +1108,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), diff --git a/commit-reach.c b/commit-reach.c index d5a39defd3..3ea174788a 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -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); diff --git a/commit-reach.h b/commit-reach.h index fb8082a2ec..99a43e8b64 100644 --- a/commit-reach.h +++ b/commit-reach.h @@ -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 @@ -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) @@ -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 diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c index 51cd60baa3..c0d838834a 100644 --- a/compat/regex/regcomp.c +++ b/compat/regex/regcomp.c @@ -17,6 +17,14 @@ 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, diff --git a/config.mak.dev b/config.mak.dev index bbeeff44fe..7354fe15b3 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -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 diff --git a/config.mak.uname b/config.mak.uname index e391431041..7b36a1dfe7 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -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'. diff --git a/contrib/coccinelle/object_id.cocci b/contrib/coccinelle/object_id.cocci index 6a7cf3e02d..3e536a9834 100644 --- a/contrib/coccinelle/object_id.cocci +++ b/contrib/coccinelle/object_id.cocci @@ -87,36 +87,6 @@ struct object_id OID; + 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 index 0000000000..2ee702ecf7 --- /dev/null +++ b/contrib/coccinelle/the_repository.pending.cocci @@ -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); diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9e8ec95c3c..499e56f83d 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -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 () diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh index 049d6b80f6..886bf95d1f 100644 --- a/contrib/completion/git-completion.zsh +++ b/contrib/completion/git-completion.zsh @@ -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 () diff --git a/contrib/hooks/multimail/CHANGES b/contrib/hooks/multimail/CHANGES index 2076cf972b..35791fd02c 100644 --- a/contrib/hooks/multimail/CHANGES +++ b/contrib/hooks/multimail/CHANGES @@ -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 ============= diff --git a/contrib/hooks/multimail/CONTRIBUTING.rst b/contrib/hooks/multimail/CONTRIBUTING.rst index da65570e9b..de20a54287 100644 --- a/contrib/hooks/multimail/CONTRIBUTING.rst +++ b/contrib/hooks/multimail/CONTRIBUTING.rst @@ -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 diff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git index 161b0230a0..044444245d 100644 --- a/contrib/hooks/multimail/README.Git +++ b/contrib/hooks/multimail/README.Git @@ -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. diff --git a/contrib/hooks/multimail/README b/contrib/hooks/multimail/README.rst index 5105373aea..7c0fc4a6ef 100644 --- a/contrib/hooks/multimail/README +++ b/contrib/hooks/multimail/README.rst @@ -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 diff --git a/contrib/hooks/multimail/doc/gitolite.rst b/contrib/hooks/multimail/doc/gitolite.rst index 00aedd9c57..5054833105 100644 --- a/contrib/hooks/multimail/doc/gitolite.rst +++ b/contrib/hooks/multimail/doc/gitolite.rst @@ -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. diff --git a/contrib/hooks/multimail/git_multimail.py b/contrib/hooks/multimail/git_multimail.py index 73fdda6b14..8823399e75 100755 --- a/contrib/hooks/multimail/git_multimail.py +++ b/contrib/hooks/multimail/git_multimail.py @@ -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:]) diff --git a/contrib/hooks/multimail/migrate-mailhook-config b/contrib/hooks/multimail/migrate-mailhook-config index 992657bbdc..241ba22fa3 100755 --- a/contrib/hooks/multimail/migrate-mailhook-config +++ b/contrib/hooks/multimail/migrate-mailhook-config @@ -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( diff --git a/contrib/hooks/multimail/post-receive.example b/contrib/hooks/multimail/post-receive.example index 1ea113d274..b9bb11834e 100755 --- a/contrib/hooks/multimail/post-receive.example +++ b/contrib/hooks/multimail/post-receive.example @@ -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' @@ -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) { diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c index 4dfbc8c9f9..ec1271f89c 100644 --- a/credential-cache--daemon.c +++ b/credential-cache--daemon.c @@ -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; diff --git a/delta-islands.c b/delta-islands.c index 191a930705..2186bd0738 100644 --- a/delta-islands.c +++ b/delta-islands.c @@ -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; @@ -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) diff --git a/environment.c b/environment.c index 0e37741d83..89af47cb85 100644 --- a/environment.c +++ b/environment.c @@ -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; diff --git a/fetch-pack.c b/fetch-pack.c index dd6700bda9..bf8984b8e8 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -329,9 +329,14 @@ 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; @@ -1007,6 +1012,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) @@ -1120,9 +1127,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 +1243,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 +1254,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 +1267,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 +1277,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, @@ -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/git-compat-util.h b/git-compat-util.h index b8ace77410..29a19902aa 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -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 diff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh index b4c7dbfa57..3bb0682db5 100755 --- a/git-legacy-rebase.sh +++ b/git-legacy-rebase.sh @@ -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 @@ -534,6 +553,9 @@ then # git-rebase.txt caveats with "unless you know what you are doing" test -n "$rebase_merges" && die "$(gettext "error: cannot combine '--preserve-merges' with '--rebase-merges'")" + + test -n "$reschedule_failed_exec" && + die "$(gettext "error: cannot combine '--preserve-merges' with '--reschedule-failed-exec'")" fi if test -n "$rebase_merges" @@ -332,6 +332,8 @@ def p4_check_access(min_expiration=1): die_bad_access("p4 error: {0}".format(data)) else: die_bad_access("unknown error") + elif code == "info": + return else: die_bad_access("unknown error code {0}".format(code)) @@ -1859,6 +1861,7 @@ class P4Submit(Command, P4UserMap): filesToAdd.remove(path) elif modifier == "C": src, dest = diff['src'], diff['dst'] + all_files.append(dest) p4_integrate(src, dest) pureRenameCopy.add(dest) if diff['src_sha1'] != diff['dst_sha1']: @@ -1875,6 +1878,7 @@ class P4Submit(Command, P4UserMap): editedFiles.add(dest) elif modifier == "R": src, dest = diff['src'], diff['dst'] + all_files.append(dest) if self.p4HasMoveCommand: p4_edit(src) # src must be open before move p4_move(src, dest) # opens for (move/delete, move/add) diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 6d3a88decd..e3d3909743 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -8,6 +8,7 @@ n,dry-run dry run author= author name and email address for patches without any patches= path to the quilt patches series= path to the quilt series file +keep-non-patch Pass -b to git mailinfo " SUBDIRECTORY_ON=Yes . git-sh-setup @@ -32,6 +33,9 @@ do shift QUILT_SERIES="$1" ;; + --keep-non-patch) + MAILINFO_OPT="-b" + ;; --) shift break;; @@ -98,7 +102,7 @@ do continue fi echo $patch_name - git mailinfo "$tmp_msg" "$tmp_patch" \ + git mailinfo $MAILINFO_OPT "$tmp_msg" "$tmp_patch" \ <"$QUILT_PATCHES/$patch_name" >"$tmp_info" || exit 3 test -s "$tmp_patch" || { echo "Patch is empty. Was it split wrong?" diff --git a/git-rebase--common.sh b/git-rebase--common.sh index 7e39d22871..a8a44608e0 100644 --- a/git-rebase--common.sh +++ b/git-rebase--common.sh @@ -19,6 +19,7 @@ write_basic_state () { "$state_dir"/allow_rerere_autoupdate test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff + test -n "$reschedule_failed_exec" && : > "$state_dir"/reschedule-failed-exec } apply_autostash () { @@ -98,7 +98,8 @@ static int list_cmds(const char *spec) return 0; } -static void commit_pager_choice(void) { +static void commit_pager_choice(void) +{ switch (use_pager) { case 0: setenv("GIT_PAGER", "cat", 1); @@ -15,6 +15,14 @@ #include "block-sha1/sha1.h" #endif +#if defined(SHA256_GCRYPT) +#include "sha256/gcrypt.h" +#elif defined(SHA256_OPENSSL) +#include <openssl/sha.h> +#else +#include "sha256/block/sha256.h" +#endif + #ifndef platform_SHA_CTX /* * platform's underlying implementation of SHA-1; could be OpenSSL, @@ -34,6 +42,18 @@ #define git_SHA1_Update platform_SHA1_Update #define git_SHA1_Final platform_SHA1_Final +#ifndef platform_SHA256_CTX +#define platform_SHA256_CTX SHA256_CTX +#define platform_SHA256_Init SHA256_Init +#define platform_SHA256_Update SHA256_Update +#define platform_SHA256_Final SHA256_Final +#endif + +#define git_SHA256_CTX platform_SHA256_CTX +#define git_SHA256_Init platform_SHA256_Init +#define git_SHA256_Update platform_SHA256_Update +#define git_SHA256_Final platform_SHA256_Final + #ifdef SHA1_MAX_BLOCK_SIZE #include "compat/sha1-chunked.h" #undef git_SHA1_Update @@ -52,12 +72,15 @@ #define GIT_HASH_UNKNOWN 0 /* SHA-1 */ #define GIT_HASH_SHA1 1 +/* SHA-256 */ +#define GIT_HASH_SHA256 2 /* Number of algorithms supported (including unknown). */ -#define GIT_HASH_NALGOS (GIT_HASH_SHA1 + 1) +#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1) /* A suitably aligned type for stack allocations of hash contexts. */ union git_hash_ctx { git_SHA_CTX sha1; + git_SHA256_CTX sha256; }; typedef union git_hash_ctx git_hash_ctx; @@ -81,6 +104,9 @@ struct git_hash_algo { /* The length of the hash in hex characters. */ size_t hexsz; + /* The block size of the hash. */ + size_t blksz; + /* The hash initialization function. */ git_hash_init_fn init_fn; @@ -98,4 +124,17 @@ struct git_hash_algo { }; extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS]; +/* + * Return a GIT_HASH_* constant based on the name. Returns GIT_HASH_UNKNOWN if + * the name doesn't match a known algorithm. + */ +int hash_algo_by_name(const char *name); +/* Identical, except based on the format ID. */ +int hash_algo_by_id(uint32_t format_id); +/* Identical, except for a pointer to struct git_hash_algo. */ +static inline int hash_algo_by_ptr(const struct git_hash_algo *p) +{ + return p - hash_algos; +} + #endif @@ -73,14 +73,15 @@ int parse_oid_hex(const char *hex, struct object_id *oid, const char **end) return ret; } -char *sha1_to_hex_r(char *buffer, const unsigned char *sha1) +char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, + const struct git_hash_algo *algop) { static const char hex[] = "0123456789abcdef"; char *buf = buffer; int i; - for (i = 0; i < the_hash_algo->rawsz; i++) { - unsigned int val = *sha1++; + for (i = 0; i < algop->rawsz; i++) { + unsigned int val = *hash++; *buf++ = hex[val >> 4]; *buf++ = hex[val & 0xf]; } @@ -89,20 +90,35 @@ char *sha1_to_hex_r(char *buffer, const unsigned char *sha1) return buffer; } +char *sha1_to_hex_r(char *buffer, const unsigned char *sha1) +{ + return hash_to_hex_algop_r(buffer, sha1, &hash_algos[GIT_HASH_SHA1]); +} + char *oid_to_hex_r(char *buffer, const struct object_id *oid) { - return sha1_to_hex_r(buffer, oid->hash); + return hash_to_hex_algop_r(buffer, oid->hash, the_hash_algo); } -char *sha1_to_hex(const unsigned char *sha1) +char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *algop) { static int bufno; static char hexbuffer[4][GIT_MAX_HEXSZ + 1]; bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer); - return sha1_to_hex_r(hexbuffer[bufno], sha1); + return hash_to_hex_algop_r(hexbuffer[bufno], hash, algop); +} + +char *sha1_to_hex(const unsigned char *sha1) +{ + return hash_to_hex_algop(sha1, &hash_algos[GIT_HASH_SHA1]); +} + +char *hash_to_hex(const unsigned char *hash) +{ + return hash_to_hex_algop(hash, the_hash_algo); } char *oid_to_hex(const struct object_id *oid) { - return sha1_to_hex(oid->hash); + return hash_to_hex_algop(oid->hash, the_hash_algo); } diff --git a/http-push.c b/http-push.c index cd48590912..bb802d80ee 100644 --- a/http-push.c +++ b/http-push.c @@ -1311,11 +1311,11 @@ static struct object_list **process_tree(struct tree *tree, while (tree_entry(&desc, &entry)) switch (object_type(entry.mode)) { case OBJ_TREE: - p = process_tree(lookup_tree(the_repository, entry.oid), + p = process_tree(lookup_tree(the_repository, &entry.oid), p); break; case OBJ_BLOB: - p = process_blob(lookup_blob(the_repository, entry.oid), + p = process_blob(lookup_blob(the_repository, &entry.oid), p); break; default: @@ -1876,8 +1876,6 @@ static int http_request(const char *url, strbuf_addstr(&buf, "Pragma:"); if (options && options->no_cache) strbuf_addstr(&buf, " no-cache"); - if (options && options->keep_error) - curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0); if (options && options->initial_request && http_follow_config == HTTP_FOLLOW_INITIAL) curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1); @@ -1895,6 +1893,7 @@ static int http_request(const char *url, curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(slot->curl, CURLOPT_ENCODING, ""); + curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0); ret = run_one_slot(slot, &results); @@ -1989,19 +1988,26 @@ static int http_request_reauth(const char *url, return ret; /* - * If we are using KEEP_ERROR, the previous request may have - * put cruft into our output stream; we should clear it out before - * making our next request. We only know how to do this for - * the strbuf case, but that is enough to satisfy current callers. + * The previous request may have put cruft into our output stream; we + * should clear it out before making our next request. */ - if (options && options->keep_error) { - switch (target) { - case HTTP_REQUEST_STRBUF: - strbuf_reset(result); - break; - default: - BUG("HTTP_KEEP_ERROR is only supported with strbufs"); + switch (target) { + case HTTP_REQUEST_STRBUF: + strbuf_reset(result); + break; + case HTTP_REQUEST_FILE: + if (fflush(result)) { + error_errno("unable to flush a file"); + return HTTP_START_FAILED; } + rewind(result); + if (ftruncate(fileno(result), 0) < 0) { + error_errno("unable to truncate a file"); + return HTTP_START_FAILED; + } + break; + default: + BUG("Unknown http_request target"); } credential_fill(&http_auth); @@ -146,7 +146,6 @@ extern char *get_remote_object_url(const char *url, const char *hex, /* Options for http_get_*() */ struct http_get_options { unsigned no_cache:1, - keep_error:1, initial_request:1; /* If non-NULL, returns the content-type of the response. */ diff --git a/imap-send.c b/imap-send.c index b4eb886e2a..18ca6ba10a 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1471,7 +1471,8 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred) } static int curl_append_msgs_to_imap(struct imap_server_conf *server, - struct strbuf* all_msgs, int total) { + struct strbuf* all_msgs, int total) +{ int ofs = 0; int n = 0; struct buffer msgbuf = { STRBUF_INIT, 0 }; diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c index e8da2e8581..9efb3e9902 100644 --- a/list-objects-filter-options.c +++ b/list-objects-filter-options.c @@ -18,8 +18,9 @@ * See Documentation/rev-list-options.txt for allowed values for <arg>. * * Capture the given arg as the "filter_spec". This can be forwarded to - * subordinate commands when necessary. We also "intern" the arg for - * the convenience of the current command. + * subordinate commands when necessary (although it's better to pass it through + * expand_list_objects_filter_spec() first). We also "intern" the arg for the + * convenience of the current command. */ static int gently_parse_list_objects_filter( struct list_objects_filter_options *filter_options, @@ -50,16 +51,15 @@ static int gently_parse_list_objects_filter( } } else if (skip_prefix(arg, "tree:", &v0)) { - unsigned long depth; - if (!git_parse_ulong(v0, &depth) || depth != 0) { + if (!git_parse_ulong(v0, &filter_options->tree_exclude_depth)) { if (errbuf) { strbuf_addstr( errbuf, - _("only 'tree:0' is supported")); + _("expected 'tree:<depth>'")); } return 1; } - filter_options->choice = LOFC_TREE_NONE; + filter_options->choice = LOFC_TREE_DEPTH; return 0; } else if (skip_prefix(arg, "sparse:oid=", &v0)) { @@ -112,6 +112,21 @@ int opt_parse_list_objects_filter(const struct option *opt, return parse_list_objects_filter(filter_options, arg); } +void expand_list_objects_filter_spec( + const struct list_objects_filter_options *filter, + struct strbuf *expanded_spec) +{ + strbuf_init(expanded_spec, strlen(filter->filter_spec)); + if (filter->choice == LOFC_BLOB_LIMIT) + strbuf_addf(expanded_spec, "blob:limit=%lu", + filter->blob_limit_value); + else if (filter->choice == LOFC_TREE_DEPTH) + strbuf_addf(expanded_spec, "tree:%lu", + filter->tree_exclude_depth); + else + strbuf_addstr(expanded_spec, filter->filter_spec); +} + void list_objects_filter_release( struct list_objects_filter_options *filter_options) { diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h index af64e5c66f..e3adc78ebf 100644 --- a/list-objects-filter-options.h +++ b/list-objects-filter-options.h @@ -2,6 +2,7 @@ #define LIST_OBJECTS_FILTER_OPTIONS_H #include "parse-options.h" +#include "strbuf.h" /* * The list of defined filters for list-objects. @@ -10,7 +11,7 @@ enum list_objects_filter_choice { LOFC_DISABLED = 0, LOFC_BLOB_NONE, LOFC_BLOB_LIMIT, - LOFC_TREE_NONE, + LOFC_TREE_DEPTH, LOFC_SPARSE_OID, LOFC_SPARSE_PATH, LOFC__COUNT /* must be last */ @@ -20,8 +21,9 @@ struct list_objects_filter_options { /* * 'filter_spec' is the raw argument value given on the command line * or protocol request. (The part after the "--keyword=".) For - * commands that launch filtering sub-processes, this value should be - * passed to them as received by the current process. + * commands that launch filtering sub-processes, or for communication + * over the network, don't use this value; use the result of + * expand_list_objects_filter_spec() instead. */ char *filter_spec; @@ -44,6 +46,7 @@ struct list_objects_filter_options { struct object_id *sparse_oid_value; char *sparse_path_value; unsigned long blob_limit_value; + unsigned long tree_exclude_depth; }; /* Normalized command line arguments */ @@ -61,6 +64,17 @@ int opt_parse_list_objects_filter(const struct option *opt, N_("object filtering"), 0, \ opt_parse_list_objects_filter } +/* + * Translates abbreviated numbers in the filter's filter_spec into their + * fully-expanded forms (e.g., "limit:blob=1k" becomes "limit:blob=1024"). + * + * This form should be used instead of the raw filter_spec field when + * communicating with a remote process or subprocess. + */ +void expand_list_objects_filter_spec( + const struct list_objects_filter_options *filter, + struct strbuf *expanded_spec); + void list_objects_filter_release( struct list_objects_filter_options *filter_options); diff --git a/list-objects-filter.c b/list-objects-filter.c index a62624a1ce..ee449de3f7 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -10,6 +10,7 @@ #include "list-objects.h" #include "list-objects-filter.h" #include "list-objects-filter-options.h" +#include "oidmap.h" #include "oidset.h" #include "object-store.h" @@ -84,11 +85,44 @@ static void *filter_blobs_none__init( * A filter for list-objects to omit ALL trees and blobs from the traversal. * Can OPTIONALLY collect a list of the omitted OIDs. */ -struct filter_trees_none_data { +struct filter_trees_depth_data { struct oidset *omits; + + /* + * Maps trees to the minimum depth at which they were seen. It is not + * necessary to re-traverse a tree at deeper or equal depths than it has + * already been traversed. + * + * We can't use LOFR_MARK_SEEN for tree objects since this will prevent + * it from being traversed at shallower depths. + */ + struct oidmap seen_at_depth; + + unsigned long exclude_depth; + unsigned long current_depth; }; -static enum list_objects_filter_result filter_trees_none( +struct seen_map_entry { + struct oidmap_entry base; + size_t depth; +}; + +/* Returns 1 if the oid was in the omits set before it was invoked. */ +static int filter_trees_update_omits( + struct object *obj, + struct filter_trees_depth_data *filter_data, + int include_it) +{ + if (!filter_data->omits) + return 0; + + if (include_it) + return oidset_remove(filter_data->omits, &obj->oid); + else + return oidset_insert(filter_data->omits, &obj->oid); +} + +static enum list_objects_filter_result filter_trees_depth( struct repository *r, enum list_objects_filter_situation filter_situation, struct object *obj, @@ -96,43 +130,91 @@ static enum list_objects_filter_result filter_trees_none( const char *filename, void *filter_data_) { - struct filter_trees_none_data *filter_data = filter_data_; + struct filter_trees_depth_data *filter_data = filter_data_; + struct seen_map_entry *seen_info; + int include_it = filter_data->current_depth < + filter_data->exclude_depth; + int filter_res; + int already_seen; + + /* + * Note that we do not use _MARK_SEEN in order to allow re-traversal in + * case we encounter a tree or blob again at a shallower depth. + */ switch (filter_situation) { default: BUG("unknown filter_situation: %d", filter_situation); - case LOFS_BEGIN_TREE: + case LOFS_END_TREE: + assert(obj->type == OBJ_TREE); + filter_data->current_depth--; + return LOFR_ZERO; + case LOFS_BLOB: - if (filter_data->omits) { - oidset_insert(filter_data->omits, &obj->oid); - /* _MARK_SEEN but not _DO_SHOW (hard omit) */ - return LOFR_MARK_SEEN; + filter_trees_update_omits(obj, filter_data, include_it); + return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO; + + case LOFS_BEGIN_TREE: + seen_info = oidmap_get( + &filter_data->seen_at_depth, &obj->oid); + if (!seen_info) { + seen_info = xcalloc(1, sizeof(*seen_info)); + oidcpy(&seen_info->base.oid, &obj->oid); + seen_info->depth = filter_data->current_depth; + oidmap_put(&filter_data->seen_at_depth, seen_info); + already_seen = 0; } else { - /* - * Not collecting omits so no need to to traverse tree. - */ - return LOFR_SKIP_TREE | LOFR_MARK_SEEN; + already_seen = + filter_data->current_depth >= seen_info->depth; } - case LOFS_END_TREE: - assert(obj->type == OBJ_TREE); - return LOFR_ZERO; + if (already_seen) { + filter_res = LOFR_SKIP_TREE; + } else { + int been_omitted = filter_trees_update_omits( + obj, filter_data, include_it); + seen_info->depth = filter_data->current_depth; + + if (include_it) + filter_res = LOFR_DO_SHOW; + else if (filter_data->omits && !been_omitted) + /* + * Must update omit information of children + * recursively; they have not been omitted yet. + */ + filter_res = LOFR_ZERO; + else + filter_res = LOFR_SKIP_TREE; + } + filter_data->current_depth++; + return filter_res; } } -static void* filter_trees_none__init( +static void filter_trees_free(void *filter_data) { + struct filter_trees_depth_data *d = filter_data; + if (!d) + return; + oidmap_free(&d->seen_at_depth, 1); + free(d); +} + +static void *filter_trees_depth__init( struct oidset *omitted, struct list_objects_filter_options *filter_options, filter_object_fn *filter_fn, filter_free_fn *filter_free_fn) { - struct filter_trees_none_data *d = xcalloc(1, sizeof(*d)); + struct filter_trees_depth_data *d = xcalloc(1, sizeof(*d)); d->omits = omitted; + oidmap_init(&d->seen_at_depth, 0); + d->exclude_depth = filter_options->tree_exclude_depth; + d->current_depth = 0; - *filter_fn = filter_trees_none; - *filter_free_fn = free; + *filter_fn = filter_trees_depth; + *filter_free_fn = filter_trees_free; return d; } @@ -430,7 +512,7 @@ static filter_init_fn s_filters[] = { NULL, filter_blobs_none__init, filter_blobs_limit__init, - filter_trees_none__init, + filter_trees_depth__init, filter_sparse_oid__init, filter_sparse_path__init, }; diff --git a/list-objects.c b/list-objects.c index 4e2789768d..a2296a8e7b 100644 --- a/list-objects.c +++ b/list-objects.c @@ -124,15 +124,15 @@ static void process_tree_contents(struct traversal_context *ctx, } if (S_ISDIR(entry.mode)) { - struct tree *t = lookup_tree(ctx->revs->repo, entry.oid); + struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid); t->object.flags |= NOT_USER_GIVEN; process_tree(ctx, t, base, entry.path); } else if (S_ISGITLINK(entry.mode)) - process_gitlink(ctx, entry.oid->hash, + process_gitlink(ctx, entry.oid.hash, base, entry.path); else { - struct blob *b = lookup_blob(ctx->revs->repo, entry.oid); + struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid); b->object.flags |= NOT_USER_GIVEN; process_blob(ctx, b, base, entry.path); } diff --git a/log-tree.c b/log-tree.c index 10680c139e..3cb14256ec 100644 --- a/log-tree.c +++ b/log-tree.c @@ -700,6 +700,7 @@ void show_log(struct rev_info *opt) ctx.color = opt->diffopt.use_color; ctx.expand_tabs_in_log = opt->expand_tabs_in_log; ctx.output_encoding = get_log_output_encoding(); + ctx.rev = opt; if (opt->from_ident.mail_begin && opt->from_ident.name_begin) ctx.from_ident = &opt->from_ident; if (opt->graph) @@ -5,6 +5,7 @@ #include "argv-array.h" #include "ls-refs.h" #include "pkt-line.h" +#include "config.h" /* * Check if one of the prefixes is a prefix of the ref. @@ -40,6 +41,9 @@ static int send_ref(const char *refname, const struct object_id *oid, const char *refname_nons = strip_namespace(refname); struct strbuf refline = STRBUF_INIT; + if (ref_is_hidden(refname_nons, refname)) + return 0; + if (!ref_match(&data->prefixes, refname)) return 0; @@ -69,6 +73,16 @@ static int send_ref(const char *refname, const struct object_id *oid, return 0; } +static int ls_refs_config(const char *var, const char *value, void *data) +{ + /* + * We only serve fetches over v2 for now, so respect only "uploadpack" + * config. This may need to eventually be expanded to "receive", but we + * don't yet know how that information will be passed to ls-refs. + */ + return parse_hide_refs_config(var, value, "uploadpack"); +} + int ls_refs(struct repository *r, struct argv_array *keys, struct packet_reader *request) { @@ -76,6 +90,8 @@ int ls_refs(struct repository *r, struct argv_array *keys, memset(&data, 0, sizeof(data)); + git_config(ls_refs_config, NULL); + while (packet_reader_read(request) != PACKET_READ_FLUSH) { const char *arg = request->line; const char *out; diff --git a/match-trees.c b/match-trees.c index 2b6d31ef9d..18ab825bef 100644 --- a/match-trees.c +++ b/match-trees.c @@ -106,7 +106,7 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha update_tree_entry(&two); } else { /* path appears in both */ - if (!oideq(one.entry.oid, two.entry.oid)) { + if (!oideq(&one.entry.oid, &two.entry.oid)) { /* they are different */ score += score_differs(one.entry.mode, two.entry.mode, @@ -179,7 +179,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix, char *buf; unsigned long sz; struct tree_desc desc; - struct object_id *rewrite_here; + unsigned char *rewrite_here; const struct object_id *rewrite_with; struct object_id subtree; enum object_type type; @@ -199,15 +199,26 @@ static int splice_tree(const struct object_id *oid1, const char *prefix, while (desc.size) { const char *name; unsigned mode; - const struct object_id *oid; - oid = tree_entry_extract(&desc, &name, &mode); + tree_entry_extract(&desc, &name, &mode); if (strlen(name) == toplen && !memcmp(name, prefix, toplen)) { if (!S_ISDIR(mode)) die("entry %s in tree %s is not a tree", name, oid_to_hex(oid1)); - rewrite_here = (struct object_id *)oid; + + /* + * We cast here for two reasons: + * + * - to flip the "char *" (for the path) to "unsigned + * char *" (for the hash stored after it) + * + * - to discard the "const"; this is OK because we + * know it points into our non-const "buf" + */ + rewrite_here = (unsigned char *)(desc.entry.path + + strlen(desc.entry.path) + + 1); break; } update_tree_entry(&desc); @@ -216,14 +227,16 @@ static int splice_tree(const struct object_id *oid1, const char *prefix, die("entry %.*s not found in tree %s", toplen, prefix, oid_to_hex(oid1)); if (*subpath) { - status = splice_tree(rewrite_here, subpath, oid2, &subtree); + struct object_id tree_oid; + hashcpy(tree_oid.hash, rewrite_here); + status = splice_tree(&tree_oid, subpath, oid2, &subtree); if (status) return status; rewrite_with = &subtree; } else { rewrite_with = oid2; } - oidcpy(rewrite_here, rewrite_with); + hashcpy(rewrite_here, rewrite_with->hash); status = write_object_file(buf, sz, tree_type, result); free(buf); return status; @@ -450,7 +450,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, l = xcalloc(1, sizeof(*l)); oidcpy(&l->key_oid, &object_oid); - oidcpy(&l->val_oid, entry.oid); + oidcpy(&l->val_oid, &entry.oid); if (note_tree_insert(t, node, n, l, type, combine_notes_concatenate)) die("Failed to load %s %s into notes tree " @@ -481,7 +481,7 @@ handle_non_note: } strbuf_addstr(&non_note_path, entry.path); add_non_note(t, strbuf_detach(&non_note_path, NULL), - entry.mode, entry.oid->hash); + entry.mode, entry.oid.hash); } } free(buf); diff --git a/object-store.h b/object-store.h index 60758efad8..ba57630677 100644 --- a/object-store.h +++ b/object-store.h @@ -20,7 +20,7 @@ struct object_directory { * Be sure to call odb_load_loose_cache() before using. */ char loose_objects_subdir_seen[256]; - struct oid_array loose_objects_cache; + struct oid_array loose_objects_cache[256]; /* * Path to the alternative object store. If this is a relative path, @@ -48,11 +48,14 @@ void add_to_alternates_file(const char *dir); void add_to_alternates_memory(const char *dir); /* - * Populate an odb's loose object cache for one particular subdirectory (i.e., - * the one that corresponds to the first byte of objects you're interested in, - * from 0 to 255 inclusive). + * Populate and return the loose object cache array corresponding to the + * given object ID. */ -void odb_load_loose_cache(struct object_directory *odb, int subdir_nr); +struct oid_array *odb_loose_cache(struct object_directory *odb, + const struct object_id *oid); + +/* Empty the loose object cache for the specified object directory. */ +void odb_clear_loose_cache(struct object_directory *odb); struct packed_git { struct packed_git *next; @@ -157,13 +160,20 @@ const char *loose_object_path(struct repository *r, struct strbuf *buf, const un void *map_sha1_file(struct repository *r, const unsigned char *sha1, unsigned long *size); -extern void *read_object_file_extended(const struct object_id *oid, +extern void *read_object_file_extended(struct repository *r, + const struct object_id *oid, enum object_type *type, unsigned long *size, int lookup_replace); -static inline void *read_object_file(const struct object_id *oid, enum object_type *type, unsigned long *size) +static inline void *repo_read_object_file(struct repository *r, + const struct object_id *oid, + enum object_type *type, + unsigned long *size) { - return read_object_file_extended(oid, type, size, 1); + return read_object_file_extended(r, oid, type, size, 1); } +#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS +#define read_object_file(oid, type, size) repo_read_object_file(the_repository, oid, type, size) +#endif /* Read and unpack an object file into memory, write memory to an object file */ int oid_object_info(struct repository *r, const struct object_id *, unsigned long *); @@ -201,15 +211,27 @@ int read_loose_object(const char *path, * object_info. OBJECT_INFO_SKIP_CACHED is automatically set; pass * nonzero flags to also set other flags. */ -extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags); -static inline int has_sha1_file(const unsigned char *sha1) +int repo_has_sha1_file_with_flags(struct repository *r, + const unsigned char *sha1, int flags); +static inline int repo_has_sha1_file(struct repository *r, + const unsigned char *sha1) { - return has_sha1_file_with_flags(sha1, 0); + return repo_has_sha1_file_with_flags(r, sha1, 0); } +#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS +#define has_sha1_file_with_flags(sha1, flags) repo_has_sha1_file_with_flags(the_repository, sha1, flags) +#define has_sha1_file(sha1) repo_has_sha1_file(the_repository, sha1) +#endif + /* Same as the above, except for struct object_id. */ -extern int has_object_file(const struct object_id *oid); -extern int has_object_file_with_flags(const struct object_id *oid, int flags); +int repo_has_object_file(struct repository *r, const struct object_id *oid); +int repo_has_object_file_with_flags(struct repository *r, + const struct object_id *oid, int flags); +#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS +#define has_object_file(oid) repo_has_object_file(the_repository, oid) +#define has_object_file_with_flags(oid, flags) repo_has_object_file_with_flags(the_repository, oid, flags) +#endif /* * Return true iff an alternate object database has a loose object @@ -259,8 +259,8 @@ struct object *parse_object(struct repository *r, const struct object_id *oid) if (obj && obj->parsed) return obj; - if ((obj && obj->type == OBJ_BLOB && has_object_file(oid)) || - (!obj && has_object_file(oid) && + if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) || + (!obj && repo_has_object_file(r, oid) && oid_object_info(r, oid, NULL) == OBJ_BLOB)) { if (check_object_signature(repl, NULL, 0, NULL) < 0) { error(_("sha1 mismatch %s"), oid_to_hex(oid)); @@ -270,7 +270,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid) return lookup_object(r, oid->hash); } - buffer = read_object_file(oid, &type, &size); + buffer = repo_read_object_file(r, oid, &type, &size); if (buffer) { if (check_object_signature(repl, buffer, size, type_name(type)) < 0) { free(buffer); @@ -485,7 +485,7 @@ struct raw_object_store *raw_object_store_new(void) static void free_object_directory(struct object_directory *odb) { free(odb->path); - oid_array_clear(&odb->loose_objects_cache); + odb_clear_loose_cache(odb); free(odb); } @@ -540,7 +540,7 @@ void parsed_object_pool_clear(struct parsed_object_pool *o) if (obj->type == OBJ_TREE) free_tree_buffer((struct tree*)obj); else if (obj->type == OBJ_COMMIT) - release_commit_memory((struct commit*)obj); + release_commit_memory(o, (struct commit*)obj); else if (obj->type == OBJ_TAG) release_tag_memory((struct tag*)obj); } diff --git a/packfile.c b/packfile.c index 8c6b47cc77..16bcb75262 100644 --- a/packfile.c +++ b/packfile.c @@ -994,11 +994,8 @@ void reprepare_packed_git(struct repository *r) { struct object_directory *odb; - for (odb = r->objects->odb; odb; odb = odb->next) { - oid_array_clear(&odb->loose_objects_cache); - memset(&odb->loose_objects_subdir_seen, 0, - sizeof(odb->loose_objects_subdir_seen)); - } + for (odb = r->objects->odb; odb; odb = odb->next) + odb_clear_loose_cache(odb); r->objects->approximate_object_count_valid = 0; r->objects->packed_git_initialized = 0; @@ -1151,12 +1148,13 @@ void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1) p->num_bad_objects++; } -const struct packed_git *has_packed_and_bad(const unsigned char *sha1) +const struct packed_git *has_packed_and_bad(struct repository *r, + const unsigned char *sha1) { struct packed_git *p; unsigned i; - for (p = the_repository->objects->packed_git; p; p = p->next) + for (p = r->objects->packed_git; p; p = p->next) for (i = 0; i < p->num_bad_objects; i++) if (hasheq(sha1, p->bad_object_sha1 + the_hash_algo->rawsz * i)) @@ -2100,7 +2098,7 @@ static int add_promisor_object(const struct object_id *oid, */ return 0; while (tree_entry_gently(&desc, &entry)) - oidset_insert(set, entry.oid); + oidset_insert(set, &entry.oid); } else if (obj->type == OBJ_COMMIT) { struct commit *commit = (struct commit *) obj; struct commit_list *parents = commit->parents; diff --git a/packfile.h b/packfile.h index 6c4037605d..d70c6d9afb 100644 --- a/packfile.h +++ b/packfile.h @@ -146,7 +146,7 @@ extern int packed_object_info(struct repository *r, off_t offset, struct object_info *); extern void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1); -extern const struct packed_git *has_packed_and_bad(const unsigned char *sha1); +extern const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1); /* * Iff a pack file in the given repository contains the object named by sha1, @@ -165,7 +165,7 @@ extern void report_linked_checkout_garbage(void); const char *git_path_##var(struct repository *r) \ { \ if (!r->cached_paths.var) \ - r->cached_paths.var = git_pathdup(filename); \ + r->cached_paths.var = repo_git_path(r, filename); \ return r->cached_paths.var; \ } @@ -595,14 +595,15 @@ static char *replace_encoding_header(char *buf, const char *encoding) return strbuf_detach(&tmp, NULL); } -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) { static const char *utf8 = "UTF-8"; const char *use_encoding; char *encoding; - const char *msg = get_commit_buffer(commit, NULL); + const char *msg = repo_get_commit_buffer(r, commit, NULL); char *out; if (!output_encoding || !*output_encoding) { @@ -630,7 +631,7 @@ const char *logmsg_reencode(const struct commit *commit, * the cached copy from get_commit_buffer, we need to duplicate it * to avoid munging the cached copy. */ - if (msg == get_cached_commit_buffer(the_repository, commit, NULL)) + if (msg == get_cached_commit_buffer(r, commit, NULL)) out = xstrdup(msg); else out = (char *)msg; @@ -644,7 +645,7 @@ const char *logmsg_reencode(const struct commit *commit, */ out = reencode_string(msg, output_encoding, use_encoding); if (out) - unuse_commit_buffer(commit, msg); + repo_unuse_commit_buffer(r, commit, msg); } /* @@ -1084,6 +1085,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ struct commit_list *p; const char *arg; int ch; + char **slot; /* these are independent of the commit */ switch (placeholder[0]) { @@ -1194,6 +1196,14 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ load_ref_decorations(NULL, DECORATE_SHORT_REFS); format_decorations_extended(sb, commit, c->auto_color, "", ", ", ""); return 1; + case 'S': /* tag/branch like --source */ + if (!(c->pretty_ctx->rev && c->pretty_ctx->rev->sources)) + return 0; + slot = revision_sources_at(c->pretty_ctx->rev->sources, commit); + if (!(slot && *slot)) + return 0; + strbuf_addstr(sb, *slot); + return 1; case 'g': /* reflog info */ switch(placeholder[1]) { case 'd': /* reflog selector */ @@ -1498,6 +1508,9 @@ static size_t userformat_want_item(struct strbuf *sb, const char *placeholder, case 'N': w->notes = 1; break; + case 'S': + w->source = 1; + break; } return 0; } @@ -1515,9 +1528,10 @@ void userformat_find_requirements(const char *fmt, struct userformat_want *w) strbuf_release(&dummy); } -void format_commit_message(const struct commit *commit, - const char *format, struct strbuf *sb, - const struct pretty_print_context *pretty_ctx) +void repo_format_commit_message(struct repository *r, + const struct commit *commit, + const char *format, struct strbuf *sb, + const struct pretty_print_context *pretty_ctx) { struct format_commit_context context; const char *output_enc = pretty_ctx->output_encoding; @@ -1531,9 +1545,9 @@ void format_commit_message(const struct commit *commit, * convert a commit message to UTF-8 first * as far as 'format_commit_item' assumes it in UTF-8 */ - context.message = logmsg_reencode(commit, - &context.commit_encoding, - utf8); + context.message = repo_logmsg_reencode(r, commit, + &context.commit_encoding, + utf8); strbuf_expand(sb, format, format_commit_item, &context); rewrap_message_tail(sb, &context, 0, 0, 0); @@ -1557,7 +1571,7 @@ void format_commit_message(const struct commit *commit, } free(context.commit_encoding); - unuse_commit_buffer(commit, context.message); + repo_unuse_commit_buffer(r, commit, context.message); } static void pp_header(struct pretty_print_context *pp, @@ -60,6 +60,7 @@ static inline int cmit_fmt_is_mail(enum cmit_fmt fmt) struct userformat_want { unsigned notes:1; + unsigned source:1; }; /* Set the flag "w->notes" if there is placeholder %N in "fmt". */ @@ -103,9 +104,14 @@ void pp_remainder(struct pretty_print_context *pp, const char **msg_p, * Put the result to "sb". * Please use this function for custom formats. */ -void format_commit_message(const struct commit *commit, +void repo_format_commit_message(struct repository *r, + const struct commit *commit, const char *format, struct strbuf *sb, const struct pretty_print_context *context); +#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS +#define format_commit_message(c, f, s, con) \ + repo_format_commit_message(the_repository, c, f, s, con) +#endif /* * Parse given arguments from "arg", check it for correctness and diff --git a/ref-filter.c b/ref-filter.c index 61d75d5c86..422a9c9ae3 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -231,12 +231,30 @@ static int objecttype_atom_parser(const struct ref_format *format, struct used_a static int objectsize_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg, struct strbuf *err) { + if (!arg) { + if (*atom->name == '*') + oi_deref.info.sizep = &oi_deref.size; + else + oi.info.sizep = &oi.size; + } else if (!strcmp(arg, "disk")) { + if (*atom->name == '*') + oi_deref.info.disk_sizep = &oi_deref.disk_size; + else + oi.info.disk_sizep = &oi.disk_size; + } else + return strbuf_addf_ret(err, -1, _("unrecognized %%(objectsize) argument: %s"), arg); + return 0; +} + +static int deltabase_atom_parser(const struct ref_format *format, struct used_atom *atom, + const char *arg, struct strbuf *err) +{ if (arg) - return strbuf_addf_ret(err, -1, _("%%(objectsize) does not take arguments")); + return strbuf_addf_ret(err, -1, _("%%(deltabase) does not take arguments")); if (*atom->name == '*') - oi_deref.info.sizep = &oi_deref.size; + oi_deref.info.delta_base_sha1 = oi_deref.delta_base_oid.hash; else - oi.info.sizep = &oi.size; + oi.info.delta_base_sha1 = oi.delta_base_oid.hash; return 0; } @@ -431,6 +449,7 @@ static struct { { "objecttype", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser }, { "objectsize", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser }, { "objectname", SOURCE_OTHER, FIELD_STR, objectname_atom_parser }, + { "deltabase", SOURCE_OTHER, FIELD_STR, deltabase_atom_parser }, { "tree", SOURCE_OBJ }, { "parent", SOURCE_OBJ }, { "numparent", SOURCE_OBJ, FIELD_ULONG }, @@ -880,10 +899,14 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_ name++; if (!strcmp(name, "objecttype")) v->s = xstrdup(type_name(oi->type)); - else if (!strcmp(name, "objectsize")) { + else if (!strcmp(name, "objectsize:disk")) { + v->value = oi->disk_size; + v->s = xstrfmt("%"PRIuMAX, (uintmax_t)oi->disk_size); + } else if (!strcmp(name, "objectsize")) { v->value = oi->size; v->s = xstrfmt("%"PRIuMAX , (uintmax_t)oi->size); - } + } else if (!strcmp(name, "deltabase")) + v->s = xstrdup(oid_to_hex(&oi->delta_base_oid)); else if (deref) grab_objectname(name, &oi->oid, v, &used_atom[i]); } @@ -1482,6 +1505,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj OBJECT_INFO_LOOKUP_REPLACE)) return strbuf_addf_ret(err, -1, _("missing object %s for %s"), oid_to_hex(&oi->oid), ref->refname); + if (oi->info.disk_sizep && oi->disk_size < 0) + BUG("Object size is less than zero."); if (oi->info.contentp) { *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten); diff --git a/remote-curl.c b/remote-curl.c index 1220dffcdc..6ff9c66b90 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -380,7 +380,6 @@ static struct discovery *discover_refs(const char *service, int for_push) http_options.extra_headers = &extra_headers; http_options.initial_request = 1; http_options.no_cache = 1; - http_options.keep_error = 1; http_ret = http_get_strbuf(refs_url.buf, &buffer, &http_options); switch (http_ret) { @@ -546,14 +545,30 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp) } #endif +struct rpc_in_data { + struct rpc_state *rpc; + struct active_request_slot *slot; +}; + +/* + * A callback for CURLOPT_WRITEFUNCTION. The return value is the bytes consumed + * from ptr. + */ static size_t rpc_in(char *ptr, size_t eltsize, size_t nmemb, void *buffer_) { size_t size = eltsize * nmemb; - struct rpc_state *rpc = buffer_; + struct rpc_in_data *data = buffer_; + long response_code; + + if (curl_easy_getinfo(data->slot->curl, CURLINFO_RESPONSE_CODE, + &response_code) != CURLE_OK) + return size; + if (response_code >= 300) + return size; if (size) - rpc->any_written = 1; - write_or_die(rpc->in, ptr, size); + data->rpc->any_written = 1; + write_or_die(data->rpc->in, ptr, size); return size; } @@ -617,7 +632,8 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results) return err; } -static curl_off_t xcurl_off_t(size_t len) { +static curl_off_t xcurl_off_t(size_t len) +{ uintmax_t size = len; if (size > maximum_signed_value_of_type(curl_off_t)) die("cannot handle pushes this big"); @@ -633,6 +649,7 @@ static int post_rpc(struct rpc_state *rpc) size_t gzip_size = 0; int err, large_request = 0; int needs_100_continue = 0; + struct rpc_in_data rpc_in_data; /* Try to load the entire request, if we can fit it into the * allocated buffer space we can use HTTP/1.0 and avoid the @@ -765,7 +782,10 @@ retry: curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in); - curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc); + rpc_in_data.rpc = rpc; + rpc_in_data.slot = slot; + curl_easy_setopt(slot->curl, CURLOPT_FILE, &rpc_in_data); + curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0); rpc->any_written = 0; @@ -337,14 +337,14 @@ static int handle_config(const char *key, const char *value, void *cb) if (!name) return 0; if (!strcmp(subkey, "insteadof")) { - rewrite = make_rewrite(&rewrites, name, namelen); if (!value) return config_error_nonbool(key); + rewrite = make_rewrite(&rewrites, name, namelen); add_instead_of(rewrite, xstrdup(value)); } else if (!strcmp(subkey, "pushinsteadof")) { - rewrite = make_rewrite(&rewrites_push, name, namelen); if (!value) return config_error_nonbool(key); + rewrite = make_rewrite(&rewrites_push, name, namelen); add_instead_of(rewrite, xstrdup(value)); } } diff --git a/repository.c b/repository.c index 7b02e1dffa..20c509a922 100644 --- a/repository.c +++ b/repository.c @@ -172,30 +172,23 @@ error: return -1; } -/* - * Initialize 'submodule' as the submodule given by 'path' in parent repository - * 'superproject'. - * Return 0 upon success and a non-zero value upon failure. - */ -int repo_submodule_init(struct repository *submodule, +int repo_submodule_init(struct repository *subrepo, struct repository *superproject, - const char *path) + const struct submodule *sub) { - const struct submodule *sub; struct strbuf gitdir = STRBUF_INIT; struct strbuf worktree = STRBUF_INIT; int ret = 0; - sub = submodule_from_path(superproject, &null_oid, path); if (!sub) { ret = -1; goto out; } - strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path); - strbuf_repo_worktree_path(&worktree, superproject, "%s", path); + strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", sub->path); + strbuf_repo_worktree_path(&worktree, superproject, "%s", sub->path); - if (repo_init(submodule, gitdir.buf, worktree.buf)) { + if (repo_init(subrepo, gitdir.buf, worktree.buf)) { /* * If initilization fails then it may be due to the submodule * not being populated in the superproject's worktree. Instead @@ -207,16 +200,16 @@ int repo_submodule_init(struct repository *submodule, strbuf_repo_git_path(&gitdir, superproject, "modules/%s", sub->name); - if (repo_init(submodule, gitdir.buf, NULL)) { + if (repo_init(subrepo, gitdir.buf, NULL)) { ret = -1; goto out; } } - submodule->submodule_prefix = xstrfmt("%s%s/", - superproject->submodule_prefix ? - superproject->submodule_prefix : - "", path); + subrepo->submodule_prefix = xstrfmt("%s%s/", + superproject->submodule_prefix ? + superproject->submodule_prefix : + "", sub->path); out: strbuf_release(&gitdir); diff --git a/repository.h b/repository.h index 9f16c42c1e..0e482b7d49 100644 --- a/repository.h +++ b/repository.h @@ -116,9 +116,17 @@ void repo_set_worktree(struct repository *repo, const char *path); void repo_set_hash_algo(struct repository *repo, int algo); void initialize_the_repository(void); int repo_init(struct repository *r, const char *gitdir, const char *worktree); -int repo_submodule_init(struct repository *submodule, + +/* + * Initialize the repository 'subrepo' as the submodule given by the + * struct submodule 'sub' in parent repository 'superproject'. + * Return 0 upon success and a non-zero value upon failure, which may happen + * if the submodule is not found, or 'sub' is NULL. + */ +struct submodule; +int repo_submodule_init(struct repository *subrepo, struct repository *superproject, - const char *path); + const struct submodule *sub); void repo_clear(struct repository *repo); /* diff --git a/revision.c b/revision.c index 13cfb59b38..8f886fe8c6 100644 --- a/revision.c +++ b/revision.c @@ -67,10 +67,10 @@ static void mark_tree_contents_uninteresting(struct repository *r, while (tree_entry(&desc, &entry)) { switch (object_type(entry.mode)) { case OBJ_TREE: - mark_tree_uninteresting(r, lookup_tree(r, entry.oid)); + mark_tree_uninteresting(r, lookup_tree(r, &entry.oid)); break; case OBJ_BLOB: - mark_blob_uninteresting(lookup_blob(r, entry.oid)); + mark_blob_uninteresting(lookup_blob(r, &entry.oid)); break; default: /* Subproject commit - not in this repository */ @@ -213,7 +213,20 @@ static struct object *get_reference(struct rev_info *revs, const char *name, { struct object *object; - object = parse_object(revs->repo, oid); + /* + * If the repository has commit graphs, repo_parse_commit() avoids + * reading the object buffer, so use it whenever possible. + */ + if (oid_object_info(revs->repo, oid, NULL) == OBJ_COMMIT) { + struct commit *c = lookup_commit(revs->repo, oid); + if (!repo_parse_commit(revs->repo, c)) + object = (struct object *) c; + else + object = NULL; + } else { + object = parse_object(revs->repo, oid); + } + if (!object) { if (revs->ignore_missing) return object; diff --git a/sequencer.c b/sequencer.c index b68bca0bef..213815dbfc 100644 --- a/sequencer.c +++ b/sequencer.c @@ -158,6 +158,7 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy") static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts") static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate") static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet") +static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec") static int git_sequencer_config(const char *k, const char *v, void *cb) { @@ -1689,7 +1690,8 @@ static int update_squash_messages(struct repository *r, return res; } -static void flush_rewritten_pending(void) { +static void flush_rewritten_pending(void) +{ struct strbuf buf = STRBUF_INIT; struct object_id newoid; FILE *out; @@ -1714,7 +1716,8 @@ static void flush_rewritten_pending(void) { } static void record_in_rewritten(struct object_id *oid, - enum todo_command next_command) { + enum todo_command next_command) +{ FILE *out = fopen_or_warn(rebase_path_rewritten_pending(), "a"); if (!out) @@ -1788,9 +1791,13 @@ static int do_pick_commit(struct repository *r, return error(_("commit %s does not have parent %d"), oid_to_hex(&commit->object.oid), opts->mainline); parent = p->item; - } else if (0 < opts->mainline) - return error(_("mainline was specified but commit %s is not a merge."), - oid_to_hex(&commit->object.oid)); + } else if (1 < opts->mainline) + /* + * Non-first parent explicitly specified as mainline for + * non-merge commit + */ + return error(_("commit %s does not have parent %d"), + oid_to_hex(&commit->object.oid), opts->mainline); else parent = commit->parents->item; @@ -2388,6 +2395,9 @@ static int read_populate_opts(struct replay_opts *opts) opts->signoff = 1; } + if (file_exists(rebase_path_reschedule_failed_exec())) + opts->reschedule_failed_exec = 1; + read_strategy_opts(opts, &buf); strbuf_release(&buf); @@ -2469,6 +2479,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name, write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign); if (opts->signoff) write_file(rebase_path_signoff(), "--signoff\n"); + if (opts->reschedule_failed_exec) + write_file(rebase_path_reschedule_failed_exec(), "%s", ""); return 0; } @@ -3626,9 +3638,10 @@ static int pick_commits(struct repository *r, *end_of_arg = saved; /* Reread the todo file if it has changed. */ - if (res) - ; /* fall through */ - else if (stat(get_todo_path(opts), &st)) + if (res) { + if (opts->reschedule_failed_exec) + reschedule = 1; + } else if (stat(get_todo_path(opts), &st)) res = error_errno(_("could not stat '%s'"), get_todo_path(opts)); else if (match_stat_data(&todo_list->stat, &st)) { diff --git a/sequencer.h b/sequencer.h index 9d83f0f3e9..5d7bc04751 100644 --- a/sequencer.h +++ b/sequencer.h @@ -40,6 +40,7 @@ struct replay_opts { int allow_empty_message; int keep_redundant_commits; int verbose; + int reschedule_failed_exec; int mainline; @@ -831,16 +831,6 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset, return NULL; } -static const char *setup_nongit(const char *cwd, int *nongit_ok) -{ - if (!nongit_ok) - die(_("not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT); - if (chdir(cwd)) - die_errno(_("cannot come back to cwd")); - *nongit_ok = 1; - return NULL; -} - static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len) { struct stat buf; @@ -1054,7 +1044,7 @@ const char *setup_git_directory_gently(int *nongit_ok) { static struct strbuf cwd = STRBUF_INIT; struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT; - const char *prefix; + const char *prefix = NULL; struct repository_format repo_fmt; /* @@ -1079,9 +1069,6 @@ const char *setup_git_directory_gently(int *nongit_ok) strbuf_addbuf(&dir, &cwd); switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) { - case GIT_DIR_NONE: - prefix = NULL; - break; case GIT_DIR_EXPLICIT: prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok); break; @@ -1097,29 +1084,51 @@ const char *setup_git_directory_gently(int *nongit_ok) prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok); break; case GIT_DIR_HIT_CEILING: - prefix = setup_nongit(cwd.buf, nongit_ok); + if (!nongit_ok) + die(_("not a git repository (or any of the parent directories): %s"), + DEFAULT_GIT_DIR_ENVIRONMENT); + *nongit_ok = 1; break; case GIT_DIR_HIT_MOUNT_POINT: - if (nongit_ok) { - *nongit_ok = 1; - strbuf_release(&cwd); - strbuf_release(&dir); - return NULL; - } - die(_("not a git repository (or any parent up to mount point %s)\n" - "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."), - dir.buf); + if (!nongit_ok) + die(_("not a git repository (or any parent up to mount point %s)\n" + "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."), + dir.buf); + *nongit_ok = 1; + break; + case GIT_DIR_NONE: + /* + * As a safeguard against setup_git_directory_gently_1 returning + * this value, fallthrough to BUG. Otherwise it is possible to + * set startup_info->have_repository to 1 when we did nothing to + * find a repository. + */ default: BUG("unhandled setup_git_directory_1() result"); } - if (prefix) - setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1); - else + /* + * At this point, nongit_ok is stable. If it is non-NULL and points + * to a non-zero value, then this means that we haven't found a + * repository and that the caller expects startup_info to reflect + * this. + * + * Regardless of the state of nongit_ok, startup_info->prefix and + * the GIT_PREFIX environment variable must always match. For details + * see Documentation/config/alias.txt. + */ + if (nongit_ok && *nongit_ok) { + startup_info->have_repository = 0; + startup_info->prefix = NULL; setenv(GIT_PREFIX_ENVIRONMENT, "", 1); - - startup_info->have_repository = !nongit_ok || !*nongit_ok; - startup_info->prefix = prefix; + } else { + startup_info->have_repository = 1; + startup_info->prefix = prefix; + if (prefix) + setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1); + else + setenv(GIT_PREFIX_ENVIRONMENT, "", 1); + } /* * Not all paths through the setup code will call 'set_git_dir()' (which @@ -1132,7 +1141,10 @@ const char *setup_git_directory_gently(int *nongit_ok) * the user has set GIT_DIR. It may be beneficial to disallow bogus * GIT_DIR values at some point in the future. */ - if (startup_info->have_repository || getenv(GIT_DIR_ENVIRONMENT)) { + if (/* GIT_DIR_EXPLICIT, GIT_DIR_DISCOVERED, GIT_DIR_BARE */ + startup_info->have_repository || + /* GIT_DIR_EXPLICIT */ + getenv(GIT_DIR_ENVIRONMENT)) { if (!the_repository->gitdir) { const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); if (!gitdir) diff --git a/sha1-array.c b/sha1-array.c index b94e0ec0f5..d922e94e3f 100644 --- a/sha1-array.c +++ b/sha1-array.c @@ -77,3 +77,20 @@ int oid_array_for_each_unique(struct oid_array *array, } return 0; } + +void oid_array_filter(struct oid_array *array, + for_each_oid_fn want, + void *cb_data) +{ + unsigned nr = array->nr, src, dst; + struct object_id *oids = array->oid; + + for (src = dst = 0; src < nr; src++) { + if (want(&oids[src], cb_data)) { + if (src != dst) + oidcpy(&oids[dst], &oids[src]); + dst++; + } + } + array->nr = dst; +} diff --git a/sha1-array.h b/sha1-array.h index 232bf95017..55d016c4bf 100644 --- a/sha1-array.h +++ b/sha1-array.h @@ -22,5 +22,8 @@ int oid_array_for_each(struct oid_array *array, int oid_array_for_each_unique(struct oid_array *array, for_each_oid_fn fn, void *data); +void oid_array_filter(struct oid_array *array, + for_each_oid_fn want, + void *cbdata); #endif /* SHA1_ARRAY_H */ diff --git a/sha1-file.c b/sha1-file.c index 5a272f70de..c8da9f3475 100644 --- a/sha1-file.c +++ b/sha1-file.c @@ -40,10 +40,20 @@ #define EMPTY_TREE_SHA1_BIN_LITERAL \ "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \ "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04" +#define EMPTY_TREE_SHA256_BIN_LITERAL \ + "\x6e\xf1\x9b\x41\x22\x5c\x53\x69\xf1\xc1" \ + "\x04\xd4\x5d\x8d\x85\xef\xa9\xb0\x57\xb5" \ + "\x3b\x14\xb4\xb9\xb9\x39\xdd\x74\xde\xcc" \ + "\x53\x21" #define EMPTY_BLOB_SHA1_BIN_LITERAL \ "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \ "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91" +#define EMPTY_BLOB_SHA256_BIN_LITERAL \ + "\x47\x3a\x0f\x4c\x3b\xe8\xa9\x36\x81\xa2" \ + "\x67\xe3\xb1\xe9\xa7\xdc\xda\x11\x85\x43" \ + "\x6f\xe1\x41\xf7\x74\x91\x20\xa3\x03\x72" \ + "\x18\x13" const unsigned char null_sha1[GIT_MAX_RAWSZ]; const struct object_id null_oid; @@ -53,6 +63,12 @@ static const struct object_id empty_tree_oid = { static const struct object_id empty_blob_oid = { EMPTY_BLOB_SHA1_BIN_LITERAL }; +static const struct object_id empty_tree_oid_sha256 = { + EMPTY_TREE_SHA256_BIN_LITERAL +}; +static const struct object_id empty_blob_oid_sha256 = { + EMPTY_BLOB_SHA256_BIN_LITERAL +}; static void git_hash_sha1_init(git_hash_ctx *ctx) { @@ -69,6 +85,22 @@ static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx) git_SHA1_Final(hash, &ctx->sha1); } + +static void git_hash_sha256_init(git_hash_ctx *ctx) +{ + git_SHA256_Init(&ctx->sha256); +} + +static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + git_SHA256_Update(&ctx->sha256, data, len); +} + +static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx) +{ + git_SHA256_Final(hash, &ctx->sha256); +} + static void git_hash_unknown_init(git_hash_ctx *ctx) { BUG("trying to init unknown hash"); @@ -90,6 +122,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { 0x00000000, 0, 0, + 0, git_hash_unknown_init, git_hash_unknown_update, git_hash_unknown_final, @@ -97,17 +130,31 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { NULL, }, { - "sha-1", + "sha1", /* "sha1", big-endian */ 0x73686131, GIT_SHA1_RAWSZ, GIT_SHA1_HEXSZ, + GIT_SHA1_BLKSZ, git_hash_sha1_init, git_hash_sha1_update, git_hash_sha1_final, &empty_tree_oid, &empty_blob_oid, }, + { + "sha256", + /* "s256", big-endian */ + 0x73323536, + GIT_SHA256_RAWSZ, + GIT_SHA256_HEXSZ, + GIT_SHA256_BLKSZ, + git_hash_sha256_init, + git_hash_sha256_update, + git_hash_sha256_final, + &empty_tree_oid_sha256, + &empty_blob_oid_sha256, + } }; const char *empty_tree_oid_hex(void) @@ -122,6 +169,27 @@ const char *empty_blob_oid_hex(void) return oid_to_hex_r(buf, the_hash_algo->empty_blob); } +int hash_algo_by_name(const char *name) +{ + int i; + if (!name) + return GIT_HASH_UNKNOWN; + for (i = 1; i < GIT_HASH_NALGOS; i++) + if (!strcmp(name, hash_algos[i].name)) + return i; + return GIT_HASH_UNKNOWN; +} + +int hash_algo_by_id(uint32_t format_id) +{ + int i; + for (i = 1; i < GIT_HASH_NALGOS; i++) + if (format_id == hash_algos[i].format_id) + return i; + return GIT_HASH_UNKNOWN; +} + + /* * This is meant to hold a *small* number of objects that you would * want read_sha1_file() to be able to return, but yet you do not want @@ -924,7 +992,6 @@ static int open_sha1_file(struct repository *r, static int quick_has_loose(struct repository *r, const unsigned char *sha1) { - int subdir_nr = sha1[0]; struct object_id oid; struct object_directory *odb; @@ -932,8 +999,7 @@ static int quick_has_loose(struct repository *r, prepare_alt_odb(r); for (odb = r->objects->odb; odb; odb = odb->next) { - odb_load_loose_cache(odb, subdir_nr); - if (oid_array_lookup(&odb->loose_objects_cache, &oid) >= 0) + if (oid_array_lookup(odb_loose_cache(odb, &oid), &oid) >= 0) return 1; } return 0; @@ -962,6 +1028,7 @@ static void *map_sha1_file_1(struct repository *r, const char *path, if (!*size) { /* mmap() is forbidden on empty files */ error(_("object file %s is empty"), path); + close(fd); return NULL; } map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); @@ -1354,7 +1421,9 @@ int oid_object_info(struct repository *r, return type; } -static void *read_object(const unsigned char *sha1, enum object_type *type, +static void *read_object(struct repository *r, + const unsigned char *sha1, + enum object_type *type, unsigned long *size) { struct object_id oid; @@ -1366,7 +1435,7 @@ static void *read_object(const unsigned char *sha1, enum object_type *type, hashcpy(oid.hash, sha1); - if (oid_object_info_extended(the_repository, &oid, &oi, 0) < 0) + if (oid_object_info_extended(r, &oid, &oi, 0) < 0) return NULL; return content; } @@ -1394,7 +1463,8 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type, * deal with them should arrange to call read_object() and give error * messages themselves. */ -void *read_object_file_extended(const struct object_id *oid, +void *read_object_file_extended(struct repository *r, + const struct object_id *oid, enum object_type *type, unsigned long *size, int lookup_replace) @@ -1404,10 +1474,10 @@ void *read_object_file_extended(const struct object_id *oid, const char *path; struct stat st; const struct object_id *repl = lookup_replace ? - lookup_replace_object(the_repository, oid) : oid; + lookup_replace_object(r, oid) : oid; errno = 0; - data = read_object(repl->hash, type, size); + data = read_object(r, repl->hash, type, size); if (data) return data; @@ -1419,11 +1489,11 @@ void *read_object_file_extended(const struct object_id *oid, die(_("replacement %s not found for %s"), oid_to_hex(repl), oid_to_hex(oid)); - if (!stat_sha1_file(the_repository, repl->hash, &st, &path)) + if (!stat_sha1_file(r, repl->hash, &st, &path)) die(_("loose object %s (stored in %s) is corrupt"), oid_to_hex(repl), path); - if ((p = has_packed_and_bad(repl->hash)) != NULL) + if ((p = has_packed_and_bad(r, repl->hash)) != NULL) die(_("packed object %s (stored in %s) is corrupt"), oid_to_hex(repl), p->pack_name); @@ -1747,7 +1817,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime) if (has_loose_object(oid)) return 0; - buf = read_object(oid->hash, &type, &len); + buf = read_object(the_repository, oid->hash, &type, &len); if (!buf) return error(_("cannot read sha1_file for %s"), oid_to_hex(oid)); hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1; @@ -1757,24 +1827,27 @@ int force_object_loose(const struct object_id *oid, time_t mtime) return ret; } -int has_sha1_file_with_flags(const unsigned char *sha1, int flags) +int repo_has_sha1_file_with_flags(struct repository *r, + const unsigned char *sha1, int flags) { struct object_id oid; if (!startup_info->have_repository) return 0; hashcpy(oid.hash, sha1); - return oid_object_info_extended(the_repository, &oid, NULL, + return oid_object_info_extended(r, &oid, NULL, flags | OBJECT_INFO_SKIP_CACHED) >= 0; } -int has_object_file(const struct object_id *oid) +int repo_has_object_file(struct repository *r, + const struct object_id *oid) { - return has_sha1_file(oid->hash); + return repo_has_sha1_file(r, oid->hash); } -int has_object_file_with_flags(const struct object_id *oid, int flags) +int repo_has_object_file_with_flags(struct repository *r, + const struct object_id *oid, int flags) { - return has_sha1_file_with_flags(oid->hash, flags); + return repo_has_sha1_file_with_flags(r, oid->hash, flags); } static void check_tree(const void *buf, size_t size) @@ -2152,8 +2225,10 @@ static int append_loose_object(const struct object_id *oid, const char *path, return 0; } -void odb_load_loose_cache(struct object_directory *odb, int subdir_nr) +struct oid_array *odb_loose_cache(struct object_directory *odb, + const struct object_id *oid) { + int subdir_nr = oid->hash[0]; struct strbuf buf = STRBUF_INIT; if (subdir_nr < 0 || @@ -2161,15 +2236,26 @@ void odb_load_loose_cache(struct object_directory *odb, int subdir_nr) BUG("subdir_nr out of range"); if (odb->loose_objects_subdir_seen[subdir_nr]) - return; + return &odb->loose_objects_cache[subdir_nr]; strbuf_addstr(&buf, odb->path); for_each_file_in_obj_subdir(subdir_nr, &buf, append_loose_object, NULL, NULL, - &odb->loose_objects_cache); + &odb->loose_objects_cache[subdir_nr]); odb->loose_objects_subdir_seen[subdir_nr] = 1; strbuf_release(&buf); + return &odb->loose_objects_cache[subdir_nr]; +} + +void odb_clear_loose_cache(struct object_directory *odb) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(odb->loose_objects_cache); i++) + oid_array_clear(&odb->loose_objects_cache[i]); + memset(&odb->loose_objects_subdir_seen, 0, + sizeof(odb->loose_objects_subdir_seen)); } static int check_stream_sha1(git_zstream *stream, diff --git a/sha1-name.c b/sha1-name.c index b24502811b..a656481c6a 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -87,21 +87,21 @@ static int match_sha(unsigned, const unsigned char *, const unsigned char *); static void find_short_object_filename(struct disambiguate_state *ds) { - int subdir_nr = ds->bin_pfx.hash[0]; struct object_directory *odb; for (odb = the_repository->objects->odb; odb && !ds->ambiguous; odb = odb->next) { int pos; + struct oid_array *loose_objects; - odb_load_loose_cache(odb, subdir_nr); - pos = oid_array_lookup(&odb->loose_objects_cache, &ds->bin_pfx); + loose_objects = odb_loose_cache(odb, &ds->bin_pfx); + pos = oid_array_lookup(loose_objects, &ds->bin_pfx); if (pos < 0) pos = -1 - pos; - while (!ds->ambiguous && pos < odb->loose_objects_cache.nr) { + while (!ds->ambiguous && pos < loose_objects->nr) { const struct object_id *oid; - oid = odb->loose_objects_cache.oid + pos; + oid = loose_objects->oid + pos; if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash)) break; update_candidates(ds, oid); diff --git a/sha256/block/sha256.c b/sha256/block/sha256.c new file mode 100644 index 0000000000..37850b4e52 --- /dev/null +++ b/sha256/block/sha256.c @@ -0,0 +1,196 @@ +#include "git-compat-util.h" +#include "./sha256.h" + +#undef RND +#undef BLKSIZE + +#define BLKSIZE blk_SHA256_BLKSIZE + +void blk_SHA256_Init(blk_SHA256_CTX *ctx) +{ + ctx->offset = 0; + ctx->size = 0; + ctx->state[0] = 0x6a09e667ul; + ctx->state[1] = 0xbb67ae85ul; + ctx->state[2] = 0x3c6ef372ul; + ctx->state[3] = 0xa54ff53aul; + ctx->state[4] = 0x510e527ful; + ctx->state[5] = 0x9b05688cul; + ctx->state[6] = 0x1f83d9abul; + ctx->state[7] = 0x5be0cd19ul; +} + +static inline uint32_t ror(uint32_t x, unsigned n) +{ + return (x >> n) | (x << (32 - n)); +} + +static inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z) +{ + return z ^ (x & (y ^ z)); +} + +static inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z) +{ + return ((x | y) & z) | (x & y); +} + +static inline uint32_t sigma0(uint32_t x) +{ + return ror(x, 2) ^ ror(x, 13) ^ ror(x, 22); +} + +static inline uint32_t sigma1(uint32_t x) +{ + return ror(x, 6) ^ ror(x, 11) ^ ror(x, 25); +} + +static inline uint32_t gamma0(uint32_t x) +{ + return ror(x, 7) ^ ror(x, 18) ^ (x >> 3); +} + +static inline uint32_t gamma1(uint32_t x) +{ + return ror(x, 17) ^ ror(x, 19) ^ (x >> 10); +} + +static void blk_SHA256_Transform(blk_SHA256_CTX *ctx, const unsigned char *buf) +{ + + uint32_t S[8], W[64], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) + S[i] = ctx->state[i]; + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++, buf += sizeof(uint32_t)) + W[i] = get_be32(buf); + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) + W[i] = gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16]; + +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + sigma1(e) + ch(e, f, g) + ki + W[i]; \ + t1 = sigma0(a) + maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); + + for (i = 0; i < 8; i++) + ctx->state[i] += S[i]; +} + +void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len) +{ + unsigned int len_buf = ctx->size & 63; + + ctx->size += len; + + /* Read the data into buf and process blocks as they get full */ + if (len_buf) { + unsigned int left = 64 - len_buf; + if (len < left) + left = len; + memcpy(len_buf + ctx->buf, data, left); + len_buf = (len_buf + left) & 63; + len -= left; + data = ((const char *)data + left); + if (len_buf) + return; + blk_SHA256_Transform(ctx, ctx->buf); + } + while (len >= 64) { + blk_SHA256_Transform(ctx, data); + data = ((const char *)data + 64); + len -= 64; + } + if (len) + memcpy(ctx->buf, data, len); +} + +void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx) +{ + static const unsigned char pad[64] = { 0x80 }; + unsigned int padlen[2]; + int i; + + /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ + padlen[0] = htonl((uint32_t)(ctx->size >> 29)); + padlen[1] = htonl((uint32_t)(ctx->size << 3)); + + i = ctx->size & 63; + blk_SHA256_Update(ctx, pad, 1 + (63 & (55 - i))); + blk_SHA256_Update(ctx, padlen, 8); + + /* copy output */ + for (i = 0; i < 8; i++, digest += sizeof(uint32_t)) + put_be32(digest, ctx->state[i]); +} diff --git a/sha256/block/sha256.h b/sha256/block/sha256.h new file mode 100644 index 0000000000..5099d6421d --- /dev/null +++ b/sha256/block/sha256.h @@ -0,0 +1,24 @@ +#ifndef SHA256_BLOCK_SHA256_H +#define SHA256_BLOCK_SHA256_H + +#define blk_SHA256_BLKSIZE 64 + +struct blk_SHA256_CTX { + uint32_t state[8]; + uint64_t size; + uint32_t offset; + uint8_t buf[blk_SHA256_BLKSIZE]; +}; + +typedef struct blk_SHA256_CTX blk_SHA256_CTX; + +void blk_SHA256_Init(blk_SHA256_CTX *ctx); +void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len); +void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx); + +#define platform_SHA256_CTX blk_SHA256_CTX +#define platform_SHA256_Init blk_SHA256_Init +#define platform_SHA256_Update blk_SHA256_Update +#define platform_SHA256_Final blk_SHA256_Final + +#endif diff --git a/sha256/gcrypt.h b/sha256/gcrypt.h new file mode 100644 index 0000000000..09bd8bb200 --- /dev/null +++ b/sha256/gcrypt.h @@ -0,0 +1,30 @@ +#ifndef SHA256_GCRYPT_H +#define SHA256_GCRYPT_H + +#include <gcrypt.h> + +#define SHA256_DIGEST_SIZE 32 + +typedef gcry_md_hd_t gcrypt_SHA256_CTX; + +inline void gcrypt_SHA256_Init(gcrypt_SHA256_CTX *ctx) +{ + gcry_md_open(ctx, GCRY_MD_SHA256, 0); +} + +inline void gcrypt_SHA256_Update(gcrypt_SHA256_CTX *ctx, const void *data, size_t len) +{ + gcry_md_write(*ctx, data, len); +} + +inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx) +{ + memcpy(digest, gcry_md_read(*ctx, GCRY_MD_SHA256), SHA256_DIGEST_SIZE); +} + +#define platform_SHA256_CTX gcrypt_SHA256_CTX +#define platform_SHA256_Init gcrypt_SHA256_Init +#define platform_SHA256_Update gcrypt_SHA256_Update +#define platform_SHA256_Final gcrypt_SHA256_Final + +#endif @@ -43,6 +43,13 @@ int register_shallow(struct repository *r, const struct object_id *oid) int is_repository_shallow(struct repository *r) { + /* + * NEEDSWORK: This function updates + * r->parsed_objects->{is_shallow,shallow_stat} as a side effect but + * there is no corresponding function to clear them when the shallow + * file is updated. + */ + FILE *fp; char buf[1024]; const char *path = r->parsed_objects->alternate_shallow_file; diff --git a/streaming.c b/streaming.c index ac7c7a22f9..891f82f51a 100644 --- a/streaming.c +++ b/streaming.c @@ -499,7 +499,7 @@ static struct stream_vtbl incore_vtbl = { static open_method_decl(incore) { - st->u.incore.buf = read_object_file_extended(oid, type, &st->size, 0); + st->u.incore.buf = read_object_file_extended(the_repository, oid, type, &st->size, 0); st->u.incore.read_ptr = 0; st->vtbl = &incore_vtbl; diff --git a/string-list.c b/string-list.c index 1f6063f2a2..a917955fbd 100644 --- a/string-list.c +++ b/string-list.c @@ -155,7 +155,8 @@ static int item_is_not_empty(struct string_list_item *item, void *unused) return *item->string != '\0'; } -void string_list_remove_empty_items(struct string_list *list, int free_util) { +void string_list_remove_empty_items(struct string_list *list, int free_util) +{ filter_string_list(list, free_util, item_is_not_empty, NULL); } diff --git a/submodule.c b/submodule.c index 6415cc5580..a5f2694a5f 100644 --- a/submodule.c +++ b/submodule.c @@ -25,7 +25,6 @@ #include "commit-reach.h" static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF; -static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP; static int initialized_fetch_ref_tips; static struct oid_array ref_tips_before_fetch; static struct oid_array ref_tips_after_fetch; @@ -457,7 +456,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path, return prepare_revision_walk(rev); } -static void print_submodule_summary(struct rev_info *rev, struct diff_options *o) +static void print_submodule_summary(struct repository *r, struct rev_info *rev, struct diff_options *o) { static const char format[] = " %m %s"; struct strbuf sb = STRBUF_INIT; @@ -468,7 +467,8 @@ static void print_submodule_summary(struct rev_info *rev, struct diff_options *o ctx.date_mode = rev->date_mode; ctx.output_encoding = get_log_output_encoding(); strbuf_setlen(&sb, 0); - format_commit_message(commit, format, &sb, &ctx); + repo_format_commit_message(r, commit, format, &sb, + &ctx); strbuf_addch(&sb, '\n'); if (commit->object.flags & SYMMETRIC_LEFT) diff_emit_submodule_del(o, sb.buf); @@ -495,14 +495,52 @@ void prepare_submodule_repo_env(struct argv_array *out) DEFAULT_GIT_DIR_ENVIRONMENT); } -/* Helper function to display the submodule header line prior to the full - * summary output. If it can locate the submodule objects directory it will - * attempt to lookup both the left and right commits and put them into the - * left and right pointers. +static void prepare_submodule_repo_env_in_gitdir(struct argv_array *out) +{ + prepare_submodule_repo_env_no_git_dir(out); + argv_array_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT); +} + +/* + * Initialize a repository struct for a submodule based on the provided 'path'. + * + * Unlike repo_submodule_init, this tolerates submodules not present + * in .gitmodules. This function exists only to preserve historical behavior, + * + * Returns the repository struct on success, + * NULL when the submodule is not present. */ -static void show_submodule_header(struct diff_options *o, const char *path, +static struct repository *open_submodule(const char *path) +{ + struct strbuf sb = STRBUF_INIT; + struct repository *out = xmalloc(sizeof(*out)); + + if (submodule_to_gitdir(&sb, path) || repo_init(out, sb.buf, NULL)) { + strbuf_release(&sb); + free(out); + return NULL; + } + + /* Mark it as a submodule */ + out->submodule_prefix = xstrdup(path); + + strbuf_release(&sb); + return out; +} + +/* + * Helper function to display the submodule header line prior to the full + * summary output. + * + * If it can locate the submodule git directory it will create a repository + * handle for the submodule and lookup both the left and right commits and + * put them into the left and right pointers. + */ +static void show_submodule_header(struct diff_options *o, + const char *path, struct object_id *one, struct object_id *two, unsigned dirty_submodule, + struct repository *sub, struct commit **left, struct commit **right, struct commit_list **merge_bases) { @@ -521,7 +559,7 @@ static void show_submodule_header(struct diff_options *o, const char *path, else if (is_null_oid(two)) message = "(submodule deleted)"; - if (add_submodule_odb(path)) { + if (!sub) { if (!message) message = "(commits not present)"; goto output_header; @@ -531,8 +569,8 @@ static void show_submodule_header(struct diff_options *o, const char *path, * Attempt to lookup the commit references, and determine if this is * a fast forward or fast backwards update. */ - *left = lookup_commit_reference(the_repository, one); - *right = lookup_commit_reference(the_repository, two); + *left = lookup_commit_reference(sub, one); + *right = lookup_commit_reference(sub, two); /* * Warn about missing commits in the submodule project, but only if @@ -542,7 +580,7 @@ static void show_submodule_header(struct diff_options *o, const char *path, (!is_null_oid(two) && !*right)) message = "(commits not present)"; - *merge_bases = get_merge_bases(*left, *right); + *merge_bases = repo_get_merge_bases(sub, *left, *right); if (*merge_bases) { if ((*merge_bases)->item == *left) fast_forward = 1; @@ -576,16 +614,18 @@ void show_submodule_summary(struct diff_options *o, const char *path, struct rev_info rev; struct commit *left = NULL, *right = NULL; struct commit_list *merge_bases = NULL; + struct repository *sub; + sub = open_submodule(path); show_submodule_header(o, path, one, two, dirty_submodule, - &left, &right, &merge_bases); + sub, &left, &right, &merge_bases); /* * If we don't have both a left and a right pointer, there is no * reason to try and display a summary. The header line should contain * all the information the user needs. */ - if (!left || !right) + if (!left || !right || !sub) goto out; /* Treat revision walker failure the same as missing commits */ @@ -594,13 +634,17 @@ void show_submodule_summary(struct diff_options *o, const char *path, goto out; } - print_submodule_summary(&rev, o); + print_submodule_summary(sub, &rev, o); out: if (merge_bases) free_commit_list(merge_bases); clear_commit_marks(left, ~0); clear_commit_marks(right, ~0); + if (sub) { + repo_clear(sub); + free(sub); + } } void show_submodule_inline_diff(struct diff_options *o, const char *path, @@ -612,9 +656,11 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path, struct commit_list *merge_bases = NULL; struct child_process cp = CHILD_PROCESS_INIT; struct strbuf sb = STRBUF_INIT; + struct repository *sub; + sub = open_submodule(path); show_submodule_header(o, path, one, two, dirty_submodule, - &left, &right, &merge_bases); + sub, &left, &right, &merge_bases); /* We need a valid left and right commit to display a difference */ if (!(left || is_null_oid(one)) || @@ -675,6 +721,10 @@ done: clear_commit_marks(left, ~0); if (right) clear_commit_marks(right, ~0); + if (sub) { + repo_clear(sub); + free(sub); + } } int should_update_submodules(void) @@ -999,9 +1049,6 @@ static int push_submodule(const char *path, const struct string_list *push_options, int dry_run) { - if (add_submodule_odb(path)) - return 1; - if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) { struct child_process cp = CHILD_PROCESS_INIT; argv_array_push(&cp.args, "push"); @@ -1136,11 +1183,11 @@ void check_for_new_submodule_commits(struct object_id *oid) oid_array_append(&ref_tips_after_fetch, oid); } -static void calculate_changed_submodule_paths(struct repository *r) +static void calculate_changed_submodule_paths(struct repository *r, + struct string_list *changed_submodule_names) { struct argv_array argv = ARGV_ARRAY_INIT; - struct string_list changed_submodules = STRING_LIST_INIT_DUP; - const struct string_list_item *name; + struct string_list_item *name; /* No need to check if there are no submodules configured */ if (!submodule_from_path(r, NULL, NULL)) @@ -1157,9 +1204,9 @@ static void calculate_changed_submodule_paths(struct repository *r) * Collect all submodules (whether checked out or not) for which new * commits have been recorded upstream in "changed_submodule_names". */ - collect_changed_submodules(r, &changed_submodules, &argv); + collect_changed_submodules(r, changed_submodule_names, &argv); - for_each_string_list_item(name, &changed_submodules) { + for_each_string_list_item(name, changed_submodule_names) { struct oid_array *commits = name->util; const struct submodule *submodule; const char *path = NULL; @@ -1173,11 +1220,14 @@ static void calculate_changed_submodule_paths(struct repository *r) if (!path) continue; - if (!submodule_has_commits(r, path, commits)) - string_list_append(&changed_submodule_names, name->string); + if (submodule_has_commits(r, path, commits)) { + oid_array_clear(commits); + *name->string = '\0'; + } } - free_submodules_oids(&changed_submodules); + string_list_remove_empty_items(changed_submodule_names, 1); + argv_array_clear(&argv); oid_array_clear(&ref_tips_before_fetch); oid_array_clear(&ref_tips_after_fetch); @@ -1221,8 +1271,16 @@ struct submodule_parallel_fetch { int default_option; int quiet; int result; + + struct string_list changed_submodule_names; + + /* Pending fetches by OIDs */ + struct fetch_task **oid_fetch_tasks; + int oid_fetch_tasks_nr, oid_fetch_tasks_alloc; }; -#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0} +#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, \ + STRING_LIST_INIT_DUP, \ + NULL, 0, 0} static int get_fetch_recurse_config(const struct submodule *submodule, struct submodule_parallel_fetch *spf) @@ -1249,40 +1307,126 @@ static int get_fetch_recurse_config(const struct submodule *submodule, return spf->default_option; } +/* + * Fetch in progress (if callback data) or + * pending (if in oid_fetch_tasks in struct submodule_parallel_fetch) + */ +struct fetch_task { + struct repository *repo; + const struct submodule *sub; + unsigned free_sub : 1; /* Do we need to free the submodule? */ + + struct oid_array *commits; /* Ensure these commits are fetched */ +}; + +/** + * When a submodule is not defined in .gitmodules, we cannot access it + * via the regular submodule-config. Create a fake submodule, which we can + * work on. + */ +static const struct submodule *get_non_gitmodules_submodule(const char *path) +{ + struct submodule *ret = NULL; + const char *name = default_name_or_path(path); + + if (!name) + return NULL; + + ret = xmalloc(sizeof(*ret)); + memset(ret, 0, sizeof(*ret)); + ret->path = name; + ret->name = name; + + return (const struct submodule *) ret; +} + +static struct fetch_task *fetch_task_create(struct repository *r, + const char *path) +{ + struct fetch_task *task = xmalloc(sizeof(*task)); + memset(task, 0, sizeof(*task)); + + task->sub = submodule_from_path(r, &null_oid, path); + if (!task->sub) { + /* + * No entry in .gitmodules? Technically not a submodule, + * but historically we supported repositories that happen to be + * in-place where a gitlink is. Keep supporting them. + */ + task->sub = get_non_gitmodules_submodule(path); + if (!task->sub) { + free(task); + return NULL; + } + + task->free_sub = 1; + } + + return task; +} + +static void fetch_task_release(struct fetch_task *p) +{ + if (p->free_sub) + free((void*)p->sub); + p->free_sub = 0; + p->sub = NULL; + + if (p->repo) + repo_clear(p->repo); + FREE_AND_NULL(p->repo); +} + +static struct repository *get_submodule_repo_for(struct repository *r, + const struct submodule *sub) +{ + struct repository *ret = xmalloc(sizeof(*ret)); + + if (repo_submodule_init(ret, r, sub)) { + /* + * No entry in .gitmodules? Technically not a submodule, + * but historically we supported repositories that happen to be + * in-place where a gitlink is. Keep supporting them. + */ + struct strbuf gitdir = STRBUF_INIT; + strbuf_repo_worktree_path(&gitdir, r, "%s/.git", sub->path); + if (repo_init(ret, gitdir.buf, NULL)) { + strbuf_release(&gitdir); + free(ret); + return NULL; + } + strbuf_release(&gitdir); + } + + return ret; +} + static int get_next_submodule(struct child_process *cp, struct strbuf *err, void *data, void **task_cb) { - int ret = 0; struct submodule_parallel_fetch *spf = data; for (; spf->count < spf->r->index->cache_nr; spf->count++) { - struct strbuf submodule_path = STRBUF_INIT; - struct strbuf submodule_git_dir = STRBUF_INIT; - struct strbuf submodule_prefix = STRBUF_INIT; const struct cache_entry *ce = spf->r->index->cache[spf->count]; - const char *git_dir, *default_argv; - const struct submodule *submodule; - struct submodule default_submodule = SUBMODULE_INIT; + const char *default_argv; + struct fetch_task *task; if (!S_ISGITLINK(ce->ce_mode)) continue; - submodule = submodule_from_path(spf->r, &null_oid, ce->name); - if (!submodule) { - const char *name = default_name_or_path(ce->name); - if (name) { - default_submodule.path = default_submodule.name = name; - submodule = &default_submodule; - } - } + task = fetch_task_create(spf->r, ce->name); + if (!task) + continue; - switch (get_fetch_recurse_config(submodule, spf)) + switch (get_fetch_recurse_config(task->sub, spf)) { default: case RECURSE_SUBMODULES_DEFAULT: case RECURSE_SUBMODULES_ON_DEMAND: - if (!submodule || !unsorted_string_list_lookup(&changed_submodule_names, - submodule->name)) + if (!task->sub || + !string_list_lookup( + &spf->changed_submodule_names, + task->sub->name)) continue; default_argv = "on-demand"; break; @@ -1293,16 +1437,12 @@ static int get_next_submodule(struct child_process *cp, continue; } - strbuf_repo_worktree_path(&submodule_path, spf->r, "%s", ce->name); - strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf); - strbuf_addf(&submodule_prefix, "%s%s/", spf->prefix, ce->name); - git_dir = read_gitfile(submodule_git_dir.buf); - if (!git_dir) - git_dir = submodule_git_dir.buf; - if (is_directory(git_dir)) { + task->repo = get_submodule_repo_for(spf->r, task->sub); + if (task->repo) { + struct strbuf submodule_prefix = STRBUF_INIT; child_process_init(cp); - cp->dir = strbuf_detach(&submodule_path, NULL); - prepare_submodule_repo_env(&cp->env_array); + cp->dir = task->repo->gitdir; + prepare_submodule_repo_env_in_gitdir(&cp->env_array); cp->git_cmd = 1; if (!spf->quiet) strbuf_addf(err, "Fetching submodule %s%s\n", @@ -1311,17 +1451,66 @@ static int get_next_submodule(struct child_process *cp, argv_array_pushv(&cp->args, spf->args.argv); argv_array_push(&cp->args, default_argv); argv_array_push(&cp->args, "--submodule-prefix"); + + strbuf_addf(&submodule_prefix, "%s%s/", + spf->prefix, + task->sub->path); argv_array_push(&cp->args, submodule_prefix.buf); - ret = 1; - } - strbuf_release(&submodule_path); - strbuf_release(&submodule_git_dir); - strbuf_release(&submodule_prefix); - if (ret) { + spf->count++; + *task_cb = task; + + strbuf_release(&submodule_prefix); return 1; + } else { + + fetch_task_release(task); + free(task); + + /* + * An empty directory is normal, + * the submodule is not initialized + */ + if (S_ISGITLINK(ce->ce_mode) && + !is_empty_dir(ce->name)) { + spf->result = 1; + strbuf_addf(err, + _("Could not access submodule '%s'"), + ce->name); + } } } + + if (spf->oid_fetch_tasks_nr) { + struct fetch_task *task = + spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1]; + struct strbuf submodule_prefix = STRBUF_INIT; + spf->oid_fetch_tasks_nr--; + + strbuf_addf(&submodule_prefix, "%s%s/", + spf->prefix, task->sub->path); + + child_process_init(cp); + prepare_submodule_repo_env_in_gitdir(&cp->env_array); + cp->git_cmd = 1; + cp->dir = task->repo->gitdir; + + argv_array_init(&cp->args); + argv_array_pushv(&cp->args, spf->args.argv); + argv_array_push(&cp->args, "on-demand"); + argv_array_push(&cp->args, "--submodule-prefix"); + argv_array_push(&cp->args, submodule_prefix.buf); + + /* NEEDSWORK: have get_default_remote from submodule--helper */ + argv_array_push(&cp->args, "origin"); + oid_array_for_each_unique(task->commits, + append_oid_to_argv, &cp->args); + + *task_cb = task; + strbuf_release(&submodule_prefix); + return 1; + } + return 0; } @@ -1329,20 +1518,66 @@ static int fetch_start_failure(struct strbuf *err, void *cb, void *task_cb) { struct submodule_parallel_fetch *spf = cb; + struct fetch_task *task = task_cb; spf->result = 1; + fetch_task_release(task); return 0; } +static int commit_missing_in_sub(const struct object_id *oid, void *data) +{ + struct repository *subrepo = data; + + enum object_type type = oid_object_info(subrepo, oid, NULL); + + return type != OBJ_COMMIT; +} + static int fetch_finish(int retvalue, struct strbuf *err, void *cb, void *task_cb) { struct submodule_parallel_fetch *spf = cb; + struct fetch_task *task = task_cb; + + struct string_list_item *it; + struct oid_array *commits; if (retvalue) spf->result = 1; + if (!task || !task->sub) + BUG("callback cookie bogus"); + + /* Is this the second time we process this submodule? */ + if (task->commits) + goto out; + + it = string_list_lookup(&spf->changed_submodule_names, task->sub->name); + if (!it) + /* Could be an unchanged submodule, not contained in the list */ + goto out; + + commits = it->util; + oid_array_filter(commits, + commit_missing_in_sub, + task->repo); + + /* Are there commits we want, but do not exist? */ + if (commits->nr) { + task->commits = commits; + ALLOC_GROW(spf->oid_fetch_tasks, + spf->oid_fetch_tasks_nr + 1, + spf->oid_fetch_tasks_alloc); + spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr] = task; + spf->oid_fetch_tasks_nr++; + return 0; + } + +out: + fetch_task_release(task); + return 0; } @@ -1373,7 +1608,8 @@ int fetch_populated_submodules(struct repository *r, argv_array_push(&spf.args, "--recurse-submodules-default"); /* default value, "--submodule-prefix" and its value are added later */ - calculate_changed_submodule_paths(r); + calculate_changed_submodule_paths(r, &spf.changed_submodule_names); + string_list_sort(&spf.changed_submodule_names); run_processes_parallel(max_parallel_jobs, get_next_submodule, fetch_start_failure, @@ -1382,7 +1618,7 @@ int fetch_populated_submodules(struct repository *r, argv_array_clear(&spf.args); out: - string_list_clear(&changed_submodule_names, 1); + free_submodules_oids(&spf.changed_submodule_names); return spf.result; } @@ -1561,6 +1797,18 @@ out: return ret; } +void submodule_unset_core_worktree(const struct submodule *sub) +{ + char *config_path = xstrfmt("%s/modules/%s/config", + get_git_common_dir(), sub->name); + + if (git_config_set_in_file_gently(config_path, "core.worktree", NULL)) + warning(_("Could not unset core.worktree setting in submodule '%s'"), + sub->path); + + free(config_path); +} + static const char *get_super_prefix_or_empty(void) { const char *s = get_super_prefix(); @@ -1726,6 +1974,8 @@ int submodule_move_head(const char *path, if (is_empty_dir(path)) rmdir_or_warn(path); + + submodule_unset_core_worktree(sub); } } out: diff --git a/submodule.h b/submodule.h index a680214c01..9e18e9b807 100644 --- a/submodule.h +++ b/submodule.h @@ -131,6 +131,8 @@ int submodule_move_head(const char *path, const char *new_head, unsigned flags); +void submodule_unset_core_worktree(const struct submodule *sub); + /* * Prepare the "env_array" parameter of a "struct child_process" for executing * a submodule by clearing any repo-specific environment variables, but @@ -186,6 +186,22 @@ appropriately before running "make". this feature by setting the GIT_TEST_CHAIN_LINT environment variable to "1" or "0", respectively. +--stress:: +--stress=<N>:: + Run the test script repeatedly in multiple parallel jobs until + one of them fails. Useful for reproducing rare failures in + flaky tests. The number of parallel jobs is, in order of + precedence: <N>, or the value of the GIT_TEST_STRESS_LOAD + environment variable, or twice the number of available + processors (as shown by the 'getconf' utility), or 8. + Implies `--verbose -x --immediate` to get the most information + about the failure. Note that the verbose output of each test + job is saved to 't/test-results/$TEST_NAME.stress-<nr>.out', + and only the output of the failed test job is shown on the + terminal. The names of the trash directories get a + '.stress-<nr>' suffix, and the trash directory of the failed + test job is renamed to end with a '.stress-failed' suffix. + You can also set the GIT_TEST_INSTALLED environment variable to the bindir of an existing git installation to test that installation. You still need to have built this git sandbox, from which various @@ -425,7 +441,8 @@ This test harness library does the following things: - Creates an empty test directory with an empty .git/objects database and chdir(2) into it. This directory is 't/trash directory.$test_name_without_dotsh', with t/ subject to change by - the --root option documented above. + the --root option documented above, and a '.stress-<N>' suffix + appended by the --stress option. - Defines standard test helper functions for your scripts to use. These functions are designed to make all scripts behave diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c new file mode 100644 index 0000000000..432233c7f0 --- /dev/null +++ b/t/helper/test-hash-speed.c @@ -0,0 +1,61 @@ +#include "test-tool.h" +#include "cache.h" + +#define NUM_SECONDS 3 + +static inline void compute_hash(const struct git_hash_algo *algo, git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len) +{ + algo->init_fn(ctx); + algo->update_fn(ctx, p, len); + algo->final_fn(final, ctx); +} + +int cmd__hash_speed(int ac, const char **av) +{ + git_hash_ctx ctx; + unsigned char hash[GIT_MAX_RAWSZ]; + clock_t initial, start, end; + unsigned bufsizes[] = { 64, 256, 1024, 8192, 16384 }; + int i; + void *p; + const struct git_hash_algo *algo = NULL; + + if (ac == 2) { + for (i = 1; i < GIT_HASH_NALGOS; i++) { + if (!strcmp(av[1], hash_algos[i].name)) { + algo = &hash_algos[i]; + break; + } + } + } + if (!algo) + die("usage: test-tool hash-speed algo_name"); + + /* Use this as an offset to make overflow less likely. */ + initial = clock(); + + printf("algo: %s\n", algo->name); + + for (i = 0; i < ARRAY_SIZE(bufsizes); i++) { + unsigned long j, kb; + double kb_per_sec; + p = xcalloc(1, bufsizes[i]); + start = end = clock() - initial; + for (j = 0; ((end - start) / CLOCKS_PER_SEC) < NUM_SECONDS; j++) { + compute_hash(algo, &ctx, hash, p, bufsizes[i]); + + /* + * Only check elapsed time every 128 iterations to avoid + * dominating the runtime with system calls. + */ + if (!(j & 127)) + end = clock() - initial; + } + kb = j * bufsizes[i]; + kb_per_sec = kb / (1024 * ((double)end - start) / CLOCKS_PER_SEC); + printf("size %u: %lu iters; %lu KiB; %0.2f KiB/s\n", bufsizes[i], j, kb, kb_per_sec); + free(p); + } + + exit(0); +} diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c new file mode 100644 index 0000000000..0a31de66f3 --- /dev/null +++ b/t/helper/test-hash.c @@ -0,0 +1,58 @@ +#include "test-tool.h" +#include "cache.h" + +int cmd_hash_impl(int ac, const char **av, int algo) +{ + git_hash_ctx ctx; + unsigned char hash[GIT_MAX_HEXSZ]; + unsigned bufsz = 8192; + int binary = 0; + char *buffer; + const struct git_hash_algo *algop = &hash_algos[algo]; + + if (ac == 2) { + if (!strcmp(av[1], "-b")) + binary = 1; + else + bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024; + } + + if (!bufsz) + bufsz = 8192; + + while ((buffer = malloc(bufsz)) == NULL) { + fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz); + bufsz /= 2; + if (bufsz < 1024) + die("OOPS"); + } + + algop->init_fn(&ctx); + + while (1) { + ssize_t sz, this_sz; + char *cp = buffer; + unsigned room = bufsz; + this_sz = 0; + while (room) { + sz = xread(0, cp, room); + if (sz == 0) + break; + if (sz < 0) + die_errno("test-hash"); + this_sz += sz; + cp += sz; + room -= sz; + } + if (this_sz == 0) + break; + algop->update_fn(&ctx, buffer, this_sz); + } + algop->final_fn(hash, &ctx); + + if (binary) + fwrite(hash, 1, algop->rawsz, stdout); + else + puts(hash_to_hex_algop(hash, algop)); + exit(0); +} diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c index 6a84a53efb..f7f8618445 100644 --- a/t/helper/test-repository.c +++ b/t/helper/test-repository.c @@ -17,6 +17,11 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree, setup_git_env(gitdir); + memset(the_repository, 0, sizeof(*the_repository)); + + /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */ + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); @@ -43,6 +48,11 @@ static void test_get_commit_tree_in_graph(const char *gitdir, setup_git_env(gitdir); + memset(the_repository, 0, sizeof(*the_repository)); + + /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */ + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c index 1ba0675c75..d860c387c3 100644 --- a/t/helper/test-sha1.c +++ b/t/helper/test-sha1.c @@ -3,55 +3,5 @@ int cmd__sha1(int ac, const char **av) { - git_SHA_CTX ctx; - unsigned char sha1[20]; - unsigned bufsz = 8192; - int binary = 0; - char *buffer; - - if (ac == 2) { - if (!strcmp(av[1], "-b")) - binary = 1; - else - bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024; - } - - if (!bufsz) - bufsz = 8192; - - while ((buffer = malloc(bufsz)) == NULL) { - fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz); - bufsz /= 2; - if (bufsz < 1024) - die("OOPS"); - } - - git_SHA1_Init(&ctx); - - while (1) { - ssize_t sz, this_sz; - char *cp = buffer; - unsigned room = bufsz; - this_sz = 0; - while (room) { - sz = xread(0, cp, room); - if (sz == 0) - break; - if (sz < 0) - die_errno("test-sha1"); - this_sz += sz; - cp += sz; - room -= sz; - } - if (this_sz == 0) - break; - git_SHA1_Update(&ctx, buffer, this_sz); - } - git_SHA1_Final(sha1, &ctx); - - if (binary) - fwrite(sha1, 1, 20, stdout); - else - puts(sha1_to_hex(sha1)); - exit(0); + return cmd_hash_impl(ac, av, GIT_HASH_SHA1); } diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c new file mode 100644 index 0000000000..0ac6a99d5f --- /dev/null +++ b/t/helper/test-sha256.c @@ -0,0 +1,7 @@ +#include "test-tool.h" +#include "cache.h" + +int cmd__sha256(int ac, const char **av) +{ + return cmd_hash_impl(ac, av, GIT_HASH_SHA256); +} diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c index 77ac5bc33f..d013bccdda 100644 --- a/t/helper/test-sigchain.c +++ b/t/helper/test-sigchain.c @@ -14,7 +14,8 @@ X(two) X(three) #undef X -int cmd__sigchain(int argc, const char **argv) { +int cmd__sigchain(int argc, const char **argv) +{ sigchain_push(SIGTERM, one); sigchain_push(SIGTERM, two); sigchain_push(SIGTERM, three); diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c index a31e2a9bea..bc97929bbc 100644 --- a/t/helper/test-submodule-nested-repo-config.c +++ b/t/helper/test-submodule-nested-repo-config.c @@ -10,19 +10,21 @@ static void die_usage(int argc, const char **argv, const char *msg) int cmd__submodule_nested_repo_config(int argc, const char **argv) { - struct repository submodule; + struct repository subrepo; + const struct submodule *sub; if (argc < 3) die_usage(argc, argv, "Wrong number of arguments."); setup_git_directory(); - if (repo_submodule_init(&submodule, the_repository, argv[1])) { + sub = submodule_from_path(the_repository, &null_oid, argv[1]); + if (repo_submodule_init(&subrepo, the_repository, sub)) { die_usage(argc, argv, "Submodule not found."); } /* Read the config of _child_ submodules. */ - print_config_from_gitmodules(&submodule, argv[2]); + print_config_from_gitmodules(&subrepo, argv[2]); submodule_free(the_repository); diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index bfb195b1a8..5b137874e1 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -20,6 +20,7 @@ static struct test_cmd cmds[] = { { "example-decorate", cmd__example_decorate }, { "genrandom", cmd__genrandom }, { "hashmap", cmd__hashmap }, + { "hash-speed", cmd__hash_speed }, { "index-version", cmd__index_version }, { "json-writer", cmd__json_writer }, { "lazy-init-name-hash", cmd__lazy_init_name_hash }, @@ -42,6 +43,7 @@ static struct test_cmd cmds[] = { { "scrap-cache-tree", cmd__scrap_cache_tree }, { "sha1", cmd__sha1 }, { "sha1-array", cmd__sha1_array }, + { "sha256", cmd__sha256 }, { "sigchain", cmd__sigchain }, { "strcmp-offset", cmd__strcmp_offset }, { "string-list", cmd__string_list }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 042f12464b..ca5c88edb2 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -16,6 +16,7 @@ int cmd__dump_untracked_cache(int argc, const char **argv); int cmd__example_decorate(int argc, const char **argv); int cmd__genrandom(int argc, const char **argv); int cmd__hashmap(int argc, const char **argv); +int cmd__hash_speed(int argc, const char **argv); int cmd__index_version(int argc, const char **argv); int cmd__json_writer(int argc, const char **argv); int cmd__lazy_init_name_hash(int argc, const char **argv); @@ -38,6 +39,7 @@ int cmd__run_command(int argc, const char **argv); int cmd__scrap_cache_tree(int argc, const char **argv); int cmd__sha1(int argc, const char **argv); int cmd__sha1_array(int argc, const char **argv); +int cmd__sha256(int argc, const char **argv); int cmd__sigchain(int argc, const char **argv); int cmd__strcmp_offset(int argc, const char **argv); int cmd__string_list(int argc, const char **argv); @@ -51,4 +53,6 @@ int cmd__windows_named_pipe(int argc, const char **argv); #endif int cmd__write_cache(int argc, const char **argv); +int cmd_hash_impl(int ac, const char **av, int algo); + #endif diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh index f98de95c15..79db3b7ae5 100644 --- a/t/lib-git-daemon.sh +++ b/t/lib-git-daemon.sh @@ -28,7 +28,7 @@ then test_skip_or_die $GIT_TEST_GIT_DAEMON "file system does not support FIFOs" fi -LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-${this_test#t}} +test_set_port LIB_GIT_DAEMON_PORT GIT_DAEMON_PID= GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo @@ -54,19 +54,11 @@ start_git_daemon() { "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ >&3 2>git_daemon_output & GIT_DAEMON_PID=$! - >daemon.log { read -r line <&7 - printf "%s\n" "$line" - printf >&4 "%s\n" "$line" - ( - while read -r line <&7 - do - printf "%s\n" "$line" - printf >&4 "%s\n" "$line" - done - ) & - } 7<git_daemon_output >>"$TRASH_DIRECTORY/daemon.log" && + printf "%s\n" "$line" >&4 + cat <&7 >&4 & + } 7<git_daemon_output && # Check expected output if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble" diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh index c27599474c..b3be3ba011 100644 --- a/t/lib-git-p4.sh +++ b/t/lib-git-p4.sh @@ -53,14 +53,7 @@ time_in_seconds () { (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))') } -# Try to pick a unique port: guess a large number, then hope -# no more than one of each test is running. -# -# This does not handle the case where somebody else is running the -# same tests and has chosen the same ports. -testid=${this_test#t} -git_p4_test_start=9800 -P4DPORT=$((10669 + ($testid - $git_p4_test_start))) +test_set_port P4DPORT P4PORT=localhost:$P4DPORT P4CLIENT=client diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index a8130f9119..f3b478c307 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -13,6 +13,7 @@ fi GIT_DIR=$PWD/.git GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn SVN_TREE=$GIT_SVN_DIR/svn-tree +test_set_port SVNSERVE_PORT svn >/dev/null 2>&1 if test $? -ne 1 @@ -119,7 +120,6 @@ require_svnserve () { } start_svnserve () { - SVNSERVE_PORT=${SVNSERVE_PORT-${this_test#t}} svnserve --listen-port $SVNSERVE_PORT \ --root "$rawsvnrepo" \ --listen-once \ diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index a8729f8232..e465116ef9 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -82,7 +82,7 @@ case $(uname) in esac LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"} -LIB_HTTPD_PORT=${LIB_HTTPD_PORT-${this_test#t}} +test_set_port LIB_HTTPD_PORT TEST_PATH="$TEST_DIRECTORY"/lib-httpd HTTPD_ROOT_PATH="$PWD"/httpd diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 581c010d8f..cc4b87507e 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -115,6 +115,7 @@ Alias /auth/dumb/ www/auth/dumb/ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL </LocationMatch> +ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/ ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1 ScriptAlias /broken_smart/ broken-smart-http.sh/ ScriptAlias /error/ error.sh/ diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index 016391723c..5b56b23166 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -235,7 +235,7 @@ reset_work_tree_to_interested () { then mkdir -p submodule_update/.git/modules/sub1/modules && cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2 - GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree + # core.worktree is unset for sub2 as it is not checked out fi && # indicate we are interested in the submodule: git -C submodule_update config submodule.sub1.url "bogus" && @@ -709,7 +709,8 @@ test_submodule_recursing_with_args_common() { git branch -t remove_sub1 origin/remove_sub1 && $command remove_sub1 && test_superproject_content origin/remove_sub1 && - ! test -e sub1 + ! test -e sub1 && + test_must_fail git config -f .git/modules/sub1/config core.worktree ) ' # ... absorbing a .git directory along the way. diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh new file mode 100755 index 0000000000..291e9061f3 --- /dev/null +++ b/t/t0015-hash.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +test_description='test basic hash implementation' +. ./test-lib.sh + + +test_expect_success 'test basic SHA-1 hash values' ' + test-tool sha1 </dev/null >actual && + grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual && + printf "a" | test-tool sha1 >actual && + grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual && + printf "abc" | test-tool sha1 >actual && + grep a9993e364706816aba3e25717850c26c9cd0d89d actual && + printf "message digest" | test-tool sha1 >actual && + grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual && + printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual && + grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual && + perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \ + test-tool sha1 >actual && + grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual && + printf "blob 0\0" | test-tool sha1 >actual && + grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual && + printf "blob 3\0abc" | test-tool sha1 >actual && + grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual && + printf "tree 0\0" | test-tool sha1 >actual && + grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual +' + +test_expect_success 'test basic SHA-256 hash values' ' + test-tool sha256 </dev/null >actual && + grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual && + printf "a" | test-tool sha256 >actual && + grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual && + printf "abc" | test-tool sha256 >actual && + grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual && + printf "message digest" | test-tool sha256 >actual && + grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual && + printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual && + grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual && + # Try to exercise the chunking code by turning autoflush on. + perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \ + test-tool sha256 >actual && + grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual && + perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | \ + test-tool sha256 >actual && + grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual && + printf "blob 0\0" | test-tool sha256 >actual && + grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual && + printf "blob 3\0abc" | test-tool sha256 >actual && + grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual && + printf "tree 0\0" | test-tool sha256 >actual && + grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual +' + +test_done diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index 5ce47e8af5..0c24a0f9a3 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -430,9 +430,15 @@ test_expect_success '-c with changed comment char' ' test_expect_success '-c with comment char defined in .git/config' ' test_config core.commentchar = && printf "= foo\n" >expect && - printf "foo" | ( - mkdir sub && cd sub && git stripspace -c - ) >actual && + rm -fr sub && + mkdir sub && + printf "foo" | git -C sub stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '-c outside git repository' ' + printf "# foo\n" >expect && + printf "foo" | nongit git stripspace -c >actual && test_cmp expect actual ' diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh index 169f7f10a7..bce02788e6 100755 --- a/t/t0410-partial-clone.sh +++ b/t/t0410-partial-clone.sh @@ -492,7 +492,6 @@ test_expect_success 'gc stops traversal when a missing but promised object is re ! grep "$TREE_HASH" out ' -LIB_HTTPD_PORT=12345 # default port, 410, cannot be used as non-root . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh index 33c0337733..939d18d728 100755 --- a/t/t2028-worktree-move.sh +++ b/t/t2028-worktree-move.sh @@ -112,6 +112,26 @@ test_expect_success 'move locked worktree (force)' ' git worktree move --force --force flump ploof ' +test_expect_success 'move a repo with uninitialized submodule' ' + git init withsub && + ( + cd withsub && + test_commit initial && + git submodule add "$PWD"/.git sub && + git commit -m withsub && + git worktree add second HEAD && + git worktree move second third + ) +' + +test_expect_success 'not move a repo with initialized submodule' ' + ( + cd withsub && + git -C third submodule update && + test_must_fail git worktree move third forth + ) +' + test_expect_success 'remove main worktree' ' test_must_fail git worktree remove . ' @@ -185,4 +205,21 @@ test_expect_success 'remove cleans up .git/worktrees when empty' ' ) ' +test_expect_success 'remove a repo with uninitialized submodule' ' + ( + cd withsub && + git worktree add to-remove HEAD && + git worktree remove to-remove + ) +' + +test_expect_success 'not remove a repo with initialized submodule' ' + ( + cd withsub && + git worktree add to-remove HEAD && + git -C to-remove submodule update && + test_must_fail git worktree remove to-remove + ) +' + test_done diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index 0210b2ac6f..25aaacacfc 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -254,4 +254,18 @@ test_expect_success 'the todo command "break" works' ' test_path_is_file execed ' +test_expect_success '--reschedule-failed-exec' ' + test_when_finished "git rebase --abort" && + test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ && + grep "^exec false" .git/rebase-merge/git-rebase-todo && + git rebase --abort && + test_must_fail git -c rebase.rescheduleFailedExec=true \ + rebase -x false HEAD^ 2>err && + grep "^exec false" .git/rebase-merge/git-rebase-todo && + test_i18ngrep "has been rescheduled" err && + git rebase --abort && + test_must_fail git rebase -y false HEAD^ 2>err && + test_i18ngrep "has been rescheduled" err +' + test_done diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh index b1602718f8..8b635a196d 100755 --- a/t/t3502-cherry-pick-merge.sh +++ b/t/t3502-cherry-pick-merge.sh @@ -40,12 +40,12 @@ test_expect_success 'cherry-pick -m complains of bogus numbers' ' test_expect_code 129 git cherry-pick -m 0 b ' -test_expect_success 'cherry-pick a non-merge with -m should fail' ' +test_expect_success 'cherry-pick explicit first parent of a non-merge' ' git reset --hard && git checkout a^0 && - test_expect_code 128 git cherry-pick -m 1 b && - git diff --exit-code a -- + git cherry-pick -m 1 b && + git diff --exit-code c -- ' @@ -84,12 +84,12 @@ test_expect_success 'cherry pick a merge relative to nonexistent parent should f ' -test_expect_success 'revert a non-merge with -m should fail' ' +test_expect_success 'revert explicit first parent of a non-merge' ' git reset --hard && git checkout c^0 && - test_must_fail git revert -m 1 b && - git diff --exit-code c + git revert -m 1 b && + git diff --exit-code a -- ' diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh index fb889ac6f0..127dd0082f 100755 --- a/t/t3506-cherry-pick-ff.sh +++ b/t/t3506-cherry-pick-ff.sh @@ -64,10 +64,10 @@ test_expect_success 'merge setup' ' git checkout -b new A ' -test_expect_success 'cherry-pick a non-merge with --ff and -m should fail' ' +test_expect_success 'cherry-pick explicit first parent of a non-merge with --ff' ' git reset --hard A -- && - test_must_fail git cherry-pick --ff -m 1 B && - git diff --exit-code A -- + git cherry-pick --ff -m 1 B && + git diff --exit-code C -- ' test_expect_success 'cherry pick a merge with --ff but without -m should fail' ' diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index c84eeefdc9..941d5026da 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -61,7 +61,11 @@ test_expect_success 'cherry-pick mid-cherry-pick-sequence' ' test_expect_success 'cherry-pick persists opts correctly' ' pristine_detach initial && - test_expect_code 128 git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours initial..anotherpick && + # to make sure that the session to cherry-pick a sequence + # gets interrupted, use a high-enough number that is larger + # than the number of parents of any commit we have created + mainline=4 && + test_expect_code 128 git cherry-pick -s -m $mainline --strategy=recursive -X patience -X ours initial..anotherpick && test_path_is_dir .git/sequencer && test_path_is_file .git/sequencer/head && test_path_is_file .git/sequencer/todo && @@ -69,7 +73,7 @@ test_expect_success 'cherry-pick persists opts correctly' ' echo "true" >expect && git config --file=.git/sequencer/opts --get-all options.signoff >actual && test_cmp expect actual && - echo "1" >expect && + echo "$mainline" >expect && git config --file=.git/sequencer/opts --get-all options.mainline >actual && test_cmp expect actual && echo "recursive" >expect && diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 9a3e4fdfec..ab4670d236 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -1802,8 +1802,8 @@ test_expect_success 'only move detection ignores white spaces' ' <BOLD;MAGENTA>-a long line to exceed per-line minimum<RESET> <BOLD;MAGENTA>-another long line to exceed per-line minimum<RESET> <RED>-original file<RESET> - <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>a long line to exceed per-line minimum<RESET> - <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>another long line to exceed per-line minimum<RESET> + <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>a long line to exceed per-line minimum<RESET> + <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>another long line to exceed per-line minimum<RESET> <GREEN>+<RESET><GREEN>new file<RESET> EOF test_cmp expected actual @@ -1827,6 +1827,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' QQQthat has similar lines QQQto previous blocks, but with different indent QQQYetQAnotherQoutlierQ + QLine with internal w h i t e s p a c e change EOF git add text.txt && @@ -1847,6 +1848,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' QQthat has similar lines QQto previous blocks, but with different indent QQYetQAnotherQoutlier + QLine with internal whitespace change EOF git diff --color --color-moved --color-moved-ws=allow-indentation-change >actual.raw && @@ -1856,7 +1858,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' <BOLD>diff --git a/text.txt b/text.txt<RESET> <BOLD>--- a/text.txt<RESET> <BOLD>+++ b/text.txt<RESET> - <CYAN>@@ -1,14 +1,14 @@<RESET> + <CYAN>@@ -1,15 +1,15 @@<RESET> <BOLD;MAGENTA>-QIndented<RESET> <BOLD;MAGENTA>-QText across<RESET> <BOLD;MAGENTA>-Qsome lines<RESET> @@ -1871,6 +1873,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' <BOLD;MAGENTA>-QQQthat has similar lines<RESET> <BOLD;MAGENTA>-QQQto previous blocks, but with different indent<RESET> <RED>-QQQYetQAnotherQoutlierQ<RESET> + <RED>-QLine with internal w h i t e s p a c e change<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>some lines<RESET> @@ -1885,6 +1888,7 @@ test_expect_success 'compare whitespace delta across moved blocks' ' <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>that has similar lines<RESET> <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>to previous blocks, but with different indent<RESET> <GREEN>+<RESET>QQ<GREEN>YetQAnotherQoutlier<RESET> + <GREEN>+<RESET>Q<GREEN>Line with internal whitespace change<RESET> EOF test_cmp expected actual @@ -1915,4 +1919,93 @@ test_expect_success 'compare whitespace delta incompatible with other space opti test_i18ngrep allow-indentation-change err ' +EMPTY='' +test_expect_success 'compare mixed whitespace delta across moved blocks' ' + + git reset --hard && + tr Q_ "\t " <<-EOF >text.txt && + ${EMPTY} + ____too short without + ${EMPTY} + ___being grouped across blank line + ${EMPTY} + context + lines + to + anchor + ____Indented text to + _Q____be further indented by four spaces across + ____Qseveral lines + QQ____These two lines have had their + ____indentation reduced by four spaces + Qdifferent indentation change + ____too short + EOF + + git add text.txt && + git commit -m "add text.txt" && + + tr Q_ "\t " <<-EOF >text.txt && + context + lines + to + anchor + QIndented text to + QQbe further indented by four spaces across + Q____several lines + ${EMPTY} + QQtoo short without + ${EMPTY} + Q_______being grouped across blank line + ${EMPTY} + Q_QThese two lines have had their + indentation reduced by four spaces + QQdifferent indentation change + __Qtoo short + EOF + + git -c color.diff.whitespace="normal red" \ + -c core.whitespace=space-before-tab \ + diff --color --color-moved --ws-error-highlight=all \ + --color-moved-ws=allow-indentation-change >actual.raw && + grep -v "index" actual.raw | test_decode_color >actual && + + cat <<-\EOF >expected && + <BOLD>diff --git a/text.txt b/text.txt<RESET> + <BOLD>--- a/text.txt<RESET> + <BOLD>+++ b/text.txt<RESET> + <CYAN>@@ -1,16 +1,16 @@<RESET> + <BOLD;MAGENTA>-<RESET> + <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> too short without<RESET> + <BOLD;MAGENTA>-<RESET> + <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> being grouped across blank line<RESET> + <BOLD;MAGENTA>-<RESET> + <RESET>context<RESET> + <RESET>lines<RESET> + <RESET>to<RESET> + <RESET>anchor<RESET> + <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> Indented text to<RESET> + <BOLD;MAGENTA>-<RESET><BRED> <RESET> <BOLD;MAGENTA> be further indented by four spaces across<RESET> + <BOLD;MAGENTA>-<RESET><BRED> <RESET> <BOLD;MAGENTA>several lines<RESET> + <BOLD;BLUE>-<RESET> <BOLD;BLUE> These two lines have had their<RESET> + <BOLD;BLUE>-<RESET><BOLD;BLUE> indentation reduced by four spaces<RESET> + <BOLD;MAGENTA>-<RESET> <BOLD;MAGENTA>different indentation change<RESET> + <RED>-<RESET><RED> too short<RESET> + <BOLD;CYAN>+<RESET> <BOLD;CYAN>Indented text to<RESET> + <BOLD;CYAN>+<RESET> <BOLD;CYAN>be further indented by four spaces across<RESET> + <BOLD;CYAN>+<RESET> <BOLD;CYAN> several lines<RESET> + <BOLD;YELLOW>+<RESET> + <BOLD;YELLOW>+<RESET> <BOLD;YELLOW>too short without<RESET> + <BOLD;YELLOW>+<RESET> + <BOLD;YELLOW>+<RESET> <BOLD;YELLOW> being grouped across blank line<RESET> + <BOLD;YELLOW>+<RESET> + <BOLD;CYAN>+<RESET> <BRED> <RESET> <BOLD;CYAN>These two lines have had their<RESET> + <BOLD;CYAN>+<RESET><BOLD;CYAN>indentation reduced by four spaces<RESET> + <BOLD;YELLOW>+<RESET> <BOLD;YELLOW>different indentation change<RESET> + <GREEN>+<RESET><BRED> <RESET> <GREEN>too short<RESET> + EOF + + test_cmp expected actual +' + test_done diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 978a8a66ff..7df8c3d4ec 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -621,4 +621,54 @@ test_expect_success 'trailer parsing not fooled by --- line' ' test_cmp expect actual ' +test_expect_success 'set up %S tests' ' + git checkout --orphan source-a && + test_commit one && + test_commit two && + git checkout -b source-b HEAD^ && + test_commit three +' + +test_expect_success 'log --format=%S paints branch names' ' + cat >expect <<-\EOF && + source-b + source-a + source-b + EOF + git log --format=%S source-a source-b >actual && + test_cmp expect actual +' + +test_expect_success 'log --format=%S paints tag names' ' + git tag -m tagged source-tag && + cat >expect <<-\EOF && + source-tag + source-a + source-tag + EOF + git log --format=%S source-tag source-a >actual && + test_cmp expect actual +' + +test_expect_success 'log --format=%S paints symmetric ranges' ' + cat >expect <<-\EOF && + source-b + source-a + EOF + git log --format=%S source-a...source-b >actual && + test_cmp expect actual +' + +test_expect_success '%S in git log --format works with other placeholders (part 1)' ' + git log --format="source-b %h" source-b >expect && + git log --format="%S %h" source-b >actual && + test_cmp expect actual +' + +test_expect_success '%S in git log --format works with other placeholders (part 2)' ' + git log --format="%h source-b" source-b >expect && + git log --format="%h %S" source-b >actual && + test_cmp expect actual +' + test_done diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index fc898c9eac..a539ffc080 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh @@ -7,82 +7,70 @@ test_description='Test the post-checkout hook.' . ./test-lib.sh test_expect_success setup ' - echo Data for commit0. >a && - echo Data for commit0. >b && - git update-index --add a && - git update-index --add b && - tree0=$(git write-tree) && - commit0=$(echo setup | git commit-tree $tree0) && - git update-ref refs/heads/master $commit0 && - git clone ./. clone1 && - git clone ./. clone2 && - GIT_DIR=clone2/.git git branch new2 && - echo Data for commit1. >clone2/b && - GIT_DIR=clone2/.git git add clone2/b && - GIT_DIR=clone2/.git git commit -m new2 -' - -for clone in 1 2; do - cat >clone${clone}/.git/hooks/post-checkout <<'EOF' -#!/bin/sh -echo $@ > $GIT_DIR/post-checkout.args -EOF - chmod u+x clone${clone}/.git/hooks/post-checkout -done - -test_expect_success 'post-checkout runs as expected ' ' - GIT_DIR=clone1/.git git checkout master && - test -e clone1/.git/post-checkout.args + mkdir -p .git/hooks && + write_script .git/hooks/post-checkout <<-\EOF && + echo "$@" >.git/post-checkout.args + EOF + test_commit one && + test_commit two && + test_commit rebase-on-me && + git reset --hard HEAD^ && + test_commit three ' test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' ' - old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout master && + read old new flag <.git/post-checkout.args && test $old = $new && test $flag = 1 ' -test_expect_success 'post-checkout runs as expected ' ' - GIT_DIR=clone1/.git git checkout master && - test -e clone1/.git/post-checkout.args -' - test_expect_success 'post-checkout args are correct with git checkout -b ' ' - GIT_DIR=clone1/.git git checkout -b new1 && - old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout -b new1 && + read old new flag <.git/post-checkout.args && test $old = $new && test $flag = 1 ' test_expect_success 'post-checkout receives the right args with HEAD changed ' ' - GIT_DIR=clone2/.git git checkout new2 && - old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout two && + read old new flag <.git/post-checkout.args && test $old != $new && test $flag = 1 ' test_expect_success 'post-checkout receives the right args when not switching branches ' ' - GIT_DIR=clone2/.git git checkout master b && - old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && - new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && - flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test_when_finished "rm -f .git/post-checkout.args" && + git checkout master -- three.t && + read old new flag <.git/post-checkout.args && test $old = $new && test $flag = 0 ' -if test "$(git config --bool core.filemode)" = true; then -mkdir -p templates/hooks -cat >templates/hooks/post-checkout <<'EOF' -#!/bin/sh -echo $@ > $GIT_DIR/post-checkout.args -EOF -chmod +x templates/hooks/post-checkout +test_expect_success 'post-checkout is triggered on rebase' ' + test_when_finished "rm -f .git/post-checkout.args" && + git checkout -b rebase-test master && + rm -f .git/post-checkout.args && + git rebase rebase-on-me && + read old new flag <.git/post-checkout.args && + test $old != $new && test $flag = 1 +' + +test_expect_success 'post-checkout is triggered on rebase with fast-forward' ' + test_when_finished "rm -f .git/post-checkout.args" && + git checkout -b ff-rebase-test rebase-on-me^ && + rm -f .git/post-checkout.args && + git rebase rebase-on-me && + read old new flag <.git/post-checkout.args && + test $old != $new && test $flag = 1 +' test_expect_success 'post-checkout hook is triggered by clone' ' + mkdir -p templates/hooks && + write_script templates/hooks/post-checkout <<-\EOF && + echo "$@" >$GIT_DIR/post-checkout.args + EOF git clone --template=templates . clone3 && test -f clone3/.git/post-checkout.args ' -fi test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 086f2c40f6..49c540b1e1 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -439,15 +439,23 @@ test_expect_success 'setup tests for the --stdin parameter' ' ) >input.dup ' -test_expect_success 'fetch refs from cmdline' ' - ( - cd client && - git fetch-pack --no-progress .. $(cat ../input) - ) >output && - cut -d " " -f 2 <output | sort >actual && - test_cmp expect actual +test_expect_success 'setup fetch refs from cmdline v[12]' ' + cp -r client client1 && + cp -r client client2 ' +for version in '' 1 2 +do + test_expect_success "protocol.version=$version fetch refs from cmdline" " + ( + cd client$version && + GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. \$(cat ../input) + ) >output && + cut -d ' ' -f 2 <output | sort >actual && + test_cmp expect actual + " +done + test_expect_success 'fetch refs from stdin' ' ( cd client && diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 32e722db2e..ced15ae122 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -204,6 +204,12 @@ test_expect_success 'overrides work between mixed transfer/upload-pack hideRefs' grep refs/tags/magic actual ' +test_expect_success 'protocol v2 supports hiderefs' ' + test_config uploadpack.hiderefs refs/tags && + git -c protocol.version=2 ls-remote . >actual && + ! grep refs/tags actual +' + test_expect_success 'ls-remote --symref' ' git fetch origin && cat >expect <<-EOF && @@ -260,7 +266,7 @@ test_lazy_prereq GIT_DAEMON ' # This test spawns a daemon, so run it only if the user would be OK with # testing with git-daemon. test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-compliant empty remote' ' - JGIT_DAEMON_PORT=${JGIT_DAEMON_PORT-${this_test#t}} && + test_set_port JGIT_DAEMON_PORT && JGIT_DAEMON_PID= && git init --bare empty.git && >empty.git/git-daemon-export-ok && diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 6c2f9b2ba2..63205dfdf9 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -524,6 +524,8 @@ test_expect_success 'fetching submodules respects parallel settings' ' git config fetch.recurseSubmodules true && ( cd downstream && + GIT_TRACE=$(pwd)/trace.out git fetch && + grep "1 tasks" trace.out && GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 && grep "7 tasks" trace.out && git config submodule.fetchJobs 8 && @@ -600,4 +602,121 @@ test_expect_success "fetch new commits when submodule got renamed" ' test_cmp expect actual ' +test_expect_success "fetch new submodule commits on-demand outside standard refspec" ' + # add a second submodule and ensure it is around in downstream first + git clone submodule sub1 && + git submodule add ./sub1 && + git commit -m "adding a second submodule" && + git -C downstream pull && + git -C downstream submodule update --init --recursive && + + git checkout --detach && + + C=$(git -C submodule commit-tree -m "new change outside refs/heads" HEAD^{tree}) && + git -C submodule update-ref refs/changes/1 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree -m "new change outside refs/heads" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/2 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit -m "updated submodules outside of refs/heads" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/3 $E && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/3:refs/heads/my_branch && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + git checkout --recurse-submodules FETCH_HEAD + ) +' + +test_expect_success 'fetch new submodule commit on-demand in FETCH_HEAD' ' + # depends on the previous test for setup + + C=$(git -C submodule commit-tree -m "another change outside refs/heads" HEAD^{tree}) && + git -C submodule update-ref refs/changes/4 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree -m "another change outside refs/heads" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/5 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit -m "updated submodules outside of refs/heads" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/6 $E && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/6 && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + git checkout --recurse-submodules FETCH_HEAD + ) +' + +test_expect_success 'fetch new submodule commits on-demand without .gitmodules entry' ' + # depends on the previous test for setup + + git config -f .gitmodules --remove-section submodule.sub1 && + git add .gitmodules && + git commit -m "delete gitmodules file" && + git checkout -B master && + git -C downstream fetch && + git -C downstream checkout origin/master && + + C=$(git -C submodule commit-tree -m "yet another change outside refs/heads" HEAD^{tree}) && + git -C submodule update-ref refs/changes/7 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree -m "yet another change outside refs/heads" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/8 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit -m "updated submodules outside of refs/heads" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/9 $E && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/9 && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + git checkout --recurse-submodules FETCH_HEAD + ) +' + +test_expect_success 'fetch new submodule commit intermittently referenced by superproject' ' + # depends on the previous test for setup + + D=$(git -C sub1 commit-tree -m "change 10 outside refs/heads" HEAD^{tree}) && + E=$(git -C sub1 commit-tree -m "change 11 outside refs/heads" HEAD^{tree}) && + F=$(git -C sub1 commit-tree -m "change 12 outside refs/heads" HEAD^{tree}) && + + git -C sub1 update-ref refs/changes/10 $D && + git update-index --cacheinfo 160000 $D sub1 && + git commit -m "updated submodules outside of refs/heads" && + + git -C sub1 update-ref refs/changes/11 $E && + git update-index --cacheinfo 160000 $E sub1 && + git commit -m "updated submodules outside of refs/heads" && + + git -C sub1 update-ref refs/changes/12 $F && + git update-index --cacheinfo 160000 $F sub1 && + git commit -m "updated submodules outside of refs/heads" && + + G=$(git rev-parse HEAD) && + git update-ref refs/changes/13 $G && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/13 && + + git -C sub1 cat-file -t $D && + git -C sub1 cat-file -t $E && + git -C sub1 cat-file -t $F + ) +' + test_done diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index 7466aad111..58ee787685 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -183,19 +183,6 @@ test_expect_success 'hostname cannot break out of directory' ' git ls-remote "$GIT_DAEMON_URL/escape.git" ' -test_expect_success 'daemon log records all attributes' ' - cat >expect <<-\EOF && - Extended attribute "host": localhost - Extended attribute "protocol": version=1 - EOF - >daemon.log && - GIT_OVERRIDE_VIRTUAL_HOST=localhost \ - git -c protocol.version=1 \ - ls-remote "$GIT_DAEMON_URL/interp.git" && - grep -i extended.attribute daemon.log | cut -d" " -f2- >actual && - test_cmp expect actual -' - test_expect_success FAKENC 'hostname interpolation works after LF-stripping' ' { printf "git-upload-pack /interp.git\n\0host=localhost" | packetize diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh new file mode 100755 index 0000000000..cd9283eeec --- /dev/null +++ b/t/t5581-http-curl-verbose.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +test_description='test GIT_CURL_VERBOSE' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-httpd.sh +start_httpd + +test_expect_success 'setup repository' ' + mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" --bare init && + git config push.default matching && + echo content >file && + git add file && + git commit -m one && + git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git push public master:master +' + +test_expect_success 'failure in git-upload-pack is shown' ' + test_might_fail env GIT_CURL_VERBOSE=1 \ + git clone "$HTTPD_URL/error_git_upload_pack/smart/repo.git" \ + 2>curl_log && + grep "< HTTP/1.1 500 Intentional Breakage" curl_log +' + +stop_httpd + +test_done diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 0f2b09ebb8..a738c0c4ce 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -471,6 +471,53 @@ test_expect_success 'upload-pack respects client shallows' ' grep "fetch< version 2" trace ' +test_expect_success 'ensure that multiple fetches in same process from a shallow repo works' ' + rm -rf server client trace && + + test_create_repo server && + test_commit -C server one && + test_commit -C server two && + test_commit -C server three && + git clone --shallow-exclude two "file://$(pwd)/server" client && + + git -C server tag -a -m "an annotated tag" twotag two && + + # Triggers tag following (thus, 2 fetches in one process) + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + fetch --shallow-exclude one origin && + # Ensure that protocol v2 is used + grep "fetch< version 2" trace +' + +test_expect_success 'deepen-relative' ' + rm -rf server client trace && + + test_create_repo server && + test_commit -C server one && + test_commit -C server two && + test_commit -C server three && + git clone --depth 1 "file://$(pwd)/server" client && + test_commit -C server four && + + # Sanity check that only "three" is downloaded + git -C client log --pretty=tformat:%s master >actual && + echo three >expected && + test_cmp expected actual && + + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + fetch --deepen=1 origin && + # Ensure that protocol v2 is used + grep "fetch< version 2" trace && + + git -C client log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + four + three + two + EOF + test_cmp expected actual +' + # Test protocol v2 with 'http://' transport # . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index ec42c2f779..da113d975b 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -185,6 +185,10 @@ test_expect_success 'basic colors' ' test_cmp expect actual ' +test_expect_success '%S is not a placeholder for rev-list yet' ' + git rev-list --format="%S" -1 master | grep "%S" +' + test_expect_success 'advanced colors' ' cat >expect <<-EOF && commit $head2 diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index eb32505a6e..9c11427719 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh @@ -283,7 +283,7 @@ test_expect_success 'verify tree:0 includes trees in "filtered" output' ' # Make sure tree:0 does not iterate through any trees. -test_expect_success 'filter a GIANT tree through tree:0' ' +test_expect_success 'verify skipping tree iteration when not collecting omits' ' GIT_TRACE=1 git -C r3 rev-list \ --objects --filter=tree:0 HEAD 2>filter_trace && grep "Skipping contents of tree [.][.][.]" filter_trace >actual && @@ -294,6 +294,126 @@ test_expect_success 'filter a GIANT tree through tree:0' ' ! grep "Skipping contents of tree [^.]" filter_trace ' +# Test tree:# filters. + +expect_has () { + commit=$1 && + name=$2 && + + hash=$(git -C r3 rev-parse $commit:$name) && + grep "^$hash $name$" actual +} + +test_expect_success 'verify tree:1 includes root trees' ' + git -C r3 rev-list --objects --filter=tree:1 HEAD >actual && + + # We should get two root directories and two commits. + expect_has HEAD "" && + expect_has HEAD~1 "" && + test_line_count = 4 actual +' + +test_expect_success 'verify tree:2 includes root trees and immediate children' ' + git -C r3 rev-list --objects --filter=tree:2 HEAD >actual && + + expect_has HEAD "" && + expect_has HEAD~1 "" && + expect_has HEAD dir1 && + expect_has HEAD pattern && + expect_has HEAD sparse1 && + expect_has HEAD sparse2 && + + # There are also 2 commit objects + test_line_count = 8 actual +' + +test_expect_success 'verify tree:3 includes everything expected' ' + git -C r3 rev-list --objects --filter=tree:3 HEAD >actual && + + expect_has HEAD "" && + expect_has HEAD~1 "" && + expect_has HEAD dir1 && + expect_has HEAD dir1/sparse1 && + expect_has HEAD dir1/sparse2 && + expect_has HEAD pattern && + expect_has HEAD sparse1 && + expect_has HEAD sparse2 && + + # There are also 2 commit objects + test_line_count = 10 actual +' + +# Test provisional omit collection logic with a repo that has objects appearing +# at multiple depths - first deeper than the filter's threshold, then shallow. + +test_expect_success 'setup r4' ' + git init r4 && + + echo foo > r4/foo && + mkdir r4/subdir && + echo bar > r4/subdir/bar && + + mkdir r4/filt && + cp -r r4/foo r4/subdir r4/filt && + + git -C r4 add foo subdir filt && + git -C r4 commit -m "commit msg" +' + +expect_has_with_different_name () { + repo=$1 && + name=$2 && + + hash=$(git -C $repo rev-parse HEAD:$name) && + ! grep "^$hash $name$" actual && + grep "^$hash " actual && + ! grep "~$hash" actual +} + +test_expect_success 'test tree:# filter provisional omit for blob and tree' ' + git -C r4 rev-list --objects --filter-print-omitted --filter=tree:2 \ + HEAD >actual && + expect_has_with_different_name r4 filt/foo && + expect_has_with_different_name r4 filt/subdir +' + +test_expect_success 'verify skipping tree iteration when collecting omits' ' + GIT_TRACE=1 git -C r4 rev-list --filter-print-omitted \ + --objects --filter=tree:0 HEAD 2>filter_trace && + grep "^Skipping contents of tree " filter_trace >actual && + + echo "Skipping contents of tree subdir/..." >expect && + test_cmp expect actual +' + +# Test tree:<depth> where a tree is iterated to twice - once where a subentry is +# too deep to be included, and again where the blob inside it is shallow enough +# to be included. This makes sure we don't use LOFR_MARK_SEEN incorrectly (we +# can't use it because a tree can be iterated over again at a lower depth). + +test_expect_success 'tree:<depth> where we iterate over tree at two levels' ' + git init r5 && + + mkdir -p r5/a/subdir/b && + echo foo > r5/a/subdir/b/foo && + + mkdir -p r5/subdir/b && + echo foo > r5/subdir/b/foo && + + git -C r5 add a subdir && + git -C r5 commit -m "commit msg" && + + git -C r5 rev-list --objects --filter=tree:4 HEAD >actual && + expect_has_with_different_name r5 a/subdir/b/foo +' + +test_expect_success 'tree:<depth> which filters out blob but given as arg' ' + blob_hash=$(git -C r4 rev-parse HEAD:subdir/bar) && + + git -C r4 rev-list --objects --filter=tree:1 HEAD $blob_hash >actual && + grep ^$blob_hash actual +' + # Delete some loose objects and use rev-list, but WITHOUT any filtering. # This models previously omitted objects that we did not receive. @@ -324,4 +444,21 @@ test_expect_success 'rev-list W/ missing=allow-any' ' git -C r1 rev-list --quiet --missing=allow-any --objects HEAD ' +# Test expansion of filter specs. + +test_expect_success 'expand blob limit in protocol' ' + git -C r2 config --local uploadpack.allowfilter 1 && + GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 clone \ + --filter=blob:limit=1k "file://$(pwd)/r2" limit && + ! grep "blob:limit=1k" trace && + grep "blob:limit=1024" trace +' + +test_expect_success 'expand tree depth limit in protocol' ' + GIT_TRACE_PACKET="$(pwd)/tree_trace" git -c protocol.version=2 clone \ + --filter=tree:0k "file://$(pwd)/r2" tree && + ! grep "tree:0k" tree_trace && + grep "tree:0" tree_trace +' + test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 97bfbee6e8..0ffd630713 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -83,6 +83,8 @@ test_atom head push:strip=1 remotes/myfork/master test_atom head push:strip=-1 master test_atom head objecttype commit test_atom head objectsize 171 +test_atom head objectsize:disk 138 +test_atom head deltabase 0000000000000000000000000000000000000000 test_atom head objectname $(git rev-parse refs/heads/master) test_atom head objectname:short $(git rev-parse --short refs/heads/master) test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) @@ -124,6 +126,10 @@ test_atom tag upstream '' test_atom tag push '' test_atom tag objecttype tag test_atom tag objectsize 154 +test_atom tag objectsize:disk 138 +test_atom tag '*objectsize:disk' 138 +test_atom tag deltabase 0000000000000000000000000000000000000000 +test_atom tag '*deltabase' 0000000000000000000000000000000000000000 test_atom tag objectname $(git rev-parse refs/tags/testtag) test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 76a7cb0af7..aba2d4d6ee 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -984,6 +984,11 @@ test_expect_success 'submodule deinit should remove the whole submodule section rmdir init ' +test_expect_success 'submodule deinit should unset core.worktree' ' + test_path_is_file .git/modules/example/config && + test_must_fail git config -f .git/modules/example/config core.worktree +' + test_expect_success 'submodule deinit from subdirectory' ' git submodule update --init && git config submodule.example.foo bar && diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh index ce74c12da2..1cfa150768 100755 --- a/t/t7412-submodule-absorbgitdirs.sh +++ b/t/t7412-submodule-absorbgitdirs.sh @@ -75,7 +75,12 @@ test_expect_success 're-setup nested submodule' ' GIT_WORK_TREE=../../../nested git -C sub1/.git/modules/nested config \ core.worktree "../../../nested" && # make sure this re-setup is correct - git status --ignore-submodules=none + git status --ignore-submodules=none && + + # also make sure this old setup does not regress + git submodule update --init --recursive >out 2>err && + test_must_be_empty out && + test_must_be_empty err ' test_expect_success 'absorb the git dir in a nested submodule' ' diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh index 2325599ee6..850d979119 100755 --- a/t/t9807-git-p4-submit.sh +++ b/t/t9807-git-p4-submit.sh @@ -500,6 +500,10 @@ test_expect_success 'submit --shelve' ' ) ' +last_shelve () { + p4 -G changes -s shelved -m 1 //depot/... | marshal_dump change +} + make_shelved_cl() { test_commit "$1" >/dev/null && git p4 submit --origin HEAD^ --shelve >/dev/null && @@ -533,12 +537,59 @@ test_expect_success 'submit --update-shelve' ' ) && ( cd "$cli" && - change=$(p4 -G changes -s shelved -m 1 //depot/... | \ - marshal_dump change) && + change=$(last_shelve) && p4 unshelve -c $change -s $change && grep -q updated-line shelf.t && p4 describe -S $change | grep added-file.t && - test_path_is_missing shelved-change-1.t + test_path_is_missing shelved-change-1.t && + p4 revert ... + ) +' + +test_expect_success 'update a shelve involving moved and copied files' ' + test_when_finished cleanup_git && + ( + cd "$cli" && + : >file_to_move && + p4 add file_to_move && + p4 submit -d "change1" && + p4 edit file_to_move && + echo change >>file_to_move && + p4 submit -d "change2" && + p4 opened + ) && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.detectCopies true && + git config git-p4.detectRenames true && + git config git-p4.skipSubmitEdit true && + mkdir moved && + cp file_to_move copy_of_file && + git add copy_of_file && + git mv file_to_move moved/ && + git commit -m "rename a file" && + git p4 submit -M --shelve --origin HEAD^ && + : >new_file && + git add new_file && + git commit --amend && + git show --stat HEAD && + change=$(last_shelve) && + git p4 submit -M --update-shelve $change --commit HEAD + ) && + ( + cd "$cli" && + change=$(last_shelve) && + echo change=$change && + p4 unshelve -s $change && + p4 submit -d "Testing update-shelve" && + test_path_is_file copy_of_file && + test_path_is_file moved/file_to_move && + test_path_is_missing file_to_move && + test_path_is_file new_file && + echo "unshelved and submitted change $change" && + p4 changes moved/file_to_move | grep "Testing update-shelve" && + p4 changes copy_of_file | grep "Testing update-shelve" ) ' diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 6558eee499..3a2c6326d8 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1516,8 +1516,8 @@ test_expect_success 'show completes all refs' ' test_expect_success '<ref>: completes paths' ' test_completion "git show mytag:f" <<-\EOF - file1 Z - file2 Z + file1Z + file2Z EOF ' @@ -1526,7 +1526,7 @@ test_expect_success 'complete tree filename with spaces' ' git add "name with spaces" && git commit -m spaces && test_completion "git show HEAD:nam" <<-\EOF - name with spaces Z + name with spacesZ EOF ' @@ -1535,8 +1535,8 @@ test_expect_success 'complete tree filename with metacharacters' ' git add "name with \${meta}" && git commit -m meta && test_completion "git show HEAD:nam" <<-\EOF - name with ${meta} Z - name with spaces Z + name with ${meta}Z + name with spacesZ EOF ' diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 6b3bbf99e4..92cf8f812c 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -1263,3 +1263,42 @@ test_oid () { fi && eval "printf '%s' \"\${$var}\"" } + +# Choose a port number based on the test script's number and store it in +# the given variable name, unless that variable already contains a number. +test_set_port () { + local var=$1 port + + if test $# -ne 1 || test -z "$var" + then + BUG "test_set_port requires a variable name" + fi + + eval port=\$$var + case "$port" in + "") + # No port is set in the given env var, use the test + # number as port number instead. + # Remove not only the leading 't', but all leading zeros + # as well, so the arithmetic below won't (mis)interpret + # a test number like '0123' as an octal value. + port=${this_test#${this_test%%[1-9]*}} + if test "${port:-0}" -lt 1024 + then + # root-only port, use a larger one instead. + port=$(($port + 10000)) + fi + ;; + *[^0-9]*|0*) + error >&7 "invalid port number: $port" + ;; + *) + # The user has specified the port. + ;; + esac + + # Make sure that parallel '--stress' test jobs get different + # ports. + port=$(($port + ${GIT_TEST_STRESS_JOB_NR:-0})) + eval $var=$port +} diff --git a/t/test-lib.sh b/t/test-lib.sh index 0f1faa24b2..a1abb1177a 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -71,19 +71,222 @@ then exit 1 fi +# Parse options while taking care to leave $@ intact, so we will still +# have all the original command line options when executing the test +# script again for '--tee' and '--verbose-log' below. +store_arg_to= +prev_opt= +for opt +do + if test -n "$store_arg_to" + then + eval $store_arg_to=\$opt + store_arg_to= + prev_opt= + continue + fi + + case "$opt" in + -d|--d|--de|--deb|--debu|--debug) + debug=t ;; + -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) + immediate=t ;; + -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests) + GIT_TEST_LONG=t; export GIT_TEST_LONG ;; + -r) + store_arg_to=run_list + ;; + --run=*) + run_list=${opt#--*=} ;; + -h|--h|--he|--hel|--help) + help=t ;; + -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) + verbose=t ;; + --verbose-only=*) + verbose_only=${opt#--*=} + ;; + -q|--q|--qu|--qui|--quie|--quiet) + # Ignore --quiet under a TAP::Harness. Saying how many tests + # passed without the ok/not ok details is always an error. + test -z "$HARNESS_ACTIVE" && quiet=t ;; + --with-dashes) + with_dashes=t ;; + --no-color) + color= ;; + --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) + valgrind=memcheck + tee=t + ;; + --valgrind=*) + valgrind=${opt#--*=} + tee=t + ;; + --valgrind-only=*) + valgrind_only=${opt#--*=} + tee=t + ;; + --tee) + tee=t ;; + --root=*) + root=${opt#--*=} ;; + --chain-lint) + GIT_TEST_CHAIN_LINT=1 ;; + --no-chain-lint) + GIT_TEST_CHAIN_LINT=0 ;; + -x) + trace=t ;; + -V|--verbose-log) + verbose_log=t + tee=t + ;; + --stress) + stress=t ;; + --stress=*) + stress=${opt#--*=} + case "$stress" in + *[^0-9]*|0*|"") + echo "error: --stress=<N> requires the number of jobs to run" >&2 + exit 1 + ;; + *) # Good. + ;; + esac + ;; + *) + echo "error: unknown test option '$opt'" >&2; exit 1 ;; + esac + + prev_opt=$opt +done +if test -n "$store_arg_to" +then + echo "error: $prev_opt requires an argument" >&2 + exit 1 +fi + +if test -n "$valgrind_only" +then + test -z "$valgrind" && valgrind=memcheck + test -z "$verbose" && verbose_only="$valgrind_only" +elif test -n "$valgrind" +then + test -z "$verbose_log" && verbose=t +fi + +if test -n "$stress" +then + verbose=t + trace=t + immediate=t +fi + +TEST_STRESS_JOB_SFX="${GIT_TEST_STRESS_JOB_NR:+.stress-$GIT_TEST_STRESS_JOB_NR}" +TEST_NAME="$(basename "$0" .sh)" +TEST_RESULTS_DIR="$TEST_OUTPUT_DIRECTORY/test-results" +TEST_RESULTS_BASE="$TEST_RESULTS_DIR/$TEST_NAME$TEST_STRESS_JOB_SFX" +TRASH_DIRECTORY="trash directory.$TEST_NAME$TEST_STRESS_JOB_SFX" +test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY" +case "$TRASH_DIRECTORY" in +/*) ;; # absolute path is good + *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;; +esac + +# If --stress was passed, run this test repeatedly in several parallel loops. +if test "$GIT_TEST_STRESS_STARTED" = "done" +then + : # Don't stress test again. +elif test -n "$stress" +then + if test "$stress" != t + then + job_count=$stress + elif test -n "$GIT_TEST_STRESS_LOAD" + then + job_count="$GIT_TEST_STRESS_LOAD" + elif job_count=$(getconf _NPROCESSORS_ONLN 2>/dev/null) && + test -n "$job_count" + then + job_count=$((2 * $job_count)) + else + job_count=8 + fi + + mkdir -p "$TEST_RESULTS_DIR" + stressfail="$TEST_RESULTS_BASE.stress-failed" + rm -f "$stressfail" + + stress_exit=0 + trap ' + kill $job_pids 2>/dev/null + wait + stress_exit=1 + ' TERM INT HUP + + job_pids= + job_nr=0 + while test $job_nr -lt "$job_count" + do + ( + GIT_TEST_STRESS_STARTED=done + GIT_TEST_STRESS_JOB_NR=$job_nr + export GIT_TEST_STRESS_STARTED GIT_TEST_STRESS_JOB_NR + + trap ' + kill $test_pid 2>/dev/null + wait + exit 1 + ' TERM INT + + cnt=0 + while ! test -e "$stressfail" + do + $TEST_SHELL_PATH "$0" "$@" >"$TEST_RESULTS_BASE.stress-$job_nr.out" 2>&1 & + test_pid=$! + + if wait $test_pid + then + printf "OK %2d.%d\n" $GIT_TEST_STRESS_JOB_NR $cnt + else + echo $GIT_TEST_STRESS_JOB_NR >>"$stressfail" + printf "FAIL %2d.%d\n" $GIT_TEST_STRESS_JOB_NR $cnt + fi + cnt=$(($cnt + 1)) + done + ) & + job_pids="$job_pids $!" + job_nr=$(($job_nr + 1)) + done + + wait + + if test -f "$stressfail" + then + echo "Log(s) of failed test run(s):" + for failed_job_nr in $(sort -n "$stressfail") + do + echo "Contents of '$TEST_RESULTS_BASE.stress-$failed_job_nr.out':" + cat "$TEST_RESULTS_BASE.stress-$failed_job_nr.out" + done + rm -rf "$TRASH_DIRECTORY.stress-failed" + # Move the last one. + mv "$TRASH_DIRECTORY.stress-$failed_job_nr" "$TRASH_DIRECTORY.stress-failed" + fi + + exit $stress_exit +fi + # if --tee was passed, write the output not only to the terminal, but # additionally to the file test-results/$BASENAME.out, too. -case "$GIT_TEST_TEE_STARTED, $* " in -done,*) - # do not redirect again - ;; -*' --tee '*|*' --va'*|*' -V '*|*' --verbose-log '*) - mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results" - BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)" +if test "$GIT_TEST_TEE_STARTED" = "done" +then + : # do not redirect again +elif test -n "$tee" +then + mkdir -p "$TEST_RESULTS_DIR" # Make this filename available to the sub-process in case it is using # --verbose-log. - GIT_TEST_TEE_OUTPUT_FILE=$BASE.out + GIT_TEST_TEE_OUTPUT_FILE=$TEST_RESULTS_BASE.out export GIT_TEST_TEE_OUTPUT_FILE # Truncate before calling "tee -a" to get rid of the results @@ -91,11 +294,38 @@ done,*) >"$GIT_TEST_TEE_OUTPUT_FILE" (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1; - echo $? >"$BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE" - test "$(cat "$BASE.exit")" = 0 + echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE" + test "$(cat "$TEST_RESULTS_BASE.exit")" = 0 exit - ;; -esac +fi + +if test -n "$trace" && test -n "$test_untraceable" +then + # '-x' tracing requested, but this test script can't be reliably + # traced, unless it is run with a Bash version supporting + # BASH_XTRACEFD (introduced in Bash v4.1). + # + # Perform this version check _after_ the test script was + # potentially re-executed with $TEST_SHELL_PATH for '--tee' or + # '--verbose-log', so the right shell is checked and the + # warning is issued only once. + if test -n "$BASH_VERSION" && eval ' + test ${BASH_VERSINFO[0]} -gt 4 || { + test ${BASH_VERSINFO[0]} -eq 4 && + test ${BASH_VERSINFO[1]} -ge 1 + } + ' + then + : Executed by a Bash version supporting BASH_XTRACEFD. Good. + else + echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD" + trace= + fi +fi +if test -n "$trace" && test -z "$verbose_log" +then + verbose=t +fi # For repeatability, reset the environment to known value. # TERM is sanitized below, after saving color control sequences. @@ -193,7 +423,7 @@ fi # Add libc MALLOC and MALLOC_PERTURB test # only if we are not executing the test with valgrind -if expr " $GIT_TEST_OPTS " : ".* --valgrind " >/dev/null || +if test -n "$valgrind" || test -n "$TEST_NO_MALLOC_CHECK" then setup_malloc_check () { @@ -264,100 +494,6 @@ test "x$TERM" != "xdumb" && ( ) && color=t -while test "$#" -ne 0 -do - case "$1" in - -d|--d|--de|--deb|--debu|--debug) - debug=t; shift ;; - -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) - immediate=t; shift ;; - -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests) - GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;; - -r) - shift; test "$#" -ne 0 || { - echo 'error: -r requires an argument' >&2; - exit 1; - } - run_list=$1; shift ;; - --run=*) - run_list=${1#--*=}; shift ;; - -h|--h|--he|--hel|--help) - help=t; shift ;; - -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) - verbose=t; shift ;; - --verbose-only=*) - verbose_only=${1#--*=} - shift ;; - -q|--q|--qu|--qui|--quie|--quiet) - # Ignore --quiet under a TAP::Harness. Saying how many tests - # passed without the ok/not ok details is always an error. - test -z "$HARNESS_ACTIVE" && quiet=t; shift ;; - --with-dashes) - with_dashes=t; shift ;; - --no-color) - color=; shift ;; - --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) - valgrind=memcheck - shift ;; - --valgrind=*) - valgrind=${1#--*=} - shift ;; - --valgrind-only=*) - valgrind_only=${1#--*=} - shift ;; - --tee) - shift ;; # was handled already - --root=*) - root=${1#--*=} - shift ;; - --chain-lint) - GIT_TEST_CHAIN_LINT=1 - shift ;; - --no-chain-lint) - GIT_TEST_CHAIN_LINT=0 - shift ;; - -x) - # Some test scripts can't be reliably traced with '-x', - # unless the test is run with a Bash version supporting - # BASH_XTRACEFD (introduced in Bash v4.1). Check whether - # this test is marked as such, and ignore '-x' if it - # isn't executed with a suitable Bash version. - if test -z "$test_untraceable" || { - test -n "$BASH_VERSION" && { - test ${BASH_VERSINFO[0]} -gt 4 || { - test ${BASH_VERSINFO[0]} -eq 4 && - test ${BASH_VERSINFO[1]} -ge 1 - } - } - } - then - trace=t - else - echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD" - fi - shift ;; - -V|--verbose-log) - verbose_log=t - shift ;; - *) - echo "error: unknown test option '$1'" >&2; exit 1 ;; - esac -done - -if test -n "$valgrind_only" -then - test -z "$valgrind" && valgrind=memcheck - test -z "$verbose" && verbose_only="$valgrind_only" -elif test -n "$valgrind" -then - test -z "$verbose_log" && verbose=t -fi - -if test -n "$trace" && test -z "$verbose_log" -then - verbose=t -fi - if test -n "$color" then # Save the color control sequences now rather than run tput @@ -476,7 +612,7 @@ die () { GIT_EXIT_OK= trap 'die' EXIT -trap 'exit $?' INT +trap 'exit $?' INT TERM HUP # The user-facing functions are loaded from a separate file so that # test_perf subshells can have them too @@ -818,12 +954,9 @@ test_done () { if test -z "$HARNESS_ACTIVE" then - test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results" - mkdir -p "$test_results_dir" - base=${0##*/} - test_results_path="$test_results_dir/${base%.sh}.counts" + mkdir -p "$TEST_RESULTS_DIR" - cat >"$test_results_path" <<-EOF + cat >"$TEST_RESULTS_BASE.counts" <<-EOF total $test_count success $test_success fixed $test_fixed @@ -1029,12 +1162,6 @@ then fi # Test repository -TRASH_DIRECTORY="trash directory.$(basename "$0" .sh)" -test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY" -case "$TRASH_DIRECTORY" in -/*) ;; # absolute path is good - *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;; -esac rm -fr "$TRASH_DIRECTORY" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" diff --git a/transport-helper.c b/transport-helper.c index bf225c698f..1f52c95fd8 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -679,10 +679,15 @@ static int fetch(struct transport *transport, if (data->transport_options.update_shallow) set_helper_option(transport, "update-shallow", "true"); - if (data->transport_options.filter_options.choice) - set_helper_option( - transport, "filter", - data->transport_options.filter_options.filter_spec); + if (data->transport_options.filter_options.choice) { + struct strbuf expanded_filter_spec = STRBUF_INIT; + expand_list_objects_filter_spec( + &data->transport_options.filter_options, + &expanded_filter_spec); + set_helper_option(transport, "filter", + expanded_filter_spec.buf); + strbuf_release(&expanded_filter_spec); + } if (data->transport_options.negotiation_tips) warning("Ignoring --negotiation-tip because the protocol does not support it."); @@ -1026,7 +1031,8 @@ static int push_refs(struct transport *transport, } -static int has_attribute(const char *attrs, const char *attr) { +static int has_attribute(const char *attrs, const char *attr) +{ int len; if (!attrs) return 0; @@ -1225,9 +1231,8 @@ static int udt_do_read(struct unidirectional_transfer *t) return 0; /* No space for more. */ transfer_debug("%s is readable", t->src_name); - bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse); - if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN && - errno != EINTR) { + bytes = xread(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse); + if (bytes < 0) { error_errno(_("read(%s) failed"), t->src_name); return -1; } else if (bytes == 0) { @@ -1254,7 +1259,7 @@ static int udt_do_write(struct unidirectional_transfer *t) transfer_debug("%s is writable", t->dest_name); bytes = xwrite(t->dest, t->buf, t->bufuse); - if (bytes < 0 && errno != EWOULDBLOCK) { + if (bytes < 0) { error_errno(_("write(%s) failed"), t->dest_name); return -1; } else if (bytes > 0) { diff --git a/tree-diff.c b/tree-diff.c index 34ee3b13b8..e6d306f69f 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -239,7 +239,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, DIFF_STATUS_ADDED; if (tpi_valid) { - oid_i = tp[i].entry.oid; + oid_i = &tp[i].entry.oid; mode_i = tp[i].entry.mode; } else { @@ -280,7 +280,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, /* same rule as in emitthis */ int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ); - parents_oid[i] = tpi_valid ? tp[i].entry.oid : NULL; + parents_oid[i] = tpi_valid ? &tp[i].entry.oid : NULL; } strbuf_add(base, path, pathlen); @@ -492,7 +492,7 @@ static struct combine_diff_path *ll_diff_tree_paths( continue; /* diff(t,pi) != ΓΈ */ - if (!oideq(t.entry.oid, tp[i].entry.oid) || + if (!oideq(&t.entry.oid, &tp[i].entry.oid) || (t.entry.mode != tp[i].entry.mode)) continue; diff --git a/tree-walk.c b/tree-walk.c index 08210a4109..277e3b3243 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -48,7 +48,8 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l /* Initialize the descriptor entry */ desc->entry.path = path; desc->entry.mode = canon_mode(mode); - desc->entry.oid = (const struct object_id *)(path + len); + desc->entry.pathlen = len - 1; + hashcpy(desc->entry.oid.hash, (const unsigned char *)path + len); return 0; } @@ -107,7 +108,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a) static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err) { const void *buf = desc->buffer; - const unsigned char *end = desc->entry.oid->hash + the_hash_algo->rawsz; + const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz; unsigned long size = desc->size; unsigned long len = end - (const unsigned char *)buf; @@ -175,9 +176,11 @@ void setup_traverse_info(struct traverse_info *info, const char *base) pathlen--; info->pathlen = pathlen ? pathlen + 1 : 0; info->name.path = base; - info->name.oid = (void *)(base + pathlen + 1); - if (pathlen) + info->name.pathlen = pathlen; + if (pathlen) { + hashcpy(info->name.oid.hash, (const unsigned char *)base + pathlen + 1); info->prev = &dummy; + } } char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n) @@ -502,10 +505,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_ int namelen = strlen(name); while (t->size) { const char *entry; - const struct object_id *oid; + struct object_id oid; int entrylen, cmp; - oid = tree_entry_extract(t, &entry, mode); + oidcpy(&oid, tree_entry_extract(t, &entry, mode)); entrylen = tree_entry_len(&t->entry); update_tree_entry(t); if (entrylen > namelen) @@ -516,7 +519,7 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_ if (cmp < 0) break; if (entrylen == namelen) { - oidcpy(result, oid); + oidcpy(result, &oid); return 0; } if (name[entrylen] != '/') @@ -524,10 +527,10 @@ static int find_tree_entry(struct tree_desc *t, const char *name, struct object_ if (!S_ISDIR(*mode)) break; if (++entrylen == namelen) { - oidcpy(result, oid); + oidcpy(result, &oid); return 0; } - return get_tree_entry(oid, name + entrylen, result, mode); + return get_tree_entry(&oid, name + entrylen, result, mode); } return -1; } diff --git a/tree-walk.h b/tree-walk.h index eefd26bb62..a4ad28ea5e 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -1,12 +1,12 @@ #ifndef TREE_WALK_H #define TREE_WALK_H -struct index_state; -struct strbuf; +#include "cache.h" struct name_entry { - const struct object_id *oid; + struct object_id oid; const char *path; + int pathlen; unsigned int mode; }; @@ -20,12 +20,12 @@ static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, { *pathp = desc->entry.path; *modep = desc->entry.mode; - return desc->entry.oid; + return &desc->entry.oid; } static inline int tree_entry_len(const struct name_entry *ne) { - return (const char *)ne->oid - ne->path - 1; + return ne->pathlen; } /* @@ -86,7 +86,7 @@ static int read_tree_1(struct repository *r, continue; } - switch (fn(entry.oid, base, + switch (fn(&entry.oid, base, entry.path, entry.mode, stage, context)) { case 0: continue; @@ -97,19 +97,19 @@ static int read_tree_1(struct repository *r, } if (S_ISDIR(entry.mode)) - oidcpy(&oid, entry.oid); + oidcpy(&oid, &entry.oid); else if (S_ISGITLINK(entry.mode)) { struct commit *commit; - commit = lookup_commit(r, entry.oid); + commit = lookup_commit(r, &entry.oid); if (!commit) die("Commit %s in submodule path %s%s not found", - oid_to_hex(entry.oid), + oid_to_hex(&entry.oid), base->buf, entry.path); if (parse_commit(commit)) die("Invalid commit %s in submodule path %s%s", - oid_to_hex(entry.oid), + oid_to_hex(&entry.oid), base->buf, entry.path); oidcpy(&oid, get_commit_tree_oid(commit)); diff --git a/unpack-trees.c b/unpack-trees.c index 94265a7df0..01c2175f7c 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -679,7 +679,7 @@ static int switch_cache_bottom(struct traverse_info *info) static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k) { - return name_j->oid && name_k->oid && oideq(name_j->oid, name_k->oid); + return !is_null_oid(&name_j->oid) && !is_null_oid(&name_k->oid) && oideq(&name_j->oid, &name_k->oid); } static int all_trees_same_as_cache_tree(int n, unsigned long dirmask, @@ -857,7 +857,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, else { const struct object_id *oid = NULL; if (dirmask & 1) - oid = names[i].oid; + oid = &names[i].oid; buf[nr_buf++] = fill_tree_descriptor(t + i, oid); } } @@ -981,7 +981,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, ce->ce_mode = create_ce_mode(n->mode); ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = len; - oidcpy(&ce->oid, n->oid); + oidcpy(&ce->oid, &n->oid); make_traverse_path(ce->name, info, n); return ce; diff --git a/upload-pack.c b/upload-pack.c index 5e81f1ff24..ee89381585 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -43,7 +43,6 @@ static timestamp_t oldest_have; -static int deepen_relative; static int multi_ack; static int no_done; static int use_thin_pack, use_ofs_delta, use_include_tag; @@ -140,14 +139,17 @@ static void create_pack_file(const struct object_array *have_obj, if (use_include_tag) argv_array_push(&pack_objects.args, "--include-tag"); if (filter_options.filter_spec) { + struct strbuf expanded_filter_spec = STRBUF_INIT; + expand_list_objects_filter_spec(&filter_options, + &expanded_filter_spec); if (pack_objects.use_shell) { struct strbuf buf = STRBUF_INIT; - sq_quote_buf(&buf, filter_options.filter_spec); + sq_quote_buf(&buf, expanded_filter_spec.buf); argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf); strbuf_release(&buf); } else { argv_array_pushf(&pack_objects.args, "--filter=%s", - filter_options.filter_spec); + expanded_filter_spec.buf); } } @@ -662,6 +664,9 @@ static void send_unshallow(const struct object_array *shallows, } } +static int check_ref(const char *refname_full, const struct object_id *oid, + int flag, void *cb_data); + static void deepen(int depth, int deepen_relative, struct object_array *shallows, struct object_array *want_obj) { @@ -676,6 +681,13 @@ static void deepen(int depth, int deepen_relative, struct object_array reachable_shallows = OBJECT_ARRAY_INIT; struct commit_list *result; + /* + * Checking for reachable shallows requires that our refs be + * marked with OUR_REF. + */ + head_ref_namespaced(check_ref, NULL); + for_each_namespaced_ref(check_ref, NULL); + get_reachable_list(shallows, &reachable_shallows); result = get_shallow_commits(&reachable_shallows, depth + 1, @@ -712,6 +724,7 @@ static void deepen_by_rev_list(int ac, const char **av, static int send_shallow_list(int depth, int deepen_rev_list, timestamp_t deepen_since, struct string_list *deepen_not, + int deepen_relative, struct object_array *shallows, struct object_array *want_obj) { @@ -834,6 +847,7 @@ static void receive_needs(struct object_array *want_obj) int has_non_tip = 0; timestamp_t deepen_since = 0; int deepen_rev_list = 0; + int deepen_relative = 0; shallow_nr = 0; for (;;) { @@ -925,7 +939,8 @@ static void receive_needs(struct object_array *want_obj) return; if (send_shallow_list(depth, deepen_rev_list, deepen_since, - &deepen_not, &shallows, want_obj)) + &deepen_not, deepen_relative, &shallows, + want_obj)) packet_flush(1); object_array_clear(&shallows); } @@ -1398,6 +1413,7 @@ static void send_shallow_info(struct upload_pack_data *data, if (!send_shallow_list(data->depth, data->deepen_rev_list, data->deepen_since, &data->deepen_not, + data->deepen_relative, &data->shallows, want_obj) && is_repository_shallow(the_repository)) deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows, @@ -104,7 +104,8 @@ void end_url_with_slash(struct strbuf *buf, const char *url) strbuf_complete(buf, '/'); } -void str_end_url_with_slash(const char *url, char **dest) { +void str_end_url_with_slash(const char *url, char **dest) +{ struct strbuf buf = STRBUF_INIT; end_url_with_slash(&buf, url); free(*dest); diff --git a/userdiff.c b/userdiff.c index 97007abe5b..3a78fbf504 100644 --- a/userdiff.c +++ b/userdiff.c @@ -265,7 +265,8 @@ int userdiff_config(const char *k, const char *v) return 0; } -struct userdiff_driver *userdiff_find_by_name(const char *name) { +struct userdiff_driver *userdiff_find_by_name(const char *name) +{ int len = strlen(name); return userdiff_find_by_namelen(name, len); } @@ -50,13 +50,13 @@ static int process_tree(struct walker *walker, struct tree *tree) continue; if (S_ISDIR(entry.mode)) { struct tree *tree = lookup_tree(the_repository, - entry.oid); + &entry.oid); if (tree) obj = &tree->object; } else { struct blob *blob = lookup_blob(the_repository, - entry.oid); + &entry.oid); if (blob) obj = &blob->object; } |