diff options
139 files changed, 3025 insertions, 1112 deletions
diff --git a/.travis.yml b/.travis.yml index 9c63c8c3f6..591cc57b80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,27 @@ env: matrix: include: + - env: Linux32 + os: linux + services: + - docker + before_install: + - docker pull daald/ubuntu32:xenial + before_script: + script: + - > + docker run + --interactive + --env DEFAULT_TEST_TARGET + --env GIT_PROVE_OPTS + --env GIT_TEST_OPTS + --env GIT_TEST_CLONE_2GB + --volume "${PWD}:/usr/src/git" + daald/ubuntu32:xenial + /usr/src/git/ci/run-linux32-build.sh $(id -u $USER) + # Use the following command to debug the docker build locally: + # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial + # root@container:/# /usr/src/git/ci/run-linux32-build.sh - env: Documentation os: linux compiler: clang diff --git a/Documentation/RelNotes/2.12.1.txt b/Documentation/RelNotes/2.12.1.txt new file mode 100644 index 0000000000..a74f7db747 --- /dev/null +++ b/Documentation/RelNotes/2.12.1.txt @@ -0,0 +1,41 @@ +Git v2.12.1 Release Notes +========================= + +Fixes since v2.12 +----------------- + + * Reduce authentication round-trip over HTTP when the server supports + just a single authentication method. This also improves the + behaviour when Git is misconfigured to enable http.emptyAuth + against a server that does not authenticate without a username + (i.e. not using Kerberos etc., which makes http.emptyAuth + pointless). + + * Windows port wants to use OpenSSL's implementation of SHA-1 + routines, so let them. + + * Add 32-bit Linux variant to the set of platforms to be tested with + Travis CI. + + * When a redirected http transport gets an error during the + redirected request, we ignored the error we got from the server, + and ended up giving a not-so-useful error message. + + * The patch subcommand of "git add -i" was meant to have paths + selection prompt just like other subcommand, unlike "git add -p" + directly jumps to hunk selection. Recently, this was broken and + "add -i" lost the paths selection dialog, but it now has been + fixed. + + * Git v2.12 was shipped with an embarrassing breakage where various + operations that verify paths given from the user stopped dying when + seeing an issue, and instead later triggering segfault. + + * The code to parse "git log -L..." command line was buggy when there + are many ranges specified with -L; overrun of the allocated buffer + has been fixed. + + * The command-line parsing of "git log -L" copied internal data + structures using incorrect size on ILP32 systems. + +Also contains various documentation updates and code clean-ups. diff --git a/Documentation/RelNotes/2.13.0.txt b/Documentation/RelNotes/2.13.0.txt index b0b4c36f97..5c2348db6c 100644 --- a/Documentation/RelNotes/2.13.0.txt +++ b/Documentation/RelNotes/2.13.0.txt @@ -73,6 +73,23 @@ UI, Workflows & Features but caused renaming of the current branch to something else not to be logged in a useful way. + * "Cc:" on the trailer part does not have to conform to RFC strictly, + unlike in the e-mail header. "git send-email" has been updated to + ignore anything after '>' when picking addresses, to allow non-address + cruft like " # stable 4.4" after the address. + (merge 9d3343961b jh/send-email-one-cc later to maint). + + * When "git submodule init" decides that the submodule in the working + tree is its upstream, it now gives a warning as it is not a very + common setup. + (merge d1b3b81aab sb/submodule-init-url-selection later to maint). + + * "git stash save" takes a pathspec so that the local changes can be + stashed away only partially. + (merge 9e140909f6 tg/stash-push later to maint). + + * Documentation for "git ls-files" did not refer to core.quotePath. + Performance, Internal Implementation, Development Support etc. @@ -96,6 +113,47 @@ Performance, Internal Implementation, Development Support etc. * Some warning() messages from "git clean" were updated to show the errno from failed system calls. + * The "parse_config_key()" API function has been cleaned up. + (merge ad8c7cdadd jk/parse-config-key-cleanup later to maint). + + * A test that creates a confusing branch whose name is HEAD has been + corrected not to do so. + (merge f0252ca23c jk/t6300-cleanup later to maint). + + * The code that parses header fields in the commit object has been + updated for (micro)performance and code hygiene. + (merge b072504ce1 rs/commit-parsing-optim later to maint). + + * An helper function to make it easier to append the result from + real_path() to a strbuf has been added. + (merge 33ad9ddd0b rs/strbuf-add-real-path later to maint). + + * Reduce authentication round-trip over HTTP when the server supports + just a single authentication method. This also improves the + behaviour when Git is misconfigured to enable http.emptyAuth + against a server that does not authenticate without a username + (i.e. not using Kerberos etc., which makes http.emptyAuth + pointless). + (merge 40a18fc77c jk/http-auth later to maint). + + * Windows port wants to use OpenSSL's implementation of SHA-1 + routines, so let them. + (merge 2cfc70f0de jh/mingw-openssl-sha1 later to maint). + + * The t/perf performance test suite was not prepared to test not so + old versions of Git, but now it covers versions of Git that are not + so ancient. + (merge 28e1fb5466 jt/perf-updates later to maint). + + * Add 32-bit Linux variant to the set of platforms to be tested with + Travis CI. + (merge 88dedd5e72 js/travis-32bit-linux later to maint). + + * "git branch --list" takes the "--abbrev" and "--no-abbrev" options + to control the output of the object name in its "-v"(erbose) + output, but a recent update started ignoring them; fix it before + the breakage reaches to any released version. + Also contains various documentation updates and code clean-ups. @@ -147,5 +205,93 @@ notes for details). default) by ignoring a stale "gc.log" file that is too old. (merge a831c06a2b dt/gc-ignore-old-gc-logs later to maint). + * The code to parse "git -c VAR=VAL cmd" and set configuration + variable for the duration of cmd had two small bugs, which have + been fixed. + (merge 1274a155af jc/config-case-cmdline-take-2 later to maint). + + * user.email that consists of only cruft chars should consistently + error out, but didn't. + (merge 94425552f3 jk/ident-empty later to maint). + + * "git upload-pack", which is a counter-part of "git fetch", did not + report a request for a ref that was not advertised as invalid. + This is generally not a problem (because "git fetch" will stop + before making such a request), but is the right thing to do. + (merge bdb31eada7 jt/upload-pack-error-report later to maint). + + * A leak in a codepath to read from a packed object in (rare) cases + has been plugged. + (merge 886ddf4777 rs/sha1-file-plug-fallback-base-leak later to maint). + + * When a redirected http transport gets an error during the + redirected request, we ignored the error we got from the server, + and ended up giving a not-so-useful error message. + (merge 8e27391a5f jt/http-base-url-update-upon-redirect later to maint). + + * The patch subcommand of "git add -i" was meant to have paths + selection prompt just like other subcommand, unlike "git add -p" + directly jumps to hunk selection. Recently, this was broken and + "add -i" lost the paths selection dialog, but it now has been + fixed. + (merge c852bd54bd jk/add-i-patch-do-prompt later to maint). + + * Git v2.12 was shipped with an embarrassing breakage where various + operations that verify paths given from the user stopped dying when + seeing an issue, and instead later triggering segfault. + (merge ce83eadd9a js/realpath-pathdup-fix later to maint). + + * There is no need for Python only to give a few messages to the + standard error stream, but we somehow did. + (merge b8686c661d ss/remote-bzr-hg-placeholder-wo-python later to maint). + + * The code to parse "git log -L..." command line was buggy when there + are many ranges specified with -L; overrun of the allocated buffer + has been fixed. + (merge aaae0bf787 ax/line-log-range-merge-fix later to maint). + + * The command-line parsing of "git log -L" copied internal data + structures using incorrect size on ILP32 systems. + (merge 07f546cda5 vn/line-log-memcpy-size-fix later to maint). + + * "git diff --quiet" relies on the size field in diff_filespec to be + correctly populated, but diff_populate_filespec() helper function + made an incorrect short-cut when asked only to populate the size + field for paths that need to go through convert_to_git() (e.g. CRLF + conversion). + (merge 12426e114b jc/diff-populate-filespec-size-only-fix later to maint). + + * A few tests were run conditionally under (rare) conditions where + they cannot be run (like running cvs tests under 'root' account). + (merge c6507484a2 ab/cond-skip-tests later to maint). + + * "git branch @" created refs/heads/@ as a branch, and in general the + code that handled @{-1} and @{upstream} was a bit too loose in + disambiguating. + (merge fd4692ff70 jk/interpret-branch-name later to maint). + + * "git fetch" that requests a commit by object name, when the other + side does not allow such an request, failed without much + explanation. + (merge d56583ded6 mm/fetch-show-error-message-on-unadvertised-object later to maint). + + * "git filter-branch --prune-empty" drops a single-parent commit that + becomes a no-op, but did not drop a root commit whose tree is empty. + (merge 32da7467eb dp/filter-branch-prune-empty later to maint). + + * Recent versions of Git treats http alternates (used in dumb http + transport) just like HTTP redirects and requires the client to + enable following it, due to security concerns. But we forgot to + give a warning when we decide not to honor the alternates. + (merge 5cae73d5d2 ew/http-alternates-as-redirects-warning later to maint). + + * "git push" had a handful of codepaths that could lead to a deadlock + when unexpected error happened, which has been fixed. + (merge d1a13d3fcb jk/push-deadlock-regression-fix later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 2cfa83574c mm/two-more-xstrfmt later to maint). + (merge b803ae4427 ps/docs-diffcore later to maint). + (merge bcd886d897 ew/markdown-url-in-readme later to maint). + (merge b2d593a779 rj/remove-unused-mktemp later to maint). + (merge 3255e512a8 jk/ewah-use-right-type-in-sizeof later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index 47603f5484..eccc012672 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -334,6 +334,10 @@ core.trustctime:: crawlers and some backup systems). See linkgit:git-update-index[1]. True by default. +core.splitIndex:: + If true, the split-index feature of the index will be used. + See linkgit:git-update-index[1]. False by default. + core.untrackedCache:: Determines what to do about the untracked cache feature of the index. It will be kept, if this variable is unset or set to @@ -350,16 +354,19 @@ core.checkStat:: all fields, including the sub-second part of mtime and ctime. core.quotePath:: - The commands that output paths (e.g. 'ls-files', - 'diff'), when not given the `-z` option, will quote - "unusual" characters in the pathname by enclosing the - pathname in a double-quote pair and with backslashes the - same way strings in C source code are quoted. If this - variable is set to false, the bytes higher than 0x80 are - not quoted but output as verbatim. Note that double - quote, backslash and control characters are always - quoted without `-z` regardless of the setting of this - variable. + Commands that output paths (e.g. 'ls-files', 'diff'), will + quote "unusual" characters in the pathname by enclosing the + pathname in double-quotes and escaping those characters with + backslashes in the same way C escapes control characters (e.g. + `\t` for TAB, `\n` for LF, `\\` for backslash) or bytes with + values larger than 0x80 (e.g. octal `\302\265` for "micro" in + UTF-8). If this variable is set to false, bytes higher than + 0x80 are not considered "unusual" any more. Double-quotes, + backslash and control characters are always escaped regardless + of the setting of this variable. A simple space character is + not considered "unusual". Many commands can output pathnames + completely verbatim using the `-z` option. The default value + is true. core.eol:: Sets the line ending type to use in the working directory for @@ -2847,6 +2854,31 @@ showbranch.default:: The default set of branches for linkgit:git-show-branch[1]. See linkgit:git-show-branch[1]. +splitIndex.maxPercentChange:: + When the split index feature is used, this specifies the + percent of entries the split index can contain compared to the + total number of entries in both the split index and the shared + index before a new shared index is written. + The value should be between 0 and 100. If the value is 0 then + a new shared index is always written, if it is 100 a new + shared index is never written. + By default the value is 20, so a new shared index is written + if the number of entries in the split index would be greater + than 20 percent of the total number of entries. + See linkgit:git-update-index[1]. + +splitIndex.sharedIndexExpire:: + When the split index feature is used, shared index files that + were not modified since the time this variable specifies will + be removed when a new shared index file is created. The value + "now" expires all entries immediately, and "never" suppresses + expiration altogether. + The default value is "2.weeks.ago". + Note that a shared index file is considered modified (for the + purpose of expiration) each time a new split-index file is + either created based on it or read from it. + See linkgit:git-update-index[1]. + status.relativePaths:: By default, linkgit:git-status[1] shows paths relative to the current directory. Setting this variable to `false` shows paths diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt index cf5262622f..706916c94c 100644 --- a/Documentation/diff-format.txt +++ b/Documentation/diff-format.txt @@ -78,9 +78,10 @@ Example: :100644 100644 5be4a4...... 000000...... M file.c ------------------------------------------------ -When `-z` option is not used, TAB, LF, and backslash characters -in pathnames are represented as `\t`, `\n`, and `\\`, -respectively. +Without the `-z` option, pathnames with "unusual" characters are +quoted as explained for the configuration variable `core.quotePath` +(see linkgit:git-config[1]). Using `-z` the filename is output +verbatim and the line is terminated by a NUL byte. diff format for merges ---------------------- diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt index d2a7ff56e8..231105cff4 100644 --- a/Documentation/diff-generate-patch.txt +++ b/Documentation/diff-generate-patch.txt @@ -53,10 +53,9 @@ The index line includes the SHA-1 checksum before and after the change. The <mode> is included if the file mode does not change; otherwise, separate lines indicate the old and the new mode. -3. TAB, LF, double quote and backslash characters in pathnames - are represented as `\t`, `\n`, `\"` and `\\`, respectively. - If there is need for such substitution then the whole - pathname is put in double quotes. +3. Pathnames with "unusual" characters are quoted as explained for + the configuration variable `core.quotePath` (see + linkgit:git-config[1]). 4. All the `file1` files in the output refer to files before the commit, and all the `file2` files refer to files after the commit. diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index d91ddbd5fe..89cc0f48de 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -192,10 +192,9 @@ ifndef::git-log[] given, do not munge pathnames and use NULs as output field terminators. endif::git-log[] + -Without this option, each pathname output will have TAB, LF, double quotes, -and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`, -respectively, and the pathname will be enclosed in double quotes if -any of those replacements occurred. +Without this option, pathnames with "unusual" characters are quoted as +explained for the configuration variable `core.quotePath` (see +linkgit:git-config[1]). --name-only:: Show only names of changed files. diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 8ddb207409..631cbd840a 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -108,10 +108,9 @@ the information is read from the current index instead. When `--numstat` has been given, do not munge pathnames, but use a NUL-terminated machine-readable format. + -Without this option, each pathname output will have TAB, LF, double quotes, -and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`, -respectively, and the pathname will be enclosed in double quotes if -any of those replacements occurred. +Without this option, pathnames with "unusual" characters are quoted as +explained for the configuration variable `core.quotePath` (see +linkgit:git-config[1]). -p<n>:: Remove <n> leading slashes from traditional diff paths. The diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 4f8f20a360..25dcdcc289 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -117,9 +117,12 @@ OPTIONS -z:: --null:: - When showing `short` or `porcelain` status output, terminate - entries in the status output with NUL, instead of LF. If no - format is given, implies the `--porcelain` output format. + When showing `short` or `porcelain` status output, print the + filename verbatim and terminate the entries with NUL, instead of LF. + If no format is given, implies the `--porcelain` output format. + Without the `-z` option, filenames with "unusual" characters are + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). -F <file>:: --file=<file>:: diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 0a09698c03..6e4bb02204 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -167,14 +167,12 @@ to other tags will be rewritten to point to the underlying commit. project root. Implies <<Remap_to_ancestor>>. --prune-empty:: - Some kind of filters will generate empty commits, that left the tree - untouched. This switch allow git-filter-branch to ignore such - commits. Though, this switch only applies for commits that have one - and only one parent, it will hence keep merges points. Also, this - option is not compatible with the use of `--commit-filter`. Though you - just need to use the function 'git_commit_non_empty_tree "$@"' instead - of the `git commit-tree "$@"` idiom in your commit filter to make that - happen. + Some filters will generate empty commits that leave the tree untouched. + This option instructs git-filter-branch to remove such commits if they + have exactly one or zero non-pruned parents; merge commits will + therefore remain intact. This option cannot be used together with + `--commit-filter`, though the same effect can be achieved by using the + provided `git_commit_non_empty_tree` function in a commit filter. --original <namespace>:: Use this option to set the namespace where the original commits diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 446209e206..1cab703f73 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -77,7 +77,8 @@ OPTIONS succeed. -z:: - \0 line termination on output. + \0 line termination on output and do not quote filenames. + See OUTPUT below for more information. -x <pattern>:: --exclude=<pattern>:: @@ -196,9 +197,10 @@ the index records up to three such pairs; one from tree O in stage the user (or the porcelain) to see what should eventually be recorded at the path. (see linkgit:git-read-tree[1] for more information on state) -When `-z` option is not used, TAB, LF, and backslash characters -in pathnames are represented as `\t`, `\n`, and `\\`, -respectively. +Without the `-z` option, pathnames with "unusual" characters are +quoted as explained for the configuration variable `core.quotePath` +(see linkgit:git-config[1]). Using `-z` the filename is output +verbatim and the line is terminated by a NUL byte. Exclude Patterns diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt index dbc91f98ff..9dee7bef35 100644 --- a/Documentation/git-ls-tree.txt +++ b/Documentation/git-ls-tree.txt @@ -53,7 +53,8 @@ OPTIONS Show object size of blob (file) entries. -z:: - \0 line termination on output. + \0 line termination on output and do not quote filenames. + See OUTPUT FORMAT below for more information. --name-only:: --name-status:: @@ -82,8 +83,6 @@ Output Format ------------- <mode> SP <type> SP <object> TAB <file> -Unless the `-z` option is used, TAB, LF, and backslash characters -in pathnames are represented as `\t`, `\n`, and `\\`, respectively. This output format is compatible with what `--index-info --stdin` of 'git update-index' expects. @@ -95,6 +94,11 @@ Object size identified by <object> is given in bytes, and right-justified with minimum width of 7 characters. Object size is given only for blobs (file) entries; for other entries `-` character is used in place of size. +Without the `-z` option, pathnames with "unusual" characters are +quoted as explained for the configuration variable `core.quotePath` +(see linkgit:git-config[1]). Using `-z` the filename is output +verbatim and the line is terminated by a NUL byte. + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 91c02b8c85..c40c470448 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -261,6 +261,12 @@ print a message to stderr and exit with nonzero status. --show-toplevel:: Show the absolute path of the top-level directory. +--show-superproject-working-tree + Show the absolute path of the root of the superproject's + working tree (if exists) that uses the current repository as + its submodule. Outputs nothing if the current repository is + not used as a submodule by any project. + --shared-index-path:: Show the path to the shared index file in split index mode, or empty if not in split-index mode. diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 2e9e344cd7..70191d06b6 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -13,8 +13,11 @@ SYNOPSIS 'git stash' drop [-q|--quiet] [<stash>] 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>] 'git stash' branch <branchname> [<stash>] -'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] - [-u|--include-untracked] [-a|--all] [<message>]] +'git stash' save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [<message>] +'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [-m|--message <message>]] + [--] [<pathspec>...]] 'git stash' clear 'git stash' create [<message>] 'git stash' store [-m|--message <message>] [-q|--quiet] <commit> @@ -46,14 +49,24 @@ OPTIONS ------- save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]:: +push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]:: Save your local modifications to a new 'stash' and roll them back to HEAD (in the working tree and in the index). The <message> part is optional and gives - the description along with the stashed state. For quickly making - a snapshot, you can omit _both_ "save" and <message>, but giving - only <message> does not trigger this action to prevent a misspelled - subcommand from making an unwanted stash. + the description along with the stashed state. ++ +For quickly making a snapshot, you can omit "push". In this mode, +non-option arguments are not allowed to prevent a misspelled +subcommand from making an unwanted stash. The two exceptions to this +are `stash -p` which acts as alias for `stash push -p` and pathspecs, +which are allowed after a double hyphen `--` for disambiguation. ++ +When pathspec is given to 'git stash push', the new stash records the +modified states only for the files that match the pathspec. The index +entries and working tree files are then rolled back to the state in +HEAD only for these files, too, leaving files that do not match the +pathspec intact. + If the `--keep-index` option is used, all changes already added to the index are left intact. diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 725065ef2d..ba873657cf 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -322,10 +322,9 @@ When the `-z` option is given, pathnames are printed as is and without any quoting and lines are terminated with a NUL (ASCII 0x00) byte. -Otherwise, all pathnames will be "C-quoted" if they contain any tab, -linefeed, double quote, or backslash characters. In C-quoting, these -characters will be replaced with the corresponding C-style escape -sequences and the resulting pathname will be double quoted. +Without the `-z` option, pathnames with "unusual" characters are +quoted as explained for the configuration variable `core.quotePath` +(see linkgit:git-config[1]). CONFIGURATION diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 8acc72ebb8..e05d0cddef 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -73,13 +73,17 @@ configuration entries unless `--name` is used to specify a logical name. + <repository> is the URL of the new submodule's origin repository. This may be either an absolute URL, or (if it begins with ./ -or ../), the location relative to the superproject's origin +or ../), the location relative to the superproject's default remote repository (Please note that to specify a repository 'foo.git' which is located right next to a superproject 'bar.git', you'll have to use '../foo.git' instead of './foo.git' - as one might expect when following the rules for relative URLs - because the evaluation of relative URLs in Git is identical to that of relative directories). -If the superproject doesn't have an origin configured ++ +The default remote is the remote of the remote tracking branch +of the current branch. If no such remote tracking branch exists or +the HEAD is detached, "origin" is assumed to be the default remote. +If the superproject doesn't have a default remote configured the superproject is its own authoritative upstream and the current working directory is used instead. + @@ -118,18 +122,24 @@ too (and can also report changes to a submodule's work tree). init [--] [<path>...]:: Initialize the submodules recorded in the index (which were - added and committed elsewhere) by copying submodule - names and urls from .gitmodules to .git/config. - Optional <path> arguments limit which submodules will be initialized. - It will also copy the value of `submodule.$name.update` into - .git/config. - The key used in .git/config is `submodule.$name.url`. - This command does not alter existing information in .git/config. - You can then customize the submodule clone URLs in .git/config - for your local setup and proceed to `git submodule update`; - you can also just use `git submodule update --init` without - the explicit 'init' step if you do not intend to customize - any submodule locations. + added and committed elsewhere) by setting `submodule.$name.url` + in .git/config. It uses the same setting from .gitmodules as + a template. If the URL is relative, it will be resolved using + the default remote. If there is no default remote, the current + repository will be assumed to be upstream. ++ +Optional <path> arguments limit which submodules will be initialized. +If no path is specified, all submodules are initialized. ++ +When present, it will also copy the value of `submodule.$name.update`. +This command does not alter existing information in .git/config. +You can then customize the submodule clone URLs in .git/config +for your local setup and proceed to `git submodule update`; +you can also just use `git submodule update --init` without +the explicit 'init' step if you do not intend to customize +any submodule locations. ++ +See the add subcommand for the defintion of default remote. deinit [-f|--force] (--all|[--] <path>...):: Unregister the given submodules, i.e. remove the whole diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 7386c93162..1579abf3c3 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -163,14 +163,16 @@ may not support it yet. --split-index:: --no-split-index:: - Enable or disable split index mode. If enabled, the index is - split into two files, $GIT_DIR/index and $GIT_DIR/sharedindex.<SHA-1>. - Changes are accumulated in $GIT_DIR/index while the shared - index file contains all index entries stays unchanged. If - split-index mode is already enabled and `--split-index` is - given again, all changes in $GIT_DIR/index are pushed back to - the shared index file. This mode is designed for very large - indexes that take a significant amount of time to read or write. + Enable or disable split index mode. If split-index mode is + already enabled and `--split-index` is given again, all + changes in $GIT_DIR/index are pushed back to the shared index + file. ++ +These options take effect whatever the value of the `core.splitIndex` +configuration variable (see linkgit:git-config[1]). But a warning is +emitted when the change goes against the configured value, as the +configured value will take effect next time the index is read and this +will remove the intended effect of the option. --untracked-cache:: --no-untracked-cache:: @@ -388,6 +390,31 @@ Although this bit looks similar to assume-unchanged bit, its goal is different from assume-unchanged bit's. Skip-worktree also takes precedence over assume-unchanged bit when both are set. +Split index +----------- + +This mode is designed for repositories with very large indexes, and +aims at reducing the time it takes to repeatedly write these indexes. + +In this mode, the index is split into two files, $GIT_DIR/index and +$GIT_DIR/sharedindex.<SHA-1>. Changes are accumulated in +$GIT_DIR/index, the split index, while the shared index file contains +all index entries and stays unchanged. + +All changes in the split index are pushed back to the shared index +file when the number of entries in the split index reaches a level +specified by the splitIndex.maxPercentChange config variable (see +linkgit:git-config[1]). + +Each time a new shared index file is created, the old shared index +files are deleted if their modification time is older than what is +specified by the splitIndex.sharedIndexExpire config variable (see +linkgit:git-config[1]). + +To avoid deleting a shared index file that is still used, its +modification time is updated to the current time everytime a new split +index based on the shared index file is either created or read from. + Untracked cache --------------- diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt index 46bc6d077c..c0a60f3158 100644 --- a/Documentation/gitdiffcore.txt +++ b/Documentation/gitdiffcore.txt @@ -84,8 +84,8 @@ format sections of the manual for 'git diff-{asterisk}' commands) or diff-patch format. -diffcore-break: For Splitting Up "Complete Rewrites" ----------------------------------------------------- +diffcore-break: For Splitting Up Complete Rewrites +-------------------------------------------------- The second transformation in the chain is diffcore-break, and is controlled by the -B option to the 'git diff-{asterisk}' commands. This is @@ -119,7 +119,7 @@ the original is used), and can be customized by giving a number after "-B" option (e.g. "-B75" to tell it to use 75%). -diffcore-rename: For Detection Renames and Copies +diffcore-rename: For Detecting Renames and Copies ------------------------------------------------- This transformation is used to detect renames and copies, and is @@ -177,8 +177,8 @@ the expense of making it slower. Without `--find-copies-harder`, copied happened to have been modified in the same changeset. -diffcore-merge-broken: For Putting "Complete Rewrites" Back Together --------------------------------------------------------------------- +diffcore-merge-broken: For Putting Complete Rewrites Back Together +------------------------------------------------------------------ This transformation is used to merge filepairs broken by diffcore-break, and not transformed into rename/copy by @@ -102,8 +102,6 @@ all:: # # Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash. # -# Define NO_MKSTEMPS if you don't have mkstemps in the C library. -# # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd # in the C library. # @@ -1282,9 +1280,6 @@ ifdef MKDIR_WO_TRAILING_SLASH COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH COMPAT_OBJS += compat/mkdir.o endif -ifdef NO_MKSTEMPS - COMPAT_CFLAGS += -DNO_MKSTEMPS -endif ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV COMPAT_OBJS += compat/unsetenv.o @@ -2256,6 +2251,9 @@ endif ifdef GIT_PERF_MAKE_OPTS @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+ endif +ifdef GIT_INTEROP_MAKE_OPTS + @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+ +endif ifdef TEST_GIT_INDEX_VERSION @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+ endif @@ -12,7 +12,7 @@ Torvalds with help of a group of hackers around the net. Please read the file [INSTALL][] for installation instructions. -Many Git online resources are accessible from http://git-scm.com/ +Many Git online resources are accessible from <https://git-scm.com/> including full documentation and Git related tools. See [Documentation/gittutorial.txt][] to get started, then see @@ -33,8 +33,8 @@ requests, comments and patches to git@vger.kernel.org (read [Documentation/SubmittingPatches][] for instructions on patch submission). To subscribe to the list, send an email with just "subscribe git" in the body to majordomo@vger.kernel.org. The mailing list archives are -available at https://public-inbox.org/git, -http://marc.info/?l=git and other archival sites. +available at <https://public-inbox.org/git/>, +<http://marc.info/?l=git> and other archival sites. The maintainer frequently sends the "What's cooking" reports that list the current status of various development topics to the mailing @@ -214,12 +214,12 @@ const char *real_path_if_valid(const char *path) return strbuf_realpath(&realpath, path, 0); } -char *real_pathdup(const char *path) +char *real_pathdup(const char *path, int die_on_error) { struct strbuf realpath = STRBUF_INIT; char *retval = NULL; - if (strbuf_realpath(&realpath, path, 0)) + if (strbuf_realpath(&realpath, path, die_on_error)) retval = strbuf_detach(&realpath, NULL); strbuf_release(&realpath); diff --git a/builtin/blame.c b/builtin/blame.c index cffc626540..f7aa95f4ba 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -658,8 +658,11 @@ static struct origin *find_rename(struct scoreboard *sb, /* * Append a new blame entry to a given output queue. */ -static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e) +static void add_blame_entry(struct blame_entry ***queue, + const struct blame_entry *src) { + struct blame_entry *e = xmalloc(sizeof(*e)); + memcpy(e, src, sizeof(*e)); origin_incref(e->suspect); e->next = **queue; @@ -760,21 +763,15 @@ static void split_blame(struct blame_entry ***blamed, struct blame_entry *split, struct blame_entry *e) { - struct blame_entry *new_entry; - if (split[0].suspect && split[2].suspect) { /* The first part (reuse storage for the existing entry e) */ dup_entry(unblamed, e, &split[0]); /* The last part -- me */ - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[2]), sizeof(struct blame_entry)); - add_blame_entry(unblamed, new_entry); + add_blame_entry(unblamed, &split[2]); /* ... and the middle part -- parent */ - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[1]), sizeof(struct blame_entry)); - add_blame_entry(blamed, new_entry); + add_blame_entry(blamed, &split[1]); } else if (!split[0].suspect && !split[2].suspect) /* @@ -785,18 +782,12 @@ static void split_blame(struct blame_entry ***blamed, else if (split[0].suspect) { /* me and then parent */ dup_entry(unblamed, e, &split[0]); - - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[1]), sizeof(struct blame_entry)); - add_blame_entry(blamed, new_entry); + add_blame_entry(blamed, &split[1]); } else { /* parent and then me */ dup_entry(blamed, e, &split[1]); - - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[2]), sizeof(struct blame_entry)); - add_blame_entry(unblamed, new_entry); + add_blame_entry(unblamed, &split[2]); } } diff --git a/builtin/branch.c b/builtin/branch.c index 94f7de7fa5..52688f2e1b 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -33,7 +33,7 @@ static const char * const builtin_branch_usage[] = { }; static const char *head; -static unsigned char head_sha1[20]; +static struct object_id head_oid; static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { @@ -118,13 +118,13 @@ static int branch_merged(int kind, const char *name, if (kind == FILTER_REFS_BRANCHES) { struct branch *branch = branch_get(name); const char *upstream = branch_get_upstream(branch, NULL); - unsigned char sha1[20]; + struct object_id oid; if (upstream && (reference_name = reference_name_to_free = resolve_refdup(upstream, RESOLVE_REF_READING, - sha1, NULL)) != NULL) - reference_rev = lookup_commit_reference(sha1); + oid.hash, NULL)) != NULL) + reference_rev = lookup_commit_reference(oid.hash); } if (!reference_rev) reference_rev = head_rev; @@ -154,10 +154,10 @@ static int branch_merged(int kind, const char *name, } static int check_branch_commit(const char *branchname, const char *refname, - const unsigned char *sha1, struct commit *head_rev, + const struct object_id *oid, struct commit *head_rev, int kinds, int force) { - struct commit *rev = lookup_commit_reference(sha1); + struct commit *rev = lookup_commit_reference(oid->hash); if (!rev) { error(_("Couldn't look up commit object for '%s'"), refname); return -1; @@ -184,31 +184,34 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, int quiet) { struct commit *head_rev = NULL; - unsigned char sha1[20]; + struct object_id oid; char *name = NULL; const char *fmt; int i; int ret = 0; int remote_branch = 0; struct strbuf bname = STRBUF_INIT; + unsigned allowed_interpret; switch (kinds) { case FILTER_REFS_REMOTES: fmt = "refs/remotes/%s"; /* For subsequent UI messages */ remote_branch = 1; + allowed_interpret = INTERPRET_BRANCH_REMOTE; force = 1; break; case FILTER_REFS_BRANCHES: fmt = "refs/heads/%s"; + allowed_interpret = INTERPRET_BRANCH_LOCAL; break; default: die(_("cannot use -a with -d")); } if (!force) { - head_rev = lookup_commit_reference(head_sha1); + head_rev = lookup_commit_reference(head_oid.hash); if (!head_rev) die(_("Couldn't look up commit object for HEAD")); } @@ -216,7 +219,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, char *target = NULL; int flags = 0; - strbuf_branchname(&bname, argv[i]); + strbuf_branchname(&bname, argv[i], allowed_interpret); free(name); name = mkpathdup(fmt, bname.buf); @@ -236,7 +239,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE | RESOLVE_REF_ALLOW_BAD_NAME, - sha1, &flags); + oid.hash, &flags); if (!target) { error(remote_branch ? _("remote-tracking branch '%s' not found.") @@ -246,13 +249,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, } if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) && - check_branch_commit(bname.buf, name, sha1, head_rev, kinds, + check_branch_commit(bname.buf, name, &oid, head_rev, kinds, force)) { ret = 1; goto next; } - if (delete_ref(NULL, name, is_null_sha1(sha1) ? NULL : sha1, + if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash, REF_NODEREF)) { error(remote_branch ? _("Error deleting remote-tracking branch '%s'") @@ -268,7 +271,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, bname.buf, (flags & REF_ISBROKEN) ? "broken" : (flags & REF_ISSYMREF) ? target - : find_unique_abbrev(sha1, DEFAULT_ABBREV)); + : find_unique_abbrev(oid.hash, DEFAULT_ABBREV)); } delete_branch_config(bname.buf); @@ -335,9 +338,18 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r branch_get_color(BRANCH_COLOR_CURRENT)); if (filter->verbose) { + struct strbuf obname = STRBUF_INIT; + + if (filter->abbrev < 0) + strbuf_addf(&obname, "%%(objectname:short)"); + else if (!filter->abbrev) + strbuf_addf(&obname, "%%(objectname)"); + else + strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev); + strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth); strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET)); - strbuf_addf(&local, " %%(objectname:short=7) "); + strbuf_addf(&local, " %s ", obname.buf); if (filter->verbose > 1) strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" @@ -346,10 +358,12 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r else strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); - strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)" - "%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)", + strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s" + "%%(if)%%(symref)%%(then) -> %%(symref:short)" + "%%(else) %s %%(contents:subject)%%(end)", branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix), - branch_get_color(BRANCH_COLOR_RESET)); + branch_get_color(BRANCH_COLOR_RESET), obname.buf); + strbuf_release(&obname); } else { strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)", branch_get_color(BRANCH_COLOR_RESET)); @@ -590,7 +604,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) track = git_branch_track; - head = resolve_refdup("HEAD", 0, head_sha1, NULL); + head = resolve_refdup("HEAD", 0, head_oid.hash, NULL); if (!head) die(_("Failed to resolve HEAD as a valid ref.")); if (!strcmp(head, "HEAD")) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 30383e9eb4..8b85cb8cf0 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -409,20 +409,20 @@ static int batch_object_cb(const unsigned char sha1[20], void *vdata) return 0; } -static int batch_loose_object(const unsigned char *sha1, +static int batch_loose_object(const struct object_id *oid, const char *path, void *data) { - sha1_array_append(data, sha1); + sha1_array_append(data, oid->hash); return 0; } -static int batch_packed_object(const unsigned char *sha1, +static int batch_packed_object(const struct object_id *oid, struct packed_git *pack, uint32_t pos, void *data) { - sha1_array_append(data, sha1); + sha1_array_append(data, oid->hash); return 0; } diff --git a/builtin/checkout.c b/builtin/checkout.c index f174f50303..81f07c3ef2 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -452,7 +452,7 @@ static void setup_branch_path(struct branch_info *branch) { struct strbuf buf = STRBUF_INIT; - strbuf_branchname(&buf, branch->name); + strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL); if (strcmp(buf.buf, branch->name)) branch->name = xstrdup(buf.buf); strbuf_splice(&buf, 0, 0, "refs/heads/", 11); diff --git a/builtin/clone.c b/builtin/clone.c index 3f63edbbf9..b4c929bb8a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -681,7 +681,7 @@ static void update_head(const struct ref *our, const struct ref *remote, static int checkout(int submodule_progress) { - unsigned char sha1[20]; + struct object_id oid; char *head; struct lock_file *lock_file; struct unpack_trees_options opts; @@ -692,7 +692,7 @@ static int checkout(int submodule_progress) if (option_no_checkout) return 0; - head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL); + head = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL); if (!head) { warning(_("remote HEAD refers to nonexistent ref, " "unable to checkout.\n")); @@ -700,7 +700,7 @@ static int checkout(int submodule_progress) } if (!strcmp(head, "HEAD")) { if (advice_detached_head) - detach_advice(sha1_to_hex(sha1)); + detach_advice(oid_to_hex(&oid)); } else { if (!starts_with(head, "refs/heads/")) die(_("HEAD not found below refs/heads!")); @@ -721,7 +721,7 @@ static int checkout(int submodule_progress) opts.src_index = &the_index; opts.dst_index = &the_index; - tree = parse_tree_indirect(sha1); + tree = parse_tree_indirect(oid.hash); parse_tree(tree); init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts) < 0) @@ -731,7 +731,7 @@ static int checkout(int submodule_progress) die(_("unable to write new index file")); err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1), - sha1_to_hex(sha1), "1", NULL); + oid_to_hex(&oid), "1", NULL); if (!err && option_recursive) { struct argv_array args = ARGV_ARRAY_INIT; diff --git a/builtin/commit.c b/builtin/commit.c index 2de5f6cc64..4e288bc513 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -496,7 +496,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, struct wt_status *s) { - unsigned char sha1[20]; + struct object_id oid; if (s->relative_paths) s->prefix = prefix; @@ -509,9 +509,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int s->index_file = index_file; s->fp = fp; s->nowarn = nowarn; - s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; + s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0; if (!s->is_initial) - hashcpy(s->sha1_commit, sha1); + hashcpy(s->sha1_commit, oid.hash); s->status_format = status_format; s->ignore_submodule_arg = ignore_submodule_arg; @@ -885,7 +885,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, commitable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; } else { - unsigned char sha1[20]; + struct object_id oid; const char *parent = "HEAD"; if (!active_nr && read_cache() < 0) @@ -894,7 +894,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (amend) parent = "HEAD^1"; - if (get_sha1(parent, sha1)) { + if (get_sha1(parent, oid.hash)) { int i, ita_nr = 0; for (i = 0; i < active_nr; i++) @@ -1332,7 +1332,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) { static struct wt_status s; int fd; - unsigned char sha1[20]; + struct object_id oid; static struct option builtin_status_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT_SET_INT('s', "short", &status_format, @@ -1382,9 +1382,9 @@ int cmd_status(int argc, const char **argv, const char *prefix) fd = hold_locked_index(&index_lock, 0); - s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; + s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0; if (!s.is_initial) - hashcpy(s.sha1_commit, sha1); + hashcpy(s.sha1_commit, oid.hash); s.ignore_submodule_arg = ignore_submodule_arg; s.status_format = status_format; @@ -1418,19 +1418,19 @@ static const char *implicit_ident_advice(void) } -static void print_summary(const char *prefix, const unsigned char *sha1, +static void print_summary(const char *prefix, const struct object_id *oid, int initial_commit) { struct rev_info rev; struct commit *commit; struct strbuf format = STRBUF_INIT; - unsigned char junk_sha1[20]; + struct object_id junk_oid; const char *head; struct pretty_print_context pctx = {0}; struct strbuf author_ident = STRBUF_INIT; struct strbuf committer_ident = STRBUF_INIT; - commit = lookup_commit(sha1); + commit = lookup_commit(oid->hash); if (!commit) die(_("couldn't look up newly created commit")); if (parse_commit(commit)) @@ -1477,7 +1477,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1, rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); - head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL); + head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL); if (!strcmp(head, "HEAD")) head = _("detached HEAD"); else @@ -1522,8 +1522,8 @@ static int git_commit_config(const char *k, const char *v, void *cb) return git_status_config(k, v, s); } -static int run_rewrite_hook(const unsigned char *oldsha1, - const unsigned char *newsha1) +static int run_rewrite_hook(const struct object_id *oldoid, + const struct object_id *newoid) { struct child_process proc = CHILD_PROCESS_INIT; const char *argv[3]; @@ -1544,7 +1544,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1, code = start_command(&proc); if (code) return code; - strbuf_addf(&sb, "%s %s\n", sha1_to_hex(oldsha1), sha1_to_hex(newsha1)); + strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid)); sigchain_push(SIGPIPE, SIG_IGN); write_in_full(proc.in, sb.buf, sb.len); close(proc.in); @@ -1636,7 +1636,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct strbuf author_ident = STRBUF_INIT; const char *index_file, *reflog_msg; char *nl; - unsigned char sha1[20]; + struct object_id oid; struct commit_list *parents = NULL; struct stat statbuf; struct commit *current_head = NULL; @@ -1651,10 +1651,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix) status_format = STATUS_FORMAT_NONE; /* Ignore status.short */ s.colopts = 0; - if (get_sha1("HEAD", sha1)) + if (get_sha1("HEAD", oid.hash)) current_head = NULL; else { - current_head = lookup_commit_or_die(sha1, "HEAD"); + current_head = lookup_commit_or_die(oid.hash, "HEAD"); if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } @@ -1759,7 +1759,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1, - parents, sha1, author_ident.buf, sign_commit, extra)) { + parents, oid.hash, author_ident.buf, sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); } @@ -1776,7 +1776,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, "HEAD", sha1, + ref_transaction_update(transaction, "HEAD", oid.hash, current_head ? current_head->object.oid.hash : null_sha1, 0, sb.buf, &err) || @@ -1805,13 +1805,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix) cfg = init_copy_notes_for_rewrite("amend"); if (cfg) { /* we are amending, so current_head is not NULL */ - copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1); + copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash); finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'"); } - run_rewrite_hook(current_head->object.oid.hash, sha1); + run_rewrite_hook(¤t_head->object.oid, &oid); } if (!quiet) - print_summary(prefix, sha1, !current_head); + print_summary(prefix, &oid, !current_head); strbuf_release(&err); return 0; diff --git a/builtin/count-objects.c b/builtin/count-objects.c index a04b4f2ef3..acb05940fc 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -53,7 +53,7 @@ static void loose_garbage(const char *path) report_garbage(PACKDIR_FILE_GARBAGE, path); } -static int count_loose(const unsigned char *sha1, const char *path, void *data) +static int count_loose(const struct object_id *oid, const char *path, void *data) { struct stat st; @@ -62,7 +62,7 @@ static int count_loose(const unsigned char *sha1, const char *path, void *data) else { loose_size += on_disk_bytes(st); loose++; - if (verbose && has_sha1_pack(sha1)) + if (verbose && has_sha1_pack(oid->hash)) packed_loose++; } return 0; diff --git a/builtin/describe.c b/builtin/describe.c index 6769446e1f..76c18059bf 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -40,11 +40,11 @@ static const char *diff_index_args[] = { struct commit_name { struct hashmap_entry entry; - unsigned char peeled[20]; + struct object_id peeled; struct tag *tag; unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */ unsigned name_checked:1; - unsigned char sha1[20]; + struct object_id oid; char *path; }; @@ -55,17 +55,17 @@ static const char *prio_names[] = { static int commit_name_cmp(const struct commit_name *cn1, const struct commit_name *cn2, const void *peeled) { - return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled); + return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled); } -static inline struct commit_name *find_commit_name(const unsigned char *peeled) +static inline struct commit_name *find_commit_name(const struct object_id *peeled) { - return hashmap_get_from_hash(&names, sha1hash(peeled), peeled); + return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash); } static int replace_name(struct commit_name *e, int prio, - const unsigned char *sha1, + const struct object_id *oid, struct tag **tag) { if (!e || e->prio < prio) @@ -78,13 +78,13 @@ static int replace_name(struct commit_name *e, struct tag *t; if (!e->tag) { - t = lookup_tag(e->sha1); + t = lookup_tag(e->oid.hash); if (!t || parse_tag(t)) return 1; e->tag = t; } - t = lookup_tag(sha1); + t = lookup_tag(oid->hash); if (!t || parse_tag(t)) return 0; *tag = t; @@ -97,24 +97,24 @@ static int replace_name(struct commit_name *e, } static void add_to_known_names(const char *path, - const unsigned char *peeled, + const struct object_id *peeled, int prio, - const unsigned char *sha1) + const struct object_id *oid) { struct commit_name *e = find_commit_name(peeled); struct tag *tag = NULL; - if (replace_name(e, prio, sha1, &tag)) { + if (replace_name(e, prio, oid, &tag)) { if (!e) { e = xmalloc(sizeof(struct commit_name)); - hashcpy(e->peeled, peeled); - hashmap_entry_init(e, sha1hash(peeled)); + oidcpy(&e->peeled, peeled); + hashmap_entry_init(e, sha1hash(peeled->hash)); hashmap_add(&names, e); e->path = NULL; } e->tag = tag; e->prio = prio; e->name_checked = 0; - hashcpy(e->sha1, sha1); + oidcpy(&e->oid, oid); free(e->path); e->path = xstrdup(path); } @@ -186,7 +186,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi else prio = 0; - add_to_known_names(all ? path + 5 : path + 10, peeled.hash, prio, oid->hash); + add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid); return 0; } @@ -244,7 +244,7 @@ static unsigned long finish_depth_computation( static void display_name(struct commit_name *n) { if (n->prio == 2 && !n->tag) { - n->tag = lookup_tag(n->sha1); + n->tag = lookup_tag(n->oid.hash); if (!n->tag || parse_tag(n->tag)) die(_("annotated tag %s not available"), n->path); } @@ -262,14 +262,14 @@ static void display_name(struct commit_name *n) printf("%s", n->path); } -static void show_suffix(int depth, const unsigned char *sha1) +static void show_suffix(int depth, const struct object_id *oid) { - printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev)); + printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev)); } static void describe(const char *arg, int last_one) { - unsigned char sha1[20]; + struct object_id oid; struct commit *cmit, *gave_up_on = NULL; struct commit_list *list; struct commit_name *n; @@ -278,20 +278,20 @@ static void describe(const char *arg, int last_one) unsigned long seen_commits = 0; unsigned int unannotated_cnt = 0; - if (get_sha1(arg, sha1)) + if (get_oid(arg, &oid)) die(_("Not a valid object name %s"), arg); - cmit = lookup_commit_reference(sha1); + cmit = lookup_commit_reference(oid.hash); if (!cmit) die(_("%s is not a valid '%s' object"), arg, commit_type); - n = find_commit_name(cmit->object.oid.hash); + n = find_commit_name(&cmit->object.oid); if (n && (tags || all || n->prio == 2)) { /* * Exact match to an existing ref. */ display_name(n); if (longformat) - show_suffix(0, n->tag ? n->tag->tagged->oid.hash : sha1); + show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid); if (dirty) printf("%s", dirty); printf("\n"); @@ -308,7 +308,7 @@ static void describe(const char *arg, int last_one) struct commit *c; struct commit_name *n = hashmap_iter_first(&names, &iter); for (; n; n = hashmap_iter_next(&iter)) { - c = lookup_commit_reference_gently(n->peeled, 1); + c = lookup_commit_reference_gently(n->peeled.hash, 1); if (c) c->util = n; } @@ -412,7 +412,7 @@ static void describe(const char *arg, int last_one) display_name(all_matches[0].name); if (abbrev) - show_suffix(all_matches[0].depth, cmit->object.oid.hash); + show_suffix(all_matches[0].depth, &cmit->object.oid); if (dirty) printf("%s", dirty); printf("\n"); diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 8ce00480cd..326f88b657 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -7,46 +7,44 @@ static struct rev_info log_tree_opt; -static int diff_tree_commit_sha1(const unsigned char *sha1) +static int diff_tree_commit_sha1(const struct object_id *oid) { - struct commit *commit = lookup_commit_reference(sha1); + struct commit *commit = lookup_commit_reference(oid->hash); if (!commit) return -1; return log_tree_commit(&log_tree_opt, commit); } /* Diff one or more commits. */ -static int stdin_diff_commit(struct commit *commit, char *line, int len) +static int stdin_diff_commit(struct commit *commit, const char *p) { - unsigned char sha1[20]; - if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) { - /* Graft the fake parents locally to the commit */ - int pos = 41; - struct commit_list **pptr; - - /* Free the real parent list */ - free_commit_list(commit->parents); - commit->parents = NULL; - pptr = &(commit->parents); - while (line[pos] && !get_sha1_hex(line + pos, sha1)) { - struct commit *parent = lookup_commit(sha1); - if (parent) { - pptr = &commit_list_insert(parent, pptr)->next; - } - pos += 41; + struct object_id oid; + struct commit_list **pptr = NULL; + + /* Graft the fake parents locally to the commit */ + while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) { + struct commit *parent = lookup_commit(oid.hash); + if (!pptr) { + /* Free the real parent list */ + free_commit_list(commit->parents); + commit->parents = NULL; + pptr = &(commit->parents); + } + if (parent) { + pptr = &commit_list_insert(parent, pptr)->next; } } return log_tree_commit(&log_tree_opt, commit); } /* Diff two trees. */ -static int stdin_diff_trees(struct tree *tree1, char *line, int len) +static int stdin_diff_trees(struct tree *tree1, const char *p) { - unsigned char sha1[20]; + struct object_id oid; struct tree *tree2; - if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1)) + if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p) return error("Need exactly two trees, separated by a space"); - tree2 = lookup_tree(sha1); + tree2 = lookup_tree(oid.hash); if (!tree2 || parse_tree(tree2)) return -1; printf("%s %s\n", oid_to_hex(&tree1->object.oid), @@ -60,23 +58,24 @@ static int stdin_diff_trees(struct tree *tree1, char *line, int len) static int diff_tree_stdin(char *line) { int len = strlen(line); - unsigned char sha1[20]; + struct object_id oid; struct object *obj; + const char *p; if (!len || line[len-1] != '\n') return -1; line[len-1] = 0; - if (get_sha1_hex(line, sha1)) + if (parse_oid_hex(line, &oid, &p)) return -1; - obj = parse_object(sha1); + obj = parse_object(oid.hash); if (!obj) return -1; if (obj->type == OBJ_COMMIT) - return stdin_diff_commit((struct commit *)obj, line, len); + return stdin_diff_commit((struct commit *)obj, p); if (obj->type == OBJ_TREE) - return stdin_diff_trees((struct tree *)obj, line, len); + return stdin_diff_trees((struct tree *)obj, p); error("Object %s is a %s, not a commit or tree", - sha1_to_hex(sha1), typename(obj->type)); + oid_to_hex(&oid), typename(obj->type)); return -1; } @@ -141,7 +140,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) break; case 1: tree1 = opt->pending.objects[0].item; - diff_tree_commit_sha1(tree1->oid.hash); + diff_tree_commit_sha1(&tree1->oid); break; case 2: tree1 = opt->pending.objects[0].item; @@ -164,9 +163,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | DIFF_SETUP_USE_CACHE); while (fgets(line, sizeof(line), stdin)) { - unsigned char sha1[20]; + struct object_id oid; - if (get_sha1_hex(line, sha1)) { + if (get_oid_hex(line, &oid)) { fputs(line, stdout); fflush(stdout); } diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 1e815b5577..e0220630d0 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -212,7 +212,7 @@ static char *anonymize_blob(unsigned long *size) return strbuf_detach(&out, NULL); } -static void export_blob(const unsigned char *sha1) +static void export_blob(const struct object_id *oid) { unsigned long size; enum object_type type; @@ -223,34 +223,34 @@ static void export_blob(const unsigned char *sha1) if (no_data) return; - if (is_null_sha1(sha1)) + if (is_null_oid(oid)) return; - object = lookup_object(sha1); + object = lookup_object(oid->hash); if (object && object->flags & SHOWN) return; if (anonymize) { buf = anonymize_blob(&size); - object = (struct object *)lookup_blob(sha1); + object = (struct object *)lookup_blob(oid->hash); eaten = 0; } else { - buf = read_sha1_file(sha1, &type, &size); + buf = read_sha1_file(oid->hash, &type, &size); if (!buf) - die ("Could not read blob %s", sha1_to_hex(sha1)); - if (check_sha1_signature(sha1, buf, size, typename(type)) < 0) - die("sha1 mismatch in blob %s", sha1_to_hex(sha1)); - object = parse_object_buffer(sha1, type, size, buf, &eaten); + die ("Could not read blob %s", oid_to_hex(oid)); + if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0) + die("sha1 mismatch in blob %s", oid_to_hex(oid)); + object = parse_object_buffer(oid->hash, type, size, buf, &eaten); } if (!object) - die("Could not read blob %s", sha1_to_hex(sha1)); + die("Could not read blob %s", oid_to_hex(oid)); mark_next_object(object); printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size); if (size && fwrite(buf, size, 1, stdout) != 1) - die_errno ("Could not write blob '%s'", sha1_to_hex(sha1)); + die_errno ("Could not write blob '%s'", oid_to_hex(oid)); printf("\n"); show_progress(); @@ -323,19 +323,19 @@ static void print_path(const char *path) } } -static void *generate_fake_sha1(const void *old, size_t *len) +static void *generate_fake_oid(const void *old, size_t *len) { static uint32_t counter = 1; /* avoid null sha1 */ - unsigned char *out = xcalloc(20, 1); - put_be32(out + 16, counter++); + unsigned char *out = xcalloc(GIT_SHA1_RAWSZ, 1); + put_be32(out + GIT_SHA1_RAWSZ - 4, counter++); return out; } -static const unsigned char *anonymize_sha1(const unsigned char *sha1) +static const unsigned char *anonymize_sha1(const struct object_id *oid) { static struct hashmap sha1s; - size_t len = 20; - return anonymize_mem(&sha1s, generate_fake_sha1, sha1, &len); + size_t len = GIT_SHA1_RAWSZ; + return anonymize_mem(&sha1s, generate_fake_oid, oid, &len); } static void show_filemodify(struct diff_queue_struct *q, @@ -383,7 +383,7 @@ static void show_filemodify(struct diff_queue_struct *q, if (no_data || S_ISGITLINK(spec->mode)) printf("M %06o %s ", spec->mode, sha1_to_hex(anonymize ? - anonymize_sha1(spec->oid.hash) : + anonymize_sha1(&spec->oid) : spec->oid.hash)); else { struct object *object = lookup_object(spec->oid.hash); @@ -572,7 +572,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) /* Export the referenced blobs, and remember the marks. */ for (i = 0; i < diff_queued_diff.nr; i++) if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode)) - export_blob(diff_queued_diff.queue[i]->two->oid.hash); + export_blob(&diff_queued_diff.queue[i]->two->oid); refname = commit->util; if (anonymize) { @@ -797,14 +797,14 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) for (i = 0; i < info->nr; i++) { struct rev_cmdline_entry *e = info->rev + i; - unsigned char sha1[20]; + struct object_id oid; struct commit *commit; char *full_name; if (e->flags & UNINTERESTING) continue; - if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1) + if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1) continue; if (refspecs) { @@ -828,7 +828,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) case OBJ_COMMIT: break; case OBJ_BLOB: - export_blob(commit->object.oid.hash); + export_blob(&commit->object.oid); continue; default: /* OBJ_TAG (nested tags) is already handled */ warning("Tag points to object of unexpected type %s, skipping.", @@ -912,7 +912,7 @@ static void import_marks(char *input_file) while (fgets(line, sizeof(line), f)) { uint32_t mark; char *line_end, *mark_end; - unsigned char sha1[20]; + struct object_id oid; struct object *object; struct commit *commit; enum object_type type; @@ -924,28 +924,28 @@ static void import_marks(char *input_file) mark = strtoumax(line + 1, &mark_end, 10); if (!mark || mark_end == line + 1 - || *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1)) + || *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid)) die("corrupt mark line: %s", line); if (last_idnum < mark) last_idnum = mark; - type = sha1_object_info(sha1, NULL); + type = sha1_object_info(oid.hash, NULL); if (type < 0) - die("object not found: %s", sha1_to_hex(sha1)); + die("object not found: %s", oid_to_hex(&oid)); if (type != OBJ_COMMIT) /* only commits */ continue; - commit = lookup_commit(sha1); + commit = lookup_commit(oid.hash); if (!commit) - die("not a commit? can't happen: %s", sha1_to_hex(sha1)); + die("not a commit? can't happen: %s", oid_to_hex(&oid)); object = &commit->object; if (object->flags & SHOWN) - error("Object %s already has a mark", sha1_to_hex(sha1)); + error("Object %s already has a mark", oid_to_hex(&oid)); mark_object(object, mark); diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index cfe9e447c2..2a1c1c213f 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -219,12 +219,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) * remote no-such-ref' would silently succeed without issuing * an error. */ - for (i = 0; i < nr_sought; i++) { - if (!sought[i] || sought[i]->matched) - continue; - error("no such remote ref %s", sought[i]->name); - ret = 1; - } + ret |= report_unmatched_refs(sought, nr_sought); while (ref) { printf("%s %s\n", diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index efab62fd85..6faa3c0d24 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -41,7 +41,7 @@ struct src_data { }; struct origin_data { - unsigned char sha1[20]; + struct object_id oid; unsigned is_local_branch:1; }; @@ -59,8 +59,8 @@ static struct string_list origins = STRING_LIST_INIT_DUP; struct merge_parents { int alloc, nr; struct merge_parent { - unsigned char given[20]; - unsigned char commit[20]; + struct object_id given; + struct object_id commit; unsigned char used; } *item; }; @@ -70,14 +70,14 @@ struct merge_parents { * hundreds of heads at a time anyway. */ static struct merge_parent *find_merge_parent(struct merge_parents *table, - unsigned char *given, - unsigned char *commit) + struct object_id *given, + struct object_id *commit) { int i; for (i = 0; i < table->nr; i++) { - if (given && hashcmp(table->item[i].given, given)) + if (given && oidcmp(&table->item[i].given, given)) continue; - if (commit && hashcmp(table->item[i].commit, commit)) + if (commit && oidcmp(&table->item[i].commit, commit)) continue; return &table->item[i]; } @@ -85,14 +85,14 @@ static struct merge_parent *find_merge_parent(struct merge_parents *table, } static void add_merge_parent(struct merge_parents *table, - unsigned char *given, - unsigned char *commit) + struct object_id *given, + struct object_id *commit) { if (table->nr && find_merge_parent(table, given, commit)) return; ALLOC_GROW(table->item, table->nr + 1, table->alloc); - hashcpy(table->item[table->nr].given, given); - hashcpy(table->item[table->nr].commit, commit); + oidcpy(&table->item[table->nr].given, given); + oidcpy(&table->item[table->nr].commit, commit); table->item[table->nr].used = 0; table->nr++; } @@ -106,30 +106,30 @@ static int handle_line(char *line, struct merge_parents *merge_parents) struct src_data *src_data; struct string_list_item *item; int pulling_head = 0; - unsigned char sha1[20]; + struct object_id oid; - if (len < 43 || line[40] != '\t') + if (len < GIT_SHA1_HEXSZ + 3 || line[GIT_SHA1_HEXSZ] != '\t') return 1; - if (starts_with(line + 41, "not-for-merge")) + if (starts_with(line + GIT_SHA1_HEXSZ + 1, "not-for-merge")) return 0; - if (line[41] != '\t') + if (line[GIT_SHA1_HEXSZ + 1] != '\t') return 2; - i = get_sha1_hex(line, sha1); + i = get_oid_hex(line, &oid); if (i) return 3; - if (!find_merge_parent(merge_parents, sha1, NULL)) + if (!find_merge_parent(merge_parents, &oid, NULL)) return 0; /* subsumed by other parents */ origin_data = xcalloc(1, sizeof(struct origin_data)); - hashcpy(origin_data->sha1, sha1); + oidcpy(&origin_data->oid, &oid); if (line[len - 1] == '\n') line[len - 1] = 0; - line += 42; + line += GIT_SHA1_HEXSZ + 2; /* * At this point, line points at the beginning of comment e.g. @@ -338,10 +338,10 @@ static void shortlog(const char *name, struct string_list committers = STRING_LIST_INIT_DUP; int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; struct strbuf sb = STRBUF_INIT; - const unsigned char *sha1 = origin_data->sha1; + const struct object_id *oid = &origin_data->oid; int limit = opts->shortlog_len; - branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40); + branch = deref_tag(parse_object(oid->hash), oid_to_hex(oid), GIT_SHA1_HEXSZ); if (!branch || branch->type != OBJ_COMMIT) return; @@ -531,7 +531,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out) } static void find_merge_parents(struct merge_parents *result, - struct strbuf *in, unsigned char *head) + struct strbuf *in, struct object_id *head) { struct commit_list *parents; struct commit *head_commit; @@ -542,31 +542,31 @@ static void find_merge_parents(struct merge_parents *result, int len; char *p = in->buf + pos; char *newline = strchr(p, '\n'); - unsigned char sha1[20]; + struct object_id oid; struct commit *parent; struct object *obj; len = newline ? newline - p : strlen(p); pos += len + !!newline; - if (len < 43 || - get_sha1_hex(p, sha1) || - p[40] != '\t' || - p[41] != '\t') + if (len < GIT_SHA1_HEXSZ + 3 || + get_oid_hex(p, &oid) || + p[GIT_SHA1_HEXSZ] != '\t' || + p[GIT_SHA1_HEXSZ + 1] != '\t') continue; /* skip not-for-merge */ /* * Do not use get_merge_parent() here; we do not have * "name" here and we do not want to contaminate its * util field yet. */ - obj = parse_object(sha1); + obj = parse_object(oid.hash); parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT); if (!parent) continue; commit_list_insert(parent, &parents); - add_merge_parent(result, obj->oid.hash, parent->object.oid.hash); + add_merge_parent(result, &obj->oid, &parent->object.oid); } - head_commit = lookup_commit(head); + head_commit = lookup_commit(head->hash); if (head_commit) commit_list_insert(head_commit, &parents); parents = reduce_heads(parents); @@ -574,7 +574,7 @@ static void find_merge_parents(struct merge_parents *result, while (parents) { struct commit *cmit = pop_commit(&parents); for (i = 0; i < result->nr; i++) - if (!hashcmp(result->item[i].commit, cmit->object.oid.hash)) + if (!oidcmp(&result->item[i].commit, &cmit->object.oid)) result->item[i].used = 1; } @@ -592,7 +592,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, struct fmt_merge_msg_opts *opts) { int i = 0, pos = 0; - unsigned char head_sha1[20]; + struct object_id head_oid; const char *current_branch; void *current_branch_to_free; struct merge_parents merge_parents; @@ -601,13 +601,13 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, /* get current branch */ current_branch = current_branch_to_free = - resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL); + resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL); if (!current_branch) die("No current branch"); if (starts_with(current_branch, "refs/heads/")) current_branch += 11; - find_merge_parents(&merge_parents, in, head_sha1); + find_merge_parents(&merge_parents, in, &head_oid); /* get a line */ while (pos < in->len) { @@ -633,7 +633,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, struct commit *head; struct rev_info rev; - head = lookup_commit_or_die(head_sha1, "HEAD"); + head = lookup_commit_or_die(head_oid.hash, "HEAD"); init_revisions(&rev, NULL); rev.commit_format = CMIT_FMT_ONELINE; rev.ignore_merges = 1; diff --git a/builtin/fsck.c b/builtin/fsck.c index 1a5caccd0f..f76e4163ab 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -396,13 +396,13 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type, static int default_refs; -static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1, +static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, unsigned long timestamp) { struct object *obj; - if (!is_null_sha1(sha1)) { - obj = lookup_object(sha1); + if (!is_null_oid(oid)) { + obj = lookup_object(oid->hash); if (obj && (obj->flags & HAS_OBJ)) { if (timestamp && name_objects) add_decoration(fsck_walk_options.object_names, @@ -411,13 +411,13 @@ static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1, obj->used = 1; mark_object_reachable(obj); } else { - error("%s: invalid reflog entry %s", refname, sha1_to_hex(sha1)); + error("%s: invalid reflog entry %s", refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; } } } -static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, +static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -425,10 +425,10 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, if (verbose) fprintf(stderr, "Checking reflog %s->%s\n", - sha1_to_hex(osha1), sha1_to_hex(nsha1)); + oid_to_hex(ooid), oid_to_hex(noid)); - fsck_handle_reflog_sha1(refname, osha1, 0); - fsck_handle_reflog_sha1(refname, nsha1, timestamp); + fsck_handle_reflog_oid(refname, ooid, 0); + fsck_handle_reflog_oid(refname, noid, timestamp); return 0; } @@ -491,7 +491,7 @@ static void get_default_heads(void) } } -static struct object *parse_loose_object(const unsigned char *sha1, +static struct object *parse_loose_object(const struct object_id *oid, const char *path) { struct object *obj; @@ -500,27 +500,27 @@ static struct object *parse_loose_object(const unsigned char *sha1, unsigned long size; int eaten; - if (read_loose_object(path, sha1, &type, &size, &contents) < 0) + if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0) return NULL; if (!contents && type != OBJ_BLOB) die("BUG: read_loose_object streamed a non-blob"); - obj = parse_object_buffer(sha1, type, size, contents, &eaten); + obj = parse_object_buffer(oid->hash, type, size, contents, &eaten); if (!eaten) free(contents); return obj; } -static int fsck_loose(const unsigned char *sha1, const char *path, void *data) +static int fsck_loose(const struct object_id *oid, const char *path, void *data) { - struct object *obj = parse_loose_object(sha1, path); + struct object *obj = parse_loose_object(oid, path); if (!obj) { errors_found |= ERROR_OBJECT; error("%s: object corrupt or missing: %s", - sha1_to_hex(sha1), path); + oid_to_hex(oid), path); return 0; /* keep checking other objects */ } @@ -619,26 +619,26 @@ static int fsck_cache_tree(struct cache_tree *it) return err; } -static void mark_object_for_connectivity(const unsigned char *sha1) +static void mark_object_for_connectivity(const struct object_id *oid) { - struct object *obj = lookup_unknown_object(sha1); + struct object *obj = lookup_unknown_object(oid->hash); obj->flags |= HAS_OBJ; } -static int mark_loose_for_connectivity(const unsigned char *sha1, +static int mark_loose_for_connectivity(const struct object_id *oid, const char *path, void *data) { - mark_object_for_connectivity(sha1); + mark_object_for_connectivity(oid); return 0; } -static int mark_packed_for_connectivity(const unsigned char *sha1, +static int mark_packed_for_connectivity(const struct object_id *oid, struct packed_git *pack, uint32_t pos, void *data) { - mark_object_for_connectivity(sha1); + mark_object_for_connectivity(oid); return 0; } diff --git a/builtin/gc.c b/builtin/gc.c index a2b9e8924e..c2c61a57bb 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -64,17 +64,6 @@ static void report_pack_garbage(unsigned seen_bits, const char *path) string_list_append(&pack_garbage, path); } -static void git_config_date_string(const char *key, const char **output) -{ - if (git_config_get_string_const(key, output)) - return; - if (strcmp(*output, "now")) { - unsigned long now = approxidate("now"); - if (approxidate(*output) >= now) - git_die_config(key, _("Invalid %s: '%s'"), key, *output); - } -} - static void process_log_file(void) { struct stat st; @@ -131,9 +120,9 @@ static void gc_config(void) git_config_get_int("gc.auto", &gc_auto_threshold); git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); git_config_get_bool("gc.autodetach", &detach_auto); - git_config_date_string("gc.pruneexpire", &prune_expire); - git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire); - git_config_date_string("gc.logexpiry", &gc_log_expire); + git_config_get_expiry("gc.pruneexpire", &prune_expire); + git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire); + git_config_get_expiry("gc.logexpiry", &gc_log_expire); git_config(git_default_config, NULL); } diff --git a/builtin/grep.c b/builtin/grep.c index 9304c33e75..837836fb3e 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -294,17 +294,17 @@ static int grep_cmd_config(const char *var, const char *value, void *cb) return st; } -static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) +static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size) { void *data; grep_read_lock(); - data = read_sha1_file(sha1, type, size); + data = read_sha1_file(oid->hash, type, size); grep_read_unlock(); return data; } -static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, +static int grep_oid(struct grep_opt *opt, const struct object_id *oid, const char *filename, int tree_name_len, const char *path) { @@ -323,7 +323,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); + add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid); strbuf_release(&pathbuf); return 0; } else @@ -332,7 +332,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); + grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, oid); strbuf_release(&pathbuf); hit = grep_source(opt, &gs); @@ -690,7 +690,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, ce_skip_worktree(ce)) { if (ce_stage(ce) || ce_intent_to_add(ce)) continue; - hit |= grep_sha1(opt, ce->oid.hash, ce->name, + hit |= grep_oid(opt, &ce->oid, ce->name, 0, ce->name); } else { hit |= grep_file(opt, ce->name); @@ -750,7 +750,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_sha1(opt, entry.oid->hash, 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; @@ -758,7 +758,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, void *data; unsigned long size; - data = lock_and_read_sha1_file(entry.oid->hash, &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)); @@ -787,7 +787,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct object *obj, const char *name, const char *path) { if (obj->type == OBJ_BLOB) - return grep_sha1(opt, obj->oid.hash, name, 0, path); + return grep_oid(opt, &obj->oid, name, 0, path); if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { struct tree_desc tree; void *data; @@ -1169,7 +1169,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) allow_revs = use_index && !untracked; for (i = 0; i < argc; i++) { const char *arg = argv[i]; - unsigned char sha1[20]; + struct object_id oid; struct object_context oc; struct object *object; @@ -1184,13 +1184,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix) break; } - if (get_sha1_with_context(arg, 0, sha1, &oc)) { + if (get_sha1_with_context(arg, 0, oid.hash, &oc)) { if (seen_dashdash) die(_("unable to resolve revision: %s"), arg); break; } - object = parse_object_or_die(sha1, arg); + object = parse_object_or_die(oid.hash, arg); if (!seen_dashdash) verify_non_filename(prefix, arg); add_object_array_with_path(object, arg, &list, oc.mode, oc.path); diff --git a/builtin/init-db.c b/builtin/init-db.c index 1d4d6a0078..8a6acb0ec6 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir, { int reinit; int exist_ok = flags & INIT_DB_EXIST_OK; - char *original_git_dir = real_pathdup(git_dir); + char *original_git_dir = real_pathdup(git_dir, 1); if (real_git_dir) { struct stat st; @@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); if (real_git_dir && !is_absolute_path(real_git_dir)) - real_git_dir = real_pathdup(real_git_dir); + real_git_dir = real_pathdup(real_git_dir, 1); if (argc == 1) { int mkdir_tried = 0; @@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir_parent = strrchr(git_dir, '/'); if (git_dir_parent) { char *rel = xstrndup(git_dir, git_dir_parent - git_dir); - git_work_tree_cfg = real_pathdup(rel); + git_work_tree_cfg = real_pathdup(rel, 1); free(rel); } if (!git_work_tree_cfg) diff --git a/builtin/log.c b/builtin/log.c index 55d20cc2d8..281af8c1ec 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -989,8 +989,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet)) return; - log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, - &need_8bit_cte); + log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte); for (i = 0; !need_8bit_cte && i < nr; i++) { const char *buf = get_commit_buffer(list[i], NULL); @@ -1005,6 +1004,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, msg = body; pp.fmt = CMIT_FMT_EMAIL; pp.date_mode.type = DATE_RFC2822; + pp.rev = rev; + pp.print_email_subject = 1; pp_user_info(&pp, NULL, &sb, committer, encoding); pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte); pp_remainder(&pp, &msg, &sb, 0); diff --git a/builtin/merge-base.c b/builtin/merge-base.c index b572a37c26..cfe2a796f8 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -36,12 +36,12 @@ static const char * const merge_base_usage[] = { static struct commit *get_commit_reference(const char *arg) { - unsigned char revkey[20]; + struct object_id revkey; struct commit *r; - if (get_sha1(arg, revkey)) + if (get_oid(arg, &revkey)) die("Not a valid object name %s", arg); - r = lookup_commit_reference(revkey); + r = lookup_commit_reference(revkey.hash); if (!r) die("Not a valid commit name %s", arg); @@ -113,14 +113,14 @@ struct rev_collect { unsigned int initial : 1; }; -static void add_one_commit(unsigned char *sha1, struct rev_collect *revs) +static void add_one_commit(struct object_id *oid, struct rev_collect *revs) { struct commit *commit; - if (is_null_sha1(sha1)) + if (is_null_oid(oid)) return; - commit = lookup_commit(sha1); + commit = lookup_commit(oid->hash); if (!commit || (commit->object.flags & TMP_MARK) || parse_commit(commit)) @@ -131,7 +131,7 @@ static void add_one_commit(unsigned char *sha1, struct rev_collect *revs) commit->object.flags |= TMP_MARK; } -static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, +static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *ident, unsigned long timestamp, int tz, const char *message, void *cbdata) { @@ -139,15 +139,15 @@ static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, if (revs->initial) { revs->initial = 0; - add_one_commit(osha1, revs); + add_one_commit(ooid, revs); } - add_one_commit(nsha1, revs); + add_one_commit(noid, revs); return 0; } static int handle_fork_point(int argc, const char **argv) { - unsigned char sha1[20]; + struct object_id oid; char *refname; const char *commitname; struct rev_collect revs; @@ -155,7 +155,7 @@ static int handle_fork_point(int argc, const char **argv) struct commit_list *bases; int i, ret = 0; - switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) { + switch (dwim_ref(argv[0], strlen(argv[0]), oid.hash, &refname)) { case 0: die("No such ref: '%s'", argv[0]); case 1: @@ -165,16 +165,16 @@ static int handle_fork_point(int argc, const char **argv) } commitname = (argc == 2) ? argv[1] : "HEAD"; - if (get_sha1(commitname, sha1)) + if (get_oid(commitname, &oid)) die("Not a valid object name: '%s'", commitname); - derived = lookup_commit_reference(sha1); + derived = lookup_commit_reference(oid.hash); memset(&revs, 0, sizeof(revs)); revs.initial = 1; for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); - if (!revs.nr && !get_sha1(refname, sha1)) - add_one_commit(sha1, &revs); + if (!revs.nr && !get_oid(refname, &oid)) + add_one_commit(&oid, &revs); for (i = 0; i < revs.nr; i++) revs.commit[i]->object.flags &= ~TMP_MARK; diff --git a/builtin/merge.c b/builtin/merge.c index a96d4fb501..7554b8d412 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -244,7 +244,7 @@ static void drop_save(void) unlink(git_path_merge_mode()); } -static int save_state(unsigned char *stash) +static int save_state(struct object_id *stash) { int len; struct child_process cp = CHILD_PROCESS_INIT; @@ -265,7 +265,7 @@ static int save_state(unsigned char *stash) else if (!len) /* no changes */ return -1; strbuf_setlen(&buffer, buffer.len-1); - if (get_sha1(buffer.buf, stash)) + if (get_oid(buffer.buf, stash)) die(_("not a valid object: %s"), buffer.buf); return 0; } @@ -305,18 +305,18 @@ static void reset_hard(unsigned const char *sha1, int verbose) die(_("read-tree failed")); } -static void restore_state(const unsigned char *head, - const unsigned char *stash) +static void restore_state(const struct object_id *head, + const struct object_id *stash) { struct strbuf sb = STRBUF_INIT; const char *args[] = { "stash", "apply", NULL, NULL }; - if (is_null_sha1(stash)) + if (is_null_oid(stash)) return; - reset_hard(head, 1); + reset_hard(head->hash, 1); - args[2] = sha1_to_hex(stash); + args[2] = oid_to_hex(stash); /* * It is OK to ignore error here, for example when there was @@ -376,10 +376,10 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead static void finish(struct commit *head_commit, struct commit_list *remoteheads, - const unsigned char *new_head, const char *msg) + const struct object_id *new_head, const char *msg) { struct strbuf reflog_message = STRBUF_INIT; - const unsigned char *head = head_commit->object.oid.hash; + const struct object_id *head = &head_commit->object.oid; if (!msg) strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); @@ -397,7 +397,7 @@ static void finish(struct commit *head_commit, else { const char *argv_gc_auto[] = { "gc", "--auto", NULL }; update_ref(reflog_message.buf, "HEAD", - new_head, head, 0, + new_head->hash, head->hash, 0, UPDATE_REFS_DIE_ON_ERR); /* * We ignore errors in 'gc --auto', since the @@ -416,7 +416,7 @@ static void finish(struct commit *head_commit, DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; diff_setup_done(&opts); - diff_tree_sha1(head, new_head, "", &opts); + diff_tree_sha1(head->hash, new_head->hash, "", &opts); diffcore_std(&opts); diff_flush(&opts); } @@ -431,35 +431,35 @@ static void finish(struct commit *head_commit, static void merge_name(const char *remote, struct strbuf *msg) { struct commit *remote_head; - unsigned char branch_head[20]; + struct object_id branch_head; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; const char *ptr; char *found_ref; int len, early; - strbuf_branchname(&bname, remote); + strbuf_branchname(&bname, remote, 0); remote = bname.buf; - memset(branch_head, 0, sizeof(branch_head)); + oidclr(&branch_head); remote_head = get_merge_parent(remote); if (!remote_head) die(_("'%s' does not point to a commit"), remote); - if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { + if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) { if (starts_with(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", - sha1_to_hex(branch_head), remote); + oid_to_hex(&branch_head), remote); goto cleanup; } if (starts_with(found_ref, "refs/tags/")) { strbuf_addf(msg, "%s\t\ttag '%s' of .\n", - sha1_to_hex(branch_head), remote); + oid_to_hex(&branch_head), remote); goto cleanup; } if (starts_with(found_ref, "refs/remotes/")) { strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n", - sha1_to_hex(branch_head), remote); + oid_to_hex(&branch_head), remote); goto cleanup; } } @@ -590,8 +590,8 @@ static int git_merge_config(const char *k, const char *v, void *cb) return git_diff_ui_config(k, v, cb); } -static int read_tree_trivial(unsigned char *common, unsigned char *head, - unsigned char *one) +static int read_tree_trivial(struct object_id *common, struct object_id *head, + struct object_id *one) { int i, nr_trees = 0; struct tree *trees[MAX_UNPACK_TREES]; @@ -606,13 +606,13 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head, opts.verbose_update = 1; opts.trivial_merges_only = 1; opts.merge = 1; - trees[nr_trees] = parse_tree_indirect(common); + trees[nr_trees] = parse_tree_indirect(common->hash); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(head); + trees[nr_trees] = parse_tree_indirect(head->hash); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(one); + trees[nr_trees] = parse_tree_indirect(one->hash); if (!trees[nr_trees++]) return -1; opts.fn = threeway_merge; @@ -626,9 +626,9 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head, return 0; } -static void write_tree_trivial(unsigned char *sha1) +static void write_tree_trivial(struct object_id *oid) { - if (write_cache_as_tree(sha1, 0, NULL)) + if (write_cache_as_tree(oid->hash, 0, NULL)) die(_("git write-tree failed to write a tree")); } @@ -781,7 +781,7 @@ static void prepare_to_commit(struct commit_list *remoteheads) static int merge_trivial(struct commit *head, struct commit_list *remoteheads) { - unsigned char result_tree[20], result_commit[20]; + struct object_id result_tree, result_commit; struct commit_list *parents, **pptr = &parents; static struct lock_file lock; @@ -792,15 +792,15 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) return error(_("Unable to write index.")); rollback_lock_file(&lock); - write_tree_trivial(result_tree); + write_tree_trivial(&result_tree); printf(_("Wonderful.\n")); pptr = commit_list_append(head, pptr); pptr = commit_list_append(remoteheads->item, pptr); prepare_to_commit(remoteheads); - if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents, - result_commit, NULL, sign_commit)) + if (commit_tree(merge_msg.buf, merge_msg.len, result_tree.hash, parents, + result_commit.hash, NULL, sign_commit)) die(_("failed to write commit object")); - finish(head, remoteheads, result_commit, "In-index merge"); + finish(head, remoteheads, &result_commit, "In-index merge"); drop_save(); return 0; } @@ -809,12 +809,12 @@ static int finish_automerge(struct commit *head, int head_subsumed, struct commit_list *common, struct commit_list *remoteheads, - unsigned char *result_tree, + struct object_id *result_tree, const char *wt_strategy) { struct commit_list *parents = NULL; struct strbuf buf = STRBUF_INIT; - unsigned char result_commit[20]; + struct object_id result_commit; free_commit_list(common); parents = remoteheads; @@ -822,11 +822,11 @@ static int finish_automerge(struct commit *head, commit_list_insert(head, &parents); strbuf_addch(&merge_msg, '\n'); prepare_to_commit(remoteheads); - if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents, - result_commit, NULL, sign_commit)) + if (commit_tree(merge_msg.buf, merge_msg.len, result_tree->hash, parents, + result_commit.hash, NULL, sign_commit)) die(_("failed to write commit object")); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); - finish(head, remoteheads, result_commit, buf.buf); + finish(head, remoteheads, &result_commit, buf.buf); strbuf_release(&buf); drop_save(); return 0; @@ -854,18 +854,18 @@ static int suggest_conflicts(void) } static struct commit *is_old_style_invocation(int argc, const char **argv, - const unsigned char *head) + const struct object_id *head) { struct commit *second_token = NULL; if (argc > 2) { - unsigned char second_sha1[20]; + struct object_id second_oid; - if (get_sha1(argv[1], second_sha1)) + if (get_oid(argv[1], &second_oid)) return NULL; - second_token = lookup_commit_reference_gently(second_sha1, 0); + second_token = lookup_commit_reference_gently(second_oid.hash, 0); if (!second_token) die(_("'%s' is not a commit"), argv[1]); - if (hashcmp(second_token->object.oid.hash, head)) + if (oidcmp(&second_token->object.oid, head)) return NULL; } return second_token; @@ -1038,7 +1038,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge die_errno(_("could not close '%s'"), filename); for (pos = 0; pos < merge_names->len; pos = npos) { - unsigned char sha1[20]; + struct object_id oid; char *ptr; struct commit *commit; @@ -1048,16 +1048,16 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge else npos = merge_names->len; - if (npos - pos < 40 + 2 || - get_sha1_hex(merge_names->buf + pos, sha1)) + if (npos - pos < GIT_SHA1_HEXSZ + 2 || + get_oid_hex(merge_names->buf + pos, &oid)) commit = NULL; /* bad */ - else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2)) + else if (memcmp(merge_names->buf + pos + GIT_SHA1_HEXSZ, "\t\t", 2)) continue; /* not-for-merge */ else { - char saved = merge_names->buf[pos + 40]; - merge_names->buf[pos + 40] = '\0'; + char saved = merge_names->buf[pos + GIT_SHA1_HEXSZ]; + merge_names->buf[pos + GIT_SHA1_HEXSZ] = '\0'; commit = get_merge_parent(merge_names->buf + pos); - merge_names->buf[pos + 40] = saved; + merge_names->buf[pos + GIT_SHA1_HEXSZ] = saved; } if (!commit) { if (ptr) @@ -1117,9 +1117,7 @@ static struct commit_list *collect_parents(struct commit *head_commit, int cmd_merge(int argc, const char **argv, const char *prefix) { - unsigned char result_tree[20]; - unsigned char stash[20]; - unsigned char head_sha1[20]; + struct object_id result_tree, stash, head_oid; struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; @@ -1138,13 +1136,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ - branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL); + branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL); if (branch && starts_with(branch, "refs/heads/")) branch += 11; - if (!branch || is_null_sha1(head_sha1)) + if (!branch || is_null_oid(&head_oid)) head_commit = NULL; else - head_commit = lookup_commit_or_die(head_sha1, "HEAD"); + head_commit = lookup_commit_or_die(head_oid.hash, "HEAD"); init_diff_ui_defaults(); git_config(git_merge_config, NULL); @@ -1242,7 +1240,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * to forbid "git merge" into a branch yet to be born. * We do the same for "git pull". */ - unsigned char *remote_head_sha1; + struct object_id *remote_head_oid; if (squash) die(_("Squash commit into empty head not supported yet")); if (fast_forward == FF_NO) @@ -1254,9 +1252,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) die(_("%s - not something we can merge"), argv[0]); if (remoteheads->next) die(_("Can merge only exactly one commit into empty head")); - remote_head_sha1 = remoteheads->item->object.oid.hash; - read_empty(remote_head_sha1, 0); - update_ref("initial pull", "HEAD", remote_head_sha1, + remote_head_oid = &remoteheads->item->object.oid; + read_empty(remote_head_oid->hash, 0); + update_ref("initial pull", "HEAD", remote_head_oid->hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; } @@ -1270,7 +1268,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * additional safety measure to check for it. */ if (!have_message && - is_old_style_invocation(argc, argv, head_commit->object.oid.hash)) { + is_old_style_invocation(argc, argv, &head_commit->object.oid)) { warning("old-style 'git merge <msg> HEAD <commit>' is deprecated."); strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; @@ -1422,7 +1420,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } - finish(head_commit, remoteheads, commit->object.oid.hash, msg.buf); + finish(head_commit, remoteheads, &commit->object.oid, msg.buf); drop_save(); goto done; } else if (!remoteheads->next && common->next) @@ -1441,9 +1439,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) /* See if it is really trivial. */ git_committer_info(IDENT_STRICT); printf(_("Trying really trivial in-index merge...\n")); - if (!read_tree_trivial(common->item->object.oid.hash, - head_commit->object.oid.hash, - remoteheads->item->object.oid.hash)) { + if (!read_tree_trivial(&common->item->object.oid, + &head_commit->object.oid, + &remoteheads->item->object.oid)) { ret = merge_trivial(head_commit, remoteheads); goto done; } @@ -1495,14 +1493,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix) /* * Stash away the local changes so that we can try more than one. */ - save_state(stash)) - hashclr(stash); + save_state(&stash)) + oidclr(&stash); for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { printf(_("Rewinding the tree to pristine...\n")); - restore_state(head_commit->object.oid.hash, stash); + restore_state(&head_commit->object.oid, &stash); } if (use_strategies_nr != 1) printf(_("Trying merge strategy %s...\n"), @@ -1547,7 +1545,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } /* Automerge succeeded. */ - write_tree_trivial(result_tree); + write_tree_trivial(&result_tree); automerge_was_ok = 1; break; } @@ -1559,7 +1557,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (automerge_was_ok) { ret = finish_automerge(head_commit, head_subsumed, common, remoteheads, - result_tree, wt_strategy); + &result_tree, wt_strategy); goto done; } @@ -1568,7 +1566,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * it up. */ if (!best_strategy) { - restore_state(head_commit->object.oid.hash, stash); + restore_state(&head_commit->object.oid, &stash); if (use_strategies_nr > 1) fprintf(stderr, _("No merge strategy handled the merge.\n")); @@ -1581,7 +1579,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) ; /* We already have its result in the working tree. */ else { printf(_("Rewinding the tree to pristine...\n")); - restore_state(head_commit->object.oid.hash, stash); + restore_state(&head_commit->object.oid, &stash); printf(_("Using the %s to prepare resolving by hand.\n"), best_strategy); try_merge_strategy(best_strategy, common, remoteheads, diff --git a/builtin/notes.c b/builtin/notes.c index 4b492abd41..0513f7455d 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -693,7 +693,7 @@ static int merge_abort(struct notes_merge_options *o) static int merge_commit(struct notes_merge_options *o) { struct strbuf msg = STRBUF_INIT; - unsigned char sha1[20], parent_sha1[20]; + struct object_id oid, parent_oid; struct notes_tree *t; struct commit *partial; struct pretty_print_context pretty_ctx; @@ -705,27 +705,27 @@ static int merge_commit(struct notes_merge_options *o) * and target notes ref from .git/NOTES_MERGE_REF. */ - if (get_sha1("NOTES_MERGE_PARTIAL", sha1)) + if (get_oid("NOTES_MERGE_PARTIAL", &oid)) die(_("failed to read ref NOTES_MERGE_PARTIAL")); - else if (!(partial = lookup_commit_reference(sha1))) + else if (!(partial = lookup_commit_reference(oid.hash))) die(_("could not find commit from NOTES_MERGE_PARTIAL.")); else if (parse_commit(partial)) die(_("could not parse commit from NOTES_MERGE_PARTIAL.")); if (partial->parents) - hashcpy(parent_sha1, partial->parents->item->object.oid.hash); + oidcpy(&parent_oid, &partial->parents->item->object.oid); else - hashclr(parent_sha1); + oidclr(&parent_oid); t = xcalloc(1, sizeof(struct notes_tree)); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); o->local_ref = local_ref_to_free = - resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL); + resolve_refdup("NOTES_MERGE_REF", 0, oid.hash, NULL); if (!o->local_ref) die(_("failed to resolve NOTES_MERGE_REF")); - if (notes_merge_commit(o, t, partial, sha1)) + if (notes_merge_commit(o, t, partial, oid.hash)) die(_("failed to finalize notes merge")); /* Reuse existing commit message in reflog message */ @@ -733,8 +733,8 @@ static int merge_commit(struct notes_merge_options *o) format_commit_message(partial, "%s", &msg, &pretty_ctx); strbuf_trim(&msg); strbuf_insert(&msg, 0, "notes: ", 7); - update_ref(msg.buf, o->local_ref, sha1, - is_null_sha1(parent_sha1) ? NULL : parent_sha1, + update_ref(msg.buf, o->local_ref, oid.hash, + is_null_oid(&parent_oid) ? NULL : parent_oid.hash, 0, UPDATE_REFS_DIE_ON_ERR); free_notes(t); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index f294dcffa9..16517f2637 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2612,17 +2612,17 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) free(in_pack.array); } -static int add_loose_object(const unsigned char *sha1, const char *path, +static int add_loose_object(const struct object_id *oid, const char *path, void *data) { - enum object_type type = sha1_object_info(sha1, NULL); + enum object_type type = sha1_object_info(oid->hash, NULL); if (type < 0) { warning("loose object at %s could not be examined", path); return 0; } - add_object_entry(sha1, type, "", 0); + add_object_entry(oid->hash, type, "", 0); return 0; } diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index 7cf900ea07..c026299e78 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -19,12 +19,12 @@ static int prune_subdir(int nr, const char *path, void *data) return 0; } -static int prune_object(const unsigned char *sha1, const char *path, +static int prune_object(const struct object_id *oid, const char *path, void *data) { int *opts = data; - if (!has_sha1_pack(sha1)) + if (!has_sha1_pack(oid->hash)) return 0; if (*opts & PRUNE_PACKED_DRY_RUN) diff --git a/builtin/prune.c b/builtin/prune.c index 8f4f052285..42633e0c6e 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -30,7 +30,7 @@ static int prune_tmp_file(const char *fullpath) return 0; } -static int prune_object(const unsigned char *sha1, const char *fullpath, +static int prune_object(const struct object_id *oid, const char *fullpath, void *data) { struct stat st; @@ -39,7 +39,7 @@ static int prune_object(const unsigned char *sha1, const char *fullpath, * Do we know about this object? * It must have been reachable */ - if (lookup_object(sha1)) + if (lookup_object(oid->hash)) return 0; if (lstat(fullpath, &st)) { @@ -50,8 +50,8 @@ static int prune_object(const unsigned char *sha1, const char *fullpath, if (st.st_mtime > expire) return 0; if (show_only || verbose) { - enum object_type type = sha1_object_info(sha1, NULL); - printf("%s %s\n", sha1_to_hex(sha1), + enum object_type type = sha1_object_info(oid->hash, NULL); + printf("%s %s\n", oid_to_hex(oid), (type > 0) ? typename(type) : "unknown"); } if (!show_only) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 9ed8fbbfad..83492af05f 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1417,7 +1417,7 @@ static void execute_commands(struct command *commands, { struct check_connected_options opt = CHECK_CONNECTED_INIT; struct command *cmd; - unsigned char sha1[20]; + struct object_id oid; struct iterate_data data; struct async muxer; int err_fd = 0; @@ -1474,7 +1474,7 @@ static void execute_commands(struct command *commands, check_aliased_updates(commands); free(head_name_to_free); - head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL); + head_name = head_name_to_free = resolve_refdup("HEAD", 0, oid.hash, NULL); if (use_atomic) execute_commands_atomic(commands, si); @@ -1667,8 +1667,11 @@ static const char *unpack(int err_fd, struct shallow_info *si) } tmp_objdir = tmp_objdir_create(); - if (!tmp_objdir) + if (!tmp_objdir) { + if (err_fd > 0) + close(err_fd); return "unable to create temporary object directory"; + } child.env = tmp_objdir_env(tmp_objdir); /* diff --git a/builtin/reflog.c b/builtin/reflog.c index 7a7136e53e..7472775778 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -615,7 +615,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) return status; } -static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1, +static int count_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { diff --git a/builtin/replace.c b/builtin/replace.c index 226d0f9523..f83e7b8fc1 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -88,78 +88,78 @@ static int list_replace_refs(const char *pattern, const char *format) } typedef int (*each_replace_name_fn)(const char *name, const char *ref, - const unsigned char *sha1); + const struct object_id *oid); static int for_each_replace_name(const char **argv, each_replace_name_fn fn) { const char **p, *full_hex; char ref[PATH_MAX]; int had_error = 0; - unsigned char sha1[20]; + struct object_id oid; for (p = argv; *p; p++) { - if (get_sha1(*p, sha1)) { + if (get_oid(*p, &oid)) { error("Failed to resolve '%s' as a valid ref.", *p); had_error = 1; continue; } - full_hex = sha1_to_hex(sha1); + full_hex = oid_to_hex(&oid); snprintf(ref, sizeof(ref), "%s%s", git_replace_ref_base, full_hex); /* read_ref() may reuse the buffer */ full_hex = ref + strlen(git_replace_ref_base); - if (read_ref(ref, sha1)) { + if (read_ref(ref, oid.hash)) { error("replace ref '%s' not found.", full_hex); had_error = 1; continue; } - if (fn(full_hex, ref, sha1)) + if (fn(full_hex, ref, &oid)) had_error = 1; } return had_error; } static int delete_replace_ref(const char *name, const char *ref, - const unsigned char *sha1) + const struct object_id *oid) { - if (delete_ref(NULL, ref, sha1, 0)) + if (delete_ref(NULL, ref, oid->hash, 0)) return 1; printf("Deleted replace ref '%s'\n", name); return 0; } -static void check_ref_valid(unsigned char object[20], - unsigned char prev[20], +static void check_ref_valid(struct object_id *object, + struct object_id *prev, char *ref, int ref_size, int force) { if (snprintf(ref, ref_size, "%s%s", git_replace_ref_base, - sha1_to_hex(object)) > ref_size - 1) + oid_to_hex(object)) > ref_size - 1) die("replace ref name too long: %.*s...", 50, ref); if (check_refname_format(ref, 0)) die("'%s' is not a valid ref name.", ref); - if (read_ref(ref, prev)) - hashclr(prev); + if (read_ref(ref, prev->hash)) + oidclr(prev); else if (!force) die("replace ref '%s' already exists", ref); } -static int replace_object_sha1(const char *object_ref, - unsigned char object[20], +static int replace_object_oid(const char *object_ref, + struct object_id *object, const char *replace_ref, - unsigned char repl[20], + struct object_id *repl, int force) { - unsigned char prev[20]; + struct object_id prev; enum object_type obj_type, repl_type; char ref[PATH_MAX]; struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; - obj_type = sha1_object_info(object, NULL); - repl_type = sha1_object_info(repl, NULL); + obj_type = sha1_object_info(object->hash, NULL); + repl_type = sha1_object_info(repl->hash, NULL); if (!force && obj_type != repl_type) die("Objects must be of the same type.\n" "'%s' points to a replaced object of type '%s'\n" @@ -167,11 +167,11 @@ static int replace_object_sha1(const char *object_ref, object_ref, typename(obj_type), replace_ref, typename(repl_type)); - check_ref_valid(object, prev, ref, sizeof(ref), force); + check_ref_valid(object, &prev, ref, sizeof(ref), force); transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, ref, repl, prev, + ref_transaction_update(transaction, ref, repl->hash, prev.hash, 0, NULL, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); @@ -182,14 +182,14 @@ static int replace_object_sha1(const char *object_ref, static int replace_object(const char *object_ref, const char *replace_ref, int force) { - unsigned char object[20], repl[20]; + struct object_id object, repl; - if (get_sha1(object_ref, object)) + if (get_oid(object_ref, &object)) die("Failed to resolve '%s' as a valid ref.", object_ref); - if (get_sha1(replace_ref, repl)) + if (get_oid(replace_ref, &repl)) die("Failed to resolve '%s' as a valid ref.", replace_ref); - return replace_object_sha1(object_ref, object, replace_ref, repl, force); + return replace_object_oid(object_ref, &object, replace_ref, &repl, force); } /* @@ -197,7 +197,7 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f * If "raw" is true, then the object's raw contents are printed according to * "type". Otherwise, we pretty-print the contents for human editing. */ -static void export_object(const unsigned char *sha1, enum object_type type, +static void export_object(const struct object_id *oid, enum object_type type, int raw, const char *filename) { struct child_process cmd = CHILD_PROCESS_INIT; @@ -213,7 +213,7 @@ static void export_object(const unsigned char *sha1, enum object_type type, argv_array_push(&cmd.args, typename(type)); else argv_array_push(&cmd.args, "-p"); - argv_array_push(&cmd.args, sha1_to_hex(sha1)); + argv_array_push(&cmd.args, oid_to_hex(oid)); cmd.git_cmd = 1; cmd.out = fd; @@ -226,7 +226,7 @@ static void export_object(const unsigned char *sha1, enum object_type type, * interpreting it as "type", and writing the result to the object database. * The sha1 of the written object is returned via sha1. */ -static void import_object(unsigned char *sha1, enum object_type type, +static void import_object(struct object_id *oid, enum object_type type, int raw, const char *filename) { int fd; @@ -254,7 +254,7 @@ static void import_object(unsigned char *sha1, enum object_type type, if (finish_command(&cmd)) die("mktree reported failure"); - if (get_sha1_hex(result.buf, sha1) < 0) + if (get_oid_hex(result.buf, oid) < 0) die("mktree did not return an object name"); strbuf_release(&result); @@ -264,7 +264,7 @@ static void import_object(unsigned char *sha1, enum object_type type, if (fstat(fd, &st) < 0) die_errno("unable to fstat %s", filename); - if (index_fd(sha1, fd, &st, type, NULL, flags) < 0) + if (index_fd(oid->hash, fd, &st, type, NULL, flags) < 0) die("unable to write object to database"); /* index_fd close()s fd for us */ } @@ -279,29 +279,29 @@ static int edit_and_replace(const char *object_ref, int force, int raw) { char *tmpfile = git_pathdup("REPLACE_EDITOBJ"); enum object_type type; - unsigned char old[20], new[20], prev[20]; + struct object_id old, new, prev; char ref[PATH_MAX]; - if (get_sha1(object_ref, old) < 0) + if (get_oid(object_ref, &old) < 0) die("Not a valid object name: '%s'", object_ref); - type = sha1_object_info(old, NULL); + type = sha1_object_info(old.hash, NULL); if (type < 0) - die("unable to get object type for %s", sha1_to_hex(old)); + die("unable to get object type for %s", oid_to_hex(&old)); - check_ref_valid(old, prev, ref, sizeof(ref), force); + check_ref_valid(&old, &prev, ref, sizeof(ref), force); - export_object(old, type, raw, tmpfile); + export_object(&old, type, raw, tmpfile); if (launch_editor(tmpfile, NULL, NULL) < 0) die("editing object file failed"); - import_object(new, type, raw, tmpfile); + import_object(&new, type, raw, tmpfile); free(tmpfile); - if (!hashcmp(old, new)) - return error("new object is the same as the old one: '%s'", sha1_to_hex(old)); + if (!oidcmp(&old, &new)) + return error("new object is the same as the old one: '%s'", oid_to_hex(&old)); - return replace_object_sha1(object_ref, old, "replacement", new, force); + return replace_object_oid(object_ref, &old, "replacement", &new, force); } static void replace_parents(struct strbuf *buf, int argc, const char **argv) @@ -312,7 +312,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv) /* find existing parents */ parent_start = buf->buf; - parent_start += 46; /* "tree " + "hex sha1" + "\n" */ + parent_start += GIT_SHA1_HEXSZ + 6; /* "tree " + "hex sha1" + "\n" */ parent_end = parent_start; while (starts_with(parent_end, "parent ")) @@ -320,11 +320,11 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv) /* prepare new parents */ for (i = 0; i < argc; i++) { - unsigned char sha1[20]; - if (get_sha1(argv[i], sha1) < 0) + struct object_id oid; + if (get_oid(argv[i], &oid) < 0) die(_("Not a valid object name: '%s'"), argv[i]); - lookup_commit_or_die(sha1, argv[i]); - strbuf_addf(&new_parents, "parent %s\n", sha1_to_hex(sha1)); + lookup_commit_or_die(oid.hash, argv[i]); + strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid)); } /* replace existing parents with new ones */ @@ -345,12 +345,12 @@ static void check_one_mergetag(struct commit *commit, { struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data; const char *ref = mergetag_data->argv[0]; - unsigned char tag_sha1[20]; + struct object_id tag_oid; struct tag *tag; int i; - hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_sha1); - tag = lookup_tag(tag_sha1); + hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash); + tag = lookup_tag(tag_oid.hash); if (!tag) die(_("bad mergetag in commit '%s'"), ref); if (parse_tag_buffer(tag, extra->value, extra->len)) @@ -366,7 +366,7 @@ static void check_one_mergetag(struct commit *commit, } die(_("original commit '%s' contains mergetag '%s' that is discarded; " - "use --edit instead of --graft"), ref, sha1_to_hex(tag_sha1)); + "use --edit instead of --graft"), ref, oid_to_hex(&tag_oid)); } static void check_mergetags(struct commit *commit, int argc, const char **argv) @@ -380,16 +380,16 @@ static void check_mergetags(struct commit *commit, int argc, const char **argv) static int create_graft(int argc, const char **argv, int force) { - unsigned char old[20], new[20]; + struct object_id old, new; const char *old_ref = argv[0]; struct commit *commit; struct strbuf buf = STRBUF_INIT; const char *buffer; unsigned long size; - if (get_sha1(old_ref, old) < 0) + if (get_oid(old_ref, &old) < 0) die(_("Not a valid object name: '%s'"), old_ref); - commit = lookup_commit_or_die(old, old_ref); + commit = lookup_commit_or_die(old.hash, old_ref); buffer = get_commit_buffer(commit, &size); strbuf_add(&buf, buffer, size); @@ -404,15 +404,15 @@ static int create_graft(int argc, const char **argv, int force) check_mergetags(commit, argc, argv); - if (write_sha1_file(buf.buf, buf.len, commit_type, new)) + if (write_sha1_file(buf.buf, buf.len, commit_type, new.hash)) die(_("could not write replacement commit for: '%s'"), old_ref); strbuf_release(&buf); - if (!hashcmp(old, new)) - return error("new commit is the same as the old one: '%s'", sha1_to_hex(old)); + if (!oidcmp(&old, &new)) + return error("new commit is the same as the old one: '%s'", oid_to_hex(&old)); - return replace_object_sha1(old_ref, old, "replacement", new, force); + return replace_object_oid(old_ref, &old, "replacement", &new, force); } int cmd_replace(int argc, const char **argv, const char *prefix) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index e08677e559..2549643267 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -12,6 +12,7 @@ #include "diff.h" #include "revision.h" #include "split-index.h" +#include "submodule.h" #define DO_REVS 1 #define DO_NOREV 2 @@ -779,6 +780,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) puts(work_tree); continue; } + if (!strcmp(arg, "--show-superproject-working-tree")) { + const char *superproject = get_superproject_working_tree(); + if (superproject) + puts(superproject); + continue; + } if (!strcmp(arg, "--show-prefix")) { if (prefix) puts(prefix); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index c9585d475d..f78bb4818d 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -148,7 +148,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) ctx.fmt = CMIT_FMT_USERFORMAT; ctx.abbrev = log->abbrev; - ctx.subject = ""; + ctx.print_email_subject = 1; ctx.after_subject = ""; ctx.date_mode.type = DATE_NORMAL; ctx.output_encoding = get_log_output_encoding(); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 899dc334e3..15a5430c00 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -356,12 +356,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) strbuf_addf(&remotesb, "remote.%s.url", remote); free(remote); - if (git_config_get_string(remotesb.buf, &remoteurl)) - /* - * The repository is its own - * authoritative upstream - */ + if (git_config_get_string(remotesb.buf, &remoteurl)) { + warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf); remoteurl = xgetcwd(); + } relurl = relative_url(remoteurl, url, NULL); strbuf_release(&remotesb); free(remoteurl); diff --git a/builtin/update-index.c b/builtin/update-index.c index d530e89368..d74d72cc7f 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -1099,17 +1099,20 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) } if (split_index > 0) { - init_split_index(&the_index); - the_index.cache_changed |= SPLIT_INDEX_ORDERED; - } else if (!split_index && the_index.split_index) { - /* - * can't discard_split_index(&the_index); because that - * will destroy split_index->base->cache[], which may - * be shared with the_index.cache[]. So yeah we're - * leaking a bit here. - */ - the_index.split_index = NULL; - the_index.cache_changed |= SOMETHING_CHANGED; + if (git_config_get_split_index() == 0) + warning(_("core.splitIndex is set to false; " + "remove or change it, if you really want to " + "enable split index")); + if (the_index.split_index) + the_index.cache_changed |= SPLIT_INDEX_ORDERED; + else + add_split_index(&the_index); + } else if (!split_index) { + if (git_config_get_split_index() == 1) + warning(_("core.splitIndex is set to true; " + "remove or change it, if you really want to " + "disable split index")); + remove_split_index(&the_index); } switch (untracked_cache) { @@ -1045,9 +1045,6 @@ static inline int is_empty_tree_oid(const struct object_id *oid) return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN); } - -int git_mkstemp(char *path, size_t n, const char *template); - /* set default permissions by passing mode arguments to open(2) */ int git_mkstemps_mode(char *pattern, int suffix_len, int mode); int git_mkstemp_mode(char *pattern, int mode); @@ -1153,7 +1150,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path, int die_on_error); const char *real_path(const char *path); const char *real_path_if_valid(const char *path); -char *real_pathdup(const char *path); +char *real_pathdup(const char *path, int die_on_error); const char *absolute_path(const char *path); char *absolute_pathdup(const char *path); const char *remove_leading_path(const char *in, const char *prefix); @@ -1273,6 +1270,9 @@ extern int has_pack_index(const unsigned char *sha1); extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect); +/* Helper to check and "touch" a file */ +extern int check_and_freshen_file(const char *fn, int freshen); + extern const signed char hexval_table[256]; static inline unsigned int hexval(unsigned char c) { @@ -1363,7 +1363,46 @@ 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 */ -extern int interpret_branch_name(const char *str, int len, struct strbuf *); +/* + * Parse a 40-character hexadecimal object ID starting from hex, updating the + * pointer specified by end when parsing stops. The resulting object ID is + * stored in oid. Returns 0 on success. Parsing will stop on the first NUL or + * other invalid character. end is only updated on success; otherwise, it is + * unmodified. + */ +extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end); + +/* + * This reads short-hand syntax that not only evaluates to a commit + * object name, but also can act as if the end user spelled the name + * of the branch from the command line. + * + * - "@{-N}" finds the name of the Nth previous branch we were on, and + * places the name of the branch in the given buf and returns the + * number of characters parsed if successful. + * + * - "<branch>@{upstream}" finds the name of the other ref that + * <branch> is configured to merge with (missing <branch> defaults + * to the current branch), and places the name of the branch in the + * given buf and returns the number of characters parsed if + * successful. + * + * If the input is not of the accepted format, it returns a negative + * number to signal an error. + * + * If the input was ok but there are not N branch switches in the + * reflog, it returns 0. + * + * If "allowed" is non-zero, it is a treated as a bitfield of allowable + * expansions: local branches ("refs/heads/"), remote branches + * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is + * allowed, even ones to refs outside of those namespaces. + */ +#define INTERPRET_BRANCH_LOCAL (1<<0) +#define INTERPRET_BRANCH_REMOTE (1<<1) +#define INTERPRET_BRANCH_HEAD (1<<2) +extern int interpret_branch_name(const char *str, int len, struct strbuf *, + unsigned allowed); extern int get_oid_mb(const char *str, struct object_id *oid); extern int validate_headref(const char *ref); @@ -1643,6 +1682,12 @@ extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr); * error. */ extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n); +/* + * Like nth_packed_object_sha1, but write the data into the object specified by + * the the first argument. Returns the first argument on success, and NULL on + * error. + */ +extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n); /* * Return the offset of the nth object within the specified packfile. @@ -1684,7 +1729,7 @@ extern int unpack_object_header(struct packed_git *, struct pack_window **, off_ * scratch buffer, but restored to its original contents before * the function returns. */ -typedef int each_loose_object_fn(const unsigned char *sha1, +typedef int each_loose_object_fn(const struct object_id *oid, const char *path, void *data); typedef int each_loose_cruft_fn(const char *basename, @@ -1710,7 +1755,7 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path, * LOCAL_ONLY flag is set). */ #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1 -typedef int each_packed_object_fn(const unsigned char *sha1, +typedef int each_packed_object_fn(const struct object_id *oid, struct packed_git *pack, uint32_t pos, void *data); @@ -1863,8 +1908,11 @@ extern int git_config_include(const char *name, const char *value, void *data); * * (i.e., what gets handed to a config_fn_t). The caller provides the section; * we return -1 if it does not match, 0 otherwise. The subsection and key - * out-parameters are filled by the function (and subsection is NULL if it is + * out-parameters are filled by the function (and *subsection is NULL if it is * missing). + * + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if + * there is no subsection at all. */ extern int parse_config_key(const char *var, const char *section, @@ -1926,6 +1974,11 @@ extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest); extern int git_config_get_maybe_bool(const char *key, int *dest); extern int git_config_get_pathname(const char *key, const char **dest); extern int git_config_get_untracked_cache(void); +extern int git_config_get_split_index(void); +extern int git_config_get_max_percent_split_change(void); + +/* This dies if the configured or default date is in the future */ +extern int git_config_get_expiry(const char *key, const char **output); /* * This is a hack for test programs like test-dump-untracked-cache to diff --git a/ci/run-linux32-build.sh b/ci/run-linux32-build.sh new file mode 100755 index 0000000000..e30fb2cddc --- /dev/null +++ b/ci/run-linux32-build.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Build and test Git in a 32-bit environment +# +# Usage: +# run-linux32-build.sh [host-user-id] +# + +# Update packages to the latest available versions +linux32 --32bit i386 sh -c ' + apt update >/dev/null && + apt install -y build-essential libcurl4-openssl-dev libssl-dev \ + libexpat-dev gettext python >/dev/null +' && + +# If this script runs inside a docker container, then all commands are +# usually executed as root. Consequently, the host user might not be +# able to access the test output files. +# If a host user id is given, then create a user "ci" with the host user +# id to make everything accessible to the host user. +HOST_UID=$1 && +CI_USER=$USER && +test -z $HOST_UID || (CI_USER="ci" && useradd -u $HOST_UID $CI_USER) && + +# Build and test +linux32 --32bit i386 su -m -l $CI_USER -c ' + cd /usr/src/git && + make --jobs=2 && + make --quiet test +' @@ -1307,11 +1307,11 @@ void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data) static inline int standard_header_field(const char *field, size_t len) { - return ((len == 4 && !memcmp(field, "tree ", 5)) || - (len == 6 && !memcmp(field, "parent ", 7)) || - (len == 6 && !memcmp(field, "author ", 7)) || - (len == 9 && !memcmp(field, "committer ", 10)) || - (len == 8 && !memcmp(field, "encoding ", 9))); + return ((len == 4 && !memcmp(field, "tree", 4)) || + (len == 6 && !memcmp(field, "parent", 6)) || + (len == 6 && !memcmp(field, "author", 6)) || + (len == 9 && !memcmp(field, "committer", 9)) || + (len == 8 && !memcmp(field, "encoding", 8))); } static int excluded_header_field(const char *field, size_t len, const char **exclude) @@ -1321,8 +1321,7 @@ static int excluded_header_field(const char *field, size_t len, const char **exc while (*exclude) { size_t xlen = strlen(*exclude); - if (len == xlen && - !memcmp(field, *exclude, xlen) && field[xlen] == ' ') + if (len == xlen && !memcmp(field, *exclude, xlen)) return 1; exclude++; } @@ -1353,12 +1352,11 @@ static struct commit_extra_header *read_commit_extra_header_lines( strbuf_reset(&buf); it = NULL; - eof = strchr(line, ' '); - if (next <= eof) + eof = memchr(line, ' ', next - line); + if (!eof) eof = next; - - if (standard_header_field(line, eof - line) || - excluded_header_field(line, eof - line, exclude)) + else if (standard_header_field(line, eof - line) || + excluded_header_field(line, eof - line, exclude)) continue; it = xcalloc(1, sizeof(*it)); @@ -142,21 +142,24 @@ static inline int cmit_fmt_is_mail(enum cmit_fmt fmt) return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD); } +struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ + struct pretty_print_context { /* * Callers should tweak these to change the behavior of pp_* functions. */ enum cmit_fmt fmt; int abbrev; - const char *subject; const char *after_subject; int preserve_subject; struct date_mode date_mode; unsigned date_mode_explicit:1; + int print_email_subject; int expand_tabs_in_log; int need_8bit_cte; char *notes_message; struct reflog_walk_info *reflog_info; + struct rev_info *rev; const char *output_encoding; struct string_list *mailmap; int color; @@ -175,7 +178,6 @@ struct userformat_want { }; extern int has_non_ascii(const char *text); -struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ extern const char *logmsg_reencode(const struct commit *commit, char **commit_encoding, const char *output_encoding); @@ -201,11 +201,105 @@ void git_config_push_parameter(const char *text) strbuf_release(&env); } +static inline int iskeychar(int c) +{ + return isalnum(c) || c == '-'; +} + +/* + * Auxiliary function to sanity-check and split the key into the section + * identifier and variable name. + * + * Returns 0 on success, -1 when there is an invalid character in the key and + * -2 if there is no section name in the key. + * + * store_key - pointer to char* which will hold a copy of the key with + * lowercase section and variable name + * baselen - pointer to int which will hold the length of the + * section + subsection part, can be NULL + */ +static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet) +{ + int i, dot, baselen; + const char *last_dot = strrchr(key, '.'); + + /* + * Since "key" actually contains the section name and the real + * key name separated by a dot, we have to know where the dot is. + */ + + if (last_dot == NULL || last_dot == key) { + if (!quiet) + error("key does not contain a section: %s", key); + return -CONFIG_NO_SECTION_OR_NAME; + } + + if (!last_dot[1]) { + if (!quiet) + error("key does not contain variable name: %s", key); + return -CONFIG_NO_SECTION_OR_NAME; + } + + baselen = last_dot - key; + if (baselen_) + *baselen_ = baselen; + + /* + * Validate the key and while at it, lower case it for matching. + */ + if (store_key) + *store_key = xmallocz(strlen(key)); + + dot = 0; + for (i = 0; key[i]; i++) { + unsigned char c = key[i]; + if (c == '.') + dot = 1; + /* Leave the extended basename untouched.. */ + if (!dot || i > baselen) { + if (!iskeychar(c) || + (i == baselen + 1 && !isalpha(c))) { + if (!quiet) + error("invalid key: %s", key); + goto out_free_ret_1; + } + c = tolower(c); + } else if (c == '\n') { + if (!quiet) + error("invalid key (newline): %s", key); + goto out_free_ret_1; + } + if (store_key) + (*store_key)[i] = c; + } + + return 0; + +out_free_ret_1: + if (store_key) { + free(*store_key); + *store_key = NULL; + } + return -CONFIG_INVALID_KEY; +} + +int git_config_parse_key(const char *key, char **store_key, int *baselen) +{ + return git_config_parse_key_1(key, store_key, baselen, 0); +} + +int git_config_key_is_valid(const char *key) +{ + return !git_config_parse_key_1(key, NULL, NULL, 1); +} + int git_config_parse_parameter(const char *text, config_fn_t fn, void *data) { const char *value; + char *canonical_name; struct strbuf **pair; + int ret; pair = strbuf_split_str(text, '=', 2); if (!pair[0]) @@ -223,13 +317,15 @@ int git_config_parse_parameter(const char *text, strbuf_list_free(pair); return error("bogus config parameter: %s", text); } - strbuf_tolower(pair[0]); - if (fn(pair[0]->buf, value, data) < 0) { - strbuf_list_free(pair); - return -1; + + if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) { + ret = -1; + } else { + ret = (fn(canonical_name, value, data) < 0) ? -1 : 0; + free(canonical_name); } strbuf_list_free(pair); - return 0; + return ret; } int git_config_from_parameters(config_fn_t fn, void *data) @@ -356,11 +452,6 @@ static char *parse_value(void) } } -static inline int iskeychar(int c) -{ - return isalnum(c) || c == '-'; -} - static int get_value(config_fn_t fn, void *data, struct strbuf *name) { int c; @@ -1712,6 +1803,19 @@ int git_config_get_pathname(const char *key, const char **dest) return ret; } +int git_config_get_expiry(const char *key, const char **output) +{ + int ret = git_config_get_string_const(key, output); + if (ret) + return ret; + if (strcmp(*output, "now")) { + unsigned long now = approxidate("now"); + if (approxidate(*output) >= now) + git_die_config(key, _("Invalid %s: '%s'"), key, *output); + } + return ret; +} + int git_config_get_untracked_cache(void) { int val = -1; @@ -1728,14 +1832,39 @@ int git_config_get_untracked_cache(void) if (!strcasecmp(v, "keep")) return -1; - error("unknown core.untrackedCache value '%s'; " - "using 'keep' default value", v); + error(_("unknown core.untrackedCache value '%s'; " + "using 'keep' default value"), v); return -1; } return -1; /* default value */ } +int git_config_get_split_index(void) +{ + int val; + + if (!git_config_get_maybe_bool("core.splitindex", &val)) + return val; + + return -1; /* default value */ +} + +int git_config_get_max_percent_split_change(void) +{ + int val = -1; + + if (!git_config_get_int("splitindex.maxpercentchange", &val)) { + if (0 <= val && val <= 100) + return val; + + return error(_("splitIndex.maxPercentChange value '%d' " + "should be between 0 and 100"), val); + } + + return -1; /* default value */ +} + NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr) { @@ -1990,93 +2119,6 @@ void git_config_set(const char *key, const char *value) } /* - * Auxiliary function to sanity-check and split the key into the section - * identifier and variable name. - * - * Returns 0 on success, -1 when there is an invalid character in the key and - * -2 if there is no section name in the key. - * - * store_key - pointer to char* which will hold a copy of the key with - * lowercase section and variable name - * baselen - pointer to int which will hold the length of the - * section + subsection part, can be NULL - */ -static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet) -{ - int i, dot, baselen; - const char *last_dot = strrchr(key, '.'); - - /* - * Since "key" actually contains the section name and the real - * key name separated by a dot, we have to know where the dot is. - */ - - if (last_dot == NULL || last_dot == key) { - if (!quiet) - error("key does not contain a section: %s", key); - return -CONFIG_NO_SECTION_OR_NAME; - } - - if (!last_dot[1]) { - if (!quiet) - error("key does not contain variable name: %s", key); - return -CONFIG_NO_SECTION_OR_NAME; - } - - baselen = last_dot - key; - if (baselen_) - *baselen_ = baselen; - - /* - * Validate the key and while at it, lower case it for matching. - */ - if (store_key) - *store_key = xmallocz(strlen(key)); - - dot = 0; - for (i = 0; key[i]; i++) { - unsigned char c = key[i]; - if (c == '.') - dot = 1; - /* Leave the extended basename untouched.. */ - if (!dot || i > baselen) { - if (!iskeychar(c) || - (i == baselen + 1 && !isalpha(c))) { - if (!quiet) - error("invalid key: %s", key); - goto out_free_ret_1; - } - c = tolower(c); - } else if (c == '\n') { - if (!quiet) - error("invalid key (newline): %s", key); - goto out_free_ret_1; - } - if (store_key) - (*store_key)[i] = c; - } - - return 0; - -out_free_ret_1: - if (store_key) { - free(*store_key); - *store_key = NULL; - } - return -CONFIG_INVALID_KEY; -} - -int git_config_parse_key(const char *key, char **store_key, int *baselen) -{ - return git_config_parse_key_1(key, store_key, baselen, 0); -} - -int git_config_key_is_valid(const char *key) -{ - return !git_config_parse_key_1(key, NULL, NULL, 1); -} - -/* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. * if value_regex==CONFIG_REGEX_NONE, do not match any existing values @@ -2536,11 +2578,10 @@ int parse_config_key(const char *var, const char **subsection, int *subsection_len, const char **key) { - int section_len = strlen(section); const char *dot; /* Does it start with "section." ? */ - if (!starts_with(var, section) || var[section_len] != '.') + if (!skip_prefix(var, section, &var) || *var != '.') return -1; /* @@ -2552,12 +2593,16 @@ int parse_config_key(const char *var, *key = dot + 1; /* Did we have a subsection at all? */ - if (dot == var + section_len) { - *subsection = NULL; - *subsection_len = 0; + if (dot == var) { + if (subsection) { + *subsection = NULL; + *subsection_len = 0; + } } else { - *subsection = var + section_len + 1; + if (!subsection) + return -1; + *subsection = var + 1; *subsection_len = dot - *subsection; } diff --git a/config.mak.uname b/config.mak.uname index 447f36ac2e..399fe19271 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -27,7 +27,6 @@ endif ifeq ($(uname_S),Linux) HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease HAVE_DEV_TTY = YesPlease @@ -41,7 +40,6 @@ endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease @@ -55,7 +53,6 @@ ifeq ($(uname_S),UnixWare) SHELL_PATH = /usr/local/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -Kthread BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib @@ -79,7 +76,6 @@ ifeq ($(uname_S),SCO_SV) SHELL_PATH = /usr/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib NO_STRCASESTR = YesPlease @@ -122,7 +118,6 @@ ifeq ($(uname_S),SunOS) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_REGEX = YesPlease NO_MSGFMT_EXTENDED_OPTIONS = YesPlease HAVE_DEV_TTY = YesPlease @@ -168,7 +163,6 @@ ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_SYMLINK_HEAD = YesPlease NO_IPV6 = YesPlease OLD_ICONV = UnfortunatelyYes @@ -233,7 +227,6 @@ ifeq ($(uname_S),NetBSD) BASIC_CFLAGS += -I/usr/pkg/include BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib USE_ST_TIMESPEC = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease HAVE_BSD_SYSCTL = YesPlease endif @@ -242,7 +235,6 @@ ifeq ($(uname_S),AIX) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_NSEC = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes @@ -263,7 +255,6 @@ ifeq ($(uname_S),GNU) # GNU/Hurd HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease endif @@ -272,7 +263,6 @@ ifeq ($(uname_S),IRIX) NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_MKDTEMP = YesPlease # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), @@ -291,7 +281,6 @@ ifeq ($(uname_S),IRIX64) NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_MKDTEMP = YesPlease # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), @@ -311,7 +300,6 @@ ifeq ($(uname_S),HP-UX) NO_SETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_MKDTEMP = YesPlease NO_UNSETENV = YesPlease @@ -352,7 +340,6 @@ ifeq ($(uname_S),Windows) NO_ICONV = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease SNPRINTF_RETURNS_BOGUS = YesPlease NO_SVN_TESTS = YesPlease RUNTIME_PREFIX = YesPlease @@ -402,7 +389,6 @@ ifeq ($(uname_S),Interix) NO_MKDTEMP = YesPlease NO_STRTOUMAX = YesPlease NO_NSEC = YesPlease - NO_MKSTEMPS = YesPlease ifeq ($(uname_R),3.5) NO_INET_NTOP = YesPlease NO_INET_PTON = YesPlease @@ -461,7 +447,6 @@ ifeq ($(uname_S),NONSTOP_KERNEL) NO_SETENV = YesPlease NO_UNSETENV = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease # Currently libiconv-1.9.1. OLD_ICONV = UnfortunatelyYes NO_REGEX = YesPlease @@ -503,7 +488,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) NEEDS_LIBICONV = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_SVN_TESTS = YesPlease NO_PERL_MAKEMAKER = YesPlease RUNTIME_PREFIX = YesPlease @@ -515,7 +499,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo NO_REGEX = YesPlease NO_PYTHON = YesPlease - BLK_SHA1 = YesPlease ETAGS_TARGET = ETAGS NO_INET_PTON = YesPlease NO_INET_NTOP = YesPlease @@ -584,7 +567,6 @@ ifeq ($(uname_S),QNX) NO_ICONV = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_NSEC = YesPlease NO_PTHREADS = YesPlease NO_R_TO_GCC_LINKER = YesPlease diff --git a/configure.ac b/configure.ac index 0b15f04b10..128165529f 100644 --- a/configure.ac +++ b/configure.ac @@ -1050,12 +1050,6 @@ GIT_CHECK_FUNC(mkdtemp, [NO_MKDTEMP=YesPlease]) GIT_CONF_SUBST([NO_MKDTEMP]) # -# Define NO_MKSTEMPS if you don't have mkstemps in the C library. -GIT_CHECK_FUNC(mkstemps, -[NO_MKSTEMPS=], -[NO_MKSTEMPS=YesPlease]) -GIT_CONF_SUBST([NO_MKSTEMPS]) -# # Define NO_INITGROUPS if you don't have initgroups in the C library. GIT_CHECK_FUNC(initgroups, [NO_INITGROUPS=], diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci index 2d7f25d99f..4ba98b7eaf 100644 --- a/contrib/coccinelle/array.cocci +++ b/contrib/coccinelle/array.cocci @@ -24,3 +24,19 @@ expression n; @@ - memcpy(dst, src, n * sizeof(T)); + COPY_ARRAY(dst, src, n); + +@@ +type T; +T *ptr; +expression n; +@@ +- ptr = xmalloc(n * sizeof(*ptr)); ++ ALLOC_ARRAY(ptr, n); + +@@ +type T; +T *ptr; +expression n; +@@ +- ptr = xmalloc(n * sizeof(T)); ++ ALLOC_ARRAY(ptr, n); diff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci index 63995f22ff..1d580e49b0 100644 --- a/contrib/coccinelle/strbuf.cocci +++ b/contrib/coccinelle/strbuf.cocci @@ -38,3 +38,9 @@ expression E1, E2, E3; @@ - strbuf_addstr(E1, find_unique_abbrev(E2, E3)); + strbuf_add_unique_abbrev(E1, E2, E3); + +@@ +expression E1, E2; +@@ +- strbuf_addstr(E1, real_path(E2)); ++ strbuf_add_real_path(E1, E2); diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index 712a1377e2..1c3d87f861 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -1,13 +1,11 @@ -#!/usr/bin/env python +#!/bin/sh -import sys - -sys.stderr.write('WARNING: git-remote-bzr is now maintained independently.\n') -sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-bzr\n') - -sys.stderr.write('''WARNING: +cat >&2 <<'EOT' +WARNING: git-remote-bzr is now maintained independently. +WARNING: For more information visit https://github.com/felipec/git-remote-bzr +WARNING: WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-bzr \\ +WARNING: $ wget -O $HOME/bin/git-remote-bzr \ WARNING: https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr WARNING: $ chmod +x $HOME/bin/git-remote-bzr -''') +EOT diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 4255ad6312..8e9188364c 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -1,13 +1,11 @@ -#!/usr/bin/env python +#!/bin/sh -import sys - -sys.stderr.write('WARNING: git-remote-hg is now maintained independently.\n') -sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-hg\n') - -sys.stderr.write('''WARNING: +cat >&2 <<'EOT' +WARNING: git-remote-hg is now maintained independently. +WARNING: For more information visit https://github.com/felipec/git-remote-hg +WARNING: WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-hg \\ +WARNING: $ wget -O $HOME/bin/git-remote-hg \ WARNING: https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg WARNING: $ chmod +x $HOME/bin/git-remote-hg -''') +EOT @@ -2870,8 +2870,25 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) s->should_free = 1; return 0; } - if (size_only) + + /* + * Even if the caller would be happy with getting + * only the size, we cannot return early at this + * point if the path requires us to run the content + * conversion. + */ + if (size_only && !would_convert_to_git(s->path)) return 0; + + /* + * Note: this check uses xsize_t(st.st_size) that may + * not be the true size of the blob after it goes + * through convert_to_git(). This may not strictly be + * correct, but the whole point of big_file_threshold + * and is_binary check being that we want to avoid + * opening the file and inspecting the contents, this + * is probably fine. + */ if ((flags & CHECK_BINARY) && s->size > big_file_threshold && s->is_binary == -1) { s->is_binary = 1; @@ -2730,8 +2730,8 @@ void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_) { struct strbuf file_name = STRBUF_INIT; struct strbuf rel_path = STRBUF_INIT; - char *git_dir = real_pathdup(git_dir_); - char *work_tree = real_pathdup(work_tree_); + char *git_dir = real_pathdup(git_dir_, 1); + char *work_tree = real_pathdup(work_tree_, 1); /* Update gitfile */ strbuf_addf(&file_name, "%s/.git", work_tree); diff --git a/environment.c b/environment.c index c07fb17fb7..42dc3106d2 100644 --- a/environment.c +++ b/environment.c @@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree) return; } git_work_tree_initialized = 1; - work_tree = real_pathdup(new_work_tree); + work_tree = real_pathdup(new_work_tree, 1); } const char *get_git_work_tree(void) diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c index 61f6a43579..f73210973f 100644 --- a/ewah/ewah_io.c +++ b/ewah/ewah_io.c @@ -142,8 +142,8 @@ int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len) * the endianness conversion in a separate pass to ensure * we're loading 8-byte aligned words. */ - memcpy(self->buffer, ptr, self->buffer_size * sizeof(uint64_t)); - ptr += self->buffer_size * sizeof(uint64_t); + memcpy(self->buffer, ptr, self->buffer_size * sizeof(eword_t)); + ptr += self->buffer_size * sizeof(eword_t); for (i = 0; i < self->buffer_size; ++i) self->buffer[i] = ntohll(self->buffer[i]); diff --git a/fetch-pack.c b/fetch-pack.c index e0f5d5ce87..d07d85ce30 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -614,7 +614,7 @@ static void filter_refs(struct fetch_pack_args *args, break; /* definitely do not have it */ else if (cmp == 0) { keep = 1; /* definitely have it */ - sought[i]->matched = 1; + sought[i]->match_status = REF_MATCHED; } i++; } @@ -634,22 +634,24 @@ static void filter_refs(struct fetch_pack_args *args, } /* Append unmatched requests to the list */ - if ((allow_unadvertised_object_request & - (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) { - for (i = 0; i < nr_sought; i++) { - unsigned char sha1[20]; + for (i = 0; i < nr_sought; i++) { + unsigned char sha1[20]; - ref = sought[i]; - if (ref->matched) - continue; - if (get_sha1_hex(ref->name, sha1) || - ref->name[40] != '\0' || - hashcmp(sha1, ref->old_oid.hash)) - continue; + ref = sought[i]; + if (ref->match_status != REF_NOT_MATCHED) + continue; + if (get_sha1_hex(ref->name, sha1) || + ref->name[40] != '\0' || + hashcmp(sha1, ref->old_oid.hash)) + continue; - ref->matched = 1; + if ((allow_unadvertised_object_request & + (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) { + ref->match_status = REF_MATCHED; *newtail = copy_ref(ref); newtail = &(*newtail)->next; + } else { + ref->match_status = REF_UNADVERTISED_NOT_ALLOWED; } } *refs = newlist; @@ -1130,3 +1132,26 @@ struct ref *fetch_pack(struct fetch_pack_args *args, clear_shallow_info(&si); return ref_cpy; } + +int report_unmatched_refs(struct ref **sought, int nr_sought) +{ + int i, ret = 0; + + for (i = 0; i < nr_sought; i++) { + if (!sought[i]) + continue; + switch (sought[i]->match_status) { + case REF_MATCHED: + continue; + case REF_NOT_MATCHED: + error(_("no such remote ref %s"), sought[i]->name); + break; + case REF_UNADVERTISED_NOT_ALLOWED: + error(_("Server does not allow request for unadvertised object %s"), + sought[i]->name); + break; + } + ret = 1; + } + return ret; +} diff --git a/fetch-pack.h b/fetch-pack.h index c912e3d321..a2d46e6e75 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -45,4 +45,10 @@ struct ref *fetch_pack(struct fetch_pack_args *args, struct sha1_array *shallow, char **pack_lockfile); +/* + * Print an appropriate error message for each sought ref that wasn't + * matched. Return 0 if all sought refs were matched, otherwise 1. + */ +int report_unmatched_refs(struct ref **sought, int nr_sought); + #endif diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 982593c89e..f5c816e273 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -92,7 +92,7 @@ sub colored { } # command line options -my $cmd; +my $patch_mode_only; my $patch_mode; my $patch_mode_revision; @@ -1299,7 +1299,7 @@ sub patch_update_cmd { } return 0; } - if ($patch_mode) { + if ($patch_mode_only) { @them = @mods; } else { @@ -1721,7 +1721,7 @@ sub process_args { die sprintf(__("invalid argument %s, expecting --"), $arg) unless $arg eq "--"; %patch_mode_flavour = %{$patch_modes{$patch_mode}}; - $cmd = 1; + $patch_mode_only = 1; } elsif ($arg ne "--") { die sprintf(__("invalid argument %s, expecting --"), $arg); @@ -1758,7 +1758,7 @@ sub main_loop { process_args(); refresh(); -if ($cmd) { +if ($patch_mode_only) { patch_update_cmd(); } else { diff --git a/git-compat-util.h b/git-compat-util.h index ef6d560e15..e626851fe9 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -639,11 +639,6 @@ extern int gitsetenv(const char *, const char *, int); extern char *gitmkdtemp(char *); #endif -#ifdef NO_MKSTEMPS -#define mkstemps gitmkstemps -extern int gitmkstemps(char *, int); -#endif - #ifdef NO_UNSETENV #define unsetenv gitunsetenv extern void gitunsetenv(const char *); diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 86b2ff1e07..2b8cdba157 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -46,6 +46,8 @@ git_commit_non_empty_tree() { if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then map "$3" + elif test $# = 1 && test "$1" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904; then + : else git commit-tree "$@" fi diff --git a/git-send-email.perl b/git-send-email.perl index 068d60b3e6..eea0a517f7 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1563,7 +1563,7 @@ foreach my $t (@files) { # Now parse the message body while(<$fh>) { $message .= $_; - if (/^(Signed-off-by|Cc): (.*)$/i) { + if (/^(Signed-off-by|Cc): ([^>]*>?)/i) { chomp; my ($what, $c) = ($1, $2); chomp $c; diff --git a/git-stash.sh b/git-stash.sh index 10c284d1aa..9c70662cc8 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -7,8 +7,11 @@ USAGE="list [<options>] or: $dashless drop [-q|--quiet] [<stash>] or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>] or: $dashless branch <branchname> [<stash>] - or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] - [-u|--include-untracked] [-a|--all] [<message>]] + or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [<message>] + or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet] + [-u|--include-untracked] [-a|--all] [-m <message>] + [-- <pathspec>...]] or: $dashless clear" SUBDIRECTORY_OK=Yes @@ -33,15 +36,15 @@ else fi no_changes () { - git diff-index --quiet --cached HEAD --ignore-submodules -- && - git diff-files --quiet --ignore-submodules && + git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" && + git diff-files --quiet --ignore-submodules -- "$@" && (test -z "$untracked" || test -z "$(untracked_files)") } untracked_files () { excl_opt=--exclude-standard test "$untracked" = "all" && excl_opt= - git ls-files -o -z $excl_opt + git ls-files -o -z $excl_opt -- "$@" } clear_stash () { @@ -56,11 +59,29 @@ clear_stash () { } create_stash () { - stash_msg="$1" - untracked="$2" + stash_msg= + untracked= + while test $# != 0 + do + case "$1" in + -m|--message) + shift + stash_msg=${1?"BUG: create_stash () -m requires an argument"} + ;; + -u|--include-untracked) + shift + untracked=${1?"BUG: create_stash () -u requires an argument"} + ;; + --) + shift + break + ;; + esac + shift + done git update-index -q --refresh - if no_changes + if no_changes "$@" then exit 0 fi @@ -92,7 +113,7 @@ create_stash () { # Untracked files are stored by themselves in a parentless commit, for # ease of unpacking later. u_commit=$( - untracked_files | ( + untracked_files "$@" | ( GIT_INDEX_FILE="$TMPindex" && export GIT_INDEX_FILE && rm -f "$TMPindex" && @@ -115,7 +136,7 @@ create_stash () { git read-tree --index-output="$TMPindex" -m $i_tree && GIT_INDEX_FILE="$TMPindex" && export GIT_INDEX_FILE && - git diff-index --name-only -z HEAD -- >"$TMP-stagenames" && + git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" && git update-index -z --add --remove --stdin <"$TMP-stagenames" && git write-tree && rm -f "$TMPindex" @@ -129,7 +150,7 @@ create_stash () { # find out what the user wants GIT_INDEX_FILE="$TMP-index" \ - git add--interactive --patch=stash -- && + git add--interactive --patch=stash -- "$@" && # state of the working tree w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) || @@ -189,10 +210,11 @@ store_stash () { return $ret } -save_stash () { +push_stash () { keep_index= patch_mode= untracked= + stash_msg= while test $# != 0 do case "$1" in @@ -216,6 +238,11 @@ save_stash () { -a|--all) untracked=all ;; + -m|--message) + shift + test -z ${1+x} && usage + stash_msg=$1 + ;; --help) show_help ;; @@ -251,29 +278,38 @@ save_stash () { die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")" fi - stash_msg="$*" + test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1 git update-index -q --refresh - if no_changes + if no_changes "$@" then say "$(gettext "No local changes to save")" exit 0 fi + git reflog exists $ref_stash || clear_stash || die "$(gettext "Cannot initialize stash")" - create_stash "$stash_msg" $untracked + create_stash -m "$stash_msg" -u "$untracked" -- "$@" store_stash -m "$stash_msg" -q $w_commit || die "$(gettext "Cannot save the current status")" say "$(eval_gettext "Saved working directory and index state \$stash_msg")" if test -z "$patch_mode" then - git reset --hard ${GIT_QUIET:+-q} + if test $# != 0 + then + git reset ${GIT_QUIET:+-q} -- "$@" + git ls-files -z --modified -- "$@" | + git checkout-index -z --force --stdin + git clean --force ${GIT_QUIET:+-q} -d -- "$@" + else + git reset --hard ${GIT_QUIET:+-q} + fi test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION= if test -n "$untracked" then - git clean --force --quiet -d $CLEAN_X_OPTION + git clean --force --quiet -d $CLEAN_X_OPTION -- "$@" fi if test "$keep_index" = "t" && test -n "$i_tree" @@ -291,6 +327,36 @@ save_stash () { fi } +save_stash () { + push_options= + while test $# != 0 + do + case "$1" in + --) + shift + break + ;; + -*) + # pass all options through to push_stash + push_options="$push_options $1" + ;; + *) + break + ;; + esac + shift + done + + stash_msg="$*" + + if test -z "$stash_msg" + then + push_stash $push_options + else + push_stash $push_options -m "$stash_msg" + fi +} + have_stash () { git rev-parse --verify --quiet $ref_stash >/dev/null } @@ -590,18 +656,21 @@ apply_to_branch () { } } +test "$1" = "-p" && set "push" "$@" + PARSE_CACHE='--not-parsed' -# The default command is "save" if nothing but options are given +# The default command is "push" if nothing but options are given seen_non_option= for opt do case "$opt" in + --) break ;; -*) ;; *) seen_non_option=t; break ;; esac done -test -n "$seen_non_option" || set "save" "$@" +test -n "$seen_non_option" || set "push" "$@" # Main command set case "$1" in @@ -617,6 +686,10 @@ save) shift save_stash "$@" ;; +push) + shift + push_stash "$@" + ;; apply) shift apply_stash "$@" @@ -627,7 +700,7 @@ clear) ;; create) shift - create_stash "$*" && echo "$w_commit" + create_stash -m "$*" && echo "$w_commit" ;; store) shift @@ -648,7 +721,7 @@ branch) *) case $# in 0) - save_stash && + push_stash && say "$(gettext "(To restore them type \"git stash apply\")")" ;; *) @@ -53,6 +53,14 @@ int get_oid_hex(const char *hex, struct object_id *oid) return get_sha1_hex(hex, oid->hash); } +int parse_oid_hex(const char *hex, struct object_id *oid, const char **end) +{ + int ret = get_oid_hex(hex, oid); + if (!ret) + *end = hex + GIT_SHA1_HEXSZ; + return ret; +} + char *sha1_to_hex_r(char *buffer, const unsigned char *sha1) { static const char hex[] = "0123456789abcdef"; diff --git a/http-walker.c b/http-walker.c index b34b6ace7c..ab7d5537ae 100644 --- a/http-walker.c +++ b/http-walker.c @@ -168,6 +168,11 @@ static int is_alternate_allowed(const char *url) }; int i; + if (http_follow_config != HTTP_FOLLOW_ALWAYS) { + warning("alternate disabled by http.followRedirects: %s", url); + return 0; + } + for (i = 0; i < ARRAY_SIZE(protocols); i++) { const char *end; if (skip_prefix(url, protocols[i], &end) && @@ -314,6 +319,8 @@ static void process_alternates_response(void *callback_data) while (tail->next != NULL) tail = tail->next; tail->next = newalt; + } else { + strbuf_release(&target); } } } @@ -331,9 +338,6 @@ static void fetch_alternates(struct walker *walker, const char *base) struct alternates_request alt_req; struct walker_data *cdata = walker->data; - if (http_follow_config != HTTP_FOLLOW_ALWAYS) - return; - /* * If another request has already started fetching alternates, * wait for them to arrive and return to processing this request's @@ -109,7 +109,7 @@ static int curl_save_cookies; struct credential http_auth = CREDENTIAL_INIT; static int http_proactive_auth; static const char *user_agent; -static int curl_empty_auth; +static int curl_empty_auth = -1; enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL; @@ -125,6 +125,14 @@ static struct credential cert_auth = CREDENTIAL_INIT; static int ssl_cert_password_required; #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY static unsigned long http_auth_methods = CURLAUTH_ANY; +static int http_auth_methods_restricted; +/* Modes for which empty_auth cannot actually help us. */ +static unsigned long empty_auth_useless = + CURLAUTH_BASIC +#ifdef CURLAUTH_DIGEST_IE + | CURLAUTH_DIGEST_IE +#endif + | CURLAUTH_DIGEST; #endif static struct curl_slist *pragma_header; @@ -333,7 +341,10 @@ static int http_options(const char *var, const char *value, void *cb) return git_config_string(&user_agent, var, value); if (!strcmp("http.emptyauth", var)) { - curl_empty_auth = git_config_bool(var, value); + if (value && !strcmp("auto", value)) + curl_empty_auth = -1; + else + curl_empty_auth = git_config_bool(var, value); return 0; } @@ -382,10 +393,37 @@ static int http_options(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } +static int curl_empty_auth_enabled(void) +{ + if (curl_empty_auth >= 0) + return curl_empty_auth; + +#ifndef LIBCURL_CAN_HANDLE_AUTH_ANY + /* + * Our libcurl is too old to do AUTH_ANY in the first place; + * just default to turning the feature off. + */ +#else + /* + * In the automatic case, kick in the empty-auth + * hack as long as we would potentially try some + * method more exotic than "Basic" or "Digest". + * + * But only do this when this is our second or + * subsequent request, as by then we know what + * methods are available. + */ + if (http_auth_methods_restricted && + (http_auth_methods & ~empty_auth_useless)) + return 1; +#endif + return 0; +} + static void init_curl_http_auth(CURL *result) { if (!http_auth.username || !*http_auth.username) { - if (curl_empty_auth) + if (curl_empty_auth_enabled()) curl_easy_setopt(result, CURLOPT_USERPWD, ":"); return; } @@ -1079,7 +1117,7 @@ struct active_request_slot *get_active_slot(void) #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods); #endif - if (http_auth.password || curl_empty_auth) + if (http_auth.password || curl_empty_auth_enabled()) init_curl_http_auth(slot->curl); return slot; @@ -1347,6 +1385,10 @@ static int handle_curl_result(struct slot_results *results) } else { #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + if (results->auth_avail) { + http_auth_methods &= results->auth_avail; + http_auth_methods_restricted = 1; + } #endif return HTTP_REAUTH; } @@ -1727,6 +1769,9 @@ static int http_request_reauth(const char *url, { int ret = http_request(url, result, target, options); + if (ret != HTTP_OK && ret != HTTP_REAUTH) + return ret; + if (options && options->effective_url && options->base_url) { if (update_url_from_redirect(options->base_url, url, options->effective_url)) { @@ -153,7 +153,7 @@ static void copy_email(const struct passwd *pw, struct strbuf *email, const char *ident_default_name(void) { - if (!git_default_name.len) { + if (!(ident_config_given & IDENT_NAME_GIVEN) && !git_default_name.len) { copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name); strbuf_trim(&git_default_name); } @@ -162,7 +162,7 @@ const char *ident_default_name(void) const char *ident_default_email(void) { - if (!git_default_email.len) { + if (!(ident_config_given & IDENT_MAIL_GIVEN) && !git_default_email.len) { const char *email = getenv("EMAIL"); if (email && email[0]) { @@ -203,6 +203,15 @@ static int crud(unsigned char c) c == '\''; } +static int has_non_crud(const char *str) +{ + for (; *str; str++) { + if (!crud(*str)) + return 1; + } + return 0; +} + /* * Copy over a string to the destination, but avoid special * characters ('\n', '<' and '>') and remove crud at the end @@ -351,19 +360,32 @@ const char *fmt_ident(const char *name, const char *email, int want_date = !(flag & IDENT_NO_DATE); int want_name = !(flag & IDENT_NO_NAME); + if (!email) { + if (strict && ident_use_config_only + && !(ident_config_given & IDENT_MAIL_GIVEN)) { + fputs(_(env_hint), stderr); + die(_("no email was given and auto-detection is disabled")); + } + email = ident_default_email(); + if (strict && default_email_is_bogus) { + fputs(_(env_hint), stderr); + die(_("unable to auto-detect email address (got '%s')"), email); + } + } + if (want_name) { int using_default = 0; if (!name) { if (strict && ident_use_config_only && !(ident_config_given & IDENT_NAME_GIVEN)) { fputs(_(env_hint), stderr); - die("no name was given and auto-detection is disabled"); + die(_("no name was given and auto-detection is disabled")); } name = ident_default_name(); using_default = 1; if (strict && default_name_is_bogus) { fputs(_(env_hint), stderr); - die("unable to auto-detect name (got '%s')", name); + die(_("unable to auto-detect name (got '%s')"), name); } } if (!*name) { @@ -371,24 +393,13 @@ const char *fmt_ident(const char *name, const char *email, if (strict) { if (using_default) fputs(_(env_hint), stderr); - die("empty ident name (for <%s>) not allowed", email); + die(_("empty ident name (for <%s>) not allowed"), email); } pw = xgetpwuid_self(NULL); name = pw->pw_name; } - } - - if (!email) { - if (strict && ident_use_config_only - && !(ident_config_given & IDENT_MAIL_GIVEN)) { - fputs(_(env_hint), stderr); - die("no email was given and auto-detection is disabled"); - } - email = ident_default_email(); - if (strict && default_email_is_bogus) { - fputs(_(env_hint), stderr); - die("unable to auto-detect email address (got '%s')", email); - } + if (strict && !has_non_crud(name)) + die(_("name consists only of disallowed characters: %s"), name); } strbuf_reset(&ident); @@ -403,7 +414,7 @@ const char *fmt_ident(const char *name, const char *email, strbuf_addch(&ident, ' '); if (date_str && date_str[0]) { if (parse_date(date_str, &ident) < 0) - die("invalid date format: %s", date_str); + die(_("invalid date format: %s"), date_str); } else strbuf_addstr(&ident, ident_default_date()); diff --git a/line-log.c b/line-log.c index 65f3558b3b..a23b910471 100644 --- a/line-log.c +++ b/line-log.c @@ -43,9 +43,10 @@ void range_set_release(struct range_set *rs) static void range_set_copy(struct range_set *dst, struct range_set *src) { range_set_init(dst, src->nr); - memcpy(dst->ranges, src->ranges, src->nr*sizeof(struct range_set)); + COPY_ARRAY(dst->ranges, src->ranges, src->nr); dst->nr = src->nr; } + static void range_set_move(struct range_set *dst, struct range_set *src) { range_set_release(dst); @@ -144,7 +145,7 @@ void sort_and_merge_range_set(struct range_set *rs) static void range_set_union(struct range_set *out, struct range_set *a, struct range_set *b) { - int i = 0, j = 0, o = 0; + int i = 0, j = 0; struct range *ra = a->ranges; struct range *rb = b->ranges; /* cannot make an alias of out->ranges: it may change during grow */ @@ -167,16 +168,15 @@ static void range_set_union(struct range_set *out, new = &rb[j++]; if (new->start == new->end) ; /* empty range */ - else if (!o || out->ranges[o-1].end < new->start) { + else if (!out->nr || out->ranges[out->nr-1].end < new->start) { range_set_grow(out, 1); - out->ranges[o].start = new->start; - out->ranges[o].end = new->end; - o++; - } else if (out->ranges[o-1].end < new->end) { - out->ranges[o-1].end = new->end; + out->ranges[out->nr].start = new->start; + out->ranges[out->nr].end = new->end; + out->nr++; + } else if (out->ranges[out->nr-1].end < new->end) { + out->ranges[out->nr-1].end = new->end; } } - out->nr = o; } /* diff --git a/log-tree.c b/log-tree.c index 8c2415747a..4618dd04ca 100644 --- a/log-tree.c +++ b/log-tree.c @@ -332,35 +332,31 @@ void fmt_output_commit(struct strbuf *filename, strbuf_release(&subject); } +void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt) +{ + if (opt->total > 0) { + strbuf_addf(sb, "Subject: [%s%s%0*d/%d] ", + opt->subject_prefix, + *opt->subject_prefix ? " " : "", + digits_in_number(opt->total), + opt->nr, opt->total); + } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) { + strbuf_addf(sb, "Subject: [%s] ", + opt->subject_prefix); + } else { + strbuf_addstr(sb, "Subject: "); + } +} + void log_write_email_headers(struct rev_info *opt, struct commit *commit, - const char **subject_p, const char **extra_headers_p, int *need_8bit_cte_p) { - const char *subject = NULL; const char *extra_headers = opt->extra_headers; const char *name = oid_to_hex(opt->zero_commit ? &null_oid : &commit->object.oid); *need_8bit_cte_p = 0; /* unknown */ - if (opt->total > 0) { - static char buffer[64]; - snprintf(buffer, sizeof(buffer), - "Subject: [%s%s%0*d/%d] ", - opt->subject_prefix, - *opt->subject_prefix ? " " : "", - digits_in_number(opt->total), - opt->nr, opt->total); - subject = buffer; - } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) { - static char buffer[256]; - snprintf(buffer, sizeof(buffer), - "Subject: [%s] ", - opt->subject_prefix); - subject = buffer; - } else { - subject = "Subject: "; - } fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name); graph_show_oneline(opt->graph); @@ -417,7 +413,6 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, opt->diffopt.stat_sep = buffer; strbuf_release(&filename); } - *subject_p = subject; *extra_headers_p = extra_headers; } @@ -602,8 +597,10 @@ void show_log(struct rev_info *opt) */ if (cmit_fmt_is_mail(opt->commit_format)) { - log_write_email_headers(opt, commit, &ctx.subject, &extra_headers, + log_write_email_headers(opt, commit, &extra_headers, &ctx.need_8bit_cte); + ctx.rev = opt; + ctx.print_email_subject = 1; } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file); if (opt->commit_format != CMIT_FMT_ONELINE) diff --git a/log-tree.h b/log-tree.h index c8116e60cd..48f11fb740 100644 --- a/log-tree.h +++ b/log-tree.h @@ -22,7 +22,6 @@ void format_decorations_extended(struct strbuf *sb, const struct commit *commit, format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")") void show_decorations(struct rev_info *opt, struct commit *commit); void log_write_email_headers(struct rev_info *opt, struct commit *commit, - const char **subject_p, const char **extra_headers_p, int *need_8bit_cte_p); void load_ref_decorations(int flags); @@ -30,5 +29,6 @@ void load_ref_decorations(int flags); #define FORMAT_PATCH_NAME_MAX 64 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *); void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *); +void fmt_output_email_subject(struct strbuf *, struct rev_info *); #endif @@ -1607,8 +1607,9 @@ void pp_title_line(struct pretty_print_context *pp, pp->preserve_subject ? "\n" : " "); strbuf_grow(sb, title.len + 1024); - if (pp->subject) { - strbuf_addstr(sb, pp->subject); + if (pp->print_email_subject) { + if (pp->rev) + fmt_output_email_subject(sb, pp->rev); if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT)) add_rfc2047(sb, title.buf, title.len, encoding, RFC2047_SUBJECT); @@ -1818,7 +1819,7 @@ void pretty_print_commit(struct pretty_print_context *pp, } pp_header(pp, encoding, commit, &msg, sb); - if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) { + if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) { strbuf_addch(sb, '\n'); } diff --git a/reachable.c b/reachable.c index d0199cace4..a8a979bd4f 100644 --- a/reachable.c +++ b/reachable.c @@ -58,7 +58,7 @@ struct recent_data { unsigned long timestamp; }; -static void add_recent_object(const unsigned char *sha1, +static void add_recent_object(const struct object_id *oid, unsigned long mtime, struct recent_data *data) { @@ -75,37 +75,37 @@ static void add_recent_object(const unsigned char *sha1, * later processing, and the revision machinery expects * commits and tags to have been parsed. */ - type = sha1_object_info(sha1, NULL); + type = sha1_object_info(oid->hash, NULL); if (type < 0) - die("unable to get object info for %s", sha1_to_hex(sha1)); + die("unable to get object info for %s", oid_to_hex(oid)); switch (type) { case OBJ_TAG: case OBJ_COMMIT: - obj = parse_object_or_die(sha1, NULL); + obj = parse_object_or_die(oid->hash, NULL); break; case OBJ_TREE: - obj = (struct object *)lookup_tree(sha1); + obj = (struct object *)lookup_tree(oid->hash); break; case OBJ_BLOB: - obj = (struct object *)lookup_blob(sha1); + obj = (struct object *)lookup_blob(oid->hash); break; default: die("unknown object type for %s: %s", - sha1_to_hex(sha1), typename(type)); + oid_to_hex(oid), typename(type)); } if (!obj) - die("unable to lookup %s", sha1_to_hex(sha1)); + die("unable to lookup %s", oid_to_hex(oid)); add_pending_object(data->revs, obj, ""); } -static int add_recent_loose(const unsigned char *sha1, +static int add_recent_loose(const struct object_id *oid, const char *path, void *data) { struct stat st; - struct object *obj = lookup_object(sha1); + struct object *obj = lookup_object(oid->hash); if (obj && obj->flags & SEEN) return 0; @@ -119,22 +119,22 @@ static int add_recent_loose(const unsigned char *sha1, */ if (errno == ENOENT) return 0; - return error_errno("unable to stat %s", sha1_to_hex(sha1)); + return error_errno("unable to stat %s", oid_to_hex(oid)); } - add_recent_object(sha1, st.st_mtime, data); + add_recent_object(oid, st.st_mtime, data); return 0; } -static int add_recent_packed(const unsigned char *sha1, +static int add_recent_packed(const struct object_id *oid, struct packed_git *p, uint32_t pos, void *data) { - struct object *obj = lookup_object(sha1); + struct object *obj = lookup_object(oid->hash); if (obj && obj->flags & SEEN) return 0; - add_recent_object(sha1, p->mtime, data); + add_recent_object(oid, p->mtime, data); return 0; } diff --git a/read-cache.c b/read-cache.c index 9054369dd0..e447751823 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1558,10 +1558,27 @@ static void tweak_untracked_cache(struct index_state *istate) } } +static void tweak_split_index(struct index_state *istate) +{ + switch (git_config_get_split_index()) { + case -1: /* unset: do nothing */ + break; + case 0: /* false */ + remove_split_index(istate); + break; + case 1: /* true */ + add_split_index(istate); + break; + default: /* unknown value: do nothing */ + break; + } +} + static void post_read_index_from(struct index_state *istate) { check_ce_order(istate); tweak_untracked_cache(istate); + tweak_split_index(istate); } /* remember to discard_cache() before reading a different cache! */ @@ -1657,10 +1674,25 @@ unmap: die("index file corrupt"); } +/* + * Signal that the shared index is used by updating its mtime. + * + * This way, shared index can be removed if they have not been used + * for some time. + */ +static void freshen_shared_index(char *base_sha1_hex, int warn) +{ + const char *shared_index = git_path("sharedindex.%s", base_sha1_hex); + if (!check_and_freshen_file(shared_index, 1) && warn) + warning("could not freshen shared index '%s'", shared_index); +} + int read_index_from(struct index_state *istate, const char *path) { struct split_index *split_index; int ret; + char *base_sha1_hex; + const char *base_path; /* istate->initialized covers both .git/index and .git/sharedindex.xxx */ if (istate->initialized) @@ -1678,15 +1710,16 @@ int read_index_from(struct index_state *istate, const char *path) discard_index(split_index->base); else split_index->base = xcalloc(1, sizeof(*split_index->base)); - ret = do_read_index(split_index->base, - git_path("sharedindex.%s", - sha1_to_hex(split_index->base_sha1)), 1); + + base_sha1_hex = sha1_to_hex(split_index->base_sha1); + base_path = git_path("sharedindex.%s", base_sha1_hex); + ret = do_read_index(split_index->base, base_path, 1); if (hashcmp(split_index->base_sha1, split_index->base->sha1)) die("broken index, expect %s in %s, got %s", - sha1_to_hex(split_index->base_sha1), - git_path("sharedindex.%s", - sha1_to_hex(split_index->base_sha1)), + base_sha1_hex, base_path, sha1_to_hex(split_index->base->sha1)); + + freshen_shared_index(base_sha1_hex, 0); merge_base_index(istate); post_read_index_from(istate); return ret; @@ -2169,6 +2202,65 @@ static int write_split_index(struct index_state *istate, return ret; } +static const char *shared_index_expire = "2.weeks.ago"; + +static unsigned long get_shared_index_expire_date(void) +{ + static unsigned long shared_index_expire_date; + static int shared_index_expire_date_prepared; + + if (!shared_index_expire_date_prepared) { + git_config_get_expiry("splitindex.sharedindexexpire", + &shared_index_expire); + shared_index_expire_date = approxidate(shared_index_expire); + shared_index_expire_date_prepared = 1; + } + + return shared_index_expire_date; +} + +static int should_delete_shared_index(const char *shared_index_path) +{ + struct stat st; + unsigned long expiration; + + /* Check timestamp */ + expiration = get_shared_index_expire_date(); + if (!expiration) + return 0; + if (stat(shared_index_path, &st)) + return error_errno(_("could not stat '%s"), shared_index_path); + if (st.st_mtime > expiration) + return 0; + + return 1; +} + +static int clean_shared_index_files(const char *current_hex) +{ + struct dirent *de; + DIR *dir = opendir(get_git_dir()); + + if (!dir) + return error_errno(_("unable to open git dir: %s"), get_git_dir()); + + while ((de = readdir(dir)) != NULL) { + const char *sha1_hex; + const char *shared_index_path; + if (!skip_prefix(de->d_name, "sharedindex.", &sha1_hex)) + continue; + if (!strcmp(sha1_hex, current_hex)) + continue; + shared_index_path = git_path("%s", de->d_name); + if (should_delete_shared_index(shared_index_path) > 0 && + unlink(shared_index_path)) + warning_errno(_("unable to unlink: %s"), shared_index_path); + } + closedir(dir); + + return 0; +} + static struct tempfile temporary_sharedindex; static int write_shared_index(struct index_state *istate, @@ -2190,14 +2282,48 @@ static int write_shared_index(struct index_state *istate, } ret = rename_tempfile(&temporary_sharedindex, git_path("sharedindex.%s", sha1_to_hex(si->base->sha1))); - if (!ret) + if (!ret) { hashcpy(si->base_sha1, si->base->sha1); + clean_shared_index_files(sha1_to_hex(si->base->sha1)); + } + return ret; } +static const int default_max_percent_split_change = 20; + +static int too_many_not_shared_entries(struct index_state *istate) +{ + int i, not_shared = 0; + int max_split = git_config_get_max_percent_split_change(); + + switch (max_split) { + case -1: + /* not or badly configured: use the default value */ + max_split = default_max_percent_split_change; + break; + case 0: + return 1; /* 0% means always write a new shared index */ + case 100: + return 0; /* 100% means never write a new shared index */ + default: + break; /* just use the configured value */ + } + + /* Count not shared entries */ + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + if (!ce->index) + not_shared++; + } + + return (int64_t)istate->cache_nr * max_split < (int64_t)not_shared * 100; +} + int write_locked_index(struct index_state *istate, struct lock_file *lock, unsigned flags) { + int new_shared_index, ret; struct split_index *si = istate->split_index; if (!si || alternate_index_output || @@ -2212,13 +2338,24 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, if ((v & 15) < 6) istate->cache_changed |= SPLIT_INDEX_ORDERED; } - if (istate->cache_changed & SPLIT_INDEX_ORDERED) { - int ret = write_shared_index(istate, lock, flags); + if (too_many_not_shared_entries(istate)) + istate->cache_changed |= SPLIT_INDEX_ORDERED; + + new_shared_index = istate->cache_changed & SPLIT_INDEX_ORDERED; + + if (new_shared_index) { + ret = write_shared_index(istate, lock, flags); if (ret) return ret; } - return write_split_index(istate, lock, flags); + ret = write_split_index(istate, lock, flags); + + /* Freshen the shared index only if the split-index was written */ + if (!ret && !new_shared_index) + freshen_shared_index(sha1_to_hex(si->base_sha1), 1); + + return ret; } /* diff --git a/ref-filter.c b/ref-filter.c index 7eeecc608f..9c82b5b9d6 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1298,9 +1298,9 @@ static void populate_value(struct ref_array_item *ref) ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) { - unsigned char unused1[20]; + struct object_id unused1; ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING, - unused1, NULL); + unused1.hash, NULL); if (!ref->symref) ref->symref = ""; } diff --git a/reflog-walk.c b/reflog-walk.c index a246af2767..99679f5825 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -10,7 +10,7 @@ struct complete_reflogs { char *ref; const char *short_ref; struct reflog_info { - unsigned char osha1[20], nsha1[20]; + struct object_id ooid, noid; char *email; unsigned long timestamp; int tz; @@ -19,7 +19,7 @@ struct complete_reflogs { int nr, alloc; }; -static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1, +static int read_one_reflog(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -28,8 +28,8 @@ static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1, ALLOC_GROW(array->items, array->nr + 1, array->alloc); item = array->items + array->nr; - hashcpy(item->osha1, osha1); - hashcpy(item->nsha1, nsha1); + oidcpy(&item->ooid, ooid); + oidcpy(&item->noid, noid); item->email = xstrdup(email); item->timestamp = timestamp; item->tz = tz; @@ -45,11 +45,11 @@ static struct complete_reflogs *read_complete_reflog(const char *ref) reflogs->ref = xstrdup(ref); for_each_reflog_ent(ref, read_one_reflog, reflogs); if (reflogs->nr == 0) { - unsigned char sha1[20]; + struct object_id oid; const char *name; void *name_to_free; name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING, - sha1, NULL); + oid.hash, NULL); if (name) { for_each_reflog_ent(name, read_one_reflog, reflogs); free(name_to_free); @@ -172,18 +172,18 @@ int add_reflog_for_walk(struct reflog_walk_info *info, reflogs = item->util; else { if (*branch == '\0') { - unsigned char sha1[20]; + struct object_id oid; free(branch); - branch = resolve_refdup("HEAD", 0, sha1, NULL); + branch = resolve_refdup("HEAD", 0, oid.hash, NULL); if (!branch) die ("No current branch"); } reflogs = read_complete_reflog(branch); if (!reflogs || reflogs->nr == 0) { - unsigned char sha1[20]; + struct object_id oid; char *b; - if (dwim_log(branch, strlen(branch), sha1, &b) == 1) { + if (dwim_log(branch, strlen(branch), oid.hash, &b) == 1) { if (reflogs) { free(reflogs->ref); free(reflogs); @@ -238,13 +238,13 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) do { reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; commit_reflog->recno--; - logobj = parse_object(reflog->osha1); + logobj = parse_object(reflog->ooid.hash); } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT)); - if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->osha1)) { + if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->ooid.hash)) { /* a root commit, but there are still more entries to show */ reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; - logobj = parse_object(reflog->nsha1); + logobj = parse_object(reflog->noid.hash); } if (!logobj || logobj->type != OBJ_COMMIT) { @@ -405,7 +405,7 @@ int refname_match(const char *abbrev_name, const char *full_name) static char *substitute_branch_name(const char **string, int *len) { struct strbuf buf = STRBUF_INIT; - int ret = interpret_branch_name(*string, *len, &buf); + int ret = interpret_branch_name(*string, *len, &buf, 0); if (ret == *len) { size_t size; @@ -675,7 +675,7 @@ struct read_ref_at_cb { int *cutoff_cnt; }; -static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1, +static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -699,30 +699,30 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1, * hold the values for the previous record. */ if (!is_null_sha1(cb->osha1)) { - hashcpy(cb->sha1, nsha1); - if (hashcmp(cb->osha1, nsha1)) + hashcpy(cb->sha1, noid->hash); + if (hashcmp(cb->osha1, noid->hash)) warning("Log for ref %s has gap after %s.", cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); } else if (cb->date == cb->at_time) - hashcpy(cb->sha1, nsha1); - else if (hashcmp(nsha1, cb->sha1)) + hashcpy(cb->sha1, noid->hash); + else if (hashcmp(noid->hash, cb->sha1)) warning("Log for ref %s unexpectedly ended on %s.", cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); - hashcpy(cb->osha1, osha1); - hashcpy(cb->nsha1, nsha1); + hashcpy(cb->osha1, ooid->hash); + hashcpy(cb->nsha1, noid->hash); cb->found_it = 1; return 1; } - hashcpy(cb->osha1, osha1); - hashcpy(cb->nsha1, nsha1); + hashcpy(cb->osha1, ooid->hash); + hashcpy(cb->nsha1, noid->hash); if (cb->cnt > 0) cb->cnt--; return 0; } -static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1, +static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -736,9 +736,9 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1, *cb->cutoff_tz = tz; if (cb->cutoff_cnt) *cb->cutoff_cnt = cb->reccnt; - hashcpy(cb->sha1, osha1); + hashcpy(cb->sha1, ooid->hash); if (is_null_sha1(cb->sha1)) - hashcpy(cb->sha1, nsha1); + hashcpy(cb->sha1, noid->hash); /* We just want the first entry */ return 1; } @@ -1035,10 +1035,10 @@ static struct string_list *hide_refs; int parse_hide_refs_config(const char *var, const char *value, const char *section) { + const char *key; if (!strcmp("transfer.hiderefs", var) || - /* NEEDSWORK: use parse_config_key() once both are merged */ - (starts_with(var, section) && var[strlen(section)] == '.' && - !strcmp(var + strlen(section), ".hiderefs"))) { + (!parse_config_key(var, section, NULL, NULL, &key) && + !strcmp(key, "hiderefs"))) { char *ref; int len; @@ -292,7 +292,7 @@ int delete_reflog(const char *refname); /* iterate over reflog entries */ typedef int each_reflog_ent_fn( - unsigned char *old_sha1, unsigned char *new_sha1, + struct object_id *old_oid, struct object_id *new_oid, const char *committer, unsigned long timestamp, int tz, const char *msg, void *cb_data); diff --git a/refs/files-backend.c b/refs/files-backend.c index b42df147c9..50188e92f9 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3102,16 +3102,17 @@ static int files_delete_reflog(struct ref_store *ref_store, static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data) { - unsigned char osha1[20], nsha1[20]; + struct object_id ooid, noid; char *email_end, *message; unsigned long timestamp; int tz; + const char *p = sb->buf; /* old SP new SP name <email> SP time TAB msg LF */ - if (sb->len < 83 || sb->buf[sb->len - 1] != '\n' || - get_sha1_hex(sb->buf, osha1) || sb->buf[40] != ' ' || - get_sha1_hex(sb->buf + 41, nsha1) || sb->buf[81] != ' ' || - !(email_end = strchr(sb->buf + 82, '>')) || + if (!sb->len || sb->buf[sb->len - 1] != '\n' || + parse_oid_hex(p, &ooid, &p) || *p++ != ' ' || + parse_oid_hex(p, &noid, &p) || *p++ != ' ' || + !(email_end = strchr(p, '>')) || email_end[1] != ' ' || !(timestamp = strtoul(email_end + 2, &message, 10)) || !message || message[0] != ' ' || @@ -3125,7 +3126,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c message += 6; else message += 7; - return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data); + return fn(&ooid, &noid, p, timestamp, tz, message, cb_data); } static char *find_beginning_of_line(char *bob, char *scan) @@ -3954,10 +3955,10 @@ struct expire_reflog_cb { reflog_expiry_should_prune_fn *should_prune_fn; void *policy_cb; FILE *newlog; - unsigned char last_kept_sha1[20]; + struct object_id last_kept_oid; }; -static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, +static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -3965,9 +3966,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, struct expire_reflog_policy_cb *policy_cb = cb->policy_cb; if (cb->flags & EXPIRE_REFLOGS_REWRITE) - osha1 = cb->last_kept_sha1; + ooid = &cb->last_kept_oid; - if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz, + if ((*cb->should_prune_fn)(ooid->hash, noid->hash, email, timestamp, tz, message, policy_cb)) { if (!cb->newlog) printf("would prune %s", message); @@ -3976,9 +3977,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, } else { if (cb->newlog) { fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s", - sha1_to_hex(osha1), sha1_to_hex(nsha1), + oid_to_hex(ooid), oid_to_hex(noid), email, timestamp, tz, message); - hashcpy(cb->last_kept_sha1, nsha1); + oidcpy(&cb->last_kept_oid, noid); } if (cb->flags & EXPIRE_REFLOGS_VERBOSE) printf("keep %s", message); @@ -4065,14 +4066,14 @@ static int files_reflog_expire(struct ref_store *ref_store, */ int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) && !(type & REF_ISSYMREF) && - !is_null_sha1(cb.last_kept_sha1); + !is_null_oid(&cb.last_kept_oid); if (close_lock_file(&reflog_lock)) { status |= error("couldn't write %s: %s", log_file, strerror(errno)); } else if (update && (write_in_full(get_lock_file_fd(lock->lk), - sha1_to_hex(cb.last_kept_sha1), 40) != 40 || + oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ || write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 || close_ref(lock) < 0)) { status |= error("couldn't write %s", @@ -89,8 +89,13 @@ struct ref { force:1, forced_update:1, expect_old_sha1:1, - deletion:1, - matched:1; + deletion:1; + + enum { + REF_NOT_MATCHED = 0, /* initial value */ + REF_MATCHED, + REF_UNADVERTISED_NOT_ALLOWED + } match_status; /* * Order is important here, as we write to FETCH_HEAD diff --git a/revision.c b/revision.c index b37dbec378..7ff61ff5f7 100644 --- a/revision.c +++ b/revision.c @@ -147,7 +147,7 @@ static void add_pending_object_with_path(struct rev_info *revs, revs->no_walk = 0; if (revs->reflog_info && obj->type == OBJ_COMMIT) { struct strbuf buf = STRBUF_INIT; - int len = interpret_branch_name(name, 0, &buf); + int len = interpret_branch_name(name, 0, &buf, 0); int st; if (0 < len && name[len] && buf.len) @@ -1196,11 +1196,11 @@ static void handle_refs(const char *submodule, struct rev_info *revs, unsigned f for_each(submodule, handle_one_ref, &cb); } -static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data) +static void handle_one_reflog_commit(struct object_id *oid, void *cb_data) { struct all_refs_cb *cb = cb_data; - if (!is_null_sha1(sha1)) { - struct object *o = parse_object(sha1); + if (!is_null_oid(oid)) { + struct object *o = parse_object(oid->hash); if (o) { o->flags |= cb->all_flags; /* ??? CMDLINEFLAGS ??? */ @@ -1214,12 +1214,12 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data) } } -static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, +static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { - handle_one_reflog_commit(osha1, cb_data); - handle_one_reflog_commit(nsha1, cb_data); + handle_one_reflog_commit(ooid, cb_data); + handle_one_reflog_commit(noid, cb_data); return 0; } diff --git a/send-pack.c b/send-pack.c index 6195b43e9a..d2d2a49a02 100644 --- a/send-pack.c +++ b/send-pack.c @@ -72,6 +72,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru struct child_process po = CHILD_PROCESS_INIT; FILE *po_in; int i; + int rc; i = 4; if (args->use_thin_pack) @@ -125,27 +126,44 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru po.out = -1; } - if (finish_command(&po)) + rc = finish_command(&po); + if (rc) { + /* + * For a normal non-zero exit, we assume pack-objects wrote + * something useful to stderr. For death by signal, though, + * we should mention it to the user. The exception is SIGPIPE + * (141), because that's a normal occurence if the remote end + * hangs up (and we'll report that by trying to read the unpack + * status). + */ + if (rc > 128 && rc != 141) + error("pack-objects died of signal %d", rc - 128); return -1; + } + return 0; +} + +static int receive_unpack_status(int in) +{ + const char *line = packet_read_line(in, NULL); + if (!skip_prefix(line, "unpack ", &line)) + return error(_("unable to parse remote unpack status: %s"), line); + if (strcmp(line, "ok")) + return error(_("remote unpack failed: %s"), line); return 0; } static int receive_status(int in, struct ref *refs) { struct ref *hint; - int ret = 0; - char *line = packet_read_line(in, NULL); - if (!starts_with(line, "unpack ")) - return error("did not receive remote status"); - if (strcmp(line, "unpack ok")) { - error("unpack failed: %s", line + 7); - ret = -1; - } + int ret; + hint = NULL; + ret = receive_unpack_status(in); while (1) { char *refname; char *msg; - line = packet_read_line(in, NULL); + char *line = packet_read_line(in, NULL); if (!line) break; if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) { @@ -557,6 +575,14 @@ int send_pack(struct send_pack_args *args, close(out); if (git_connection_is_socket(conn)) shutdown(fd[0], SHUT_WR); + + /* + * Do not even bother with the return value; we know we + * are failing, and just want the error() side effects. + */ + if (status_report) + receive_unpack_status(in); + if (use_sideband) { close(demux.out); finish_async(&demux); @@ -254,7 +254,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir) if (!is_absolute_path(data.buf)) strbuf_addf(&path, "%s/", gitdir); strbuf_addbuf(&path, &data); - strbuf_addstr(sb, real_path(path.buf)); + strbuf_add_real_path(sb, path.buf); ret = 1; } else { strbuf_addstr(sb, gitdir); @@ -698,7 +698,7 @@ static const char *setup_discovered_git_dir(const char *gitdir, /* --work-tree is set without --git-dir; use discovered one */ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { if (offset != cwd->len && !is_absolute_path(gitdir)) - gitdir = real_pathdup(gitdir); + gitdir = real_pathdup(gitdir, 1); if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); return setup_explicit_git_dir(gitdir, cwd, nongit_ok); @@ -806,7 +806,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item, /* Keep entry but do not canonicalize it */ return 1; } else { - char *real_path = real_pathdup(ceil); + char *real_path = real_pathdup(ceil, 0); if (!real_path) { return 0; } diff --git a/sha1_file.c b/sha1_file.c index 6628f06da3..29bbc5f427 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -667,7 +667,7 @@ static int freshen_file(const char *fn) * either does not exist on disk, or has a stale mtime and may be subject to * pruning). */ -static int check_and_freshen_file(const char *fn, int freshen) +int check_and_freshen_file(const char *fn, int freshen) { if (access(fn, F_OK)) return 0; @@ -2606,6 +2606,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, while (delta_stack_nr) { void *delta_data; void *base = data; + void *external_base = NULL; unsigned long delta_size, base_size = size; int i; @@ -2632,6 +2633,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, p->pack_name); mark_bad_packed_object(p, base_sha1); base = read_object(base_sha1, &type, &base_size); + external_base = base; } } @@ -2650,6 +2652,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, "at offset %"PRIuMAX" from %s", (uintmax_t)curpos, p->pack_name); data = NULL; + free(external_base); continue; } @@ -2669,6 +2672,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, error("failed to apply delta"); free(delta_data); + free(external_base); } *final_type = type; @@ -2702,6 +2706,17 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p, } } +const struct object_id *nth_packed_object_oid(struct object_id *oid, + struct packed_git *p, + uint32_t n) +{ + const unsigned char *hash = nth_packed_object_sha1(p, n); + if (!hash) + return NULL; + hashcpy(oid->hash, hash); + return oid; +} + void check_pack_index_ptr(const struct packed_git *p, const void *vptr) { const unsigned char *ptr = vptr; @@ -3748,15 +3763,15 @@ static int for_each_file_in_obj_subdir(int subdir_nr, strbuf_setlen(path, baselen); strbuf_addf(path, "/%s", de->d_name); - if (strlen(de->d_name) == 38) { - char hex[41]; - unsigned char sha1[20]; + if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2) { + char hex[GIT_SHA1_HEXSZ+1]; + struct object_id oid; snprintf(hex, sizeof(hex), "%02x%s", subdir_nr, de->d_name); - if (!get_sha1_hex(hex, sha1)) { + if (!get_oid_hex(hex, &oid)) { if (obj_cb) { - r = obj_cb(sha1, path->buf, data); + r = obj_cb(&oid, path->buf, data); if (r) break; } @@ -3862,13 +3877,13 @@ static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn c int r = 0; for (i = 0; i < p->num_objects; i++) { - const unsigned char *sha1 = nth_packed_object_sha1(p, i); + struct object_id oid; - if (!sha1) + if (!nth_packed_object_oid(&oid, p, i)) return error("unable to get sha1 of object %u in %s", i, p->pack_name); - r = cb(sha1, p, i, data); + r = cb(&oid, p, i, data); if (r) break; } diff --git a/sha1_name.c b/sha1_name.c index 73a915ff1b..cda9e49b12 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1051,7 +1051,7 @@ struct grab_nth_branch_switch_cbdata { struct strbuf buf; }; -static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, +static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -1176,7 +1176,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str return 1; } -static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf) +static int reinterpret(const char *name, int namelen, int len, + struct strbuf *buf, unsigned allowed) { /* we have extra data, which might need further processing */ struct strbuf tmp = STRBUF_INIT; @@ -1184,7 +1185,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu int ret; strbuf_add(buf, name + len, namelen - len); - ret = interpret_branch_name(buf->buf, buf->len, &tmp); + ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed); /* that data was not interpreted, remove our cruft */ if (ret < 0) { strbuf_setlen(buf, used); @@ -1205,11 +1206,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref) free(s); } +static int branch_interpret_allowed(const char *refname, unsigned allowed) +{ + if (!allowed) + return 1; + + if ((allowed & INTERPRET_BRANCH_LOCAL) && + starts_with(refname, "refs/heads/")) + return 1; + if ((allowed & INTERPRET_BRANCH_REMOTE) && + starts_with(refname, "refs/remotes/")) + return 1; + + return 0; +} + static int interpret_branch_mark(const char *name, int namelen, int at, struct strbuf *buf, int (*get_mark)(const char *, int), const char *(*get_data)(struct branch *, - struct strbuf *)) + struct strbuf *), + unsigned allowed) { int len; struct branch *branch; @@ -1234,64 +1251,55 @@ static int interpret_branch_mark(const char *name, int namelen, if (!value) die("%s", err.buf); + if (!branch_interpret_allowed(value, allowed)) + return -1; + set_shortened_ref(buf, value); return len + at; } -/* - * This reads short-hand syntax that not only evaluates to a commit - * object name, but also can act as if the end user spelled the name - * of the branch from the command line. - * - * - "@{-N}" finds the name of the Nth previous branch we were on, and - * places the name of the branch in the given buf and returns the - * number of characters parsed if successful. - * - * - "<branch>@{upstream}" finds the name of the other ref that - * <branch> is configured to merge with (missing <branch> defaults - * to the current branch), and places the name of the branch in the - * given buf and returns the number of characters parsed if - * successful. - * - * If the input is not of the accepted format, it returns a negative - * number to signal an error. - * - * If the input was ok but there are not N branch switches in the - * reflog, it returns 0. - */ -int interpret_branch_name(const char *name, int namelen, struct strbuf *buf) +int interpret_branch_name(const char *name, int namelen, struct strbuf *buf, + unsigned allowed) { char *at; const char *start; - int len = interpret_nth_prior_checkout(name, namelen, buf); + int len; if (!namelen) namelen = strlen(name); - if (!len) { - return len; /* syntax Ok, not enough switches */ - } else if (len > 0) { - if (len == namelen) - return len; /* consumed all */ - else - return reinterpret(name, namelen, len, buf); + if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) { + len = interpret_nth_prior_checkout(name, namelen, buf); + if (!len) { + return len; /* syntax Ok, not enough switches */ + } else if (len > 0) { + if (len == namelen) + return len; /* consumed all */ + else + return reinterpret(name, namelen, len, buf, allowed); + } } for (start = name; (at = memchr(start, '@', namelen - (start - name))); start = at + 1) { - len = interpret_empty_at(name, namelen, at - name, buf); - if (len > 0) - return reinterpret(name, namelen, len, buf); + if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) { + len = interpret_empty_at(name, namelen, at - name, buf); + if (len > 0) + return reinterpret(name, namelen, len, buf, + allowed); + } len = interpret_branch_mark(name, namelen, at - name, buf, - upstream_mark, branch_get_upstream); + upstream_mark, branch_get_upstream, + allowed); if (len > 0) return len; len = interpret_branch_mark(name, namelen, at - name, buf, - push_mark, branch_get_push); + push_mark, branch_get_push, + allowed); if (len > 0) return len; } @@ -1299,22 +1307,19 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf) return -1; } -int strbuf_branchname(struct strbuf *sb, const char *name) +void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed) { int len = strlen(name); - int used = interpret_branch_name(name, len, sb); + int used = interpret_branch_name(name, len, sb, allowed); - if (used == len) - return 0; if (used < 0) used = 0; strbuf_add(sb, name + used, len - used); - return len; } int strbuf_check_branch_ref(struct strbuf *sb, const char *name) { - strbuf_branchname(sb, name); + strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL); if (name[0] == '-') return -1; strbuf_splice(sb, 0, 0, "refs/heads/", 11); diff --git a/split-index.c b/split-index.c index 615f4cac05..f519e60f87 100644 --- a/split-index.c +++ b/split-index.c @@ -317,3 +317,25 @@ void replace_index_entry_in_base(struct index_state *istate, istate->split_index->base->cache[new->index - 1] = new; } } + +void add_split_index(struct index_state *istate) +{ + if (!istate->split_index) { + init_split_index(istate); + istate->cache_changed |= SPLIT_INDEX_ORDERED; + } +} + +void remove_split_index(struct index_state *istate) +{ + if (istate->split_index) { + /* + * can't discard_split_index(&the_index); because that + * will destroy split_index->base->cache[], which may + * be shared with the_index.cache[]. So yeah we're + * leaking a bit here. + */ + istate->split_index = NULL; + istate->cache_changed |= SOMETHING_CHANGED; + } +} diff --git a/split-index.h b/split-index.h index c1324f521a..df91c1bda8 100644 --- a/split-index.h +++ b/split-index.h @@ -31,5 +31,7 @@ void merge_base_index(struct index_state *istate); void prepare_to_write_split_index(struct index_state *istate); void finish_writing_split_index(struct index_state *istate); void discard_split_index(struct index_state *istate); +void add_split_index(struct index_state *istate); +void remove_split_index(struct index_state *istate); #endif @@ -707,6 +707,17 @@ void strbuf_add_absolute_path(struct strbuf *sb, const char *path) strbuf_addstr(sb, path); } +void strbuf_add_real_path(struct strbuf *sb, const char *path) +{ + if (sb->len) { + struct strbuf resolved = STRBUF_INIT; + strbuf_realpath(&resolved, path, 1); + strbuf_addbuf(sb, &resolved); + strbuf_release(&resolved); + } else + strbuf_realpath(sb, path, 1); +} + int printf_ln(const char *fmt, ...) { int ret; @@ -441,6 +441,20 @@ extern int strbuf_getcwd(struct strbuf *sb); */ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path); +/** + * Canonize `path` (make it absolute, resolve symlinks, remove extra + * slashes) and append it to `sb`. Die with an informative error + * message if there is a problem. + * + * The directory part of `path` (i.e., everything up to the last + * dir_sep) must denote a valid, existing directory, but the last + * component need not exist. + * + * Callers that don't mind links should use the more lightweight + * strbuf_add_absolute_path() instead. + */ +extern void strbuf_add_real_path(struct strbuf *sb, const char *path); + /** * Normalize in-place the path contained in the strbuf. See @@ -560,7 +574,26 @@ static inline void strbuf_complete_line(struct strbuf *sb) strbuf_complete(sb, '\n'); } -extern int strbuf_branchname(struct strbuf *sb, const char *name); +/* + * Copy "name" to "sb", expanding any special @-marks as handled by + * interpret_branch_name(). The result is a non-qualified branch name + * (so "foo" or "origin/master" instead of "refs/heads/foo" or + * "refs/remotes/origin/master"). + * + * Note that the resulting name may not be a syntactically valid refname. + * + * If "allowed" is non-zero, restrict the set of allowed expansions. See + * interpret_branch_name() for details. + */ +extern void strbuf_branchname(struct strbuf *sb, const char *name, + unsigned allowed); + +/* + * Like strbuf_branchname() above, but confirm that the result is + * syntactically valid to be used as a local branch name in refs/heads/. + * + * The return value is "0" if the result is valid, and "-1" otherwise. + */ extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); extern void strbuf_addstr_urlencode(struct strbuf *, const char *, diff --git a/submodule.c b/submodule.c index 3b98766a6b..3200b7bb2b 100644 --- a/submodule.c +++ b/submodule.c @@ -1403,7 +1403,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, /* If it is an actual gitfile, it doesn't need migration. */ return; - real_old_git_dir = real_pathdup(old_git_dir); + real_old_git_dir = real_pathdup(old_git_dir, 1); sub = submodule_from_path(null_sha1, path); if (!sub) @@ -1412,7 +1412,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, new_git_dir = git_path("modules/%s", sub->name); if (safe_create_leading_directories_const(new_git_dir) < 0) die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir); + real_new_git_dir = real_pathdup(new_git_dir, 1); if (!prefix) prefix = get_super_prefix(); @@ -1472,14 +1472,14 @@ void absorb_git_dir_into_superproject(const char *prefix, new_git_dir = git_path("modules/%s", sub->name); if (safe_create_leading_directories_const(new_git_dir) < 0) die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir); + real_new_git_dir = real_pathdup(new_git_dir, 1); connect_work_tree_and_git_dir(path, real_new_git_dir); free(real_new_git_dir); } else { /* Is it already absorbed into the superprojects git dir? */ - char *real_sub_git_dir = real_pathdup(sub_git_dir); - char *real_common_git_dir = real_pathdup(get_git_common_dir()); + char *real_sub_git_dir = real_pathdup(sub_git_dir, 1); + char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1); if (!starts_with(real_sub_git_dir, real_common_git_dir)) relocate_single_git_dir_into_superproject(prefix, path); @@ -1514,3 +1514,85 @@ void absorb_git_dir_into_superproject(const char *prefix, strbuf_release(&sb); } } + +const char *get_superproject_working_tree(void) +{ + struct child_process cp = CHILD_PROCESS_INIT; + struct strbuf sb = STRBUF_INIT; + const char *one_up = real_path_if_valid("../"); + const char *cwd = xgetcwd(); + const char *ret = NULL; + const char *subpath; + int code; + ssize_t len; + + if (!is_inside_work_tree()) + /* + * FIXME: + * We might have a superproject, but it is harder + * to determine. + */ + return NULL; + + if (!one_up) + return NULL; + + subpath = relative_path(cwd, one_up, &sb); + + prepare_submodule_repo_env(&cp.env_array); + argv_array_pop(&cp.env_array); + + argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..", + "ls-files", "-z", "--stage", "--full-name", "--", + subpath, NULL); + strbuf_reset(&sb); + + cp.no_stdin = 1; + cp.no_stderr = 1; + cp.out = -1; + cp.git_cmd = 1; + + if (start_command(&cp)) + die(_("could not start ls-files in ..")); + + len = strbuf_read(&sb, cp.out, PATH_MAX); + close(cp.out); + + if (starts_with(sb.buf, "160000")) { + int super_sub_len; + int cwd_len = strlen(cwd); + char *super_sub, *super_wt; + + /* + * There is a superproject having this repo as a submodule. + * The format is <mode> SP <hash> SP <stage> TAB <full name> \0, + * We're only interested in the name after the tab. + */ + super_sub = strchr(sb.buf, '\t') + 1; + super_sub_len = sb.buf + sb.len - super_sub - 1; + + if (super_sub_len > cwd_len || + strcmp(&cwd[cwd_len - super_sub_len], super_sub)) + die (_("BUG: returned path string doesn't match cwd?")); + + super_wt = xstrdup(cwd); + super_wt[cwd_len - super_sub_len] = '\0'; + + ret = real_path(super_wt); + free(super_wt); + } + strbuf_release(&sb); + + code = finish_command(&cp); + + if (code == 128) + /* '../' is not a git repository */ + return NULL; + if (code == 0 && len == 0) + /* There is an unrelated git repository at '../' */ + return NULL; + if (code) + die(_("ls-tree returned unexpected return code %d"), code); + + return ret; +} diff --git a/submodule.h b/submodule.h index 05ab674f06..c8a0c9cb29 100644 --- a/submodule.h +++ b/submodule.h @@ -93,4 +93,12 @@ extern void prepare_submodule_repo_env(struct argv_array *out); extern void absorb_git_dir_into_superproject(const char *prefix, const char *path, unsigned flags); + +/* + * Return the absolute path of the working tree of the superproject, which this + * project is a submodule of. If this repository is not a submodule of + * another repository, return NULL. + */ +extern const char *get_superproject_working_tree(void); + #endif diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh index d5dab5a94f..006d2a8152 100644 --- a/t/gitweb-lib.sh +++ b/t/gitweb-lib.sh @@ -110,7 +110,12 @@ perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || { } perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || { - skip_all='skipping gitweb tests, CGI module unusable' + skip_all='skipping gitweb tests, CGI & CGI::Util & CGI::Carp modules not available' + test_done +} + +perl -mTime::HiRes -e 0 >/dev/null 2>&1 || { + skip_all='skipping gitweb tests, Time::HiRes module not available' test_done } diff --git a/t/interop/.gitignore b/t/interop/.gitignore new file mode 100644 index 0000000000..49c78d3dba --- /dev/null +++ b/t/interop/.gitignore @@ -0,0 +1,4 @@ +/trash directory*/ +/test-results/ +/.prove/ +/build/ diff --git a/t/interop/Makefile b/t/interop/Makefile new file mode 100644 index 0000000000..31a4bbc716 --- /dev/null +++ b/t/interop/Makefile @@ -0,0 +1,16 @@ +-include ../../config.mak +export GIT_TEST_OPTIONS + +SHELL_PATH ?= $(SHELL) +SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) +T = $(sort $(wildcard i[0-9][0-9][0-9][0-9]-*.sh)) + +all: $(T) + +$(T): + @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS) + +clean: + rm -rf build "trash directory".* test-results + +.PHONY: all clean $(T) diff --git a/t/interop/README b/t/interop/README new file mode 100644 index 0000000000..72d42bd856 --- /dev/null +++ b/t/interop/README @@ -0,0 +1,85 @@ +Git version interoperability tests +================================== + +This directory has interoperability tests for git. Each script is +similar to the normal test scripts found in t/, but with the added twist +that two special versions of git, "git.a" and "git.b", are available in +the PATH. Individual tests can then check the interaction between the +two versions. + +When you add a feature that handles backwards compatibility between git +versions, it's encouraged to add a test here to make sure it behaves as +you expect. + + +Running Tests +------------- + +The easiest way to run tests is to say "make". This runs all +the tests against their default versions. + +You can run a single test like: + + $ ./i0000-basic.sh + ok 1 - bare git is forbidden + ok 2 - git.a version (v1.6.6.3) + ok 3 - git.b version (v2.11.1) + # passed all 3 test(s) + 1..3 + +Each test contains default versions to run against. You may override +these by setting `GIT_TEST_VERSION_A` and `GIT_TEST_VERSION_B` in the +environment. Note that not all combinations will give sensible outcomes +for all tests (e.g., a test checking for a specific old/new interaction +may want something "old" enough" and something "new" enough; see +individual tests for details). + +Version names should be resolvable as revisions in the current +repository. They will be exported and built as needed using the +config.mak files found at the root of your working tree. + +The exception is the special version "." which uses the currently-built +contents of your working tree. + +You can set the following variables (in the environment or in your config.mak): + + GIT_INTEROP_MAKE_OPTS + Options to pass to `make` when building a git version (e.g., + `-j8`). + +You can also pass any command-line options taken by ordinary git tests (e.g., +"-v"). + + +Naming Tests +------------ + +The interop test files are named like: + + iNNNN-short-description.sh + +where N is a decimal digit. The same conventions for choosing NNNN as +for normal tests apply. + + +Writing Tests +------------- + +An interop test script starts like a normal script, declaring a few +variables and then including interop-lib.sh (which includes test-lib.sh). +Besides test_description, you should also set the $VERSION_A and $VERSION_B +variables to give the default versions to test against. See t0000-basic.sh for +an example. + +You can then use test_expect_success as usual, with a few differences: + + 1. The special commands "git.a" and "git.b" correspond to the + two versions. + + 2. You cannot call a bare "git". This is to prevent accidents where + you meant "git.a" or "git.b". + + 3. The trash directory is _not_ a git repository by default. You + should create one with the appropriate version of git. + +At the end of the script, call test_done as usual. diff --git a/t/interop/i0000-basic.sh b/t/interop/i0000-basic.sh new file mode 100755 index 0000000000..903e9193f8 --- /dev/null +++ b/t/interop/i0000-basic.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Note that this test only works on real version numbers, +# as it depends on matching the output to "git version". +VERSION_A=v1.6.6.3 +VERSION_B=v2.11.1 + +test_description='sanity test interop library' +. ./interop-lib.sh + +test_expect_success 'bare git is forbidden' ' + test_must_fail git version +' + +test_expect_success "git.a version ($VERSION_A)" ' + echo git version ${VERSION_A#v} >expect && + git.a version >actual && + test_cmp expect actual +' + +test_expect_success "git.b version ($VERSION_B)" ' + echo git version ${VERSION_B#v} >expect && + git.b version >actual && + test_cmp expect actual +' + +test_done diff --git a/t/interop/i5500-git-daemon.sh b/t/interop/i5500-git-daemon.sh new file mode 100755 index 0000000000..1daf69420b --- /dev/null +++ b/t/interop/i5500-git-daemon.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +VERSION_A=. +VERSION_B=v1.0.0 + +: ${LIB_GIT_DAEMON_PORT:=5500} +LIB_GIT_DAEMON_COMMAND='git.a daemon' + +test_description='clone and fetch by older client' +. ./interop-lib.sh +. "$TEST_DIRECTORY"/lib-git-daemon.sh + +start_git_daemon --export-all + +repo=$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo + +test_expect_success "create repo served by $VERSION_A" ' + git.a init "$repo" && + git.a -C "$repo" commit --allow-empty -m one +' + +test_expect_success "clone with $VERSION_B" ' + git.b clone "$GIT_DAEMON_URL/repo" child && + echo one >expect && + git.a -C child log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success "fetch with $VERSION_B" ' + git.a -C "$repo" commit --allow-empty -m two && + ( + cd child && + git.b fetch + ) && + echo two >expect && + git.a -C child log -1 --format=%s FETCH_HEAD >actual && + test_cmp expect actual +' + +stop_git_daemon +test_done diff --git a/t/interop/interop-lib.sh b/t/interop/interop-lib.sh new file mode 100644 index 0000000000..3e0a2911d4 --- /dev/null +++ b/t/interop/interop-lib.sh @@ -0,0 +1,92 @@ +# Interoperability testing framework. Each script should source +# this after setting default $VERSION_A and $VERSION_B variables. + +. ../../GIT-BUILD-OPTIONS +INTEROP_ROOT=$(pwd) +BUILD_ROOT=$INTEROP_ROOT/build + +build_version () { + if test -z "$1" + then + echo >&2 "error: test script did not set default versions" + return 1 + fi + + if test "$1" = "." + then + git rev-parse --show-toplevel + return 0 + fi + + sha1=$(git rev-parse "$1^{tree}") || return 1 + dir=$BUILD_ROOT/$sha1 + + if test -e "$dir/.built" + then + echo "$dir" + return 0 + fi + + echo >&2 "==> Building $1..." + + mkdir -p "$dir" || return 1 + + (cd "$(git rev-parse --show-cdup)" && git archive --format=tar "$sha1") | + (cd "$dir" && tar x) || + return 1 + + for config in config.mak config.mak.autogen config.status + do + if test -e "$INTEROP_ROOT/../../$config" + then + cp "$INTEROP_ROOT/../../$config" "$dir/" || return 1 + fi + done + + ( + cd "$dir" && + make $GIT_INTEROP_MAKE_OPTS >&2 && + touch .built + ) || return 1 + + echo "$dir" +} + +# Old versions of git don't have bin-wrappers, so let's give a rough emulation. +wrap_git () { + write_script "$1" <<-EOF + GIT_EXEC_PATH="$2" + export GIT_EXEC_PATH + PATH="$2:\$PATH" + export GIT_EXEC_PATH + exec git "\$@" + EOF +} + +generate_wrappers () { + mkdir -p .bin && + wrap_git .bin/git.a "$DIR_A" && + wrap_git .bin/git.b "$DIR_B" && + write_script .bin/git <<-\EOF && + echo >&2 fatal: test tried to run generic git + exit 1 + EOF + PATH=$(pwd)/.bin:$PATH +} + +VERSION_A=${GIT_TEST_VERSION_A:-$VERSION_A} +VERSION_B=${GIT_TEST_VERSION_B:-$VERSION_B} + +if ! DIR_A=$(build_version "$VERSION_A") || + ! DIR_B=$(build_version "$VERSION_B") +then + echo >&2 "fatal: unable to build git versions" + exit 1 +fi + +TEST_DIRECTORY=$INTEROP_ROOT/.. +TEST_OUTPUT_DIRECTORY=$INTEROP_ROOT +TEST_NO_CREATE_REPO=t +. "$TEST_DIRECTORY"/test-lib.sh + +generate_wrappers || die "unable to set up interop test environment" diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh index f9cbd47931..987d40680b 100644 --- a/t/lib-git-daemon.sh +++ b/t/lib-git-daemon.sh @@ -46,7 +46,8 @@ start_git_daemon() { say >&3 "Starting git daemon ..." mkfifo git_daemon_output - git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \ + ${LIB_GIT_DAEMON_COMMAND:-git daemon} \ + --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \ --reuseaddr --verbose \ --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 69174c6e31..0642ae7e6e 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -133,6 +133,15 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302] RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302] RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302] +# redir-to/502/x?y -> really-redir-to?path=502/x&qs=y which returns 502 +# redir-to/x?y -> really-redir-to?path=x&qs=y -> x?y +RewriteCond %{QUERY_STRING} ^(.*)$ +RewriteRule ^/redir-to/(.*)$ /really-redir-to?path=$1&qs=%1 [R=302] +RewriteCond %{QUERY_STRING} ^path=502/(.*)&qs=(.*)$ +RewriteRule ^/really-redir-to$ - [R=502,L] +RewriteCond %{QUERY_STRING} ^path=(.*)&qs=(.*)$ +RewriteRule ^/really-redir-to$ /%1?%2 [R=302] + # The first rule issues a client-side redirect to something # that _doesn't_ look like a git repo. The second rule is a # server-side rewrite, so that it turns out the odd-looking diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh index 16359d51ae..ebf172401b 100755 --- a/t/perf/p0001-rev-list.sh +++ b/t/perf/p0001-rev-list.sh @@ -15,7 +15,8 @@ test_perf 'rev-list --all --objects' ' ' test_expect_success 'create new unreferenced commit' ' - commit=$(git commit-tree HEAD^{tree} -p HEAD) + commit=$(git commit-tree HEAD^{tree} -p HEAD) && + test_export commit ' test_perf 'rev-list $commit --not --all' ' diff --git a/t/perf/p7000-filter-branch.sh b/t/perf/p7000-filter-branch.sh index 15ee5d1d53..b029586ccb 100755 --- a/t/perf/p7000-filter-branch.sh +++ b/t/perf/p7000-filter-branch.sh @@ -16,4 +16,9 @@ test_perf 'noop filter' ' git filter-branch -f base..HEAD ' +test_perf 'noop prune-empty' ' + git checkout --detach tip && + git filter-branch -f --prune-empty base..HEAD +' + test_done diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index 46f08ee087..ab4b8b06ae 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -83,7 +83,7 @@ test_perf_create_repo_from () { error "bug in the test script: not 2 parameters to test-create-repo" repo="$1" source="$2" - source_git="$(git -C "$source" rev-parse --git-dir)" + source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)" objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)" mkdir -p "$repo/.git" ( @@ -102,7 +102,7 @@ test_perf_create_repo_from () { ) && ( cd "$repo" && - git init -q && { + "$MODERN_GIT" init -q && { test_have_prereq SYMLINKS || git config core.symlinks false } && diff --git a/t/perf/run b/t/perf/run index e8adedadfd..c788d713ae 100755 --- a/t/perf/run +++ b/t/perf/run @@ -63,6 +63,9 @@ run_dirs_helper () { unset GIT_TEST_INSTALLED else GIT_TEST_INSTALLED="$mydir/bin-wrappers" + # Older versions of git lacked bin-wrappers; fallback to the + # files in the root. + test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir export GIT_TEST_INSTALLED fi run_one_dir "$@" diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh index e0a6940232..58c0b7e9b6 100755 --- a/t/t0100-previous.sh +++ b/t/t0100-previous.sh @@ -56,5 +56,13 @@ test_expect_success 'merge @{-100} before checking out that many branches yet' ' test_must_fail git merge @{-100} ' +test_expect_success 'log -g @{-1}' ' + git checkout -b last_branch && + git checkout -b new_branch && + echo "last_branch@{0}" >expect && + git log -g --format=%gd @{-1} >actual && + test_cmp expect actual +' + test_done diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 052f120216..afcca0d52c 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1097,6 +1097,68 @@ test_expect_success 'multiple git -c appends config' ' test_cmp expect actual ' +test_expect_success 'last one wins: two level vars' ' + + # sec.var and sec.VAR are the same variable, as the first + # and the last level of a configuration variable name is + # case insensitive. + + echo VAL >expect && + + git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual && + test_cmp expect actual && + git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual && + test_cmp expect actual && + + git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual && + test_cmp expect actual && + git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual && + test_cmp expect actual +' + +test_expect_success 'last one wins: three level vars' ' + + # v.a.r and v.A.r are not the same variable, as the middle + # level of a three-level configuration variable name is + # case sensitive. + + echo val >expect && + git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual && + test_cmp expect actual && + + # v.a.r and V.a.R are the same variable, as the first + # and the last level of a configuration variable name is + # case insensitive. + + echo VAL >expect && + git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual && + test_cmp expect actual && + git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual && + test_cmp expect actual +' + +for VAR in a .a a. a.0b a."b c". a."b c".0d +do + test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" ' + test_must_fail git -c "$VAR=VAL" config -l + ' +done + +for VAR in a.b a."b c".d +do + test_expect_success "git -c $VAR=VAL works with valid '$VAR'" ' + echo VAL >expect && + git -c "$VAR=VAL" config --get "$VAR" >actual && + test_cmp expect actual + ' +done + test_expect_success 'git -c is not confused by empty environment' ' GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list ' diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 9ed8b8ccba..03d3c7f6d6 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -116,4 +116,18 @@ test_expect_success 'git-path inside sub-dir' ' test_cmp expect actual ' +test_expect_success 'showing the superproject correctly' ' + git rev-parse --show-superproject-working-tree >out && + test_must_be_empty out && + + test_create_repo super && + test_commit -C super test_commit && + test_create_repo sub && + test_commit -C sub test_commit && + git -C super submodule add ../sub dir/sub && + echo $(pwd)/super >expect && + git -C super/dir/sub rev-parse --show-superproject-working-tree >out && + test_cmp expect out +' + test_done diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh index cc5b870e58..b06210ec5e 100755 --- a/t/t1501-work-tree.sh +++ b/t/t1501-work-tree.sh @@ -423,4 +423,12 @@ test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' ' ) ' +test_expect_success 'error out gracefully on invalid $GIT_WORK_TREE' ' + ( + GIT_WORK_TREE=/.invalid/work/tree && + export GIT_WORK_TREE && + test_expect_code 128 git rev-parse + ) +' + test_done diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index 6096f2c630..af3ec0da5a 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -8,6 +8,7 @@ test_description='split index mode tests' sane_unset GIT_TEST_SPLIT_INDEX test_expect_success 'enable split index' ' + git config splitIndex.maxPercentChange 100 && git update-index --split-index && test-dump-split-index .git/index >actual && indexversion=$(test-index-version <.git/index) && @@ -19,12 +20,12 @@ test_expect_success 'enable split index' ' own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339 base=39d890139ee5356c7ef572216cebcd27aa41f9df fi && - cat >expect <<EOF && -own $own -base $base -replacements: -deletions: -EOF + cat >expect <<-EOF && + own $own + base $base + replacements: + deletions: + EOF test_cmp expect actual ' @@ -32,51 +33,51 @@ test_expect_success 'add one file' ' : >one && git update-index --add one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -base $base -100644 $EMPTY_BLOB 0 one -replacements: -deletions: -EOF + cat >expect <<-EOF && + base $base + 100644 $EMPTY_BLOB 0 one + replacements: + deletions: + EOF test_cmp expect actual ' test_expect_success 'disable split index' ' git update-index --no-split-index && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && BASE=$(test-dump-split-index .git/index | grep "^own" | sed "s/own/base/") && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -not a split index -EOF + cat >expect <<-EOF && + not a split index + EOF test_cmp expect actual ' test_expect_success 'enable split index again, "one" now belongs to base index"' ' git update-index --split-index && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -$BASE -replacements: -deletions: -EOF + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF test_cmp expect actual ' @@ -84,18 +85,18 @@ test_expect_success 'modify original file, base index untouched' ' echo modified >one && git update-index one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - q_to_tab >expect <<EOF && -$BASE -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q -replacements: 0 -deletions: -EOF + q_to_tab >expect <<-EOF && + $BASE + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q + replacements: 0 + deletions: + EOF test_cmp expect actual ' @@ -103,54 +104,54 @@ test_expect_success 'add another file, which stays index' ' : >two && git update-index --add two && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one -100644 $EMPTY_BLOB 0 two -EOF + cat >ls-files.expect <<-EOF && + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one + 100644 $EMPTY_BLOB 0 two + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - q_to_tab >expect <<EOF && -$BASE -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q -100644 $EMPTY_BLOB 0 two -replacements: 0 -deletions: -EOF + q_to_tab >expect <<-EOF && + $BASE + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q + 100644 $EMPTY_BLOB 0 two + replacements: 0 + deletions: + EOF test_cmp expect actual ' test_expect_success 'remove file not in base index' ' git update-index --force-remove two && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - q_to_tab >expect <<EOF && -$BASE -100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q -replacements: 0 -deletions: -EOF + q_to_tab >expect <<-EOF && + $BASE + 100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q + replacements: 0 + deletions: + EOF test_cmp expect actual ' test_expect_success 'remove file in base index' ' git update-index --force-remove one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -EOF + cat >ls-files.expect <<-EOF && + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -$BASE -replacements: -deletions: 0 -EOF + cat >expect <<-EOF && + $BASE + replacements: + deletions: 0 + EOF test_cmp expect actual ' @@ -158,18 +159,18 @@ test_expect_success 'add original file back' ' : >one && git update-index --add one && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -$BASE -100644 $EMPTY_BLOB 0 one -replacements: -deletions: 0 -EOF + cat >expect <<-EOF && + $BASE + 100644 $EMPTY_BLOB 0 one + replacements: + deletions: 0 + EOF test_cmp expect actual ' @@ -177,26 +178,26 @@ test_expect_success 'add new file' ' : >two && git update-index --add two && git ls-files --stage >actual && - cat >expect <<EOF && -100644 $EMPTY_BLOB 0 one -100644 $EMPTY_BLOB 0 two -EOF + cat >expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + 100644 $EMPTY_BLOB 0 two + EOF test_cmp expect actual ' test_expect_success 'unify index, two files remain' ' git update-index --no-split-index && git ls-files --stage >ls-files.actual && - cat >ls-files.expect <<EOF && -100644 $EMPTY_BLOB 0 one -100644 $EMPTY_BLOB 0 two -EOF + cat >ls-files.expect <<-EOF && + 100644 $EMPTY_BLOB 0 one + 100644 $EMPTY_BLOB 0 two + EOF test_cmp ls-files.expect ls-files.actual && test-dump-split-index .git/index | sed "/^own/d" >actual && - cat >expect <<EOF && -not a split index -EOF + cat >expect <<-EOF && + not a split index + EOF test_cmp expect actual ' @@ -216,4 +217,157 @@ test_expect_success 'rev-parse --shared-index-path' ' ) ' +test_expect_success 'set core.splitIndex config variable to true' ' + git config core.splitIndex true && + : >three && + git update-index --add three && + git ls-files --stage >ls-files.actual && + cat >ls-files.expect <<-EOF && + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 three + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two + EOF + test_cmp ls-files.expect ls-files.actual && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'set core.splitIndex config variable to false' ' + git config core.splitIndex false && + git update-index --force-remove three && + git ls-files --stage >ls-files.actual && + cat >ls-files.expect <<-EOF && + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two + EOF + test_cmp ls-files.expect ls-files.actual && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + not a split index + EOF + test_cmp expect actual +' + +test_expect_success 'set core.splitIndex config variable to true' ' + git config core.splitIndex true && + : >three && + git update-index --add three && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual && + : >four && + git update-index --add four && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 four + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'check behavior with splitIndex.maxPercentChange unset' ' + git config --unset splitIndex.maxPercentChange && + : >five && + git update-index --add five && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual && + : >six && + git update-index --add six && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 six + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'check splitIndex.maxPercentChange set to 0' ' + git config splitIndex.maxPercentChange 0 && + : >seven && + git update-index --add seven && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual && + : >eight && + git update-index --add eight && + BASE=$(test-dump-split-index .git/index | grep "^base") && + test-dump-split-index .git/index | sed "/^own/d" >actual && + cat >expect <<-EOF && + $BASE + replacements: + deletions: + EOF + test_cmp expect actual +' + +test_expect_success 'shared index files expire after 2 weeks by default' ' + : >ten && + git update-index --add ten && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + just_under_2_weeks_ago=$((5-14*86400)) && + test-chmtime =$just_under_2_weeks_ago .git/sharedindex.* && + : >eleven && + git update-index --add eleven && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + just_over_2_weeks_ago=$((-1-14*86400)) && + test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* && + : >twelve && + git update-index --add twelve && + test $(ls .git/sharedindex.* | wc -l) -le 2 +' + +test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' ' + git config splitIndex.sharedIndexExpire "16.days.ago" && + test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* && + : >thirteen && + git update-index --add thirteen && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + just_over_16_days_ago=$((-1-16*86400)) && + test-chmtime =$just_over_16_days_ago .git/sharedindex.* && + : >fourteen && + git update-index --add fourteen && + test $(ls .git/sharedindex.* | wc -l) -le 2 +' + +test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"' ' + git config splitIndex.sharedIndexExpire never && + just_10_years_ago=$((-365*10*86400)) && + test-chmtime =$just_10_years_ago .git/sharedindex.* && + : >fifteen && + git update-index --add fifteen && + test $(ls .git/sharedindex.* | wc -l) -gt 2 && + git config splitIndex.sharedIndexExpire now && + just_1_second_ago=-1 && + test-chmtime =$just_1_second_ago .git/sharedindex.* && + : >sixteen && + git update-index --add sixteen && + test $(ls .git/sharedindex.* | wc -l) -le 2 +' + test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index e36ed3b4e1..9f353c0efc 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -213,6 +213,31 @@ test_expect_success 'git branch --list -d t should fail' ' test_path_is_missing .git/refs/heads/t ' +test_expect_success 'git branch --list -v with --abbrev' ' + test_when_finished "git branch -D t" && + git branch t && + git branch -v --list t >actual.default && + git branch -v --list --abbrev t >actual.abbrev && + test_cmp actual.default actual.abbrev && + + git branch -v --list --no-abbrev t >actual.noabbrev && + git branch -v --list --abbrev=0 t >actual.0abbrev && + test_cmp actual.noabbrev actual.0abbrev && + + git branch -v --list --abbrev=36 t >actual.36abbrev && + # how many hexdigits are used? + read name objdefault rest <actual.abbrev && + read name obj36 rest <actual.36abbrev && + objfull=$(git rev-parse --verify t) && + + # are we really getting abbreviations? + test "$obj36" != "$objdefault" && + expr "$obj36" : "$objdefault" >/dev/null && + test "$objfull" != "$obj36" && + expr "$objfull" : "$obj36" >/dev/null + +' + test_expect_success 'git branch --column' ' COLUMNS=81 git branch --column=column >actual && cat >expected <<\EOF && diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh new file mode 100755 index 0000000000..698d9cc4f3 --- /dev/null +++ b/t/t3204-branch-name-interpretation.sh @@ -0,0 +1,133 @@ +#!/bin/sh + +test_description='interpreting exotic branch name arguments + +Branch name arguments are usually names which are taken to be inside of +refs/heads/, but we interpret some magic syntax like @{-1}, @{upstream}, etc. +This script aims to check the behavior of those corner cases. +' +. ./test-lib.sh + +expect_branch() { + git log -1 --format=%s "$1" >actual && + echo "$2" >expect && + test_cmp expect actual +} + +expect_deleted() { + test_must_fail git rev-parse --verify "$1" +} + +test_expect_success 'set up repo' ' + test_commit one && + test_commit two && + git remote add origin foo.git +' + +test_expect_success 'update branch via @{-1}' ' + git branch previous one && + + git checkout previous && + git checkout master && + + git branch -f @{-1} two && + expect_branch previous two +' + +test_expect_success 'update branch via local @{upstream}' ' + git branch local one && + git branch --set-upstream-to=local && + + git branch -f @{upstream} two && + expect_branch local two +' + +test_expect_success 'disallow updating branch via remote @{upstream}' ' + git update-ref refs/remotes/origin/remote one && + git branch --set-upstream-to=origin/remote && + + test_must_fail git branch -f @{upstream} two +' + +test_expect_success 'create branch with pseudo-qualified name' ' + git branch refs/heads/qualified two && + expect_branch refs/heads/refs/heads/qualified two +' + +test_expect_success 'delete branch via @{-1}' ' + git branch previous-del && + + git checkout previous-del && + git checkout master && + + git branch -D @{-1} && + expect_deleted previous-del +' + +test_expect_success 'delete branch via local @{upstream}' ' + git branch local-del && + git branch --set-upstream-to=local-del && + + git branch -D @{upstream} && + expect_deleted local-del +' + +test_expect_success 'delete branch via remote @{upstream}' ' + git update-ref refs/remotes/origin/remote-del two && + git branch --set-upstream-to=origin/remote-del && + + git branch -r -D @{upstream} && + expect_deleted origin/remote-del +' + +# Note that we create two oddly named local branches here. We want to make +# sure that we do not accidentally delete either of them, even if +# shorten_unambiguous_ref() tweaks the name to avoid ambiguity. +test_expect_success 'delete @{upstream} expansion matches -r option' ' + git update-ref refs/remotes/origin/remote-del two && + git branch --set-upstream-to=origin/remote-del && + git update-ref refs/heads/origin/remote-del two && + git update-ref refs/heads/remotes/origin/remote-del two && + + test_must_fail git branch -D @{upstream} && + expect_branch refs/heads/origin/remote-del two && + expect_branch refs/heads/remotes/origin/remote-del two +' + +test_expect_success 'disallow deleting remote branch via @{-1}' ' + git update-ref refs/remotes/origin/previous one && + + git checkout -b origin/previous two && + git checkout master && + + test_must_fail git branch -r -D @{-1} && + expect_branch refs/remotes/origin/previous one && + expect_branch refs/heads/origin/previous two +' + +# The thing we are testing here is that "@" is the real branch refs/heads/@, +# and not refs/heads/HEAD. These tests should not imply that refs/heads/@ is a +# sane thing, but it _is_ technically allowed for now. If we disallow it, these +# can be switched to test_must_fail. +test_expect_success 'create branch named "@"' ' + git branch -f @ one && + expect_branch refs/heads/@ one +' + +test_expect_success 'delete branch named "@"' ' + git update-ref refs/heads/@ two && + git branch -D @ && + expect_deleted refs/heads/@ +' + +test_expect_success 'checkout does not treat remote @{upstream} as a branch' ' + git update-ref refs/remotes/origin/checkout one && + git branch --set-upstream-to=origin/checkout && + git update-ref refs/heads/origin/checkout two && + git update-ref refs/heads/remotes/origin/checkout two && + + git checkout @{upstream} && + expect_branch HEAD one +' + +test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 5ffe78e920..aaa258daa3 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -394,4 +394,22 @@ test_expect_success 'diffs can be colorized' ' grep "$(printf "\\033")" output ' +test_expect_success 'patch-mode via -i prompts for files' ' + git reset --hard && + + echo one >file && + echo two >test && + git add -i <<-\EOF && + patch + test + + y + quit + EOF + + echo test >expect && + git diff --cached --name-only >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 2de3e18ce6..89877e4b52 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -274,9 +274,7 @@ test_expect_success 'stash --invalid-option' ' git add file2 && test_must_fail git stash --invalid-option && test_must_fail git stash save --invalid-option && - test bar5,bar6 = $(cat file),$(cat file2) && - git stash -- -message-starting-with-dash && - test bar,bar2 = $(cat file),$(cat file2) + test bar5,bar6 = $(cat file),$(cat file2) ' test_expect_success 'stash an added file' ' @@ -775,4 +773,138 @@ test_expect_success 'stash is not confused by partial renames' ' test_path_is_missing file ' +test_expect_success 'push -m shows right message' ' + >foo && + git add foo && + git stash push -m "test message" && + echo "stash@{0}: On master: test message" >expect && + git stash list -1 >actual && + test_cmp expect actual +' + +test_expect_success 'create stores correct message' ' + >foo && + git add foo && + STASH_ID=$(git stash create "create test message") && + echo "On master: create test message" >expect && + git show --pretty=%s -s ${STASH_ID} >actual && + test_cmp expect actual +' + +test_expect_success 'create with multiple arguments for the message' ' + >foo && + git add foo && + STASH_ID=$(git stash create test untracked) && + echo "On master: test untracked" >expect && + git show --pretty=%s -s ${STASH_ID} >actual && + test_cmp expect actual +' + +test_expect_success 'stash -- <pathspec> stashes and restores the file' ' + >foo && + >bar && + git add foo bar && + git stash push -- foo && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file foo && + test_path_is_file bar +' + +test_expect_success 'stash with multiple pathspec arguments' ' + >foo && + >bar && + >extra && + git add foo bar extra && + git stash push -- foo bar && + test_path_is_missing bar && + test_path_is_missing foo && + test_path_is_file extra && + git stash pop && + test_path_is_file foo && + test_path_is_file bar && + test_path_is_file extra +' + +test_expect_success 'stash with file including $IFS character' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash push -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + +test_expect_success 'stash with pathspec matching multiple paths' ' + echo original >file && + echo original >other-file && + git commit -m "two" file other-file && + echo modified >file && + echo modified >other-file && + git stash push -- "*file" && + echo original >expect && + test_cmp expect file && + test_cmp expect other-file && + git stash pop && + echo modified >expect && + test_cmp expect file && + test_cmp expect other-file +' + +test_expect_success 'stash push -p with pathspec shows no changes only once' ' + >foo && + git add foo && + git commit -m "tmp" && + git stash push -p foo >actual && + echo "No local changes to save" >expect && + git reset --hard HEAD~ && + test_cmp expect actual +' + +test_expect_success 'stash push with pathspec shows no changes when there are none' ' + >foo && + git add foo && + git commit -m "tmp" && + git stash push foo >actual && + echo "No local changes to save" >expect && + git reset --hard HEAD~ && + test_cmp expect actual +' + +test_expect_success 'stash push with pathspec not in the repository errors out' ' + >untracked && + test_must_fail git stash push untracked && + test_path_is_file untracked +' + +test_expect_success 'untracked files are left in place when -u is not given' ' + >file && + git add file && + >untracked && + git stash push file && + test_path_is_file untracked +' + +test_expect_success 'stash without verb with pathspec' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + test_done diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index f372fc8ca8..193adc7b68 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -185,4 +185,30 @@ test_expect_success 'stash save --all is stash poppable' ' test -s .gitignore ' +test_expect_success 'stash push --include-untracked with pathspec' ' + >foo && + >bar && + git stash push --include-untracked -- foo && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file bar && + test_path_is_file foo +' + +test_expect_success 'stash push with $IFS character' ' + >"foo bar" && + >foo && + >bar && + git add foo* && + git stash push --include-untracked -- "foo b*" && + test_path_is_missing "foo bar" && + test_path_is_file foo && + test_path_is_file bar && + git stash pop && + test_path_is_file "foo bar" && + test_path_is_file foo && + test_path_is_file bar +' + test_done diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh index 461f4bb583..2f1737fcef 100755 --- a/t/t4035-diff-quiet.sh +++ b/t/t4035-diff-quiet.sh @@ -152,4 +152,13 @@ test_expect_success 'git diff --quiet ignores stat-change only entries' ' test_expect_code 1 git diff --quiet ' +test_expect_success 'git diff --quiet on a path that need conversion' ' + echo "crlf.txt text=auto" >.gitattributes && + printf "Hello\r\nWorld\r\n" >crlf.txt && + git add .gitattributes crlf.txt && + + printf "Hello\r\nWorld\n" >crlf.txt && + git diff --quiet crlf.txt +' + test_done diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index 9d87777b59..d0377fae5c 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -106,4 +106,14 @@ test_expect_success '-L with --output' ' test_line_count = 70 log ' +test_expect_success 'range_set_union' ' + test_seq 500 > c.c && + git add c.c && + git commit -m "many lines" && + test_seq 1000 > c.c && + git add c.c && + git commit -m "modify many lines" && + git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done) +' + test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 505e1b4a7f..b5865b385d 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -484,7 +484,7 @@ test_expect_success 'test lonely missing ref' ' cd client && test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy ) >/dev/null 2>error-m && - test_cmp expect-error error-m + test_i18ncmp expect-error error-m ' test_expect_success 'test missing ref after existing' ' @@ -492,7 +492,7 @@ test_expect_success 'test missing ref after existing' ' cd client && test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy ) >/dev/null 2>error-em && - test_cmp expect-error error-em + test_i18ncmp expect-error error-em ' test_expect_success 'test missing ref before existing' ' @@ -500,7 +500,7 @@ test_expect_success 'test missing ref before existing' ' cd client && test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A ) >/dev/null 2>error-me && - test_cmp expect-error error-me + test_i18ncmp expect-error error-me ' test_expect_success 'test --all, --depth, and explicit head' ' diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 0fc5a7c596..177897ea0b 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1098,7 +1098,8 @@ test_expect_success 'fetch exact SHA1' ' test_must_fail git cat-file -t $the_commit && # fetching the hidden object should fail by default - test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy && + test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err && + test_i18ngrep "Server does not allow request for unadvertised object" err && test_must_fail git rev-parse --verify refs/heads/copy && # the server side can allow it to succeed diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index b69ece1d66..87308cdced 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -387,5 +387,14 @@ test_expect_success 'http-alternates triggers not-from-user protocol check' ' clone $HTTPD_URL/dumb/evil.git evil-user ' +test_expect_success 'can redirect through non-"info/refs?service=git-upload-pack" URL' ' + git clone "$HTTPD_URL/redir-to/dumb/repo.git" +' + +test_expect_success 'print HTTP error when any intermediate redirect throws error' ' + test_must_fail git clone "$HTTPD_URL/redir-to/502" 2> stderr && + test_i18ngrep "unable to access.*/redir-to/502" stderr +' + stop_httpd test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index c87dc1f8bc..834a9ed168 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -584,7 +584,7 @@ test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' test_when_finished "git checkout master" && git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && sed -e "s/^\* / /" actual >expect && - git checkout --orphan HEAD && + git checkout --orphan orphaned-branch && git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && test_cmp expect actual ' diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index cb8fbd8e5e..7cb60799be 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -313,6 +313,27 @@ test_expect_success 'Tag name filtering allows slashes in tag names' ' git cat-file tag X/2 > actual && test_cmp expect actual ' +test_expect_success 'setup --prune-empty comparisons' ' + git checkout --orphan master-no-a && + git rm -rf . && + unset test_tick && + test_tick && + GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" && + test_commit --notick B B.t B Bx && + git checkout -b branch-no-a Bx && + test_commit D D.t D Dx && + mkdir dir && + test_commit dir/D dir/D.t dir/D dir/Dx && + test_commit E E.t E Ex && + git checkout master-no-a && + test_commit C C.t C Cx && + git checkout branch-no-a && + git merge Cx -m "Merge tag '\''C'\'' into branch" && + git tag Fx && + test_commit G G.t G Gx && + test_commit H H.t H Hx && + git checkout branch +' test_expect_success 'Prune empty commits' ' git rev-list HEAD > expect && @@ -341,6 +362,22 @@ test_expect_success 'prune empty works even without index/tree filters' ' test_cmp expect actual ' +test_expect_success '--prune-empty is able to prune root commit' ' + git rev-list branch-no-a >expect && + git branch testing H && + git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t" testing && + git rev-list testing >actual && + git branch -D testing && + test_cmp expect actual +' + +test_expect_success '--prune-empty is able to prune entire branch' ' + git branch prune-entire B && + git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire && + test_path_is_missing .git/refs/heads/prune-entire && + test_must_fail git reflog exists refs/heads/prune-entire +' + test_expect_success '--remap-to-ancestor with filename filters' ' git checkout master && git reset --hard A && diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh new file mode 100755 index 0000000000..b22f631261 --- /dev/null +++ b/t/t7518-ident-corner-cases.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +test_description='corner cases in ident strings' +. ./test-lib.sh + +# confirm that we do not segfault _and_ that we do not say "(null)", as +# glibc systems will quietly handle our NULL pointer +# +# Note also that we can't use "env" here because we need to unset a variable, +# and "-u" is not portable. +test_expect_success 'empty name and missing email' ' + ( + sane_unset GIT_AUTHOR_EMAIL && + GIT_AUTHOR_NAME= && + test_must_fail git commit --allow-empty -m foo 2>err && + test_i18ngrep ! null err + ) +' + +test_expect_success 'commit rejects all-crud name' ' + test_must_fail env GIT_AUTHOR_NAME=" .;<>" \ + git commit --allow-empty -m foo +' + +# We must test the actual error message here, as an unwanted +# auto-detection could fail for other reasons. +test_expect_success 'empty configured name does not auto-detect' ' + ( + sane_unset GIT_AUTHOR_NAME && + test_must_fail \ + git -c user.name= commit --allow-empty -m foo 2>err && + test_i18ngrep "empty ident name" err + ) +' + +test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 0f398dd160..60a80f60b2 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -148,7 +148,6 @@ cat >expected-cc <<\EOF !two@example.com! !three@example.com! !four@example.com! -!five@example.com! EOF " @@ -159,9 +158,9 @@ test_expect_success $PREREQ 'cc trailer with various syntax' ' Test Cc: trailers. Cc: one@example.com - Cc: <two@example.com> # this is part of the name - Cc: <three@example.com>, <four@example.com> # not.five@example.com - Cc: "Some # Body" <five@example.com> [part.of.name.too] + Cc: <two@example.com> # trailing comments are ignored + Cc: <three@example.com>, <not.four@example.com> one address per line + Cc: "Some # Body" <four@example.com> [ <also.a.comment> ] EOF clean_fake_sendmail && git send-email -1 --to=recipient@example.com \ diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index bb879a527d..1319415ba8 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -18,6 +18,11 @@ then test_done fi +if ! test_have_prereq NOT_ROOT; then + skip_all='When cvs is compiled with CVS_BADROOT commits as root fail' + test_done +fi + CVSROOT=$PWD/tmpcvsroot CVSWORK=$PWD/cvswork GIT_DIR=$PWD/.git diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 4c384ff023..804ce3850f 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -3,6 +3,11 @@ test_description='git cvsimport basic tests' . ./lib-cvs.sh +if ! test_have_prereq NOT_ROOT; then + skip_all='When cvs is compiled with CVS_BADROOT commits as root fail' + test_done +fi + test_expect_success PERL 'setup cvsroot environment' ' CVSROOT=$(pwd)/cvsroot && export CVSROOT diff --git a/transport.c b/transport.c index 5828e06afd..417ed7f19f 100644 --- a/transport.c +++ b/transport.c @@ -204,6 +204,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus static int fetch_refs_via_pack(struct transport *transport, int nr_heads, struct ref **to_fetch) { + int ret = 0; struct git_transport_data *data = transport->data; struct ref *refs; char *dest = xstrdup(transport->url); @@ -241,19 +242,22 @@ static int fetch_refs_via_pack(struct transport *transport, &transport->pack_lockfile); close(data->fd[0]); close(data->fd[1]); - if (finish_connect(data->conn)) { - free_refs(refs); - refs = NULL; - } + if (finish_connect(data->conn)) + ret = -1; data->conn = NULL; data->got_remote_heads = 0; data->options.self_contained_and_connected = args.self_contained_and_connected; + if (refs == NULL) + ret = -1; + if (report_unmatched_refs(to_fetch, nr_heads)) + ret = -1; + free_refs(refs_tmp); free_refs(refs); free(dest); - return (refs ? 0 : -1); + return ret; } static int push_had_errors(struct ref *ref) @@ -467,11 +471,11 @@ void transport_print_push_status(const char *dest, struct ref *refs, { struct ref *ref; int n = 0; - unsigned char head_sha1[20]; + struct object_id head_oid; char *head; int summary_width = transport_summary_width(refs); - head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL); + head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL); if (verbose) { for (ref = refs; ref; ref = ref->next) diff --git a/upload-pack.c b/upload-pack.c index 7597ba3405..ffb028d623 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -822,9 +822,13 @@ static void receive_needs(void) use_include_tag = 1; o = parse_object(sha1_buf); - if (!o) + if (!o) { + packet_write_fmt(1, + "ERR upload-pack: not our ref %s", + sha1_to_hex(sha1_buf)); die("git upload-pack: not our ref %s", sha1_to_hex(sha1_buf)); + } if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1 diff --git a/worktree.c b/worktree.c index d633761575..fa7bc67a50 100644 --- a/worktree.c +++ b/worktree.c @@ -175,7 +175,7 @@ struct worktree **get_worktrees(unsigned flags) struct dirent *d; int counter = 0, alloc = 2; - list = xmalloc(alloc * sizeof(struct worktree *)); + ALLOC_ARRAY(list, alloc); list[counter++] = get_main_worktree(); @@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list, return wt; arg = prefix_filename(prefix, strlen(prefix), arg); - path = real_pathdup(arg); + path = real_pathdup(arg, 1); for (; *list; list++) if (!fspathcmp(path, real_path((*list)->path))) break; @@ -440,23 +440,6 @@ int xmkstemp(char *template) return fd; } -/* git_mkstemp() - create tmp file honoring TMPDIR variable */ -int git_mkstemp(char *path, size_t len, const char *template) -{ - const char *tmp; - size_t n; - - tmp = getenv("TMPDIR"); - if (!tmp) - tmp = "/tmp"; - n = snprintf(path, len, "%s/%s", tmp, template); - if (len <= n) { - errno = ENAMETOOLONG; - return -1; - } - return mkstemp(path); -} - /* Adapted from libiberty's mkstemp.c. */ #undef TMP_MAX @@ -531,13 +514,6 @@ int git_mkstemp_mode(char *pattern, int mode) return git_mkstemps_mode(pattern, 0, mode); } -#ifdef NO_MKSTEMPS -int gitmkstemps(char *pattern, int suffix_len) -{ - return git_mkstemps_mode(pattern, suffix_len, 0600); -} -#endif - int xmkstemp_mode(char *template, int mode) { int fd; diff --git a/wt-status.c b/wt-status.c index d47012048f..a8d1faf80d 100644 --- a/wt-status.c +++ b/wt-status.c @@ -121,7 +121,7 @@ static void status_printf_more(struct wt_status *s, const char *color, void wt_status_prepare(struct wt_status *s) { - unsigned char sha1[20]; + struct object_id oid; memset(s, 0, sizeof(*s)); memcpy(s->color_palette, default_wt_status_colors, @@ -129,7 +129,7 @@ void wt_status_prepare(struct wt_status *s) s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; s->use_color = -1; s->relative_paths = 1; - s->branch = resolve_refdup("HEAD", 0, sha1, NULL); + s->branch = resolve_refdup("HEAD", 0, oid.hash, NULL); s->reference = "HEAD"; s->fp = stdout; s->index_file = get_index_file(); @@ -1115,16 +1115,16 @@ static void abbrev_sha1_in_line(struct strbuf *line) split = strbuf_split_max(line, ' ', 3); if (split[0] && split[1]) { - unsigned char sha1[20]; + struct object_id oid; /* * strbuf_split_max left a space. Trim it and re-add * it after abbreviation. */ strbuf_trim(split[1]); - if (!get_sha1(split[1]->buf, sha1)) { + if (!get_oid(split[1]->buf, &oid)) { strbuf_reset(split[1]); - strbuf_add_unique_abbrev(split[1], sha1, + strbuf_add_unique_abbrev(split[1], oid.hash, DEFAULT_ABBREV); strbuf_addch(split[1], ' '); strbuf_reset(line); @@ -1340,7 +1340,7 @@ static void show_bisect_in_progress(struct wt_status *s, static char *get_branch(const struct worktree *wt, const char *path) { struct strbuf sb = STRBUF_INIT; - unsigned char sha1[20]; + struct object_id oid; const char *branch_name; if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0) @@ -1354,9 +1354,9 @@ static char *get_branch(const struct worktree *wt, const char *path) strbuf_remove(&sb, 0, branch_name - sb.buf); else if (starts_with(sb.buf, "refs/")) ; - else if (!get_sha1_hex(sb.buf, sha1)) { + else if (!get_oid_hex(sb.buf, &oid)) { strbuf_reset(&sb); - strbuf_add_unique_abbrev(&sb, sha1, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(&sb, oid.hash, DEFAULT_ABBREV); } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */ goto got_nothing; else /* bisect */ @@ -1370,10 +1370,10 @@ got_nothing: struct grab_1st_switch_cbdata { struct strbuf buf; - unsigned char nsha1[20]; + struct object_id noid; }; -static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, +static int grab_1st_switch(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -1387,13 +1387,13 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, return 0; target += strlen(" to "); strbuf_reset(&cb->buf); - hashcpy(cb->nsha1, nsha1); + oidcpy(&cb->noid, noid); end = strchrnul(target, '\n'); strbuf_add(&cb->buf, target, end - target); if (!strcmp(cb->buf.buf, "HEAD")) { /* HEAD is relative. Resolve it to the right reflog entry. */ strbuf_reset(&cb->buf); - strbuf_add_unique_abbrev(&cb->buf, nsha1, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(&cb->buf, noid->hash, DEFAULT_ABBREV); } return 1; } @@ -1402,7 +1402,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state) { struct grab_1st_switch_cbdata cb; struct commit *commit; - unsigned char sha1[20]; + struct object_id oid; char *ref = NULL; strbuf_init(&cb.buf, 0); @@ -1411,22 +1411,22 @@ static void wt_status_get_detached_from(struct wt_status_state *state) return; } - if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 && + if (dwim_ref(cb.buf.buf, cb.buf.len, oid.hash, &ref) == 1 && /* sha1 is a commit? match without further lookup */ - (!hashcmp(cb.nsha1, sha1) || + (!oidcmp(&cb.noid, &oid) || /* perhaps sha1 is a tag, try to dereference to a commit */ - ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL && - !hashcmp(cb.nsha1, commit->object.oid.hash)))) { + ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL && + !oidcmp(&cb.noid, &commit->object.oid)))) { const char *from = ref; if (!skip_prefix(from, "refs/tags/", &from)) skip_prefix(from, "refs/remotes/", &from); state->detached_from = xstrdup(from); } else state->detached_from = - xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV)); - hashcpy(state->detached_sha1, cb.nsha1); - state->detached_at = !get_sha1("HEAD", sha1) && - !hashcmp(sha1, state->detached_sha1); + xstrdup(find_unique_abbrev(cb.noid.hash, DEFAULT_ABBREV)); + hashcpy(state->detached_sha1, cb.noid.hash); + state->detached_at = !get_oid("HEAD", &oid) && + !hashcmp(oid.hash, state->detached_sha1); free(ref); strbuf_release(&cb.buf); @@ -1476,22 +1476,22 @@ void wt_status_get_state(struct wt_status_state *state, int get_detached_from) { struct stat st; - unsigned char sha1[20]; + struct object_id oid; if (!stat(git_path_merge_head(), &st)) { state->merge_in_progress = 1; } else if (wt_status_check_rebase(NULL, state)) { ; /* all set */ } else if (!stat(git_path_cherry_pick_head(), &st) && - !get_sha1("CHERRY_PICK_HEAD", sha1)) { + !get_oid("CHERRY_PICK_HEAD", &oid)) { state->cherry_pick_in_progress = 1; - hashcpy(state->cherry_pick_head_sha1, sha1); + hashcpy(state->cherry_pick_head_sha1, oid.hash); } wt_status_check_bisect(NULL, state); if (!stat(git_path_revert_head(), &st) && - !get_sha1("REVERT_HEAD", sha1)) { + !get_oid("REVERT_HEAD", &oid)) { state->revert_in_progress = 1; - hashcpy(state->revert_head_sha1, sha1); + hashcpy(state->revert_head_sha1, oid.hash); } if (get_detached_from) |