diff options
128 files changed, 2497 insertions, 979 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..cbeebdab7a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "sha1collisiondetection"] + path = sha1collisiondetection + url = https://github.com/cr-marcstevens/sha1collisiondetection.git + branch = master diff --git a/Documentation/Makefile b/Documentation/Makefile index b5be2e2d3f..2415e0d657 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -31,6 +31,7 @@ MAN7_TXT += giteveryday.txt MAN7_TXT += gitglossary.txt MAN7_TXT += gitnamespaces.txt MAN7_TXT += gitrevisions.txt +MAN7_TXT += gitsubmodules.txt MAN7_TXT += gittutorial-2.txt MAN7_TXT += gittutorial.txt MAN7_TXT += gitworkflows.txt diff --git a/Documentation/RelNotes/1.7.10.1.txt b/Documentation/RelNotes/1.7.10.1.txt index be68524cff..71a86cb7c6 100644 --- a/Documentation/RelNotes/1.7.10.1.txt +++ b/Documentation/RelNotes/1.7.10.1.txt @@ -69,7 +69,7 @@ Fixes since v1.7.10 * The 'push to upstream' implementation was broken in some corner cases. "git push $there" without refspec, when the current branch is set to push to a remote different from $there, used to push to - $there using the upstream information to a remote unreleated to + $there using the upstream information to a remote unrelated to $there. * Giving "--continue" to a conflicted "rebase -i" session skipped a diff --git a/Documentation/RelNotes/2.12.0.txt b/Documentation/RelNotes/2.12.0.txt index 29154805b4..ef8b97da9b 100644 --- a/Documentation/RelNotes/2.12.0.txt +++ b/Documentation/RelNotes/2.12.0.txt @@ -264,7 +264,7 @@ notes for details). needed it so far. * Git 2.11 had a minor regression in "merge --ff-only" that competed - with another process that simultanously attempted to update the + with another process that simultaneously attempted to update the index. We used to explain what went wrong with an error message, but the new code silently failed. The error message has been resurrected. diff --git a/Documentation/RelNotes/2.13.3.txt b/Documentation/RelNotes/2.13.3.txt new file mode 100644 index 0000000000..5d76ad5310 --- /dev/null +++ b/Documentation/RelNotes/2.13.3.txt @@ -0,0 +1,62 @@ +Git v2.13.3 Release Notes +========================= + +Fixes since v2.13.2 +------------------- + + * The "collision detecting" SHA-1 implementation shipped with 2.13.2 + was still broken on some platforms. Update to the upstream code + again to take their fix. + + * The 'diff-highlight' program (in contrib/) has been restructured + for easier reuse by an external project 'diff-so-fancy'. + + * "git mergetool" learned to work around a wrapper MacOS X adds + around underlying meld. + + * An example in documentation that does not work in multi worktree + configuration has been corrected. + + * The pretty-format specifiers like '%h', '%t', etc. had an + optimization that no longer works correctly. In preparation/hope + of getting it correctly implemented, first discard the optimization + that is broken. + + * The code to pick up and execute command alias definition from the + configuration used to switch to the top of the working tree and + then come back when the expanded alias was executed, which was + unnecessarilyl complex. Attempt to simplify the logic by using the + early-config mechanism that does not chdir around. + + * "git add -p" were updated in 2.12 timeframe to cope with custom + core.commentchar but the implementation was buggy and a + metacharacter like $ and * did not work. + + * Fix a recent regression to "git rebase -i" and add tests that would + have caught it and others. + + * An unaligned 32-bit access in pack-bitmap code ahs been corrected. + + * Tighten error checks for invalid "git apply" input. + + * The split index code did not honor core.sharedrepository setting + correctly. + + * The Makefile rule in contrib/subtree for building documentation + learned to honour USE_ASCIIDOCTOR just like the main documentation + set does. + + * A few tests that tried to verify the contents of push certificates + did not use 'git rev-parse' to formulate the line to look for in + the certificate correctly. + + * After "git branch --move" of the currently checked out branch, the + code to walk the reflog of HEAD via "log -g" and friends + incorrectly stopped at the reflog entry that records the renaming + of the branch. + + * The rewrite of "git branch --list" using for-each-ref's internals + that happened in v2.13 regressed its handling of color.branch.local; + this has been fixed. + +Also contains various documentation updates and code clean-ups. diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index 8b80058a85..93f0654109 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -54,7 +54,7 @@ UI, Workflows & Features even when the other side hasn't enabled allowTipSHA1InWant. * The recently introduced "[includeIf "gitdir:$dir"] path=..." - mechansim has further been taught to take symlinks into account. + mechanism has further been taught to take symlinks into account. The directory "$dir" specified in "gitdir:$dir" may be a symlink to a real location, not something that $(getcwd) may return. In such a case, a realpath of "$dir" is compared with the real path of the @@ -94,6 +94,20 @@ UI, Workflows & Features * "git status" learned to optionally give how many stash entries the user has in its output. + * "git status" has long shown essentially the same message as "git + commit"; the message it gives while preparing for the root commit, + i.e. "Initial commit", was hard to understand for some new users. + Now it says "No commits yet" to stress more on the current status + (rather than the commit the user is preparing for, which is more in + line with the focus of "git commit"). + + * "git send-email" learned to overcome some SMTP server limitation + that does not allow many pieces of e-mails to be sent over a single + session. + + * An old message shown in the commit log template was removed, as it + has outlived its usefulness. + Performance, Internal Implementation, Development Support etc. @@ -184,12 +198,25 @@ Performance, Internal Implementation, Development Support etc. * The 'diff-highlight' program (in contrib/) has been restructured for easier reuse by an external project 'diff-so-fancy'. - (merge 0c977dbc81 jk/diff-highlight-module later to maint). * A common pattern to free a piece of memory and assign NULL to the pointer that used to point at it has been replaced with a new FREE_AND_NULL() macro. + * Traditionally, the default die() routine had a code to prevent it + from getting called multiple times, which interacted badly when a + threaded program used it (one downside is that the real error may + be hidden and instead the only error message given to the user may + end up being "die recursion detected", which is not very useful). + + * Introduce a "repository" object to eventually make it easier to + work in multiple repositories (the primary focus is to work with + the superproject and its submodules) in a single process. + + * Optimize "what are the object names already taken in an alternate + object database?" query that is used to derive the length of prefix + an object name is uniquely abbreviated to. + Also contains various documentation updates and code clean-ups. @@ -364,24 +391,20 @@ notes for details). * "git mergetool" learned to work around a wrapper MacOS X adds around underlying meld. - (merge 0af85f84bd da/mergetools-meld-output-opt-on-macos later to maint). * An example in documentation that does not work in multi worktree configuration has been corrected. - (merge 773a88914f ah/doc-gitattributes-empty-index later to maint). * The pretty-format specifiers like '%h', '%t', etc. had an optimization that no longer works correctly. In preparation/hope of getting it correctly implemented, first discard the optimization that is broken. - (merge fe9e2aefd4 rs/pretty-add-again later to maint). * The code to pick up and execute command alias definition from the configuration used to switch to the top of the working tree and then come back when the expanded alias was executed, which was unnecessarilyl complex. Attempt to simplify the logic by using the early-config mechanism that does not chdir around. - (merge a9bcf6586d js/alias-early-config later to maint). * Fix configuration codepath to pay proper attention to commondir that is used in multi-worktree situation, and isolate config API @@ -391,9 +414,48 @@ notes for details). * "git add -p" were updated in 2.12 timeframe to cope with custom core.commentchar but the implementation was buggy and a metacharacter like $ and * did not work. - (merge d85d7ecb80 jk/add-p-commentchar-fix later to maint). + + * A recent regression in "git rebase -i" has been fixed and tests + that would have caught it and others have been added. + + * An unaligned 32-bit access in pack-bitmap code ahs been corrected. + + * Tighten error checks for invalid "git apply" input. + + * The split index code did not honor core.sharedrepository setting + correctly. + + * The Makefile rule in contrib/subtree for building documentation + learned to honour USE_ASCIIDOCTOR just like the main documentation + set does. + + * Update the sha1dc again to fix portability glitches. + + * Code clean-up to fix possible buffer over-reading. + (merge 8bc172e5f2 rs/apply-avoid-over-reading later to maint). + + * A few tests that tried to verify the contents of push certificates + did not use 'git rev-parse' to formulate the line to look for in + the certificate correctly. + + * Update the character width tables. + (merge 7560aacd7c bb/unicode-10.0 later to maint). + + * After "git branch --move" of the currently checked out branch, the + code to walk the reflog of HEAD via "log -g" and friends + incorrectly stopped at the reflog entry that records the renaming + of the branch. + (merge e30d463d45 jk/reflog-walk-maint later to maint). + + * The rewrite of "git branch --list" using for-each-ref's internals + that happened in v2.13 regressed its handling of color.branch.local; + this has been fixed. + (merge 5b5c9c3e19 kn/ref-filter-branch-list later to maint). * Other minor doc, test and build updates and code cleanups. - (merge 68241cb9dd sb/t4005-modernize later to maint). - (merge 4fced24712 ks/t7508-indent-fix later to maint). - (merge 968b1fe263 mb/reword-autocomplete-message later to maint). + (merge 3f9c637ec7 pw/unquote-path-in-git-pm later to maint). + (merge 669638fe7a ks/typofix-commit-c-comment later to maint). + (merge 5053313562 rs/urlmatch-cleanup later to maint). + (merge 42c78a216e rs/use-div-round-up later to maint). + (merge 5e8d2729ae rs/wt-status-cleanup later to maint). + (merge 01826066b0 ks/fix-rebase-doc-picture later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index 06898a7498..d5c9c4cab6 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2946,6 +2946,16 @@ sendemail.xmailer:: sendemail.signedoffcc (deprecated):: Deprecated alias for `sendemail.signedoffbycc`. +sendemail.smtpBatchSize:: + Number of messages to be sent per connection, after that a relogin + will happen. If the value is 0 or undefined, send all messages in + one connection. + See also the `--batch-size` option of linkgit:git-send-email[1]. + +sendemail.smtpReloginDelay:: + Seconds wait before reconnecting to smtp server. + See also the `--relogin-delay` option of linkgit:git-send-email[1]. + showbranch.default:: The default set of branches for linkgit:git-show-branch[1]. See linkgit:git-show-branch[1]. diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index a5afd602d8..4f6bed61a9 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -675,7 +675,7 @@ on this 'subsystem'. You might end up with a history like the following: ------------ - o---o---o---o---o---o---o---o---o master + o---o---o---o---o---o---o---o master \ o---o---o---o---o subsystem \ diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 70f3753e1e..1d697d9962 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -294,7 +294,7 @@ $ git reset --keep start <3> Split a commit apart into a sequence of commits:: + -Suppose that you have created lots of logically separate changes and commited +Suppose that you have created lots of logically separate changes and committed them together. Then, later you decide that it might be better to have each logical chunk associated with its own commit. You can use git reset to rewind history without changing the contents of your local files, and then successively diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index 8c87e8cdd7..683e591330 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -153,8 +153,8 @@ Ignored files are deemed expendable and won't stop a submodule's work tree from being removed. If you only want to remove the local checkout of a submodule from your -work tree without committing the removal, -use linkgit:git-submodule[1] `deinit` instead. +work tree without committing the removal, use linkgit:git-submodule[1] `deinit` +instead. Also see linkgit:gitsubmodules[7] for details on submodule removal. EXAMPLES -------- diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index bb23b02caf..bac9014ac7 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -248,6 +248,21 @@ must be used for each option. commands and replies will be printed. Useful to debug TLS connection and authentication problems. +--batch-size=<num>:: + Some email servers (e.g. smtp.163.com) limit the number emails to be + sent per session (connection) and this will lead to a faliure when + sending many messages. With this option, send-email will disconnect after + sending $<num> messages and wait for a few seconds (see --relogin-delay) + and reconnect, to work around such a limit. You may want to + use some form of credential helper to avoid having to retype + your password every time this happens. Defaults to the + `sendemail.smtpBatchSize` configuration variable. + +--relogin-delay=<int>:: + Waiting $<int> seconds before reconnecting to SMTP server. Used together + with --batch-size option. Defaults to the `sendemail.smtpReloginDelay` + configuration variable. + Automating ~~~~~~~~~~ diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 74bc6200d5..ff612001d2 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -24,37 +24,7 @@ DESCRIPTION ----------- Inspects, updates and manages submodules. -A submodule allows you to keep another Git repository in a subdirectory -of your repository. The other repository has its own history, which does not -interfere with the history of the current repository. This can be used to -have external dependencies such as third party libraries for example. - -When cloning or pulling a repository containing submodules however, -these will not be checked out by default; the 'init' and 'update' -subcommands will maintain submodules checked out and at -appropriate revision in your working tree. - -Submodules are composed from a so-called `gitlink` tree entry -in the main repository that refers to a particular commit object -within the inner repository that is completely separate. -A record in the `.gitmodules` (see linkgit:gitmodules[5]) file at the -root of the source tree assigns a logical name to the submodule and -describes the default URL the submodule shall be cloned from. -The logical name can be used for overriding this URL within your -local repository configuration (see 'submodule init'). - -Submodules are not to be confused with remotes, which are other -repositories of the same project; submodules are meant for -different projects you would like to make part of your source tree, -while the history of the two projects still stays completely -independent and you cannot modify the contents of the submodule -from within the main project. -If you want to merge the project histories and want to treat the -aggregated whole as a single project from then on, you may want to -add a remote for the other project and use the 'subtree' merge strategy, -instead of treating the other project as a submodule. Directories -that come from both projects can be cloned and checked out as a whole -if you choose to go that route. +For more information about submodules, see linkgit:gitsubmodules[7]. COMMANDS -------- @@ -63,14 +33,6 @@ add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--dep to the changeset to be committed next to the current project: the current project is termed the "superproject". + -This requires at least one argument: <repository>. The optional -argument <path> is the relative location for the cloned submodule -to exist in the superproject. If <path> is not given, the -"humanish" part of the source repository is used ("repo" for -"/path/to/repo.git" and "foo" for "host.xz:foo/.git"). -The <path> is also used as the submodule's logical name in its -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 default remote @@ -87,21 +49,22 @@ 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. + -<path> is the relative location for the cloned submodule to -exist in the superproject. If <path> does not exist, then the -submodule is created by cloning from the named URL. If <path> does -exist and is already a valid Git repository, then this is added -to the changeset without cloning. This second form is provided -to ease creating a new submodule from scratch, and presumes -the user will later push the submodule to the given URL. +The optional argument <path> is the relative location for the cloned +submodule to exist in the superproject. If <path> is not given, the +canonical part of the source repository is used ("repo" for +"/path/to/repo.git" and "foo" for "host.xz:foo/.git"). If <path> +exists and is already a valid Git repository, then it is staged +for commit without cloning. The <path> is also used as the submodule's +logical name in its configuration entries unless `--name` is used +to specify a logical name. + -In either case, the given URL is recorded into .gitmodules for -use by subsequent users cloning the superproject. If the URL is -given relative to the superproject's repository, the presumption -is the superproject and submodule repositories will be kept -together in the same relative location, and only the -superproject's URL needs to be provided: git-submodule will correctly -locate the submodule using the relative URL in .gitmodules. +The given URL is recorded into `.gitmodules` for use by subsequent users +cloning the superproject. If the URL is given relative to the +superproject's repository, the presumption is the superproject and +submodule repositories will be kept together in the same relative +location, and only the superproject's URL needs to be provided. +git-submodule will correctly locate the submodule using the relative +URL in `.gitmodules`. status [--cached] [--recursive] [--] [<path>...]:: Show the status of the submodules. This will print the SHA-1 of the @@ -123,7 +86,7 @@ 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 setting `submodule.$name.url` - in .git/config. It uses the same setting from .gitmodules as + 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. @@ -141,7 +104,7 @@ 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. +See the add subcommand for the definition of default remote. deinit [-f|--force] (--all|[--] <path>...):: Unregister the given submodules, i.e. remove the whole @@ -149,15 +112,17 @@ deinit [-f|--force] (--all|[--] <path>...):: tree. Further calls to `git submodule update`, `git submodule foreach` and `git submodule sync` will skip any unregistered submodules until they are initialized again, so use this command if you don't want to - have a local checkout of the submodule in your working tree anymore. If - you really want to remove a submodule from the repository and commit - that use linkgit:git-rm[1] instead. + have a local checkout of the submodule in your working tree anymore. + When the command is run without pathspec, it errors out, instead of deinit-ing everything, to prevent mistakes. + If `--force` is specified, the submodule's working tree will be removed even if it contains local modifications. ++ +If you really want to remove a submodule from the repository and commit +that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal +options. update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]:: + @@ -197,7 +162,7 @@ configuration variable: none;; the submodule is not updated. If the submodule is not yet initialized, and you just want to use the -setting as stored in .gitmodules, you can automatically initialize the +setting as stored in `.gitmodules`, you can automatically initialize the submodule with the `--init` option. If `--recursive` is specified, this command will recurse into the @@ -220,7 +185,7 @@ foreach [--recursive] <command>:: Evaluates an arbitrary shell command in each checked out submodule. The command has access to the variables $name, $path, $sha1 and $toplevel: - $name is the name of the relevant submodule section in .gitmodules, + $name is the name of the relevant submodule section in `.gitmodules`, $path is the name of the submodule directory relative to the superproject, $sha1 is the commit as recorded in the superproject, and $toplevel is the absolute path to the top-level of the superproject. @@ -242,7 +207,7 @@ git submodule foreach 'echo $path `git rev-parse HEAD`' sync [--recursive] [--] [<path>...]:: Synchronizes submodules' remote URL configuration setting - to the value specified in .gitmodules. It will only affect those + to the value specified in `.gitmodules`. It will only affect those submodules which already have a URL entry in .git/config (that is the case when they are initialized or freshly added). This is useful when submodule URLs change upstream and you need to update your local @@ -413,7 +378,7 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully. --[no-]recommend-shallow:: This option is only valid for the update command. The initial clone of a submodule will use the recommended - `submodule.<name>.shallow` as provided by the .gitmodules file + `submodule.<name>.shallow` as provided by the `.gitmodules` file by default. To ignore the suggestions use `--no-recommend-shallow`. -j <n>:: @@ -429,12 +394,16 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully. FILES ----- -When initializing submodules, a .gitmodules file in the top-level directory +When initializing submodules, a `.gitmodules` file in the top-level directory of the containing repository is used to find the url of each submodule. This file should be formatted in the same way as `$GIT_DIR/config`. The key to each submodule url is "submodule.$name.url". See linkgit:gitmodules[5] for details. +SEE ALSO +-------- +linkgit:gitsubmodules[7], linkgit:gitmodules[5]. + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/gitsubmodules.txt b/Documentation/gitsubmodules.txt new file mode 100644 index 0000000000..46cf120f66 --- /dev/null +++ b/Documentation/gitsubmodules.txt @@ -0,0 +1,221 @@ +gitsubmodules(7) +================ + +NAME +---- +gitsubmodules - mounting one repository inside another + +SYNOPSIS +-------- + .gitmodules, $GIT_DIR/config +------------------ +git submodule +git <command> --recurse-submodules +------------------ + +DESCRIPTION +----------- + +A submodule is a repository embedded inside another repository. +The submodule has its own history; the repository it is embedded +in is called a superproject. + +On the filesystem, a submodule usually (but not always - see FORMS below) +consists of (i) a Git directory located under the `$GIT_DIR/modules/` +directory of its superproject, (ii) a working directory inside the +superproject's working directory, and a `.git` file at the root of +the submodule’s working directory pointing to (i). + +Assuming the submodule has a Git directory at `$GIT_DIR/modules/foo/` +and a working directory at `path/to/bar/`, the superproject tracks the +submodule via a `gitlink` entry in the tree at `path/to/bar` and an entry +in its `.gitmodules` file (see linkgit:gitmodules[5]) of the form +`submodule.foo.path = path/to/bar`. + +The `gitlink` entry contains the object name of the commit that the +superproject expects the submodule’s working directory to be at. + +The section `submodule.foo.*` in the `.gitmodules` file gives additional +hints to Gits porcelain layer such as where to obtain the submodule via +the `submodule.foo.url` setting. + +Submodules can be used for at least two different use cases: + +1. Using another project while maintaining independent history. + Submodules allow you to contain the working tree of another project + within your own working tree while keeping the history of both + projects separate. Also, since submodules are fixed to an arbitrary + version, the other project can be independently developed without + affecting the superproject, allowing the superproject project to + fix itself to new versions only when desired. + +2. Splitting a (logically single) project into multiple + repositories and tying them back together. This can be used to + overcome current limitations of Gits implementation to have + finer grained access: + + * Size of the git repository: + In its current form Git scales up poorly for large repositories containing + content that is not compressed by delta computation between trees. + However you can also use submodules to e.g. hold large binary assets + and these repositories are then shallowly cloned such that you do not + have a large history locally. + * Transfer size: + In its current form Git requires the whole working tree present. It + does not allow partial trees to be transferred in fetch or clone. + * Access control: + By restricting user access to submodules, this can be used to implement + read/write policies for different users. + +The configuration of submodules +------------------------------- + +Submodule operations can be configured using the following mechanisms +(from highest to lowest precedence): + + * The command line for those commands that support taking submodule specs. + Most commands have a boolean flag '--recurse-submodules' whether to + recurse into submodules. Examples are `ls-files` or `checkout`. + Some commands take enums, such as `fetch` and `push`, where you can + specify how submodules are affected. + + * The configuration inside the submodule. This includes `$GIT_DIR/config` + in the submodule, but also settings in the tree such as a `.gitattributes` + or `.gitignore` files that specify behavior of commands inside the + submodule. ++ +For example an effect from the submodule's `.gitignore` file +would be observed when you run `git status --ignore-submodules=none` in +the superproject. This collects information from the submodule's working +directory by running `status` in the submodule, which does pay attention +to its `.gitignore` file. ++ +The submodule's `$GIT_DIR/config` file would come into play when running +`git push --recurse-submodules=check` in the superproject, as this would +check if the submodule has any changes not published to any remote. The +remotes are configured in the submodule as usual in the `$GIT_DIR/config` +file. + + * The configuration file `$GIT_DIR/config` in the superproject. + Typical configuration at this place is controlling if a submodule + is recursed into at all via the `active` flag for example. ++ +If the submodule is not yet initialized, then the configuration +inside the submodule does not exist yet, so configuration where to +obtain the submodule from is configured here for example. + + * the `.gitmodules` file inside the superproject. Additionally to the + required mapping between submodule's name and path, a project usually + uses this file to suggest defaults for the upstream collection + of repositories. ++ +This file mainly serves as the mapping between name and path in +the superproject, such that the submodule's git directory can be +located. ++ +If the submodule has never been initialized, this is the only place +where submodule configuration is found. It serves as the last fallback +to specify where to obtain the submodule from. + +FORMS +----- + +Submodules can take the following forms: + + * The basic form described in DESCRIPTION with a Git directory, +a working directory, a `gitlink`, and a `.gitmodules` entry. + + * "Old-form" submodule: A working directory with an embedded +`.git` directory, and the tracking `gitlink` and `.gitmodules` entry in +the superproject. This is typically found in repositories generated +using older versions of Git. ++ +It is possible to construct these old form repositories manually. ++ +When deinitialized or deleted (see below), the submodule’s Git +directory is automatically moved to `$GIT_DIR/modules/<name>/` +of the superproject. + + * Deinitialized submodule: A `gitlink`, and a `.gitmodules` entry, +but no submodule working directory. The submodule’s git directory +may be there as after deinitializing the git directory is kept around. +The directory which is supposed to be the working directory is empty instead. ++ +A submodule can be deinitialized by running `git submodule deinit`. +Besides emptying the working directory, this command only modifies +the superproject’s `$GIT_DIR/config` file, so the superproject’s history +is not affected. This can be undone using `git submodule init`. + + * Deleted submodule: A submodule can be deleted by running +`git rm <submodule path> && git commit`. This can be undone +using `git revert`. ++ +The deletion removes the superproject’s tracking data, which are +both the `gitlink` entry and the section in the `.gitmodules` file. +The submodule’s working directory is removed from the file +system, but the Git directory is kept around as it to make it +possible to checkout past commits without requiring fetching +from another repository. ++ +To completely remove a submodule, manually delete +`$GIT_DIR/modules/<name>/`. + +Workflow for a third party library +---------------------------------- + + # add a submodule + git submodule add <url> <path> + + # occasionally update the submodule to a new version: + git -C <path> checkout <new version> + git add <path> + git commit -m "update submodule to new version" + + # See the list of submodules in a superproject + git submodule status + + # See FORMS on removing submodules + + +Workflow for an artificially split repo +-------------------------------------- + + # Enable recursion for relevant commands, such that + # regular commands recurse into submodules by default + git config --global submodule.recurse true + + # Unlike the other commands below clone still needs + # its own recurse flag: + git clone --recurse <URL> <directory> + cd <directory> + + # Get to know the code: + git grep foo + git ls-files + + # Get new code + git fetch + git pull --rebase + + # change worktree + git checkout + git reset + +Implementation details +---------------------- + +When cloning or pulling a repository containing submodules the submodules +will not be checked out by default; You can instruct 'clone' to recurse +into submodules. The 'init' and 'update' subcommands of 'git submodule' +will maintain submodules checked out and at an appropriate revision in +your working tree. Alternatively you can set 'submodule.recurse' to have +'checkout' recursing into submodules. + + +SEE ALSO +-------- +linkgit:git-submodule[1], linkgit:gitmodules[5]. + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index a48d267e26..4d6dac5770 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -174,11 +174,12 @@ endif::git-rev-list[] - '%Creset': reset color - '%C(...)': color specification, as described under Values in the "CONFIGURATION FILE" section of linkgit:git-config[1]; - adding `auto,` at the beginning will emit color only when colors are - enabled for log output (by `color.diff`, `color.ui`, or `--color`, and - respecting the `auto` settings of the former if we are going to a - terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring - on the next placeholders until the color is switched again. + adding `auto,` at the beginning (e.g. `%C(auto,red)`) will emit + color only when colors are enabled for log output (by `color.diff`, + `color.ui`, or `--color`, and respecting the `auto` settings of the + former if we are going to a terminal). `auto` alone (i.e. + `%C(auto)`) will turn on auto coloring on the next placeholders + until the color is switched again. - '%m': left (`<`), right (`>`) or boundary (`-`) mark - '%n': newline - '%%': a raw '%' @@ -162,6 +162,12 @@ all:: # algorithm. This is slower, but may detect attempted collision attacks. # Takes priority over other *_SHA1 knobs. # +# Define DC_SHA1_SUBMODULE in addition to DC_SHA1 to use the +# sha1collisiondetection shipped as a submodule instead of the +# non-submodule copy in sha1dc/. This is an experimental option used +# by the git project to migrate to using sha1collisiondetection as a +# submodule. +# # Define OPENSSL_SHA1 environment variable when running make to link # with the SHA1 routine from openssl library. # @@ -840,6 +846,7 @@ LIB_OBJS += refs/ref-cache.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace_object.o +LIB_OBJS += repository.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o LIB_OBJS += revision.o @@ -1003,6 +1010,10 @@ EXTLIBS = GIT_USER_AGENT = git/$(GIT_VERSION) +ifeq ($(wildcard sha1collisiondetection/lib/sha1.h),sha1collisiondetection/lib/sha1.h) +DC_SHA1_SUBMODULE = auto +endif + include config.mak.uname -include config.mak.autogen -include config.mak @@ -1448,8 +1459,14 @@ ifdef APPLE_COMMON_CRYPTO BASIC_CFLAGS += -DSHA1_APPLE else DC_SHA1 := YesPlease +ifdef DC_SHA1_SUBMODULE + LIB_OBJS += sha1collisiondetection/lib/sha1.o + LIB_OBJS += sha1collisiondetection/lib/ubc_check.o + BASIC_CFLAGS += -DDC_SHA1_SUBMODULE +else LIB_OBJS += sha1dc/sha1.o LIB_OBJS += sha1dc/ubc_check.o +endif BASIC_CFLAGS += \ -DSHA1_DC \ -DSHA1DC_NO_STANDARD_INCLUDES \ @@ -183,7 +183,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path, /* * use the symlink as the remaining components that - * need to be resloved + * need to be resolved */ strbuf_swap(&symlink, &remaining); } @@ -211,6 +211,7 @@ struct patch { unsigned ws_rule; int lines_added, lines_deleted; int score; + int extension_linenr; /* first line specifying delete/new/rename/copy */ unsigned int is_toplevel_relative:1; unsigned int inaccurate_eof:1; unsigned int is_binary:1; @@ -961,13 +962,12 @@ static int gitdiff_verify_name(struct apply_state *state, } if (*name) { - int len = strlen(*name); char *another; if (isnull) return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), *name, state->linenr); another = find_name(state, line, NULL, state->p_value, TERM_TAB); - if (!another || memcmp(another, *name, len + 1)) { + if (!another || strcmp(another, *name)) { free(another); return error((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : @@ -975,8 +975,7 @@ static int gitdiff_verify_name(struct apply_state *state, } free(another); } else { - /* expect "/dev/null" */ - if (memcmp("/dev/null", line, 9) || line[9] != '\n') + if (!starts_with(line, "/dev/null\n")) return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); } @@ -1001,20 +1000,27 @@ static int gitdiff_newname(struct apply_state *state, DIFF_NEW_NAME); } +static int parse_mode_line(const char *line, int linenr, unsigned int *mode) +{ + char *end; + *mode = strtoul(line, &end, 8); + if (end == line || !isspace(*end)) + return error(_("invalid mode on line %d: %s"), linenr, line); + return 0; +} + static int gitdiff_oldmode(struct apply_state *state, const char *line, struct patch *patch) { - patch->old_mode = strtoul(line, NULL, 8); - return 0; + return parse_mode_line(line, state->linenr, &patch->old_mode); } static int gitdiff_newmode(struct apply_state *state, const char *line, struct patch *patch) { - patch->new_mode = strtoul(line, NULL, 8); - return 0; + return parse_mode_line(line, state->linenr, &patch->new_mode); } static int gitdiff_delete(struct apply_state *state, @@ -1128,7 +1134,7 @@ static int gitdiff_index(struct apply_state *state, memcpy(patch->new_sha1_prefix, line, len); patch->new_sha1_prefix[len] = 0; if (*ptr == ' ') - patch->old_mode = strtoul(ptr+1, NULL, 8); + return gitdiff_oldmode(state, ptr + 1, patch); return 0; } @@ -1312,6 +1318,18 @@ static char *git_header_name(struct apply_state *state, } } +static int check_header_line(struct apply_state *state, struct patch *patch) +{ + int extensions = (patch->is_delete == 1) + (patch->is_new == 1) + + (patch->is_rename == 1) + (patch->is_copy == 1); + if (extensions > 1) + return error(_("inconsistent header lines %d and %d"), + patch->extension_linenr, state->linenr); + if (extensions && !patch->extension_linenr) + patch->extension_linenr = state->linenr; + return 0; +} + /* Verify that we recognize the lines following a git header */ static int parse_git_header(struct apply_state *state, const char *line, @@ -1378,6 +1396,8 @@ static int parse_git_header(struct apply_state *state, res = p->fn(state, line + oplen, patch); if (res < 0) return -1; + if (check_header_line(state, patch)) + return -1; if (res > 0) return offset; break; @@ -1575,7 +1595,8 @@ static int find_header(struct apply_state *state, patch->old_name = xstrdup(patch->def_name); patch->new_name = xstrdup(patch->def_name); } - if (!patch->is_delete && !patch->new_name) { + if ((!patch->new_name && !patch->is_delete) || + (!patch->old_name && !patch->is_new)) { error(_("git diff header lacks filename information " "(line %d)"), state->linenr); return -128; @@ -2078,7 +2099,7 @@ static int use_patch(struct apply_state *state, struct patch *p) /* See if it matches any of exclude/include rule */ for (i = 0; i < state->limit_by_name.nr; i++) { struct string_list_item *it = &state->limit_by_name.items[i]; - if (!wildmatch(it->string, pathname, 0, NULL)) + if (!wildmatch(it->string, pathname, 0)) return (it->util != NULL); } diff --git a/builtin/branch.c b/builtin/branch.c index c958e93257..8a0595e115 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -335,8 +335,11 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r struct strbuf local = STRBUF_INIT; struct strbuf remote = STRBUF_INIT; - strbuf_addf(&fmt, "%%(if)%%(HEAD)%%(then)* %s%%(else) %%(end)", - branch_get_color(BRANCH_COLOR_CURRENT)); + strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)", + branch_get_color(BRANCH_COLOR_CURRENT), + branch_get_color(BRANCH_COLOR_LOCAL)); + strbuf_addf(&remote, " %s", + branch_get_color(BRANCH_COLOR_REMOTE)); if (filter->verbose) { struct strbuf obname = STRBUF_INIT; @@ -359,17 +362,17 @@ 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" + strbuf_addf(&remote, "%%(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), + maxwidth, quote_literal_for_format(remote_prefix), 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)); - strbuf_addf(&remote, "%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)", - branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix), + strbuf_addf(&remote, "%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)", + quote_literal_for_format(remote_prefix), branch_get_color(BRANCH_COLOR_RESET)); } diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 7efbc4019a..96b786e489 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -57,11 +57,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, struct object_context obj_context; struct object_info oi = OBJECT_INFO_INIT; struct strbuf sb = STRBUF_INIT; - unsigned flags = LOOKUP_REPLACE_OBJECT; + unsigned flags = OBJECT_INFO_LOOKUP_REPLACE; const char *path = force_path; if (unknown_type) - flags |= LOOKUP_UNKNOWN_OBJECT; + flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE; if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH, oid.hash, &obj_context)) @@ -338,7 +338,8 @@ static void batch_object_write(const char *obj_name, struct batch_options *opt, struct strbuf buf = STRBUF_INIT; if (!data->skip_object_info && - sha1_object_info_extended(data->oid.hash, &data->info, LOOKUP_REPLACE_OBJECT) < 0) { + sha1_object_info_extended(data->oid.hash, &data->info, + OBJECT_INFO_LOOKUP_REPLACE) < 0) { printf("%s missing\n", obj_name ? obj_name : oid_to_hex(&data->oid)); fflush(stdout); diff --git a/builtin/commit.c b/builtin/commit.c index 00a01f07c3..8e93802511 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -140,7 +140,6 @@ static enum commit_whence whence; static int sequencer_in_use; static int use_editor = 1, include_status = 1; static int show_ignored_in_status, have_option_m; -static const char *only_include_assumed; static struct strbuf message = STRBUF_INIT; static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; @@ -843,9 +842,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix, "with '%c' will be kept; you may remove them" " yourself if you want to.\n" "An empty message aborts the commit.\n"), comment_line_char); - if (only_include_assumed) - status_printf_ln(s, GIT_COLOR_NORMAL, - "%s", only_include_assumed); /* * These should never fail because they come from our own @@ -879,8 +875,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, (int)(ci.name_end - ci.name_begin), ci.name_begin, (int)(ci.mail_end - ci.mail_begin), ci.mail_begin); - if (ident_shown) - status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); + status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); /* Add new line for clarity */ saved_color_setting = s->use_color; s->use_color = 0; @@ -986,7 +981,7 @@ static int rest_is_empty(struct strbuf *sb, int start) int i, eol; const char *nl; - /* Check if the rest is just whitespace and Signed-of-by's. */ + /* Check if the rest is just whitespace and Signed-off-by's. */ for (i = start; i < sb->len; i++) { nl = memchr(sb->buf + i, '\n', sb->len - i); if (nl) @@ -1210,8 +1205,6 @@ static int parse_and_validate_options(int argc, const char *argv[], die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); if (argc == 0 && (also || (only && !amend && !allow_empty))) die(_("No paths with --include/--only does not make sense.")); - if (argc > 0 && !also && !only) - only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths..."); if (!cleanup_arg || !strcmp(cleanup_arg, "default")) cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "verbatim")) @@ -1660,6 +1653,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) usage_with_options(builtin_commit_usage, builtin_commit_options); status_init_config(&s, git_commit_config); + s.commit_template = 1; status_format = STATUS_FORMAT_NONE; /* Ignore status.short */ s.colopts = 0; diff --git a/builtin/describe.c b/builtin/describe.c index 70eb144608..19eacdd170 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -143,7 +143,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi return 0; for_each_string_list_item(item, &exclude_patterns) { - if (!wildmatch(item->string, path + 10, 0, NULL)) + if (!wildmatch(item->string, path + 10, 0)) return 0; } } @@ -159,7 +159,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi return 0; for_each_string_list_item(item, &patterns) { - if (!wildmatch(item->string, path + 10, 0, NULL)) + if (!wildmatch(item->string, path + 10, 0)) break; /* If we get here, no pattern matched. */ diff --git a/builtin/fetch.c b/builtin/fetch.c index 16cf8421ce..1838a9abfb 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -250,9 +250,11 @@ static void find_non_local_tags(struct transport *transport, */ if (ends_with(ref->name, "^{}")) { if (item && - !has_object_file_with_flags(&ref->old_oid, HAS_SHA1_QUICK) && + !has_object_file_with_flags(&ref->old_oid, + OBJECT_INFO_QUICK) && !will_fetch(head, ref->old_oid.hash) && - !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) && + !has_sha1_file_with_flags(item->util, + OBJECT_INFO_QUICK) && !will_fetch(head, item->util)) item->util = NULL; item = NULL; @@ -266,7 +268,7 @@ static void find_non_local_tags(struct transport *transport, * fetch. */ if (item && - !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) && + !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) && !will_fetch(head, item->util)) item->util = NULL; @@ -287,7 +289,7 @@ static void find_non_local_tags(struct transport *transport, * checked to see if it needs fetching. */ if (item && - !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) && + !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) && !will_fetch(head, item->util)) item->util = NULL; diff --git a/builtin/fsck.c b/builtin/fsck.c index 87c6756899..99dea7adf6 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -537,7 +537,7 @@ static int fsck_cruft(const char *basename, const char *path, void *data) return 0; } -static int fsck_subdir(int nr, const char *path, void *progress) +static int fsck_subdir(unsigned int nr, const char *path, void *progress) { display_progress(progress, nr + 1); return 0; diff --git a/builtin/gc.c b/builtin/gc.c index bd91f136fe..2ba50a2873 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -149,7 +149,7 @@ static int too_many_loose_objects(void) if (!dir) return 0; - auto_threshold = (gc_auto_threshold + 255) / 256; + auto_threshold = DIV_ROUND_UP(gc_auto_threshold, 256); while ((ent = readdir(dir)) != NULL) { if (strspn(ent->d_name, "0123456789abcdef") != 38 || ent->d_name[38] != '\0') diff --git a/builtin/grep.c b/builtin/grep.c index b682966439..7e79eb1a75 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -4,6 +4,7 @@ * Copyright (c) 2006 Junio C Hamano */ #include "cache.h" +#include "repository.h" #include "config.h" #include "blob.h" #include "tree.h" @@ -542,7 +543,7 @@ static void compile_submodule_options(const struct grep_opt *opt, * submodule process has its own thread pool. */ argv_array_pushf(&submodule_options, "--threads=%d", - (num_threads + 1) / 2); + DIV_ROUND_UP(num_threads, 2)); /* Add Pathspecs */ argv_array_push(&submodule_options, "--"); @@ -643,11 +644,11 @@ static int grep_submodule_launch(struct grep_opt *opt, static int grep_submodule(struct grep_opt *opt, const struct object_id *oid, const char *filename, const char *path) { - if (!is_submodule_initialized(path)) + if (!is_submodule_active(the_repository, path)) return 0; if (!is_submodule_populated_gently(path, NULL)) { /* - * If searching history, check for the presense of the + * If searching history, check for the presence of the * submodule's gitdir before skipping the submodule. */ if (oid) { diff --git a/builtin/index-pack.c b/builtin/index-pack.c index ad4de35178..26828c1d82 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -793,7 +793,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (startup_info->have_repository) { read_lock(); - collision_test_needed = has_sha1_file_with_flags(oid->hash, HAS_SHA1_QUICK); + collision_test_needed = + has_sha1_file_with_flags(oid->hash, OBJECT_INFO_QUICK); read_unlock(); } diff --git a/builtin/log.c b/builtin/log.c index 8ca1de9894..c6362cf92e 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1308,7 +1308,7 @@ static struct commit *get_base_commit(const char *base_commit, if (rev_nr % 2) rev[i] = rev[2 * i]; - rev_nr = (rev_nr + 1) / 2; + rev_nr = DIV_ROUND_UP(rev_nr, 2); } if (!in_merge_bases(base, rev[0])) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index b12d0bb612..b8514a0029 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -5,7 +5,9 @@ * * Copyright (C) Linus Torvalds, 2005 */ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" +#include "repository.h" #include "config.h" #include "quote.h" #include "dir.h" @@ -32,10 +34,8 @@ static int line_terminator = '\n'; static int debug_mode; static int show_eol; static int recurse_submodules; -static struct argv_array submodule_options = ARGV_ARRAY_INIT; static const char *prefix; -static const char *super_prefix; static int max_prefix_len; static int prefix_len; static struct pathspec pathspec; @@ -74,24 +74,11 @@ static void write_eolinfo(const struct index_state *istate, static void write_name(const char *name) { /* - * Prepend the super_prefix to name to construct the full_name to be - * written. - */ - struct strbuf full_name = STRBUF_INIT; - if (super_prefix) { - strbuf_addstr(&full_name, super_prefix); - strbuf_addstr(&full_name, name); - name = full_name.buf; - } - - /* * With "--full-name", prefix_len=0; this caller needs to pass * an empty string in that case (a NULL is good for ""). */ write_name_quoted_relative(name, prefix_len ? prefix : NULL, stdout, line_terminator); - - strbuf_release(&full_name); } static const char *get_tag(const struct cache_entry *ce, const char *tag) @@ -210,83 +197,38 @@ static void show_killed_files(const struct index_state *istate, } } -/* - * Compile an argv_array with all of the options supported by --recurse_submodules - */ -static void compile_submodule_options(const char **argv, - const struct dir_struct *dir, - int show_tag) -{ - if (line_terminator == '\0') - argv_array_push(&submodule_options, "-z"); - if (show_tag) - argv_array_push(&submodule_options, "-t"); - if (show_valid_bit) - argv_array_push(&submodule_options, "-v"); - if (show_cached) - argv_array_push(&submodule_options, "--cached"); - if (show_eol) - argv_array_push(&submodule_options, "--eol"); - if (debug_mode) - argv_array_push(&submodule_options, "--debug"); - - /* Add Pathspecs */ - argv_array_push(&submodule_options, "--"); - for (; *argv; argv++) - argv_array_push(&submodule_options, *argv); -} +static void show_files(struct repository *repo, struct dir_struct *dir); -/** - * Recursively call ls-files on a submodule - */ -static void show_gitlink(const struct cache_entry *ce) +static void show_submodule(struct repository *superproject, + struct dir_struct *dir, const char *path) { - struct child_process cp = CHILD_PROCESS_INIT; - int status; - char *dir; - - prepare_submodule_repo_env(&cp.env_array); - argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT); - - if (prefix_len) - argv_array_pushf(&cp.env_array, "%s=%s", - GIT_TOPLEVEL_PREFIX_ENVIRONMENT, - prefix); - argv_array_pushf(&cp.args, "--super-prefix=%s%s/", - super_prefix ? super_prefix : "", - ce->name); - argv_array_push(&cp.args, "ls-files"); - argv_array_push(&cp.args, "--recurse-submodules"); - - /* add supported options */ - argv_array_pushv(&cp.args, submodule_options.argv); - - cp.git_cmd = 1; - dir = mkpathdup("%s/%s", get_git_work_tree(), ce->name); - cp.dir = dir; - status = run_command(&cp); - free(dir); - if (status) - exit(status); + struct repository submodule; + + if (repo_submodule_init(&submodule, superproject, path)) + return; + + if (repo_read_index(&submodule) < 0) + die("index file corrupt"); + + repo_read_gitmodules(&submodule); + + show_files(&submodule, dir); + + repo_clear(&submodule); } -static void show_ce_entry(const struct index_state *istate, - const char *tag, const struct cache_entry *ce) +static void show_ce(struct repository *repo, struct dir_struct *dir, + const struct cache_entry *ce, const char *fullname, + const char *tag) { - struct strbuf name = STRBUF_INIT; - int len = max_prefix_len; - if (super_prefix) - strbuf_addstr(&name, super_prefix); - strbuf_addstr(&name, ce->name); - - if (len > ce_namelen(ce)) + if (max_prefix_len > strlen(fullname)) die("git ls-files: internal error - cache entry not superset of prefix"); if (recurse_submodules && S_ISGITLINK(ce->ce_mode) && - submodule_path_match(&pathspec, name.buf, ps_matched)) { - show_gitlink(ce); - } else if (match_pathspec(&pathspec, name.buf, name.len, - len, ps_matched, + is_submodule_active(repo, ce->name)) { + show_submodule(repo, dir, ce->name); + } else if (match_pathspec(&pathspec, fullname, strlen(fullname), + max_prefix_len, ps_matched, S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode))) { tag = get_tag(ce, tag); @@ -300,12 +242,10 @@ static void show_ce_entry(const struct index_state *istate, find_unique_abbrev(ce->oid.hash, abbrev), ce_stage(ce)); } - write_eolinfo(istate, ce, ce->name); - write_name(ce->name); + write_eolinfo(repo->index, ce, fullname); + write_name(fullname); print_debug(ce); } - - strbuf_release(&name); } static void show_ru_info(const struct index_state *istate) @@ -338,59 +278,79 @@ static void show_ru_info(const struct index_state *istate) } static int ce_excluded(struct dir_struct *dir, struct index_state *istate, - const struct cache_entry *ce) + const char *fullname, const struct cache_entry *ce) { int dtype = ce_to_dtype(ce); - return is_excluded(dir, istate, ce->name, &dtype); + return is_excluded(dir, istate, fullname, &dtype); +} + +static void construct_fullname(struct strbuf *out, const struct repository *repo, + const struct cache_entry *ce) +{ + strbuf_reset(out); + if (repo->submodule_prefix) + strbuf_addstr(out, repo->submodule_prefix); + strbuf_addstr(out, ce->name); } -static void show_files(struct index_state *istate, struct dir_struct *dir) +static void show_files(struct repository *repo, struct dir_struct *dir) { int i; + struct strbuf fullname = STRBUF_INIT; /* For cached/deleted files we don't need to even do the readdir */ if (show_others || show_killed) { if (!show_others) dir->flags |= DIR_COLLECT_KILLED_ONLY; - fill_directory(dir, istate, &pathspec); + fill_directory(dir, repo->index, &pathspec); if (show_others) - show_other_files(istate, dir); + show_other_files(repo->index, dir); if (show_killed) - show_killed_files(istate, dir); + show_killed_files(repo->index, dir); } if (show_cached || show_stage) { - for (i = 0; i < istate->cache_nr; i++) { - const struct cache_entry *ce = istate->cache[i]; + for (i = 0; i < repo->index->cache_nr; i++) { + const struct cache_entry *ce = repo->index->cache[i]; + + construct_fullname(&fullname, repo, ce); + if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, istate, ce)) + !ce_excluded(dir, repo->index, fullname.buf, ce)) continue; if (show_unmerged && !ce_stage(ce)) continue; if (ce->ce_flags & CE_UPDATE) continue; - show_ce_entry(istate, ce_stage(ce) ? tag_unmerged : - (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce); + show_ce(repo, dir, ce, fullname.buf, + ce_stage(ce) ? tag_unmerged : + (ce_skip_worktree(ce) ? tag_skip_worktree : + tag_cached)); } } if (show_deleted || show_modified) { - for (i = 0; i < istate->cache_nr; i++) { - const struct cache_entry *ce = istate->cache[i]; + for (i = 0; i < repo->index->cache_nr; i++) { + const struct cache_entry *ce = repo->index->cache[i]; struct stat st; int err; + + construct_fullname(&fullname, repo, ce); + if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, istate, ce)) + !ce_excluded(dir, repo->index, fullname.buf, ce)) continue; if (ce->ce_flags & CE_UPDATE) continue; if (ce_skip_worktree(ce)) continue; - err = lstat(ce->name, &st); + err = lstat(fullname.buf, &st); if (show_deleted && err) - show_ce_entry(istate, tag_removed, ce); - if (show_modified && ie_modified(istate, ce, &st, 0)) - show_ce_entry(istate, tag_modified, ce); + show_ce(repo, dir, ce, fullname.buf, tag_removed); + if (show_modified && ie_modified(repo->index, ce, &st, 0)) + show_ce(repo, dir, ce, fullname.buf, tag_modified); } } + + strbuf_release(&fullname); } /* @@ -615,10 +575,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) prefix = cmd_prefix; if (prefix) prefix_len = strlen(prefix); - super_prefix = get_super_prefix(); git_config(git_default_config, NULL); - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die("index file corrupt"); argc = parse_options(argc, argv, prefix, builtin_ls_files_options, @@ -652,7 +611,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) setup_work_tree(); if (recurse_submodules) - compile_submodule_options(argv, &dir, show_tag); + repo_read_gitmodules(the_repository); if (recurse_submodules && (show_stage || show_deleted || show_others || show_unmerged || @@ -670,7 +629,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) /* * Find common prefix for all pathspec's * This is used as a performance optimization which unfortunately cannot - * be done when recursing into submodules + * be done when recursing into submodules because when a pathspec is + * given which spans repository boundaries you can't simply remove the + * submodule entry because the pathspec may match something inside the + * submodule. */ if (recurse_submodules) max_prefix = NULL; @@ -678,7 +640,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) max_prefix = common_prefix(&pathspec); max_prefix_len = get_common_prefix_len(max_prefix); - prune_index(&the_index, max_prefix, max_prefix_len); + prune_index(the_repository->index, max_prefix, max_prefix_len); /* Treat unmatching pathspec elements as errors */ if (pathspec.nr && error_unmatch) @@ -699,11 +661,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) */ if (show_stage || show_unmerged) die("ls-files --with-tree is incompatible with -s or -u"); - overlay_tree_on_index(&the_index, with_tree, max_prefix); + overlay_tree_on_index(the_repository->index, with_tree, max_prefix); } - show_files(&the_index, &dir); + + show_files(the_repository, &dir); + if (show_resolve_undo) - show_ru_info(&the_index); + show_ru_info(the_repository->index); if (ps_matched) { int bad; diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index b2d7d5ce68..c4be98ab9e 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -24,7 +24,7 @@ static int tail_match(const char **pattern, const char *path) pathbuf = xstrfmt("/%s", path); while ((p = *(pattern++)) != NULL) { - if (!wildmatch(p, pathbuf, 0, NULL)) { + if (!wildmatch(p, pathbuf, 0)) { free(pathbuf); return 1; } diff --git a/builtin/name-rev.c b/builtin/name-rev.c index e21715f1d0..c41ea7c2a6 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -130,7 +130,7 @@ static int subpath_matches(const char *path, const char *filter) const char *subpath = path; while (subpath) { - if (!wildmatch(filter, subpath, 0, NULL)) + if (!wildmatch(filter, subpath, 0)) return subpath - path; subpath = strchr(subpath, '/'); if (subpath) diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index c026299e78..ac978ad401 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -10,7 +10,7 @@ static const char * const prune_packed_usage[] = { static struct progress *progress; -static int prune_subdir(int nr, const char *path, void *data) +static int prune_subdir(unsigned int nr, const char *path, void *data) { int *opts = data; display_progress(progress, nr + 1); diff --git a/builtin/prune.c b/builtin/prune.c index f0e2bff04c..c378690545 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -68,7 +68,7 @@ static int prune_cruft(const char *basename, const char *path, void *data) return 0; } -static int prune_subdir(int nr, const char *path, void *data) +static int prune_subdir(unsigned int nr, const char *path, void *data) { if (!show_only) rmdir(path); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 71c0c768db..cabdc55e09 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1806,7 +1806,7 @@ static const char *unpack_with_sideband(struct shallow_info *si) static void prepare_shallow_update(struct command *commands, struct shallow_info *si) { - int i, j, k, bitmap_size = (si->ref->nr + 31) / 32; + int i, j, k, bitmap_size = DIV_ROUND_UP(si->ref->nr, 32); ALLOC_ARRAY(si->used_shallow, si->shallow->nr); assign_shallow_commits_to_refs(si, si->used_shallow, NULL); diff --git a/builtin/reflog.c b/builtin/reflog.c index 44cdc2dbd0..e237d927a0 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -486,7 +486,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c return; /* both given explicitly -- nothing to tweak */ for (ent = reflog_expire_cfg; ent; ent = ent->next) { - if (!wildmatch(ent->pattern, ref, 0, NULL)) { + if (!wildmatch(ent->pattern, ref, 0)) { if (!(slot & EXPIRE_TOTAL)) cb->expire_total = ent->expire_total; if (!(slot & EXPIRE_UNREACH)) diff --git a/builtin/replace.c b/builtin/replace.c index 80a15cf35f..fba336a68a 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -41,7 +41,7 @@ static int show_reference(const char *refname, const struct object_id *oid, { struct show_data *data = cb_data; - if (!wildmatch(data->pattern, refname, 0, NULL)) { + if (!wildmatch(data->pattern, refname, 0)) { if (data->format == REPLACE_FORMAT_SHORT) printf("%s\n", refname); else if (data->format == REPLACE_FORMAT_MEDIUM) diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 527f69e283..7073a3eb97 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -438,7 +438,7 @@ static int append_matching_ref(const char *refname, const struct object_id *oid, slash--; if (!*tail) return 0; - if (wildmatch(match_ref_pattern, tail, 0, NULL)) + if (wildmatch(match_ref_pattern, tail, 0)) return 0; if (starts_with(refname, "refs/heads/")) return append_head_ref(refname, oid, flag, cb_data); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 8517032b3e..6abdad3294 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1,11 +1,11 @@ #include "builtin.h" +#include "repository.h" #include "cache.h" #include "config.h" #include "parse-options.h" #include "quote.h" #include "pathspec.h" #include "dir.h" -#include "utf8.h" #include "submodule.h" #include "submodule-config.h" #include "string-list.h" @@ -280,7 +280,7 @@ static void module_list_active(struct module_list *list) for (i = 0; i < list->nr; i++) { const struct cache_entry *ce = list->entries[i]; - if (!is_submodule_initialized(ce->name)) + if (!is_submodule_active(the_repository, ce->name)) continue; ALLOC_GROW(active_modules.entries, @@ -326,7 +326,7 @@ static int module_list(int argc, const char **argv, const char *prefix) printf("%06o %s %d\t", ce->ce_mode, oid_to_hex(&ce->oid), ce_stage(ce)); - utf8_fprintf(stdout, "%s\n", ce->name); + fprintf(stdout, "%s\n", ce->name); } return 0; } @@ -362,7 +362,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet) * * Set active flag for the submodule being initialized */ - if (!is_submodule_initialized(path)) { + if (!is_submodule_active(the_repository, path)) { strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.active", sub->name); git_config_set_gently(sb.buf, "true"); @@ -817,7 +817,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, } /* Check if the submodule has been initialized. */ - if (!is_submodule_initialized(ce->name)) { + if (!is_submodule_active(the_repository, ce->name)) { next_submodule_warn_missing(suc, out, displaypath); goto cleanup; } @@ -1038,7 +1038,7 @@ static int update_clone(int argc, const char **argv, const char *prefix) return 1; for_each_string_list_item(item, &suc.projectlines) - utf8_fprintf(stdout, "%s", item->string); + fprintf(stdout, "%s", item->string); return 0; } @@ -1193,7 +1193,7 @@ static int is_active(int argc, const char **argv, const char *prefix) gitmodules_config(); - return !is_submodule_initialized(argv[1]); + return !is_submodule_active(the_repository, argv[1]); } #define SUPPORT_SUPER_PREFIX (1<<0) @@ -11,6 +11,8 @@ #include "string-list.h" #include "pack-revindex.h" #include "hash.h" +#include "path.h" +#include "sha1-array.h" #ifndef platform_SHA_CTX /* @@ -462,6 +464,8 @@ static inline enum object_type object_type(unsigned int mode) */ extern const char * const local_repo_env[]; +extern void setup_git_env(void); + /* * Returns true iff we have a configured git repository (either via * setup_git_directory, or in the environment via $GIT_DIR). @@ -769,7 +773,6 @@ extern int core_apply_sparse_checkout; extern int precomposed_unicode; extern int protect_hfs; extern int protect_ntfs; -extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env; /* * Include broken refs in all ref iterations, which will @@ -892,64 +895,6 @@ extern void check_repository_format(void); #define TYPE_CHANGED 0x0040 /* - * Return a statically allocated filename, either generically (mkpath), in - * the repository directory (git_path), or in a submodule's repository - * directory (git_path_submodule). In all cases, note that the result - * may be overwritten by another call to _any_ of the functions. Consider - * using the safer "dup" or "strbuf" formats below (in some cases, the - * unsafe versions have already been removed). - */ -extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); - -extern char *mksnpath(char *buf, size_t n, const char *fmt, ...) - __attribute__((format (printf, 3, 4))); -extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) - __attribute__((format (printf, 2, 3))); -extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...) - __attribute__((format (printf, 2, 3))); -extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...) - __attribute__((format (printf, 2, 3))); -extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path, - const char *fmt, ...) - __attribute__((format (printf, 3, 4))); -extern char *git_pathdup(const char *fmt, ...) - __attribute__((format (printf, 1, 2))); -extern char *mkpathdup(const char *fmt, ...) - __attribute__((format (printf, 1, 2))); -extern char *git_pathdup_submodule(const char *path, const char *fmt, ...) - __attribute__((format (printf, 2, 3))); - -extern void report_linked_checkout_garbage(void); - -/* - * You can define a static memoized git path like: - * - * static GIT_PATH_FUNC(git_path_foo, "FOO"); - * - * or use one of the global ones below. - */ -#define GIT_PATH_FUNC(func, filename) \ - const char *func(void) \ - { \ - static char *ret; \ - if (!ret) \ - ret = git_pathdup(filename); \ - return ret; \ - } - -const char *git_path_cherry_pick_head(void); -const char *git_path_revert_head(void); -const char *git_path_squash_msg(void); -const char *git_path_merge_msg(void); -const char *git_path_merge_rr(void); -const char *git_path_merge_mode(void); -const char *git_path_merge_head(void); -const char *git_path_fetch_head(void); -const char *git_path_shallow(void); - -/* * Return the name of the file in the local object database that would * be used to store a loose object with the specified sha1. The * return value is a pointer to a statically allocated buffer that is @@ -1215,13 +1160,12 @@ extern char *xdg_config_home(const char *filename); */ extern char *xdg_cache_home(const char *filename); -/* object replacement */ -#define LOOKUP_REPLACE_OBJECT 1 -#define LOOKUP_UNKNOWN_OBJECT 2 -extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag); +extern void *read_sha1_file_extended(const unsigned char *sha1, + enum object_type *type, + unsigned long *size, int lookup_replace); static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) { - return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT); + return read_sha1_file_extended(sha1, type, size, 1); } /* @@ -1243,13 +1187,6 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh return do_lookup_replace_object(sha1); } -static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag) -{ - if (!(flag & LOOKUP_REPLACE_OBJECT)) - return sha1; - return lookup_replace_object(sha1); -} - /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1); @@ -1286,15 +1223,10 @@ int read_loose_object(const char *path, void **contents); /* - * Return true iff we have an object named sha1, whether local or in - * an alternate object database, and whether packed or loose. This - * function does not respect replace references. - * - * If the QUICK flag is set, do not re-check the pack directory - * when we cannot find the object (this means we may give a false - * negative answer if another process is simultaneously repacking). + * Convenience for sha1_object_info_extended() with a NULL struct + * object_info. OBJECT_INFO_SKIP_CACHED is automatically set; pass + * nonzero flags to also set other flags. */ -#define HAS_SHA1_QUICK 0x1 extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags); static inline int has_sha1_file(const unsigned char *sha1) { @@ -1596,6 +1528,16 @@ extern struct alternate_object_database { struct strbuf scratch; size_t base_len; + /* + * Used to store the results of readdir(3) calls when searching + * for unique abbreviated hashes. This cache is never + * invalidated, thus it's racy and not necessarily accurate. + * That's fine for its purpose; don't use it for tasks requiring + * greater accuracy! + */ + char loose_objects_subdir_seen[256]; + struct oid_array loose_objects_cache; + char path[FLEX_ARRAY]; } *alt_odb_list; extern void prepare_alt_odb(void); @@ -1811,9 +1753,15 @@ typedef int each_loose_object_fn(const struct object_id *oid, typedef int each_loose_cruft_fn(const char *basename, const char *path, void *data); -typedef int each_loose_subdir_fn(int nr, +typedef int each_loose_subdir_fn(unsigned int nr, const char *path, void *data); +int for_each_file_in_obj_subdir(unsigned int subdir_nr, + struct strbuf *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data); int for_each_loose_file_in_objdir(const char *path, each_loose_object_fn obj_cb, each_loose_cruft_fn cruft_cb, @@ -1845,6 +1793,7 @@ struct object_info { off_t *disk_sizep; unsigned char *delta_base_sha1; struct strbuf *typename; + void **contentp; /* Response */ enum { @@ -1876,6 +1825,14 @@ struct object_info { */ #define OBJECT_INFO_INIT {NULL} +/* Invoke lookup_replace_object() on the given hash */ +#define OBJECT_INFO_LOOKUP_REPLACE 1 +/* Allow reading from a loose object file of unknown/bogus type */ +#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2 +/* Do not check cached storage */ +#define OBJECT_INFO_SKIP_CACHED 4 +/* Do not retry packed storage after checking packed and loose storage */ +#define OBJECT_INFO_QUICK 8 extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags); extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *); diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c index eb5e1d4439..0a745d9c3b 100644 --- a/compat/regex/regexec.c +++ b/compat/regex/regexec.c @@ -4102,7 +4102,7 @@ extend_buffers (re_match_context_t *mctx) if (BE (INT_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0)) return REG_ESPACE; - /* Double the lengthes of the buffers. */ + /* Double the lengths of the buffers. */ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); if (BE (ret != REG_NOERROR, 0)) return ret; @@ -7,6 +7,7 @@ */ #include "cache.h" #include "config.h" +#include "repository.h" #include "lockfile.h" #include "exec_cmd.h" #include "strbuf.h" @@ -72,13 +73,6 @@ static int core_compression_seen; static int pack_compression_seen; static int zlib_compression_seen; -/* - * Default config_set that contains key-value pairs from the usual set of config - * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG - * config file and the global /etc/gitconfig) - */ -static struct config_set the_config_set; - static int config_file_fgetc(struct config_source *conf) { return getc_unlocked(conf->u.file); @@ -244,7 +238,7 @@ again: } ret = !wildmatch(pattern.buf + prefix, text.buf + prefix, - icase ? WM_CASEFOLD : 0, NULL); + icase ? WM_CASEFOLD : 0); if (!ret && !already_tried_absolute) { /* @@ -1604,31 +1598,6 @@ int config_with_options(config_fn_t fn, void *data, return do_git_config_sequence(opts, fn, data); } -static void git_config_raw(config_fn_t fn, void *data) -{ - struct config_options opts = {0}; - - opts.respect_includes = 1; - if (have_git_dir()) { - opts.commondir = get_git_common_dir(); - opts.git_dir = get_git_dir(); - } - - if (config_with_options(fn, data, NULL, &opts) < 0) - /* - * config_with_options() normally returns only - * zero, as most errors are fatal, and - * non-fatal potential errors are guarded by "if" - * statements that are entered only when no error is - * possible. - * - * If we ever encounter a non-fatal error, it means - * something went really wrong and we should stop - * immediately. - */ - die(_("unknown error occurred while reading the configuration files")); -} - static void configset_iter(struct config_set *cs, config_fn_t fn, void *data) { int i, value_index; @@ -1682,14 +1651,6 @@ void read_early_config(config_fn_t cb, void *data) strbuf_release(&gitdir); } -static void git_config_check_init(void); - -void git_config(config_fn_t fn, void *data) -{ - git_config_check_init(); - configset_iter(&the_config_set, fn, data); -} - static struct config_set_element *configset_find_element(struct config_set *cs, const char *key) { struct config_set_element k; @@ -1899,87 +1860,194 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha return 1; } -static void git_config_check_init(void) +/* Functions use to read configuration from a repository */ +static void repo_read_config(struct repository *repo) { - if (the_config_set.hash_initialized) + struct config_options opts; + + opts.respect_includes = 1; + opts.commondir = repo->commondir; + opts.git_dir = repo->gitdir; + + if (!repo->config) + repo->config = xcalloc(1, sizeof(struct config_set)); + else + git_configset_clear(repo->config); + + git_configset_init(repo->config); + + if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0) + /* + * config_with_options() normally returns only + * zero, as most errors are fatal, and + * non-fatal potential errors are guarded by "if" + * statements that are entered only when no error is + * possible. + * + * If we ever encounter a non-fatal error, it means + * something went really wrong and we should stop + * immediately. + */ + die(_("unknown error occurred while reading the configuration files")); +} + +static void git_config_check_init(struct repository *repo) +{ + if (repo->config && repo->config->hash_initialized) return; - git_configset_init(&the_config_set); - git_config_raw(config_set_callback, &the_config_set); + repo_read_config(repo); } -void git_config_clear(void) +static void repo_config_clear(struct repository *repo) { - if (!the_config_set.hash_initialized) + if (!repo->config || !repo->config->hash_initialized) return; - git_configset_clear(&the_config_set); + git_configset_clear(repo->config); } -int git_config_get_value(const char *key, const char **value) +void repo_config(struct repository *repo, config_fn_t fn, void *data) { - git_config_check_init(); - return git_configset_get_value(&the_config_set, key, value); + git_config_check_init(repo); + configset_iter(repo->config, fn, data); } -const struct string_list *git_config_get_value_multi(const char *key) +int repo_config_get_value(struct repository *repo, + const char *key, const char **value) { - git_config_check_init(); - return git_configset_get_value_multi(&the_config_set, key); + git_config_check_init(repo); + return git_configset_get_value(repo->config, key, value); } -int git_config_get_string_const(const char *key, const char **dest) +const struct string_list *repo_config_get_value_multi(struct repository *repo, + const char *key) +{ + git_config_check_init(repo); + return git_configset_get_value_multi(repo->config, key); +} + +int repo_config_get_string_const(struct repository *repo, + const char *key, const char **dest) +{ + int ret; + git_config_check_init(repo); + ret = git_configset_get_string_const(repo->config, key, dest); + if (ret < 0) + git_die_config(key, NULL); + return ret; +} + +int repo_config_get_string(struct repository *repo, + const char *key, char **dest) +{ + git_config_check_init(repo); + return repo_config_get_string_const(repo, key, (const char **)dest); +} + +int repo_config_get_int(struct repository *repo, + const char *key, int *dest) +{ + git_config_check_init(repo); + return git_configset_get_int(repo->config, key, dest); +} + +int repo_config_get_ulong(struct repository *repo, + const char *key, unsigned long *dest) +{ + git_config_check_init(repo); + return git_configset_get_ulong(repo->config, key, dest); +} + +int repo_config_get_bool(struct repository *repo, + const char *key, int *dest) +{ + git_config_check_init(repo); + return git_configset_get_bool(repo->config, key, dest); +} + +int repo_config_get_bool_or_int(struct repository *repo, + const char *key, int *is_bool, int *dest) +{ + git_config_check_init(repo); + return git_configset_get_bool_or_int(repo->config, key, is_bool, dest); +} + +int repo_config_get_maybe_bool(struct repository *repo, + const char *key, int *dest) +{ + git_config_check_init(repo); + return git_configset_get_maybe_bool(repo->config, key, dest); +} + +int repo_config_get_pathname(struct repository *repo, + const char *key, const char **dest) { int ret; - git_config_check_init(); - ret = git_configset_get_string_const(&the_config_set, key, dest); + git_config_check_init(repo); + ret = git_configset_get_pathname(repo->config, key, dest); if (ret < 0) git_die_config(key, NULL); return ret; } +/* Functions used historically to read configuration from 'the_repository' */ +void git_config(config_fn_t fn, void *data) +{ + repo_config(the_repository, fn, data); +} + +void git_config_clear(void) +{ + repo_config_clear(the_repository); +} + +int git_config_get_value(const char *key, const char **value) +{ + return repo_config_get_value(the_repository, key, value); +} + +const struct string_list *git_config_get_value_multi(const char *key) +{ + return repo_config_get_value_multi(the_repository, key); +} + +int git_config_get_string_const(const char *key, const char **dest) +{ + return repo_config_get_string_const(the_repository, key, dest); +} + int git_config_get_string(const char *key, char **dest) { - git_config_check_init(); - return git_config_get_string_const(key, (const char **)dest); + return repo_config_get_string(the_repository, key, dest); } int git_config_get_int(const char *key, int *dest) { - git_config_check_init(); - return git_configset_get_int(&the_config_set, key, dest); + return repo_config_get_int(the_repository, key, dest); } int git_config_get_ulong(const char *key, unsigned long *dest) { - git_config_check_init(); - return git_configset_get_ulong(&the_config_set, key, dest); + return repo_config_get_ulong(the_repository, key, dest); } int git_config_get_bool(const char *key, int *dest) { - git_config_check_init(); - return git_configset_get_bool(&the_config_set, key, dest); + return repo_config_get_bool(the_repository, key, dest); } int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest) { - git_config_check_init(); - return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest); + return repo_config_get_bool_or_int(the_repository, key, is_bool, dest); } int git_config_get_maybe_bool(const char *key, int *dest) { - git_config_check_init(); - return git_configset_get_maybe_bool(&the_config_set, key, dest); + return repo_config_get_maybe_bool(the_repository, key, dest); } int git_config_get_pathname(const char *key, const char **dest) { - int ret; - git_config_check_init(); - ret = git_configset_get_pathname(&the_config_set, key, dest); - if (ret < 0) - git_die_config(key, NULL); - return ret; + return repo_config_get_pathname(the_repository, key, dest); } int git_config_get_expiry(const char *key, const char **output) @@ -163,6 +163,30 @@ extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest); extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest); +/* Functions for reading a repository's config */ +struct repository; +extern void repo_config(struct repository *repo, config_fn_t fn, void *data); +extern int repo_config_get_value(struct repository *repo, + const char *key, const char **value); +extern const struct string_list *repo_config_get_value_multi(struct repository *repo, + const char *key); +extern int repo_config_get_string_const(struct repository *repo, + const char *key, const char **dest); +extern int repo_config_get_string(struct repository *repo, + const char *key, char **dest); +extern int repo_config_get_int(struct repository *repo, + const char *key, int *dest); +extern int repo_config_get_ulong(struct repository *repo, + const char *key, unsigned long *dest); +extern int repo_config_get_bool(struct repository *repo, + const char *key, int *dest); +extern int repo_config_get_bool_or_int(struct repository *repo, + const char *key, int *is_bool, int *dest); +extern int repo_config_get_maybe_bool(struct repository *repo, + const char *key, int *dest); +extern int repo_config_get_pathname(struct repository *repo, + const char *key, const char **dest); + extern int git_config_get_value(const char *key, const char **value); extern const struct string_list *git_config_get_value_multi(const char *key); extern void git_config_clear(void); diff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci index f2d97e755b..4490069df9 100644 --- a/contrib/coccinelle/free.cocci +++ b/contrib/coccinelle/free.cocci @@ -11,16 +11,8 @@ expression E; free(E); @@ -type T; -T *ptr; -@@ -- free(ptr); -- ptr = NULL; -+ FREE_AND_NULL(ptr); - -@@ expression E; @@ - free(E); -- E = NULL; + FREE_AND_NULL(E); +- E = NULL; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 48a2f26622..d934417475 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2641,6 +2641,8 @@ _git_config () sendemail.thread sendemail.to sendemail.validate + sendemail.smtpbatchsize + sendemail.smtprelogindelay showbranch.default status.relativePaths status.showUntrackedFiles diff --git a/contrib/hooks/multimail/git_multimail.py b/contrib/hooks/multimail/git_multimail.py index c7f86403cf..73fdda6b14 100755 --- a/contrib/hooks/multimail/git_multimail.py +++ b/contrib/hooks/multimail/git_multimail.py @@ -2964,7 +2964,7 @@ class StaticRecipientsEnvironmentMixin(Environment): class CLIRecipientsEnvironmentMixin(Environment): - """Mixin storing recipients information comming from the + """Mixin storing recipients information coming from the command-line.""" def __init__(self, cli_recipients=None, **kw): diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl index 41e74fba1e..e7f857c1a2 100755 --- a/contrib/mw-to-git/git-remote-mediawiki.perl +++ b/contrib/mw-to-git/git-remote-mediawiki.perl @@ -857,7 +857,7 @@ sub mw_import_revids { my $n = 0; my $n_actual = 0; - my $last_timestamp = 0; # Placeholer in case $rev->timestamp is undefined + my $last_timestamp = 0; # Placeholder in case $rev->timestamp is undefined foreach my $pagerevid (@{$revision_ids}) { # Count page even if we skip it, since we display diff --git a/contrib/mw-to-git/t/README b/contrib/mw-to-git/t/README index 03f6ee5d72..2ee34be7e4 100644 --- a/contrib/mw-to-git/t/README +++ b/contrib/mw-to-git/t/README @@ -121,4 +121,4 @@ How to write a new test Please, follow the standards given by git. See git/t/README. New file should be named as t936[0-9]-*.sh. -Be sure to reset your wiki regulary with the function `wiki_reset`. +Be sure to reset your wiki regularly with the function `wiki_reset`. diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile index 6afa9aafdf..5c6cc4ab2c 100644 --- a/contrib/subtree/Makefile +++ b/contrib/subtree/Makefile @@ -19,15 +19,27 @@ htmldir ?= $(prefix)/share/doc/git-doc INSTALL ?= install RM ?= rm -f -ASCIIDOC = asciidoc -XMLTO = xmlto +ASCIIDOC = asciidoc +ASCIIDOC_CONF = -f ../../Documentation/asciidoc.conf +ASCIIDOC_HTML = xhtml11 +ASCIIDOC_DOCBOOK = docbook +ASCIIDOC_EXTRA = +XMLTO = xmlto + +ifdef USE_ASCIIDOCTOR +ASCIIDOC = asciidoctor +ASCIIDOC_CONF = +ASCIIDOC_HTML = xhtml5 +ASCIIDOC_DOCBOOK = docbook45 +ASCIIDOC_EXTRA += -I../../Documentation -rasciidoctor-extensions +ASCIIDOC_EXTRA += -alitdd='&\#x2d;&\#x2d;' +endif ifndef SHELL_PATH SHELL_PATH = /bin/sh endif SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) -ASCIIDOC_CONF = ../../Documentation/asciidoc.conf MANPAGE_XSL = ../../Documentation/manpage-normal.xsl GIT_SUBTREE_SH := git-subtree.sh @@ -65,12 +77,12 @@ $(GIT_SUBTREE_DOC): $(GIT_SUBTREE_XML) $(XMLTO) -m $(MANPAGE_XSL) man $^ $(GIT_SUBTREE_XML): $(GIT_SUBTREE_TXT) - $(ASCIIDOC) -b docbook -d manpage -f $(ASCIIDOC_CONF) \ - -agit_version=$(GIT_VERSION) $^ + $(ASCIIDOC) -b $(ASCIIDOC_DOCBOOK) -d manpage $(ASCIIDOC_CONF) \ + -agit_version=$(GIT_VERSION) $(ASCIIDOC_EXTRA) $^ $(GIT_SUBTREE_HTML): $(GIT_SUBTREE_TXT) - $(ASCIIDOC) -b xhtml11 -d manpage -f $(ASCIIDOC_CONF) \ - -agit_version=$(GIT_VERSION) $^ + $(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage $(ASCIIDOC_CONF) \ + -agit_version=$(GIT_VERSION) $(ASCIIDOC_EXTRA) $^ $(GIT_SUBTREE_TEST): $(GIT_SUBTREE) cp $< $@ @@ -256,7 +256,7 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) tm->tm_hour, tm->tm_min, tm->tm_sec, tz); else if (mode->type == DATE_STRFTIME) strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, - mode->local ? NULL : ""); + !mode->local); else strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", weekday_names[tm->tm_wday], @@ -2095,7 +2095,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o * bytes per "line". * This is stupid and ugly, but very cheap... */ - damage = (damage + 63) / 64; + damage = DIV_ROUND_UP(damage, 64); ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc); dir.files[dir.nr].name = file->name; dir.files[dir.nr].changed = damage; diff --git a/diffcore-order.c b/diffcore-order.c index 1957f822a5..19e73311f9 100644 --- a/diffcore-order.c +++ b/diffcore-order.c @@ -67,7 +67,7 @@ static int match_order(const char *path) strbuf_addstr(&p, path); while (p.buf[0]) { char *cp; - if (!wildmatch(order[i], p.buf, 0, NULL)) + if (!wildmatch(order[i], p.buf, 0)) return i; cp = strrchr(p.buf, '/'); if (!cp) @@ -92,13 +92,11 @@ int git_fnmatch(const struct pathspec_item *item, if (item->magic & PATHSPEC_GLOB) return wildmatch(pattern, string, WM_PATHNAME | - (item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0), - NULL); + (item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0)); else /* wildmatch has not learned no FNM_PATHNAME mode yet */ return wildmatch(pattern, string, - item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0, - NULL); + item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0); } static int fnmatch_icase_mem(const char *pattern, int patternlen, @@ -122,7 +120,7 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen, if (ignore_case) flags |= WM_CASEFOLD; - match_status = wildmatch(use_pat, use_str, flags, NULL); + match_status = wildmatch(use_pat, use_str, flags); strbuf_release(&pat_buf); strbuf_release(&str_buf); diff --git a/environment.c b/environment.c index d40b21fb72..3fd4b10845 100644 --- a/environment.c +++ b/environment.c @@ -8,6 +8,7 @@ * are. */ #include "cache.h" +#include "repository.h" #include "config.h" #include "refs.h" #include "fmt-merge-msg.h" @@ -95,17 +96,11 @@ int ignore_untracked_cache_config; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; -static char *work_tree; static const char *namespace; -static size_t namespace_len; static const char *super_prefix; -static const char *git_dir, *git_common_dir; -static char *git_object_dir, *git_index_file, *git_graft_file; -int git_db_env, git_index_env, git_graft_env, git_common_dir_env; - /* * Repository-local GIT_* environment variables; see cache.h for details. */ @@ -149,48 +144,17 @@ static char *expand_namespace(const char *raw_namespace) return strbuf_detach(&buf, NULL); } -static char *git_path_from_env(const char *envvar, const char *git_dir, - const char *path, int *fromenv) +void setup_git_env(void) { - const char *value = getenv(envvar); - if (!value) - return xstrfmt("%s/%s", git_dir, path); - if (fromenv) - *fromenv = 1; - return xstrdup(value); -} - -static void setup_git_env(void) -{ - struct strbuf sb = STRBUF_INIT; - const char *gitfile; const char *shallow_file; const char *replace_ref_base; - git_dir = getenv(GIT_DIR_ENVIRONMENT); - if (!git_dir) { - if (!startup_info->have_repository) - BUG("setup_git_env called without repository"); - git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; - } - gitfile = read_gitfile(git_dir); - git_dir = xstrdup(gitfile ? gitfile : git_dir); - if (get_common_dir(&sb, git_dir)) - git_common_dir_env = 1; - git_common_dir = strbuf_detach(&sb, NULL); - git_object_dir = git_path_from_env(DB_ENVIRONMENT, git_common_dir, - "objects", &git_db_env); - git_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir, - "index", &git_index_env); - git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_common_dir, - "info/grafts", &git_graft_env); if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) check_replace_refs = 0; replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT); git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base : "refs/replace/"); namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT)); - namespace_len = strlen(namespace); shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); if (shallow_file) set_alternate_shallow_file(shallow_file, 0); @@ -205,36 +169,36 @@ int is_bare_repository(void) int have_git_dir(void) { return startup_info->have_repository - || git_dir - || getenv(GIT_DIR_ENVIRONMENT); + || the_repository->gitdir; } const char *get_git_dir(void) { - if (!git_dir) - setup_git_env(); - return git_dir; + if (!the_repository->gitdir) + BUG("git environment hasn't been setup"); + return the_repository->gitdir; } const char *get_git_common_dir(void) { - if (!git_dir) - setup_git_env(); - return git_common_dir; + if (!the_repository->commondir) + BUG("git environment hasn't been setup"); + return the_repository->commondir; } const char *get_git_namespace(void) { if (!namespace) - setup_git_env(); + BUG("git environment hasn't been setup"); return namespace; } const char *strip_namespace(const char *namespaced_ref) { - if (!starts_with(namespaced_ref, get_git_namespace())) - return NULL; - return namespaced_ref + namespace_len; + const char *out; + if (skip_prefix(namespaced_ref, get_git_namespace(), &out)) + return out; + return NULL; } const char *get_super_prefix(void) @@ -258,26 +222,26 @@ void set_git_work_tree(const char *new_work_tree) { if (git_work_tree_initialized) { new_work_tree = real_path(new_work_tree); - if (strcmp(new_work_tree, work_tree)) + if (strcmp(new_work_tree, the_repository->worktree)) die("internal error: work tree has already been set\n" "Current worktree: %s\nNew worktree: %s", - work_tree, new_work_tree); + the_repository->worktree, new_work_tree); return; } git_work_tree_initialized = 1; - work_tree = real_pathdup(new_work_tree, 1); + repo_set_worktree(the_repository, new_work_tree); } const char *get_git_work_tree(void) { - return work_tree; + return the_repository->worktree; } char *get_object_directory(void) { - if (!git_object_dir) - setup_git_env(); - return git_object_dir; + if (!the_repository->objectdir) + BUG("git environment hasn't been setup"); + return the_repository->objectdir; } int odb_mkstemp(struct strbuf *template, const char *pattern) @@ -315,22 +279,23 @@ int odb_pack_keep(const char *name) char *get_index_file(void) { - if (!git_index_file) - setup_git_env(); - return git_index_file; + if (!the_repository->index_file) + BUG("git environment hasn't been setup"); + return the_repository->index_file; } char *get_graft_file(void) { - if (!git_graft_file) - setup_git_env(); - return git_graft_file; + if (!the_repository->graft_file) + BUG("git environment hasn't been setup"); + return the_repository->graft_file; } int set_git_dir(const char *path) { if (setenv(GIT_DIR_ENVIRONMENT, path, 1)) return error("Could not set GIT_DIR to '%s'", path); + repo_set_gitdir(the_repository, path); setup_git_env(); return 0; } diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c index 2dc9c82ecf..06c479f70a 100644 --- a/ewah/ewah_bitmap.c +++ b/ewah/ewah_bitmap.c @@ -210,8 +210,8 @@ size_t ewah_add(struct ewah_bitmap *self, eword_t word) void ewah_set(struct ewah_bitmap *self, size_t i) { const size_t dist = - (i + BITS_IN_EWORD) / BITS_IN_EWORD - - (self->bit_size + BITS_IN_EWORD - 1) / BITS_IN_EWORD; + DIV_ROUND_UP(i + 1, BITS_IN_EWORD) - + DIV_ROUND_UP(self->bit_size, BITS_IN_EWORD); assert(i >= self->bit_size); diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 0e8543c865..28b325d754 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -3,7 +3,7 @@ use 5.008; use strict; use warnings; -use Git; +use Git qw(unquote_path); use Git::I18N; binmode(STDOUT, ":raw"); @@ -174,47 +174,6 @@ if (!defined $GIT_DIR) { } chomp($GIT_DIR); -my %cquote_map = ( - "b" => chr(8), - "t" => chr(9), - "n" => chr(10), - "v" => chr(11), - "f" => chr(12), - "r" => chr(13), - "\\" => "\\", - "\042" => "\042", -); - -sub unquote_path { - local ($_) = @_; - my ($retval, $remainder); - if (!/^\042(.*)\042$/) { - return $_; - } - ($_, $retval) = ($1, ""); - while (/^([^\\]*)\\(.*)$/) { - $remainder = $2; - $retval .= $1; - for ($remainder) { - if (/^([0-3][0-7][0-7])(.*)$/) { - $retval .= chr(oct($1)); - $_ = $2; - last; - } - if (/^([\\\042btnvfr])(.*)$/) { - $retval .= $cquote_map{$1}; - $_ = $2; - last; - } - # This is malformed -- just return it as-is for now. - return $_[0]; - } - $_ = $remainder; - } - $retval .= $_; - return $retval; -} - sub refresh { my $fh; open $fh, 'git update-index --refresh |' diff --git a/git-rebase.sh b/git-rebase.sh index db1deed846..2cf73b88e8 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -166,14 +166,14 @@ apply_autostash () { stash_sha1=$(cat "$state_dir/autostash") if git stash apply $stash_sha1 2>&1 >/dev/null then - echo "$(gettext 'Applied autostash.')" + echo "$(gettext 'Applied autostash.')" >&2 else git stash store -m "autostash" -q $stash_sha1 || die "$(eval_gettext "Cannot store \$stash_sha1")" gettext 'Applying autostash resulted in conflicts. Your changes are safe in the stash. You can run "git stash pop" or "git stash drop" at any time. -' +' >&2 fi fi } diff --git a/git-send-email.perl b/git-send-email.perl index 7fd5874436..fa6526986e 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -82,6 +82,10 @@ git send-email --dump-aliases This setting forces to use one of the listed mechanisms. --smtp-debug <0|1> * Disable, enable Net::SMTP debug. + --batch-size <int> * send max <int> message per connection. + --relogin-delay <int> * delay <int> seconds between two successive login. + This option can only be used with --batch-size + Automating: --identity <str> * Use the sendemail.<id> options. --to-cmd <str> * Email To: via `<str> \$patch_path` @@ -154,6 +158,7 @@ my $have_email_valid = eval { require Email::Valid; 1 }; my $have_mail_address = eval { require Mail::Address; 1 }; my $smtp; my $auth; +my $num_sent = 0; # Regexes for RFC 2047 productions. my $re_token = qr/[^][()<>@,;:\\"\/?.= \000-\037\177-\377]+/; @@ -217,6 +222,7 @@ my ($cover_cc, $cover_to); my ($to_cmd, $cc_cmd); my ($smtp_server, $smtp_server_port, @smtp_server_options); my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path); +my ($batch_size, $relogin_delay); my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth); my ($validate, $confirm); my (@suppress_cc); @@ -248,6 +254,8 @@ my %config_settings = ( "smtppass" => \$smtp_authpass, "smtpdomain" => \$smtp_domain, "smtpauth" => \$smtp_auth, + "smtpbatchsize" => \$batch_size, + "smtprelogindelay" => \$relogin_delay, "to" => \@initial_to, "tocmd" => \$to_cmd, "cc" => \@initial_cc, @@ -359,6 +367,8 @@ $rc = GetOptions( "force" => \$force, "xmailer!" => \$use_xmailer, "no-xmailer" => sub {$use_xmailer = 0}, + "batch-size=i" => \$batch_size, + "relogin-delay=i" => \$relogin_delay, ); usage() if $help; @@ -1681,6 +1691,14 @@ foreach my $t (@files) { } } $message_id = undef; + $num_sent++; + if (defined $batch_size && $num_sent == $batch_size) { + $num_sent = 0; + $smtp->quit if defined $smtp; + undef $smtp; + undef $auth; + sleep($relogin_delay) if defined $relogin_delay; + } } # Execute a command (e.g. $to_cmd) to get a list of email addresses @@ -400,7 +400,7 @@ static struct cmd_struct commands[] = { { "init-db", cmd_init_db }, { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY }, { "log", cmd_log, RUN_SETUP }, - { "ls-files", cmd_ls_files, RUN_SETUP | SUPPORT_SUPER_PREFIX }, + { "ls-files", cmd_ls_files, RUN_SETUP }, { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY }, { "ls-tree", cmd_ls_tree, RUN_SETUP }, { "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY }, diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d8209c7a02..3d4a8ee27c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3125,7 +3125,7 @@ sub git_get_projects_list { return @list; } -# written with help of Tree::Trie module (Perl Artistic License, GPL compatibile) +# written with help of Tree::Trie module (Perl Artistic License, GPL compatible) # as side effects it sets 'forks' field to list of forks for forked projects sub filter_forks_from_projects_list { my $projects = shift; @@ -4376,7 +4376,7 @@ sub git_print_page_nav { "</div>\n"; } -# returns a submenu for the nagivation of the refs views (tags, heads, +# returns a submenu for the navigation of the refs views (tags, heads, # remotes) with the current view disabled and the remotes view only # available if the feature is enabled sub format_ref_views { @@ -8,7 +8,11 @@ #elif defined(SHA1_OPENSSL) #include <openssl/sha.h> #elif defined(SHA1_DC) +#ifdef DC_SHA1_SUBMODULE +#include "sha1collisiondetection/lib/sha1.h" +#else #include "sha1dc/sha1.h" +#endif #else /* SHA1_BLK */ #include "block-sha1/sha1.h" #endif diff --git a/imap-send.c b/imap-send.c index 351e84aea1..b2d0b849bb 100644 --- a/imap-send.c +++ b/imap-send.c @@ -861,7 +861,7 @@ static char hexchar(unsigned int b) return b < 10 ? '0' + b : 'a' + (b - 10); } -#define ENCODED_SIZE(n) (4*((n+2)/3)) +#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3)) static char *cram(const char *challenge_64, const char *user, const char *pass) { int i, resp_len, encoded_len, decoded_len; diff --git a/mailinfo.c b/mailinfo.c index 1652ec11f5..bd574cb752 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -58,17 +58,17 @@ static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line) static const char *unquote_comment(struct strbuf *outbuf, const char *in) { int c; - int take_next_litterally = 0; + int take_next_literally = 0; strbuf_addch(outbuf, '('); while ((c = *in++) != 0) { - if (take_next_litterally == 1) { - take_next_litterally = 0; + if (take_next_literally == 1) { + take_next_literally = 0; } else { switch (c) { case '\\': - take_next_litterally = 1; + take_next_literally = 1; continue; case '(': in = unquote_comment(outbuf, in); @@ -88,15 +88,15 @@ static const char *unquote_comment(struct strbuf *outbuf, const char *in) static const char *unquote_quoted_string(struct strbuf *outbuf, const char *in) { int c; - int take_next_litterally = 0; + int take_next_literally = 0; while ((c = *in++) != 0) { - if (take_next_litterally == 1) { - take_next_litterally = 0; + if (take_next_literally == 1) { + take_next_literally = 0; } else { switch (c) { case '\\': - take_next_litterally = 1; + take_next_literally = 1; continue; case '"': return in; diff --git a/merge-recursive.c b/merge-recursive.c index 59e5ee41a8..1494ffdb82 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -2210,11 +2210,11 @@ int parse_merge_opt(struct merge_options *o, const char *s) o->xdl_opts |= value; } else if (!strcmp(s, "ignore-space-change")) - o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + DIFF_XDL_SET(o, IGNORE_WHITESPACE_CHANGE); else if (!strcmp(s, "ignore-all-space")) - o->xdl_opts |= XDF_IGNORE_WHITESPACE; + DIFF_XDL_SET(o, IGNORE_WHITESPACE); else if (!strcmp(s, "ignore-space-at-eol")) - o->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + DIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL); else if (!strcmp(s, "renormalize")) o->renormalize = 1; else if (!strcmp(s, "no-renormalize")) diff --git a/pack-bitmap.c b/pack-bitmap.c index a3ac3dccd4..327634cd71 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -627,7 +627,7 @@ static void show_objects_for_type( sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr); if (bitmap_git.hashes) - hash = ntohl(bitmap_git.hashes[entry->nr]); + hash = get_be32(bitmap_git.hashes + entry->nr); show_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset); } @@ -2,11 +2,13 @@ * Utilities for paths and pathnames */ #include "cache.h" +#include "repository.h" #include "strbuf.h" #include "string-list.h" #include "dir.h" #include "worktree.h" #include "submodule-config.h" +#include "path.h" static int get_st_mode_bits(const char *path, int *mode) { @@ -343,8 +345,6 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len, { char *base = buf->buf + git_dir_len; init_common_trie(); - if (!common_dir) - common_dir = get_git_common_dir(); if (trie_find(&common_trie, base, check_common, NULL) > 0) replace_dir(buf, git_dir_len, common_dir); } @@ -355,7 +355,7 @@ void report_linked_checkout_garbage(void) const struct common_dir *p; int len; - if (!git_common_dir_env) + if (!the_repository->different_commondir) return; strbuf_addf(&sb, "%s/", get_git_dir()); len = sb.len; @@ -371,42 +371,78 @@ void report_linked_checkout_garbage(void) strbuf_release(&sb); } -static void adjust_git_path(struct strbuf *buf, int git_dir_len) +static void adjust_git_path(const struct repository *repo, + struct strbuf *buf, int git_dir_len) { const char *base = buf->buf + git_dir_len; - if (git_graft_env && is_dir_file(base, "info", "grafts")) + if (is_dir_file(base, "info", "grafts")) strbuf_splice(buf, 0, buf->len, - get_graft_file(), strlen(get_graft_file())); - else if (git_index_env && !strcmp(base, "index")) + repo->graft_file, strlen(repo->graft_file)); + else if (!strcmp(base, "index")) strbuf_splice(buf, 0, buf->len, - get_index_file(), strlen(get_index_file())); - else if (git_db_env && dir_prefix(base, "objects")) - replace_dir(buf, git_dir_len + 7, get_object_directory()); + repo->index_file, strlen(repo->index_file)); + else if (dir_prefix(base, "objects")) + replace_dir(buf, git_dir_len + 7, repo->objectdir); else if (git_hooks_path && dir_prefix(base, "hooks")) replace_dir(buf, git_dir_len + 5, git_hooks_path); - else if (git_common_dir_env) - update_common_dir(buf, git_dir_len, NULL); + else if (repo->different_commondir) + update_common_dir(buf, git_dir_len, repo->commondir); } -static void do_git_path(const struct worktree *wt, struct strbuf *buf, +static void strbuf_worktree_gitdir(struct strbuf *buf, + const struct repository *repo, + const struct worktree *wt) +{ + if (!wt) + strbuf_addstr(buf, repo->gitdir); + else if (!wt->id) + strbuf_addstr(buf, repo->commondir); + else + strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id); +} + +static void do_git_path(const struct repository *repo, + const struct worktree *wt, struct strbuf *buf, const char *fmt, va_list args) { int gitdir_len; - strbuf_addstr(buf, get_worktree_git_dir(wt)); + strbuf_worktree_gitdir(buf, repo, wt); if (buf->len && !is_dir_sep(buf->buf[buf->len - 1])) strbuf_addch(buf, '/'); gitdir_len = buf->len; strbuf_vaddf(buf, fmt, args); - adjust_git_path(buf, gitdir_len); + if (!wt) + adjust_git_path(repo, buf, gitdir_len); strbuf_cleanup_path(buf); } +char *repo_git_path(const struct repository *repo, + const char *fmt, ...) +{ + struct strbuf path = STRBUF_INIT; + va_list args; + va_start(args, fmt); + do_git_path(repo, NULL, &path, fmt, args); + va_end(args); + return strbuf_detach(&path, NULL); +} + +void strbuf_repo_git_path(struct strbuf *sb, + const struct repository *repo, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + do_git_path(repo, NULL, sb, fmt, args); + va_end(args); +} + char *git_path_buf(struct strbuf *buf, const char *fmt, ...) { va_list args; strbuf_reset(buf); va_start(args, fmt); - do_git_path(NULL, buf, fmt, args); + do_git_path(the_repository, NULL, buf, fmt, args); va_end(args); return buf->buf; } @@ -415,7 +451,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) { va_list args; va_start(args, fmt); - do_git_path(NULL, sb, fmt, args); + do_git_path(the_repository, NULL, sb, fmt, args); va_end(args); } @@ -424,7 +460,7 @@ const char *git_path(const char *fmt, ...) struct strbuf *pathname = get_pathname(); va_list args; va_start(args, fmt); - do_git_path(NULL, pathname, fmt, args); + do_git_path(the_repository, NULL, pathname, fmt, args); va_end(args); return pathname->buf; } @@ -434,7 +470,7 @@ char *git_pathdup(const char *fmt, ...) struct strbuf path = STRBUF_INIT; va_list args; va_start(args, fmt); - do_git_path(NULL, &path, fmt, args); + do_git_path(the_repository, NULL, &path, fmt, args); va_end(args); return strbuf_detach(&path, NULL); } @@ -465,11 +501,52 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...) struct strbuf *pathname = get_pathname(); va_list args; va_start(args, fmt); - do_git_path(wt, pathname, fmt, args); + do_git_path(the_repository, wt, pathname, fmt, args); va_end(args); return pathname->buf; } +static void do_worktree_path(const struct repository *repo, + struct strbuf *buf, + const char *fmt, va_list args) +{ + strbuf_addstr(buf, repo->worktree); + if(buf->len && !is_dir_sep(buf->buf[buf->len - 1])) + strbuf_addch(buf, '/'); + + strbuf_vaddf(buf, fmt, args); + strbuf_cleanup_path(buf); +} + +char *repo_worktree_path(const struct repository *repo, const char *fmt, ...) +{ + struct strbuf path = STRBUF_INIT; + va_list args; + + if (!repo->worktree) + return NULL; + + va_start(args, fmt); + do_worktree_path(repo, &path, fmt, args); + va_end(args); + + return strbuf_detach(&path, NULL); +} + +void strbuf_repo_worktree_path(struct strbuf *sb, + const struct repository *repo, + const char *fmt, ...) +{ + va_list args; + + if (!repo->worktree) + return; + + va_start(args, fmt); + do_worktree_path(repo, sb, fmt, args); + va_end(args); +} + /* Returns 0 on success, negative on failure. */ static int do_submodule_path(struct strbuf *buf, const char *path, const char *fmt, va_list args) @@ -524,11 +601,12 @@ int strbuf_git_path_submodule(struct strbuf *buf, const char *path, return err; } -static void do_git_common_path(struct strbuf *buf, +static void do_git_common_path(const struct repository *repo, + struct strbuf *buf, const char *fmt, va_list args) { - strbuf_addstr(buf, get_git_common_dir()); + strbuf_addstr(buf, repo->commondir); if (buf->len && !is_dir_sep(buf->buf[buf->len - 1])) strbuf_addch(buf, '/'); strbuf_vaddf(buf, fmt, args); @@ -540,16 +618,18 @@ const char *git_common_path(const char *fmt, ...) struct strbuf *pathname = get_pathname(); va_list args; va_start(args, fmt); - do_git_common_path(pathname, fmt, args); + do_git_common_path(the_repository, pathname, fmt, args); va_end(args); return pathname->buf; } -void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...) +void strbuf_git_common_path(struct strbuf *sb, + const struct repository *repo, + const char *fmt, ...) { va_list args; va_start(args, fmt); - do_git_common_path(sb, fmt, args); + do_git_common_path(repo, sb, fmt, args); va_end(args); } diff --git a/path.h b/path.h new file mode 100644 index 0000000000..9541620c79 --- /dev/null +++ b/path.h @@ -0,0 +1,82 @@ +#ifndef PATH_H +#define PATH_H + +struct repository; + +/* + * Return a statically allocated filename, either generically (mkpath), in + * the repository directory (git_path), or in a submodule's repository + * directory (git_path_submodule). In all cases, note that the result + * may be overwritten by another call to _any_ of the functions. Consider + * using the safer "dup" or "strbuf" formats below (in some cases, the + * unsafe versions have already been removed). + */ +extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); +extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); +extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); + +extern char *mksnpath(char *buf, size_t n, const char *fmt, ...) + __attribute__((format (printf, 3, 4))); +extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) + __attribute__((format (printf, 2, 3))); +extern void strbuf_git_common_path(struct strbuf *sb, + const struct repository *repo, + const char *fmt, ...) + __attribute__((format (printf, 3, 4))); +extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...) + __attribute__((format (printf, 2, 3))); +extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path, + const char *fmt, ...) + __attribute__((format (printf, 3, 4))); +extern char *git_pathdup(const char *fmt, ...) + __attribute__((format (printf, 1, 2))); +extern char *mkpathdup(const char *fmt, ...) + __attribute__((format (printf, 1, 2))); +extern char *git_pathdup_submodule(const char *path, const char *fmt, ...) + __attribute__((format (printf, 2, 3))); + +extern char *repo_git_path(const struct repository *repo, + const char *fmt, ...) + __attribute__((format (printf, 2, 3))); +extern void strbuf_repo_git_path(struct strbuf *sb, + const struct repository *repo, + const char *fmt, ...) + __attribute__((format (printf, 3, 4))); + +extern char *repo_worktree_path(const struct repository *repo, + const char *fmt, ...) + __attribute__((format (printf, 2, 3))); +extern void strbuf_repo_worktree_path(struct strbuf *sb, + const struct repository *repo, + const char *fmt, ...) + __attribute__((format (printf, 3, 4))); + +extern void report_linked_checkout_garbage(void); + +/* + * You can define a static memoized git path like: + * + * static GIT_PATH_FUNC(git_path_foo, "FOO"); + * + * or use one of the global ones below. + */ +#define GIT_PATH_FUNC(func, filename) \ + const char *func(void) \ + { \ + static char *ret; \ + if (!ret) \ + ret = git_pathdup(filename); \ + return ret; \ + } + +const char *git_path_cherry_pick_head(void); +const char *git_path_revert_head(void); +const char *git_path_squash_msg(void); +const char *git_path_merge_msg(void); +const char *git_path_merge_rr(void); +const char *git_path_merge_mode(void); +const char *git_path_merge_head(void); +const char *git_path_fetch_head(void); +const char *git_path_shallow(void); + +#endif /* PATH_H */ diff --git a/pathspec.c b/pathspec.c index a5f4df31ea..e2a23ebc96 100644 --- a/pathspec.c +++ b/pathspec.c @@ -606,7 +606,7 @@ void parse_pathspec(struct pathspec *pathspec, /* * If everything is an exclude pattern, add one positive pattern - * that matches everyting. We allocated an extra one for this. + * that matches everything. We allocated an extra one for this. */ if (nr_exclude == n) { int plen = (!(flags & PATHSPEC_PREFER_CWD)) ? 0 : prefixlen; diff --git a/perl/Git.pm b/perl/Git.pm index bfce1f795d..f4b56e6d4d 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -61,7 +61,8 @@ require Exporter; remote_refs prompt get_tz_offset get_record credential credential_read credential_write - temp_acquire temp_is_locked temp_release temp_reset temp_path); + temp_acquire temp_is_locked temp_release temp_reset temp_path + unquote_path); =head1 DESCRIPTION @@ -1451,6 +1452,57 @@ sub prefix_lines { return $string; } +=item unquote_path ( PATH ) + +Unquote a quoted path containing c-escapes as returned by ls-files etc. +when not using -z or when parsing the output of diff -u. + +=cut + +{ + my %cquote_map = ( + "a" => chr(7), + "b" => chr(8), + "t" => chr(9), + "n" => chr(10), + "v" => chr(11), + "f" => chr(12), + "r" => chr(13), + "\\" => "\\", + "\042" => "\042", + ); + + sub unquote_path { + local ($_) = @_; + my ($retval, $remainder); + if (!/^\042(.*)\042$/) { + return $_; + } + ($_, $retval) = ($1, ""); + while (/^([^\\]*)\\(.*)$/) { + $remainder = $2; + $retval .= $1; + for ($remainder) { + if (/^([0-3][0-7][0-7])(.*)$/) { + $retval .= chr(oct($1)); + $_ = $2; + last; + } + if (/^([\\\042abtnvfr])(.*)$/) { + $retval .= $cquote_map{$1}; + $_ = $2; + last; + } + # This is malformed + throw Error::Simple("invalid quoted path $_[0]"); + } + $_ = $remainder; + } + $retval .= $_; + return $retval; + } +} + =item get_comment_line_char ( ) Gets the core.commentchar configuration value. diff --git a/perl/Git/I18N.pm b/perl/Git/I18N.pm index c41425c8d0..836a5c2382 100644 --- a/perl/Git/I18N.pm +++ b/perl/Git/I18N.pm @@ -74,7 +74,7 @@ Git::I18N - Perl interface to Git's Gettext localizations printf __("The following error occurred: %s\n"), $error; - printf __n("commited %d file\n", "commited %d files\n", $files), $files; + printf __n("committed %d file\n", "committed %d files\n", $files), $files; =head1 DESCRIPTION @@ -8564,7 +8564,7 @@ msgstr "\"auto-gc\" Modus aktivieren" #: builtin/gc.c:362 msgid "force running gc even if there may be another gc running" msgstr "" -"Ausführung von \"git gc\" erwzingen, selbst wenn ein anderes\n" +"Ausführung von \"git gc\" erzwingen, selbst wenn ein anderes\n" "\"git gc\" bereits ausgeführt wird" #: builtin/gc.c:379 diff --git a/read-cache.c b/read-cache.c index 3c4354ee21..2121b6e7bb 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2431,6 +2431,14 @@ static int write_shared_index(struct index_state *istate, delete_tempfile(&temporary_sharedindex); return ret; } + ret = adjust_shared_perm(get_tempfile_path(&temporary_sharedindex)); + if (ret) { + int save_errno = errno; + error("cannot fix permission bits on %s", get_tempfile_path(&temporary_sharedindex)); + delete_tempfile(&temporary_sharedindex); + errno = save_errno; + return ret; + } ret = rename_tempfile(&temporary_sharedindex, git_path("sharedindex.%s", sha1_to_hex(si->base->sha1))); if (!ret) { diff --git a/ref-filter.c b/ref-filter.c index 72e6cb8ecc..ae6ecbd1cf 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -221,7 +221,7 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg) static void refname_atom_parser(struct used_atom *atom, const char *arg) { - return refname_atom_parser_internal(&atom->u.refname, arg, atom->name); + refname_atom_parser_internal(&atom->u.refname, arg, atom->name); } static align_type parse_align_position(const char *s) @@ -1624,7 +1624,7 @@ static int match_pattern(const struct ref_filter *filter, const char *refname) skip_prefix(refname, "refs/", &refname)); for (; *patterns; patterns++) { - if (!wildmatch(*patterns, refname, flags, NULL)) + if (!wildmatch(*patterns, refname, flags)) return 1; } return 0; @@ -1655,7 +1655,7 @@ static int match_name_as_path(const struct ref_filter *filter, const char *refna refname[plen] == '/' || p[plen-1] == '/')) return 1; - if (!wildmatch(p, refname, WM_PATHNAME, NULL)) + if (!wildmatch(p, refname, WM_PATHNAME)) return 1; } return 0; diff --git a/reflog-walk.c b/reflog-walk.c index ed99437ad2..081f89b70d 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -38,6 +38,22 @@ static int read_one_reflog(struct object_id *ooid, struct object_id *noid, return 0; } +static void free_complete_reflog(struct complete_reflogs *array) +{ + int i; + + if (!array) + return; + + for (i = 0; i < array->nr; i++) { + free(array->items[i].email); + free(array->items[i].message); + } + free(array->items); + free(array->ref); + free(array); +} + static struct complete_reflogs *read_complete_reflog(const char *ref) { struct complete_reflogs *reflogs = @@ -136,6 +152,7 @@ struct reflog_walk_info { void init_reflog_walk(struct reflog_walk_info **info) { *info = xcalloc(1, sizeof(struct reflog_walk_info)); + (*info)->complete_reflogs.strdup_strings = 1; } int add_reflog_for_walk(struct reflog_walk_info *info, @@ -188,20 +205,14 @@ int add_reflog_for_walk(struct reflog_walk_info *info, if (ret > 1) free(b); else if (ret == 1) { - if (reflogs) { - free(reflogs->ref); - free(reflogs); - } + free_complete_reflog(reflogs); free(branch); branch = b; reflogs = read_complete_reflog(branch); } } if (!reflogs || reflogs->nr == 0) { - if (reflogs) { - free(reflogs->ref); - free(reflogs); - } + free_complete_reflog(reflogs); free(branch); return -1; } @@ -214,10 +225,6 @@ int add_reflog_for_walk(struct reflog_walk_info *info, if (recno < 0) { commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); if (commit_reflog->recno < 0) { - if (reflogs) { - free(reflogs->ref); - free(reflogs); - } free(commit_reflog); return -1; } @@ -259,6 +266,8 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) /* a root commit, but there are still more entries to show */ reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; logobj = parse_object(&reflog->noid); + if (!logobj) + logobj = parse_object(&reflog->ooid); } if (!logobj || logobj->type != OBJ_COMMIT) { @@ -230,7 +230,7 @@ static int filter_refs(const char *refname, const struct object_id *oid, { struct ref_filter *filter = (struct ref_filter *)data; - if (wildmatch(filter->pattern, refname, 0, NULL)) + if (wildmatch(filter->pattern, refname, 0)) return 0; return filter->fn(refname, oid, flags, filter->cb_data); } @@ -574,7 +574,7 @@ int ref_transaction_verify(struct ref_transaction *transaction, #define TRANSACTION_GENERIC_ERROR -2 /* - * Perform the preparatory stages of commiting `transaction`. Acquire + * Perform the preparatory stages of committing `transaction`. Acquire * any needed locks, check preconditions, etc.; basically, do as much * as possible to ensure that the transaction will be able to go * through, stopping just short of making any irrevocable or @@ -586,7 +586,7 @@ int ref_transaction_verify(struct ref_transaction *transaction, * On failure, abort the transaction, write an error message to `err`, * and return one of the `TRANSACTION_*` constants. * - * Callers who don't need such fine-grained control over commiting + * Callers who don't need such fine-grained control over committing * reference transactions should just call `ref_transaction_commit()`. */ int ref_transaction_prepare(struct ref_transaction *transaction, diff --git a/repository.c b/repository.c new file mode 100644 index 0000000000..edca907404 --- /dev/null +++ b/repository.c @@ -0,0 +1,242 @@ +#include "cache.h" +#include "repository.h" +#include "config.h" +#include "submodule-config.h" + +/* The main repository */ +static struct repository the_repo; +struct repository *the_repository = &the_repo; + +static char *git_path_from_env(const char *envvar, const char *git_dir, + const char *path, int fromenv) +{ + if (fromenv) { + const char *value = getenv(envvar); + if (value) + return xstrdup(value); + } + + return xstrfmt("%s/%s", git_dir, path); +} + +static int find_common_dir(struct strbuf *sb, const char *gitdir, int fromenv) +{ + if (fromenv) { + const char *value = getenv(GIT_COMMON_DIR_ENVIRONMENT); + if (value) { + strbuf_addstr(sb, value); + return 1; + } + } + + return get_common_dir_noenv(sb, gitdir); +} + +static void repo_setup_env(struct repository *repo) +{ + struct strbuf sb = STRBUF_INIT; + + repo->different_commondir = find_common_dir(&sb, repo->gitdir, + !repo->ignore_env); + repo->commondir = strbuf_detach(&sb, NULL); + repo->objectdir = git_path_from_env(DB_ENVIRONMENT, repo->commondir, + "objects", !repo->ignore_env); + repo->graft_file = git_path_from_env(GRAFT_ENVIRONMENT, repo->commondir, + "info/grafts", !repo->ignore_env); + repo->index_file = git_path_from_env(INDEX_ENVIRONMENT, repo->gitdir, + "index", !repo->ignore_env); +} + +void repo_set_gitdir(struct repository *repo, const char *path) +{ + const char *gitfile = read_gitfile(path); + + /* + * NEEDSWORK: Eventually we want to be able to free gitdir and the rest + * of the environment before reinitializing it again, but we have some + * crazy code paths where we try to set gitdir with the current gitdir + * and we don't want to free gitdir before copying the passed in value. + */ + repo->gitdir = xstrdup(gitfile ? gitfile : path); + + repo_setup_env(repo); +} + +/* + * Attempt to resolve and set the provided 'gitdir' for repository 'repo'. + * Return 0 upon success and a non-zero value upon failure. + */ +static int repo_init_gitdir(struct repository *repo, const char *gitdir) +{ + int ret = 0; + int error = 0; + char *abspath = NULL; + const char *resolved_gitdir; + + abspath = real_pathdup(gitdir, 0); + if (!abspath) { + ret = -1; + goto out; + } + + /* 'gitdir' must reference the gitdir directly */ + resolved_gitdir = resolve_gitdir_gently(abspath, &error); + if (!resolved_gitdir) { + ret = -1; + goto out; + } + + repo_set_gitdir(repo, resolved_gitdir); + +out: + free(abspath); + return ret; +} + +void repo_set_worktree(struct repository *repo, const char *path) +{ + repo->worktree = real_pathdup(path, 1); +} + +static int read_and_verify_repository_format(struct repository_format *format, + const char *commondir) +{ + int ret = 0; + struct strbuf sb = STRBUF_INIT; + + strbuf_addf(&sb, "%s/config", commondir); + read_repository_format(format, sb.buf); + strbuf_reset(&sb); + + if (verify_repository_format(format, &sb) < 0) { + warning("%s", sb.buf); + ret = -1; + } + + strbuf_release(&sb); + return ret; +} + +/* + * Initialize 'repo' based on the provided 'gitdir'. + * Return 0 upon success and a non-zero value upon failure. + */ +int repo_init(struct repository *repo, const char *gitdir, const char *worktree) +{ + struct repository_format format; + memset(repo, 0, sizeof(*repo)); + + repo->ignore_env = 1; + + if (repo_init_gitdir(repo, gitdir)) + goto error; + + if (read_and_verify_repository_format(&format, repo->commondir)) + goto error; + + if (worktree) + repo_set_worktree(repo, worktree); + + return 0; + +error: + repo_clear(repo); + return -1; +} + +/* + * Initialize 'submodule' as the submodule given by 'path' in parent repository + * 'superproject'. + * Return 0 upon success and a non-zero value upon failure. + */ +int repo_submodule_init(struct repository *submodule, + struct repository *superproject, + const char *path) +{ + const struct submodule *sub; + struct strbuf gitdir = STRBUF_INIT; + struct strbuf worktree = STRBUF_INIT; + int ret = 0; + + sub = submodule_from_cache(superproject, null_sha1, path); + if (!sub) { + ret = -1; + goto out; + } + + strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path); + strbuf_repo_worktree_path(&worktree, superproject, "%s", path); + + if (repo_init(submodule, gitdir.buf, worktree.buf)) { + /* + * If initilization fails then it may be due to the submodule + * not being populated in the superproject's worktree. Instead + * we can try to initilize the submodule by finding it's gitdir + * in the superproject's 'modules' directory. In this case the + * submodule would not have a worktree. + */ + strbuf_reset(&gitdir); + strbuf_repo_git_path(&gitdir, superproject, + "modules/%s", sub->name); + + if (repo_init(submodule, gitdir.buf, NULL)) { + ret = -1; + goto out; + } + } + + submodule->submodule_prefix = xstrfmt("%s%s/", + superproject->submodule_prefix ? + superproject->submodule_prefix : + "", path); + +out: + strbuf_release(&gitdir); + strbuf_release(&worktree); + return ret; +} + +void repo_clear(struct repository *repo) +{ + free(repo->gitdir); + repo->gitdir = NULL; + free(repo->commondir); + repo->commondir = NULL; + free(repo->objectdir); + repo->objectdir = NULL; + free(repo->graft_file); + repo->graft_file = NULL; + free(repo->index_file); + repo->index_file = NULL; + free(repo->worktree); + repo->worktree = NULL; + free(repo->submodule_prefix); + repo->submodule_prefix = NULL; + + if (repo->config) { + git_configset_clear(repo->config); + free(repo->config); + repo->config = NULL; + } + + if (repo->submodule_cache) { + submodule_cache_free(repo->submodule_cache); + repo->submodule_cache = NULL; + } + + if (repo->index) { + discard_index(repo->index); + free(repo->index); + repo->index = NULL; + } +} + +int repo_read_index(struct repository *repo) +{ + if (!repo->index) + repo->index = xcalloc(1, sizeof(*repo->index)); + else + discard_index(repo->index); + + return read_index_from(repo->index, repo->index_file); +} diff --git a/repository.h b/repository.h new file mode 100644 index 0000000000..417787f3ef --- /dev/null +++ b/repository.h @@ -0,0 +1,97 @@ +#ifndef REPOSITORY_H +#define REPOSITORY_H + +struct config_set; +struct index_state; +struct submodule_cache; + +struct repository { + /* Environment */ + /* + * Path to the git directory. + * Cannot be NULL after initialization. + */ + char *gitdir; + + /* + * Path to the common git directory. + * Cannot be NULL after initialization. + */ + char *commondir; + + /* + * Path to the repository's object store. + * Cannot be NULL after initialization. + */ + char *objectdir; + + /* + * Path to the repository's graft file. + * Cannot be NULL after initialization. + */ + char *graft_file; + + /* + * Path to the current worktree's index file. + * Cannot be NULL after initialization. + */ + char *index_file; + + /* + * Path to the working directory. + * A NULL value indicates that there is no working directory. + */ + char *worktree; + + /* + * Path from the root of the top-level superproject down to this + * repository. This is only non-NULL if the repository is initialized + * as a submodule of another repository. + */ + char *submodule_prefix; + + /* Subsystems */ + /* + * Repository's config which contains key-value pairs from the usual + * set of config files (i.e. repo specific .git/config, user wide + * ~/.gitconfig, XDG config file and the global /etc/gitconfig) + */ + struct config_set *config; + + /* Repository's submodule config as defined by '.gitmodules' */ + struct submodule_cache *submodule_cache; + + /* + * Repository's in-memory index. + * 'repo_read_index()' can be used to populate 'index'. + */ + struct index_state *index; + + /* Configurations */ + /* + * Bit used during initialization to indicate if repository state (like + * the location of the 'objectdir') should be read from the + * environment. By default this bit will be set at the begining of + * 'repo_init()' so that all repositories will ignore the environment. + * The exception to this is 'the_repository', which doesn't go through + * the normal 'repo_init()' process. + */ + unsigned ignore_env:1; + + /* Indicate if a repository has a different 'commondir' from 'gitdir' */ + unsigned different_commondir:1; +}; + +extern struct repository *the_repository; + +extern void repo_set_gitdir(struct repository *repo, const char *path); +extern void repo_set_worktree(struct repository *repo, const char *path); +extern int repo_init(struct repository *repo, const char *gitdir, const char *worktree); +extern int repo_submodule_init(struct repository *submodule, + struct repository *superproject, + const char *path); +extern void repo_clear(struct repository *repo); + +extern int repo_read_index(struct repository *repo); + +#endif /* REPOSITORY_H */ diff --git a/revision.c b/revision.c index 207103d211..6603af9444 100644 --- a/revision.c +++ b/revision.c @@ -1142,7 +1142,7 @@ int ref_excluded(struct string_list *ref_excludes, const char *path) if (!ref_excludes) return 0; for_each_string_list_item(item, ref_excludes) { - if (!wildmatch(item->string, path, 0, NULL)) + if (!wildmatch(item->string, path, 0)) return 1; } return 0; diff --git a/send-pack.c b/send-pack.c index ed3cee3211..11d6f3d983 100644 --- a/send-pack.c +++ b/send-pack.c @@ -133,7 +133,7 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc * 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 + * (141), because that's a normal occurrence if the remote end * hangs up (and we'll report that by trying to read the unpack * status). */ diff --git a/sequencer.c b/sequencer.c index 98cdfe74fd..3010faf863 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1922,7 +1922,7 @@ static int apply_autostash(struct replay_opts *opts) argv_array_push(&child.args, "apply"); argv_array_push(&child.args, stash_sha1.buf); if (!run_command(&child)) - printf(_("Applied autostash.\n")); + fprintf(stderr, _("Applied autostash.\n")); else { struct child_process store = CHILD_PROCESS_INIT; @@ -1936,10 +1936,11 @@ static int apply_autostash(struct replay_opts *opts) if (run_command(&store)) ret = error(_("cannot store %s"), stash_sha1.buf); else - printf(_("Applying autostash resulted in conflicts.\n" - "Your changes are safe in the stash.\n" - "You can run \"git stash pop\" or" - " \"git stash drop\" at any time.\n")); + fprintf(stderr, + _("Applying autostash resulted in conflicts.\n" + "Your changes are safe in the stash.\n" + "You can run \"git stash pop\" or" + " \"git stash drop\" at any time.\n")); } strbuf_release(&stash_sha1); @@ -1,4 +1,5 @@ #include "cache.h" +#include "repository.h" #include "config.h" #include "dir.h" #include "string-list.h" @@ -398,6 +399,11 @@ void setup_work_tree(void) if (getenv(GIT_WORK_TREE_ENVIRONMENT)) setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1); + /* + * NEEDSWORK: this call can essentially be set_git_dir(get_git_dir()) + * which can cause some problems when trying to free the old value of + * gitdir. + */ set_git_dir(remove_leading_path(git_dir, work_tree)); initialized = 1; } @@ -1079,6 +1085,12 @@ const char *setup_git_directory_gently(int *nongit_ok) die("BUG: unhandled setup_git_directory_1() result"); } + /* + * NEEDSWORK: This was a hack in order to get ls-files and grep to have + * properly formated output when recursing submodules. Once ls-files + * and grep have been changed to perform this recursing in-process this + * needs to be removed. + */ env_prefix = getenv(GIT_TOPLEVEL_PREFIX_ENVIRONMENT); if (env_prefix) prefix = env_prefix; @@ -1091,6 +1103,27 @@ const char *setup_git_directory_gently(int *nongit_ok) startup_info->have_repository = !nongit_ok || !*nongit_ok; startup_info->prefix = prefix; + /* + * Not all paths through the setup code will call 'set_git_dir()' (which + * directly sets up the environment) so in order to guarantee that the + * environment is in a consistent state after setup, explicitly setup + * the environment if we have a repository. + * + * NEEDSWORK: currently we allow bogus GIT_DIR values to be set in some + * code paths so we also need to explicitly setup the environment if + * the user has set GIT_DIR. It may be beneficial to disallow bogus + * GIT_DIR values at some point in the future. + */ + if (startup_info->have_repository || getenv(GIT_DIR_ENVIRONMENT)) { + if (!the_repository->gitdir) { + const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); + if (!gitdir) + gitdir = DEFAULT_GIT_DIR_ENVIRONMENT; + repo_set_gitdir(the_repository, gitdir); + setup_git_env(); + } + } + strbuf_release(&dir); strbuf_release(&gitdir); diff --git a/sha1_file.c b/sha1_file.c index fb1fd809dc..5862386cd0 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1964,7 +1964,7 @@ static int parse_sha1_header_extended(const char *hdr, struct object_info *oi, * we're obtaining the type using '--allow-unknown-type' * option. */ - if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0)) + if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0)) type = 0; else if (type < 0) die("invalid object type"); @@ -2002,20 +2002,7 @@ int parse_sha1_header(const char *hdr, unsigned long *sizep) struct object_info oi = OBJECT_INFO_INIT; oi.sizep = sizep; - return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT); -} - -static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1) -{ - int ret; - git_zstream stream; - char hdr[8192]; - - ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)); - if (ret < Z_OK || (*type = parse_sha1_header(hdr, size)) < 0) - return NULL; - - return unpack_sha1_rest(&stream, hdr, *size, sha1); + return parse_sha1_header_extended(hdr, &oi, 0); } unsigned long get_size_from_delta(struct packed_git *p, @@ -2239,107 +2226,6 @@ unwind: goto out; } -int packed_object_info(struct packed_git *p, off_t obj_offset, - struct object_info *oi) -{ - struct pack_window *w_curs = NULL; - unsigned long size; - off_t curpos = obj_offset; - enum object_type type; - - /* - * We always get the representation type, but only convert it to - * a "real" type later if the caller is interested. - */ - type = unpack_object_header(p, &w_curs, &curpos, &size); - - if (oi->sizep) { - if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { - off_t tmp_pos = curpos; - off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos, - type, obj_offset); - if (!base_offset) { - type = OBJ_BAD; - goto out; - } - *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos); - if (*oi->sizep == 0) { - type = OBJ_BAD; - goto out; - } - } else { - *oi->sizep = size; - } - } - - if (oi->disk_sizep) { - struct revindex_entry *revidx = find_pack_revindex(p, obj_offset); - *oi->disk_sizep = revidx[1].offset - obj_offset; - } - - if (oi->typep) { - *oi->typep = packed_to_object_type(p, obj_offset, type, &w_curs, curpos); - if (*oi->typep < 0) { - type = OBJ_BAD; - goto out; - } - } - - if (oi->delta_base_sha1) { - if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { - const unsigned char *base; - - base = get_delta_base_sha1(p, &w_curs, curpos, - type, obj_offset); - if (!base) { - type = OBJ_BAD; - goto out; - } - - hashcpy(oi->delta_base_sha1, base); - } else - hashclr(oi->delta_base_sha1); - } - -out: - unuse_pack(&w_curs); - return type; -} - -static void *unpack_compressed_entry(struct packed_git *p, - struct pack_window **w_curs, - off_t curpos, - unsigned long size) -{ - int st; - git_zstream stream; - unsigned char *buffer, *in; - - buffer = xmallocz_gently(size); - if (!buffer) - return NULL; - memset(&stream, 0, sizeof(stream)); - stream.next_out = buffer; - stream.avail_out = size + 1; - - git_inflate_init(&stream); - do { - in = use_pack(p, w_curs, curpos, &stream.avail_in); - stream.next_in = in; - st = git_inflate(&stream, Z_FINISH); - if (!stream.avail_out) - break; /* the payload is larger than it should be */ - curpos += stream.next_in - in; - } while (st == Z_OK || st == Z_BUF_ERROR); - git_inflate_end(&stream); - if ((st != Z_STREAM_END) || stream.total_out != size) { - free(buffer); - return NULL; - } - - return buffer; -} - static struct hashmap delta_base_cache; static size_t delta_base_cached; @@ -2427,8 +2313,10 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, if (!ent) return unpack_entry(p, base_offset, type, base_size); - *type = ent->type; - *base_size = ent->size; + if (type) + *type = ent->type; + if (base_size) + *base_size = ent->size; return xmemdupz(ent->data, ent->size); } @@ -2477,6 +2365,123 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset, hashmap_add(&delta_base_cache, ent); } +int packed_object_info(struct packed_git *p, off_t obj_offset, + struct object_info *oi) +{ + struct pack_window *w_curs = NULL; + unsigned long size; + off_t curpos = obj_offset; + enum object_type type; + + /* + * We always get the representation type, but only convert it to + * a "real" type later if the caller is interested. + */ + if (oi->contentp) { + *oi->contentp = cache_or_unpack_entry(p, obj_offset, oi->sizep, + &type); + if (!*oi->contentp) + type = OBJ_BAD; + } else { + type = unpack_object_header(p, &w_curs, &curpos, &size); + } + + if (!oi->contentp && oi->sizep) { + if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { + off_t tmp_pos = curpos; + off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos, + type, obj_offset); + if (!base_offset) { + type = OBJ_BAD; + goto out; + } + *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos); + if (*oi->sizep == 0) { + type = OBJ_BAD; + goto out; + } + } else { + *oi->sizep = size; + } + } + + if (oi->disk_sizep) { + struct revindex_entry *revidx = find_pack_revindex(p, obj_offset); + *oi->disk_sizep = revidx[1].offset - obj_offset; + } + + if (oi->typep || oi->typename) { + enum object_type ptot; + ptot = packed_to_object_type(p, obj_offset, type, &w_curs, + curpos); + if (oi->typep) + *oi->typep = ptot; + if (oi->typename) { + const char *tn = typename(ptot); + if (tn) + strbuf_addstr(oi->typename, tn); + } + if (ptot < 0) { + type = OBJ_BAD; + goto out; + } + } + + if (oi->delta_base_sha1) { + if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { + const unsigned char *base; + + base = get_delta_base_sha1(p, &w_curs, curpos, + type, obj_offset); + if (!base) { + type = OBJ_BAD; + goto out; + } + + hashcpy(oi->delta_base_sha1, base); + } else + hashclr(oi->delta_base_sha1); + } + +out: + unuse_pack(&w_curs); + return type; +} + +static void *unpack_compressed_entry(struct packed_git *p, + struct pack_window **w_curs, + off_t curpos, + unsigned long size) +{ + int st; + git_zstream stream; + unsigned char *buffer, *in; + + buffer = xmallocz_gently(size); + if (!buffer) + return NULL; + memset(&stream, 0, sizeof(stream)); + stream.next_out = buffer; + stream.avail_out = size + 1; + + git_inflate_init(&stream); + do { + in = use_pack(p, w_curs, curpos, &stream.avail_in); + stream.next_in = in; + st = git_inflate(&stream, Z_FINISH); + if (!stream.avail_out) + break; /* the payload is larger than it should be */ + curpos += stream.next_in - in; + } while (st == Z_OK || st == Z_BUF_ERROR); + git_inflate_end(&stream); + if ((st != Z_STREAM_END) || stream.total_out != size) { + free(buffer); + return NULL; + } + + return buffer; +} + static void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size); @@ -2670,8 +2675,10 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, free(external_base); } - *final_type = type; - *final_size = size; + if (final_type) + *final_type = type; + if (final_size) + *final_size = size; unuse_pack(&w_curs); @@ -2905,6 +2912,7 @@ static int sha1_loose_object_info(const unsigned char *sha1, git_zstream stream; char hdr[32]; struct strbuf hdrbuf = STRBUF_INIT; + unsigned long size_scratch; if (oi->delta_base_sha1) hashclr(oi->delta_base_sha1); @@ -2917,7 +2925,7 @@ static int sha1_loose_object_info(const unsigned char *sha1, * return value implicitly indicates whether the * object even exists. */ - if (!oi->typep && !oi->typename && !oi->sizep) { + if (!oi->typep && !oi->typename && !oi->sizep && !oi->contentp) { const char *path; struct stat st; if (stat_sha1_file(sha1, &st, &path) < 0) @@ -2930,9 +2938,13 @@ static int sha1_loose_object_info(const unsigned char *sha1, map = map_sha1_file(sha1, &mapsize); if (!map) return -1; + + if (!oi->sizep) + oi->sizep = &size_scratch; + if (oi->disk_sizep) *oi->disk_sizep = mapsize; - if ((flags & LOOKUP_UNKNOWN_OBJECT)) { + if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) { if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0) status = error("unable to unpack %s header with --allow-unknown-type", sha1_to_hex(sha1)); @@ -2947,36 +2959,52 @@ static int sha1_loose_object_info(const unsigned char *sha1, sha1_to_hex(sha1)); } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0) status = error("unable to parse %s header", sha1_to_hex(sha1)); - git_inflate_end(&stream); + + if (status >= 0 && oi->contentp) + *oi->contentp = unpack_sha1_rest(&stream, hdr, + *oi->sizep, sha1); + else + git_inflate_end(&stream); + munmap(map, mapsize); if (status && oi->typep) *oi->typep = status; + if (oi->sizep == &size_scratch) + oi->sizep = NULL; strbuf_release(&hdrbuf); return (status < 0) ? status : 0; } int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags) { - struct cached_object *co; + static struct object_info blank_oi = OBJECT_INFO_INIT; struct pack_entry e; int rtype; - enum object_type real_type; - const unsigned char *real = lookup_replace_object_extended(sha1, flags); - - co = find_cached_object(real); - if (co) { - if (oi->typep) - *(oi->typep) = co->type; - if (oi->sizep) - *(oi->sizep) = co->size; - if (oi->disk_sizep) - *(oi->disk_sizep) = 0; - if (oi->delta_base_sha1) - hashclr(oi->delta_base_sha1); - if (oi->typename) - strbuf_addstr(oi->typename, typename(co->type)); - oi->whence = OI_CACHED; - return 0; + const unsigned char *real = (flags & OBJECT_INFO_LOOKUP_REPLACE) ? + lookup_replace_object(sha1) : + sha1; + + if (!oi) + oi = &blank_oi; + + if (!(flags & OBJECT_INFO_SKIP_CACHED)) { + struct cached_object *co = find_cached_object(real); + if (co) { + if (oi->typep) + *(oi->typep) = co->type; + if (oi->sizep) + *(oi->sizep) = co->size; + if (oi->disk_sizep) + *(oi->disk_sizep) = 0; + if (oi->delta_base_sha1) + hashclr(oi->delta_base_sha1); + if (oi->typename) + strbuf_addstr(oi->typename, typename(co->type)); + if (oi->contentp) + *oi->contentp = xmemdupz(co->buf, co->size); + oi->whence = OI_CACHED; + return 0; + } } if (!find_pack_entry(real, &e)) { @@ -2987,23 +3015,25 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, } /* Not a loose object; someone else may have just packed it. */ - reprepare_packed_git(); - if (!find_pack_entry(real, &e)) + if (flags & OBJECT_INFO_QUICK) { return -1; + } else { + reprepare_packed_git(); + if (!find_pack_entry(real, &e)) + return -1; + } } - /* - * packed_object_info() does not follow the delta chain to - * find out the real type, unless it is given oi->typep. - */ - if (oi->typename && !oi->typep) - oi->typep = &real_type; + if (oi == &blank_oi) + /* + * We know that the caller doesn't actually need the + * information below, so return early. + */ + return 0; rtype = packed_object_info(e.p, e.offset, oi); if (rtype < 0) { mark_bad_packed_object(e.p, real); - if (oi->typep == &real_type) - oi->typep = NULL; return sha1_object_info_extended(real, oi, 0); } else if (in_delta_base_cache(e.p, e.offset)) { oi->whence = OI_DBCACHED; @@ -3014,10 +3044,6 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA || rtype == OBJ_OFS_DELTA); } - if (oi->typename) - strbuf_addstr(oi->typename, typename(*oi->typep)); - if (oi->typep == &real_type) - oi->typep = NULL; return 0; } @@ -3030,7 +3056,8 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) oi.typep = &type; oi.sizep = sizep; - if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0) + if (sha1_object_info_extended(sha1, &oi, + OBJECT_INFO_LOOKUP_REPLACE) < 0) return -1; return type; } @@ -3080,28 +3107,15 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type, static void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size) { - unsigned long mapsize; - void *map, *buf; - struct cached_object *co; - - co = find_cached_object(sha1); - if (co) { - *type = co->type; - *size = co->size; - return xmemdupz(co->buf, co->size); - } + struct object_info oi = OBJECT_INFO_INIT; + void *content; + oi.typep = type; + oi.sizep = size; + oi.contentp = &content; - buf = read_packed_sha1(sha1, type, size); - if (buf) - return buf; - map = map_sha1_file(sha1, &mapsize); - if (map) { - buf = unpack_sha1_file(map, mapsize, type, size, sha1); - munmap(map, mapsize); - return buf; - } - reprepare_packed_git(); - return read_packed_sha1(sha1, type, size); + if (sha1_object_info_extended(sha1, &oi, 0) < 0) + return NULL; + return content; } /* @@ -3112,13 +3126,14 @@ static void *read_object(const unsigned char *sha1, enum object_type *type, void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, - unsigned flag) + int lookup_replace) { void *data; const struct packed_git *p; const char *path; struct stat st; - const unsigned char *repl = lookup_replace_object_extended(sha1, flag); + const unsigned char *repl = lookup_replace ? lookup_replace_object(sha1) + : sha1; errno = 0; data = read_object(repl, type, size); @@ -3479,18 +3494,10 @@ int has_sha1_pack(const unsigned char *sha1) int has_sha1_file_with_flags(const unsigned char *sha1, int flags) { - struct pack_entry e; - if (!startup_info->have_repository) return 0; - if (find_pack_entry(sha1, &e)) - return 1; - if (has_loose_object(sha1)) - return 1; - if (flags & HAS_SHA1_QUICK) - return 0; - reprepare_packed_git(); - return find_pack_entry(sha1, &e); + return sha1_object_info_extended(sha1, NULL, + flags | OBJECT_INFO_SKIP_CACHED) >= 0; } int has_object_file(const struct object_id *oid) @@ -3735,22 +3742,32 @@ void assert_sha1_type(const unsigned char *sha1, enum object_type expect) typename(expect)); } -static int for_each_file_in_obj_subdir(int subdir_nr, - struct strbuf *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data) +int for_each_file_in_obj_subdir(unsigned int subdir_nr, + struct strbuf *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) { - size_t baselen = path->len; - DIR *dir = opendir(path->buf); + size_t origlen, baselen; + DIR *dir; struct dirent *de; int r = 0; + if (subdir_nr > 0xff) + BUG("invalid loose object subdirectory: %x", subdir_nr); + + origlen = path->len; + strbuf_complete(path, '/'); + strbuf_addf(path, "%02x", subdir_nr); + baselen = path->len; + + dir = opendir(path->buf); if (!dir) { - if (errno == ENOENT) - return 0; - return error_errno("unable to open %s", path->buf); + if (errno != ENOENT) + r = error_errno("unable to open %s", path->buf); + strbuf_setlen(path, origlen); + return r; } while ((de = readdir(dir))) { @@ -3788,6 +3805,8 @@ static int for_each_file_in_obj_subdir(int subdir_nr, if (!r && subdir_cb) r = subdir_cb(subdir_nr, path->buf, data); + strbuf_setlen(path, origlen); + return r; } @@ -3797,15 +3816,12 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path, each_loose_subdir_fn subdir_cb, void *data) { - size_t baselen = path->len; int r = 0; int i; for (i = 0; i < 256; i++) { - strbuf_addf(path, "/%02x", i); r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb, subdir_cb, data); - strbuf_setlen(path, baselen); if (r) break; } diff --git a/sha1_name.c b/sha1_name.c index d2d732c19b..74fcb6d788 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -78,10 +78,19 @@ static void update_candidates(struct disambiguate_state *ds, const struct object /* otherwise, current can be discarded and candidate is still good */ } +static int append_loose_object(const struct object_id *oid, const char *path, + void *data) +{ + oid_array_append(data, oid); + return 0; +} + +static int match_sha(unsigned, const unsigned char *, const unsigned char *); + static void find_short_object_filename(struct disambiguate_state *ds) { + int subdir_nr = ds->bin_pfx.hash[0]; struct alternate_object_database *alt; - char hex[GIT_MAX_HEXSZ]; static struct alternate_object_database *fakeent; if (!fakeent) { @@ -96,29 +105,29 @@ static void find_short_object_filename(struct disambiguate_state *ds) } fakeent->next = alt_odb_list; - xsnprintf(hex, sizeof(hex), "%.2s", ds->hex_pfx); for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) { - struct strbuf *buf = alt_scratch_buf(alt); - struct dirent *de; - DIR *dir; - - strbuf_addf(buf, "%.2s/", ds->hex_pfx); - dir = opendir(buf->buf); - if (!dir) - continue; + int pos; - while (!ds->ambiguous && (de = readdir(dir)) != NULL) { - struct object_id oid; + if (!alt->loose_objects_subdir_seen[subdir_nr]) { + struct strbuf *buf = alt_scratch_buf(alt); + for_each_file_in_obj_subdir(subdir_nr, buf, + append_loose_object, + NULL, NULL, + &alt->loose_objects_cache); + alt->loose_objects_subdir_seen[subdir_nr] = 1; + } - if (strlen(de->d_name) != GIT_SHA1_HEXSZ - 2) - continue; - if (memcmp(de->d_name, ds->hex_pfx + 2, ds->len - 2)) - continue; - memcpy(hex + 2, de->d_name, GIT_SHA1_HEXSZ - 2); - if (!get_oid_hex(hex, &oid)) - update_candidates(ds, &oid); + pos = oid_array_lookup(&alt->loose_objects_cache, &ds->bin_pfx); + if (pos < 0) + pos = -1 - pos; + while (!ds->ambiguous && pos < alt->loose_objects_cache.nr) { + const struct object_id *oid; + oid = alt->loose_objects_cache.oid + pos; + if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash)) + break; + update_candidates(ds, oid); + pos++; } - closedir(dir); } } @@ -480,10 +489,9 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len) * We now know we have on the order of 2^len objects, which * expects a collision at 2^(len/2). But we also care about hex * chars, not bits, and there are 4 bits per hex. So all - * together we need to divide by 2; but we also want to round - * odd numbers up, hence adding one before dividing. + * together we need to divide by 2 and round up. */ - len = (len + 1) / 2; + len = DIV_ROUND_UP(len, 2); /* * For very small repos, we stick with our regular fallback. */ diff --git a/sha1collisiondetection b/sha1collisiondetection new file mode 160000 +Subproject 19d97bf5af05312267c2e874ee6bcf584d9e968 diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c index facea1bb56..25eded1399 100644 --- a/sha1dc/sha1.c +++ b/sha1dc/sha1.c @@ -10,6 +10,9 @@ #include <memory.h> #include <stdio.h> #include <stdlib.h> +#ifdef __unix__ +#include <sys/types.h> /* make sure macros like _BIG_ENDIAN visible */ +#endif #endif #ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C @@ -23,6 +26,13 @@ #include "sha1.h" #include "ubc_check.h" +#if (defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ + defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \ + defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \ + defined(__386) || defined(_M_X64) || defined(_M_AMD64)) +#define SHA1DC_ON_INTEL_LIKE_PROCESSOR +#endif /* Because Little-Endian architectures are most common, @@ -32,29 +42,70 @@ If you are compiling on a big endian platform and your compiler does not define one of these, you will have to add whatever macros your tool chain defines to indicate Big-Endianness. */ -#ifdef SHA1DC_BIGENDIAN -#undef SHA1DC_BIGENDIAN -#endif -#if (defined(_BYTE_ORDER) || defined(__BYTE_ORDER) || defined(__BYTE_ORDER__)) - -#if ((defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) || \ - (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \ - (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) ) +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) +/* + * Should detect Big Endian under GCC since at least 4.6.0 (gcc svn + * rev #165881). See + * https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html + * + * This also works under clang since 3.2, it copied the GCC-ism. See + * clang.git's 3b198a97d2 ("Preprocessor: add __BYTE_ORDER__ + * predefined macro", 2012-07-27) + */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define SHA1DC_BIGENDIAN #endif -#else - -#if (defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN) || defined(__BIG_ENDIAN__) || \ - defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ - defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \ - defined(__sparc)) +/* Not under GCC-alike */ +#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) +/* + * Should detect Big Endian under glibc.git since 14245eb70e ("entered + * into RCS", 1992-11-25). Defined in <endian.h> which will have been + * brought in by standard headers. See glibc.git and + * https://sourceforge.net/p/predef/wiki/Endianness/ + */ +#if __BYTE_ORDER == __BIG_ENDIAN #define SHA1DC_BIGENDIAN #endif +/* Not under GCC-alike or glibc */ +#elif defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) +/* + * *BSD and newlib (embeded linux, cygwin, etc). + * the defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) part prevents + * this condition from matching with Solaris/sparc. + * (Solaris defines only one endian macro) + */ +#if _BYTE_ORDER == _BIG_ENDIAN +#define SHA1DC_BIGENDIAN #endif +/* Not under GCC-alike or glibc or *BSD or newlib */ +#elif (defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \ + defined(__sparc)) +/* + * Should define Big Endian for a whitelist of known processors. See + * https://sourceforge.net/p/predef/wiki/Endianness/ and + * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html + */ +#define SHA1DC_BIGENDIAN + +/* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> */ +#elif defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR) +/* + * As a last resort before we do anything else we're not 100% sure + * about below, we blacklist specific processors here. We could add + * more, see e.g. https://wiki.debian.org/ArchitectureSpecificsMemo + */ +#else /* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <processor blacklist> */ + +/* We do nothing more here for now */ +/*#error "Uncomment this to see if you fall through all the detection"*/ + +#endif /* Big Endian detection */ + #if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN)) #undef SHA1DC_BIGENDIAN #endif @@ -63,15 +114,8 @@ #endif /*ENDIANNESS SELECTION*/ -#if (defined SHA1DC_FORCE_UNALIGNED_ACCESS || \ - defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ - defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \ - defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \ - defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \ - defined(__386) || defined(_M_X64) || defined(_M_AMD64)) - +#if defined(SHA1DC_FORCE_UNALIGNED_ACCESS) || defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR) #define SHA1DC_ALLOW_UNALIGNED_ACCESS - #endif /*UNALIGNMENT DETECTION*/ @@ -918,7 +962,7 @@ static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], co #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable: 4127) /* Complier complains about the checks in the above macro being constant. */ +#pragma warning(disable: 4127) /* Compiler complains about the checks in the above macro being constant. */ #endif #ifdef DOSTORESTATE0 @@ -443,7 +443,7 @@ struct paint_info { static uint32_t *paint_alloc(struct paint_info *info) { - unsigned nr = (info->nr_bits + 31) / 32; + unsigned nr = DIV_ROUND_UP(info->nr_bits, 32); unsigned size = nr * sizeof(uint32_t); void *p; if (!info->pool_count || size > info->end - info->free) { @@ -471,7 +471,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid, { unsigned int i, nr; struct commit_list *head = NULL; - int bitmap_nr = (info->nr_bits + 31) / 32; + int bitmap_nr = DIV_ROUND_UP(info->nr_bits, 32); size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr); struct commit *c = lookup_commit_reference_gently(oid, 1); uint32_t *tmp; /* to be freed before return */ @@ -611,7 +611,7 @@ void assign_shallow_commits_to_refs(struct shallow_info *info, paint_down(&pi, ref->oid + i, i); if (used) { - int bitmap_size = ((pi.nr_bits + 31) / 32) * sizeof(uint32_t); + int bitmap_size = DIV_ROUND_UP(pi.nr_bits, 32) * sizeof(uint32_t); memset(used, 0, sizeof(*used) * info->shallow->nr); for (i = 0; i < nr_shallow; i++) { const struct commit *c = lookup_commit(&oid[shallow[i]]); @@ -672,7 +672,7 @@ static void post_assign_shallow(struct shallow_info *info, struct commit *c; uint32_t **bitmap; int dst, i, j; - int bitmap_nr = (info->ref->nr + 31) / 32; + int bitmap_nr = DIV_ROUND_UP(info->ref->nr, 32); struct commit_array ca; trace_printf_key(&trace_shallow, "shallow: post_assign_shallow\n"); @@ -779,7 +779,7 @@ char *xstrfmt(const char *fmt, ...) } void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm, - int tz_offset, const char *tz_name) + int tz_offset, int suppress_tz_name) { struct strbuf munged_fmt = STRBUF_INIT; size_t hint = 128; @@ -808,8 +808,7 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm, fmt++; break; case 'Z': - if (tz_name) { - strbuf_addstr(&munged_fmt, tz_name); + if (suppress_tz_name) { fmt++; break; } @@ -334,14 +334,15 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); /** * Add the time specified by `tm`, as formatted by `strftime`. - * `tz_name` is used to expand %Z internally unless it's NULL. * `tz_offset` is in decimal hhmm format, e.g. -600 means six hours west * of Greenwich, and it's used to expand %z internally. However, tokens * with modifiers (e.g. %Ez) are passed to `strftime`. + * `suppress_tz_name`, when set, expands %Z internally to the empty + * string rather than passing it to `strftime`. */ extern void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm, int tz_offset, - const char *tz_name); + int suppress_tz_name); /** * Read a given size of data from a FILE* pointer to the buffer. diff --git a/submodule-config.c b/submodule-config.c index d8f8d5ea32..37cfcceb95 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "repository.h" #include "config.h" #include "submodule-config.h" #include "submodule.h" @@ -15,6 +16,7 @@ struct submodule_cache { struct hashmap for_path; struct hashmap for_name; + unsigned initialized:1; }; /* @@ -31,9 +33,6 @@ enum lookup_type { lookup_path }; -static struct submodule_cache the_submodule_cache; -static int is_cache_init; - static int config_path_cmp(const struct submodule_entry *a, const struct submodule_entry *b, const void *unused) @@ -50,10 +49,16 @@ static int config_name_cmp(const struct submodule_entry *a, hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1); } -static void cache_init(struct submodule_cache *cache) +static struct submodule_cache *submodule_cache_alloc(void) +{ + return xcalloc(1, sizeof(struct submodule_cache)); +} + +static void submodule_cache_init(struct submodule_cache *cache) { hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, 0); hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, 0); + cache->initialized = 1; } static void free_one_config(struct submodule_entry *entry) @@ -65,11 +70,14 @@ static void free_one_config(struct submodule_entry *entry) free(entry->config); } -static void cache_free(struct submodule_cache *cache) +static void submodule_cache_clear(struct submodule_cache *cache) { struct hashmap_iter iter; struct submodule_entry *entry; + if (!cache->initialized) + return; + /* * We iterate over the name hash here to be symmetric with the * allocation of struct submodule entries. Each is allocated by @@ -81,6 +89,13 @@ static void cache_free(struct submodule_cache *cache) hashmap_free(&cache->for_path, 1); hashmap_free(&cache->for_name, 1); + cache->initialized = 0; +} + +void submodule_cache_free(struct submodule_cache *cache) +{ + submodule_cache_clear(cache); + free(cache); } static unsigned int hash_sha1_string(const unsigned char *sha1, @@ -494,43 +509,62 @@ out: return submodule; } -static void ensure_cache_init(void) +static void submodule_cache_check_init(struct repository *repo) { - if (is_cache_init) + if (repo->submodule_cache && repo->submodule_cache->initialized) return; - cache_init(&the_submodule_cache); - is_cache_init = 1; + if (!repo->submodule_cache) + repo->submodule_cache = submodule_cache_alloc(); + + submodule_cache_init(repo->submodule_cache); } -int parse_submodule_config_option(const char *var, const char *value) +int submodule_config_option(struct repository *repo, + const char *var, const char *value) { struct parse_config_parameter parameter; - parameter.cache = &the_submodule_cache; + + submodule_cache_check_init(repo); + + parameter.cache = repo->submodule_cache; parameter.treeish_name = NULL; parameter.gitmodules_sha1 = null_sha1; parameter.overwrite = 1; - ensure_cache_init(); return parse_config(var, value, ¶meter); } +int parse_submodule_config_option(const char *var, const char *value) +{ + return submodule_config_option(the_repository, var, value); +} + const struct submodule *submodule_from_name(const unsigned char *treeish_name, const char *name) { - ensure_cache_init(); - return config_from(&the_submodule_cache, treeish_name, name, lookup_name); + submodule_cache_check_init(the_repository); + return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name); } const struct submodule *submodule_from_path(const unsigned char *treeish_name, const char *path) { - ensure_cache_init(); - return config_from(&the_submodule_cache, treeish_name, path, lookup_path); + submodule_cache_check_init(the_repository); + return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path); +} + +const struct submodule *submodule_from_cache(struct repository *repo, + const unsigned char *treeish_name, + const char *key) +{ + submodule_cache_check_init(repo); + return config_from(repo->submodule_cache, treeish_name, + key, lookup_path); } void submodule_free(void) { - cache_free(&the_submodule_cache); - is_cache_init = 0; + if (the_repository->submodule_cache) + submodule_cache_clear(the_repository->submodule_cache); } diff --git a/submodule-config.h b/submodule-config.h index d434ecdb45..bc45a25e85 100644 --- a/submodule-config.h +++ b/submodule-config.h @@ -22,14 +22,24 @@ struct submodule { int recommend_shallow; }; +struct submodule_cache; +struct repository; + +extern void submodule_cache_free(struct submodule_cache *cache); + extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg); extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg); extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg); extern int parse_submodule_config_option(const char *var, const char *value); +extern int submodule_config_option(struct repository *repo, + const char *var, const char *value); extern const struct submodule *submodule_from_name( const unsigned char *commit_or_tree, const char *name); extern const struct submodule *submodule_from_path( const unsigned char *commit_or_tree, const char *path); +extern const struct submodule *submodule_from_cache(struct repository *repo, + const unsigned char *treeish_name, + const char *key); extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1, unsigned char *gitmodules_sha1, struct strbuf *rev); diff --git a/submodule.c b/submodule.c index da0b805493..da2b484879 100644 --- a/submodule.c +++ b/submodule.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "repository.h" #include "config.h" #include "submodule-config.h" #include "submodule.h" @@ -255,6 +256,20 @@ void gitmodules_config(void) } } +static int gitmodules_cb(const char *var, const char *value, void *data) +{ + struct repository *repo = data; + return submodule_config_option(repo, var, value); +} + +void repo_read_gitmodules(struct repository *repo) +{ + char *gitmodules_path = repo_worktree_path(repo, ".gitmodules"); + + git_config_from_file(gitmodules_cb, gitmodules_path, repo); + free(gitmodules_path); +} + void gitmodules_config_sha1(const unsigned char *commit_sha1) { struct strbuf rev = STRBUF_INIT; @@ -268,21 +283,17 @@ void gitmodules_config_sha1(const unsigned char *commit_sha1) } /* - * NEEDSWORK: With the addition of different configuration options to determine - * if a submodule is of interests, the validity of this function's name comes - * into question. Once the dust has settled and more concrete terminology is - * decided upon, come up with a more proper name for this function. One - * potential candidate could be 'is_submodule_active()'. - * * Determine if a submodule has been initialized at a given 'path' */ -int is_submodule_initialized(const char *path) +int is_submodule_active(struct repository *repo, const char *path) { int ret = 0; char *key = NULL; char *value = NULL; const struct string_list *sl; - const struct submodule *module = submodule_from_path(null_sha1, path); + const struct submodule *module; + + module = submodule_from_cache(repo, null_sha1, path); /* early return if there isn't a path->module mapping */ if (!module) @@ -290,14 +301,14 @@ int is_submodule_initialized(const char *path) /* submodule.<name>.active is set */ key = xstrfmt("submodule.%s.active", module->name); - if (!git_config_get_bool(key, &ret)) { + if (!repo_config_get_bool(repo, key, &ret)) { free(key); return ret; } free(key); /* submodule.active is set */ - sl = git_config_get_value_multi("submodule.active"); + sl = repo_config_get_value_multi(repo, "submodule.active"); if (sl) { struct pathspec ps; struct argv_array args = ARGV_ARRAY_INIT; @@ -317,7 +328,7 @@ int is_submodule_initialized(const char *path) /* fallback to checking if the URL is set */ key = xstrfmt("submodule.%s.url", module->name); - ret = !git_config_get_string(key, &value); + ret = !repo_config_get_string(repo, key, &value); free(value); free(key); @@ -846,9 +857,9 @@ static int submodule_has_commits(const char *path, struct oid_array *commits) int has_commit = 1; /* - * Perform a cheap, but incorrect check for the existance of 'commits'. + * Perform a cheap, but incorrect check for the existence of 'commits'. * This is done by adding the submodule's object store to the in-core - * object store, and then querying for each commit's existance. If we + * object store, and then querying for each commit's existence. If we * do not have the commit object anywhere, there is no chance we have * it in the object store of the correct submodule and have it * reachable from a ref, so we can fail early without spawning rev-list @@ -1517,7 +1528,7 @@ int submodule_move_head(const char *path, const struct submodule *sub; int *error_code_ptr, error_code; - if (!is_submodule_initialized(path)) + if (!is_submodule_active(the_repository, path)) return 0; if (flags & SUBMODULE_MOVE_HEAD_FORCE) diff --git a/submodule.h b/submodule.h index cbe5c1726f..623ce6ad77 100644 --- a/submodule.h +++ b/submodule.h @@ -1,6 +1,7 @@ #ifndef SUBMODULE_H #define SUBMODULE_H +struct repository; struct diff_options; struct argv_array; struct oid_array; @@ -46,8 +47,9 @@ int option_parse_recurse_submodules_worktree_updater(const struct option *opt, const char *arg, int unset); void load_submodule_cache(void); extern void gitmodules_config(void); +extern void repo_read_gitmodules(struct repository *repo); extern void gitmodules_config_sha1(const unsigned char *commit_sha1); -extern int is_submodule_initialized(const char *path); +extern int is_submodule_active(struct repository *repo, const char *path); /* * Determine if a submodule has been populated at a given 'path' by checking if * the <path>/.git resolves to a valid git repository. diff --git a/t/helper/test-strcmp-offset.c b/t/helper/test-strcmp-offset.c index 4a45a54e92..e159c9a127 100644 --- a/t/helper/test-strcmp-offset.c +++ b/t/helper/test-strcmp-offset.c @@ -11,7 +11,7 @@ int cmd_main(int argc, const char **argv) result = strcmp_offset(argv[1], argv[2], &offset); /* - * Because differnt CRTs behave differently, only rely on signs + * Because different CRTs behave differently, only rely on signs * of the result values. */ result = (result < 0 ? -1 : diff --git a/t/helper/test-wildmatch.c b/t/helper/test-wildmatch.c index 52be876fed..921d7b3e7e 100644 --- a/t/helper/test-wildmatch.c +++ b/t/helper/test-wildmatch.c @@ -11,11 +11,11 @@ int cmd_main(int argc, const char **argv) argv[i] += 3; } if (!strcmp(argv[1], "wildmatch")) - return !!wildmatch(argv[3], argv[2], WM_PATHNAME, NULL); + return !!wildmatch(argv[3], argv[2], WM_PATHNAME); else if (!strcmp(argv[1], "iwildmatch")) - return !!wildmatch(argv[3], argv[2], WM_PATHNAME | WM_CASEFOLD, NULL); + return !!wildmatch(argv[3], argv[2], WM_PATHNAME | WM_CASEFOLD); else if (!strcmp(argv[1], "pathmatch")) - return !!wildmatch(argv[3], argv[2], 0, NULL); + return !!wildmatch(argv[3], argv[2], 0); else return 1; } diff --git a/t/perf/p4205-log-pretty-formats.sh b/t/perf/p4205-log-pretty-formats.sh new file mode 100755 index 0000000000..7c26f4f337 --- /dev/null +++ b/t/perf/p4205-log-pretty-formats.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +test_description='Tests the performance of various pretty format placeholders' + +. ./perf-lib.sh + +test_perf_default_repo + +for format in %H %h %T %t %P %p %h-%h-%h +do + test_perf "log with $format" " + git log --format=\"$format\" >/dev/null + " +done + +test_done diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index 1312004f8c..dfece751b5 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -19,10 +19,6 @@ test_expect_success 'shared = 0400 (faulty permission u-w)' ' ) ' -modebits () { - ls -l "$1" | sed -e 's|^\(..........\).*|\1|' -} - for u in 002 022 do test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" ' @@ -88,7 +84,7 @@ do rm -f .git/info/refs && git update-server-info && - actual="$(modebits .git/info/refs)" && + actual="$(test_modebits .git/info/refs)" && verbose test "x$actual" = "x-$y" ' @@ -98,7 +94,7 @@ do rm -f .git/info/refs && git update-server-info && - actual="$(modebits .git/info/refs)" && + actual="$(test_modebits .git/info/refs)" && verbose test "x$actual" = "x-$x" ' @@ -111,7 +107,7 @@ test_expect_success POSIXPERM 'info/refs respects umask in unshared repo' ' umask 002 && git update-server-info && echo "-rw-rw-r--" >expect && - modebits .git/info/refs >actual && + test_modebits .git/info/refs >actual && test_cmp expect actual ' @@ -177,7 +173,7 @@ test_expect_success POSIXPERM 'remote init does not use config from cwd' ' umask 0022 && git init --bare child.git && echo "-rw-r--r--" >expect && - modebits child.git/config >actual && + test_modebits child.git/config >actual && test_cmp expect actual ' @@ -187,7 +183,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (local)' ' echo whatever >templates/foo && git init --template=templates && echo "-rw-rw-rw-" >expect && - modebits .git/foo >actual && + test_modebits .git/foo >actual && test_cmp expect actual ' @@ -198,7 +194,7 @@ test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)' test_path_is_missing child.git/foo && git init --bare --template=../templates child.git && echo "-rw-rw-rw-" >expect && - modebits child.git/foo >actual && + test_modebits child.git/foo >actual && test_cmp expect actual ' @@ -209,7 +205,7 @@ test_expect_success POSIXPERM 'template can set core.sharedrepository' ' cp .git/config templates/config && git init --bare --template=../templates child.git && echo "-rw-rw-rw-" >expect && - modebits child.git/HEAD >actual && + test_modebits child.git/HEAD >actual && test_cmp expect actual ' diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh index 6ac7734d79..b9cb76654b 100755 --- a/t/t1411-reflog-show.sh +++ b/t/t1411-reflog-show.sh @@ -171,4 +171,14 @@ test_expect_success 'reflog exists works' ' ! git reflog exists refs/heads/nonexistent ' +# The behavior with two reflogs is buggy and the output is in flux; for now +# we're just checking that the program works at all without segfaulting. +test_expect_success 'showing multiple reflogs works' ' + git log -g HEAD HEAD >actual +' + +test_expect_success 'showing multiple reflogs with an old date' ' + git log -g HEAD@{1979-01-01} HEAD >actual +' + test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index adf0bc88ba..bb89e1a5db 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -573,7 +573,7 @@ test_expect_success 'fsck --name-objects' ' remove_object $(git rev-parse julius:caesar.t) && test_must_fail git fsck --name-objects >out && tree=$(git rev-parse --verify julius:) && - grep "$tree (\(refs/heads/master\|HEAD\)@{[0-9]*}:" out + egrep "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out ) ' diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index af3ec0da5a..22f69a410b 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -370,4 +370,34 @@ test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now" test $(ls .git/sharedindex.* | wc -l) -le 2 ' +while read -r mode modebits +do + test_expect_success POSIXPERM "split index respects core.sharedrepository $mode" ' + # Remove existing shared index files + git config core.splitIndex false && + git update-index --force-remove one && + rm -f .git/sharedindex.* && + # Create one new shared index file + git config core.sharedrepository "$mode" && + git config core.splitIndex true && + : >one && + git update-index --add one && + echo "$modebits" >expect && + test_modebits .git/index >actual && + test_cmp expect actual && + shared=$(ls .git/sharedindex.*) && + case "$shared" in + *" "*) + # we have more than one??? + false ;; + *) + test_modebits "$shared" >actual && + test_cmp expect actual ;; + esac + ' +done <<\EOF +0666 -rw-rw-rw- +0642 -rw-r---w- +EOF + test_done diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index 84a9028c43..1bdf38e80d 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -129,10 +129,10 @@ test_expect_success 'cache-tree does skip dir that becomes empty' ' ) ' -test_expect_success 'commit: ita entries ignored in empty intial commit check' ' - git init empty-intial-commit && +test_expect_success 'commit: ita entries ignored in empty initial commit check' ' + git init empty-initial-commit && ( - cd empty-intial-commit && + cd empty-initial-commit && : >one && git add -N one && test_must_fail git commit -m nothing-new-here diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh index ebb956fd16..318b5bce7e 100755 --- a/t/t3007-ls-files-recurse-submodules.sh +++ b/t/t3007-ls-files-recurse-submodules.sh @@ -135,6 +135,45 @@ test_expect_success '--recurse-submodules and pathspecs setup' ' test_cmp expect actual ' +test_expect_success 'inactive submodule' ' + test_when_finished "git config --bool submodule.submodule.active true" && + test_when_finished "git -C submodule config --bool submodule.subsub.active true" && + git config --bool submodule.submodule.active "false" && + + cat >expect <<-\EOF && + .gitmodules + a + b/b + h.txt + sib/file + sub/file + submodule + EOF + + git ls-files --recurse-submodules >actual && + test_cmp expect actual && + + git config --bool submodule.submodule.active "true" && + git -C submodule config --bool submodule.subsub.active "false" && + + cat >expect <<-\EOF && + .gitmodules + a + b/b + h.txt + sib/file + sub/file + submodule/.gitmodules + submodule/c + submodule/f.TXT + submodule/g.txt + submodule/subsub + EOF + + git ls-files --recurse-submodules >actual && + test_cmp expect actual +' + test_expect_success '--recurse-submodules and pathspecs' ' cat >expect <<-\EOF && h.txt diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 8fd70d3aa2..163a14a1c2 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -235,7 +235,7 @@ pathmatch 1 abcXdefXghi '*X*i' pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i' pathmatch 1 ab/cXd/efXg/hi '*Xg*i' -# Case-sensitivy features +# Case-sensitivity features match 0 x 'a' '[A-Z]' match 1 x 'A' '[A-Z]' match 0 x 'A' '[a-z]' diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 48d152b9a9..dd37ac47c5 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -162,6 +162,17 @@ test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' grep "^0\{40\}.*$msg$" .git/logs/HEAD ' +test_expect_success 'resulting reflog can be shown by log -g' ' + oid=$(git rev-parse HEAD) && + cat >expect <<-EOF && + HEAD@{0} $oid $msg + HEAD@{1} $oid $msg + HEAD@{2} $oid checkout: moving from foo to baz + EOF + git log -g --format="%gd %H %gs" -3 HEAD >actual && + test_cmp expect actual +' + test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' ' git checkout master && git worktree add -b baz bazdir && diff --git a/t/t3205-branch-color.sh b/t/t3205-branch-color.sh new file mode 100755 index 0000000000..9343550f50 --- /dev/null +++ b/t/t3205-branch-color.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +test_description='basic branch output coloring' +. ./test-lib.sh + +test_expect_success 'set up some sample branches' ' + test_commit foo && + git update-ref refs/remotes/origin/master HEAD && + git update-ref refs/heads/other HEAD +' + +# choose non-default colors to make sure config +# is taking effect +test_expect_success 'set up some color config' ' + git config color.branch always && + git config color.branch.local blue && + git config color.branch.remote yellow && + git config color.branch.current cyan +' + +test_expect_success 'regular output shows colors' ' + cat >expect <<-\EOF && + * <CYAN>master<RESET> + <BLUE>other<RESET> + <YELLOW>remotes/origin/master<RESET> + EOF + git branch -a >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + +test_expect_success 'verbose output shows colors' ' + oid=$(git rev-parse --short HEAD) && + cat >expect <<-EOF && + * <CYAN>master <RESET> $oid foo + <BLUE>other <RESET> $oid foo + <YELLOW>remotes/origin/master<RESET> $oid foo + EOF + git branch -v -a >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 5bd0275930..37821d2454 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -169,6 +169,13 @@ test_expect_success 'reflog for the branch shows state before rebase' ' test $(git rev-parse branch1@{1}) = $(git rev-parse original-branch1) ' +test_expect_success 'reflog for the branch shows correct finish message' ' + printf "rebase -i (finish): refs/heads/branch1 onto %s\n" \ + "$(git rev-parse branch2)" >expected && + git log -g --pretty=%gs -1 refs/heads/branch1 >actual && + test_cmp expected actual +' + test_expect_success 'exchange two commits' ' set_fake_editor && FAKE_LINES="2 1" git rebase -i HEAD~2 && diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh index ab8a63e8d6..e243700660 100755 --- a/t/t3420-rebase-autostash.sh +++ b/t/t3420-rebase-autostash.sh @@ -33,7 +33,123 @@ test_expect_success setup ' git commit -m "related commit" ' -testrebase() { +create_expected_success_am () { + cat >expected <<-EOF + $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) + HEAD is now at $(git rev-parse --short feature-branch) third commit + First, rewinding head to replay your work on top of it... + Applying: second commit + Applying: third commit + Applied autostash. + EOF +} + +create_expected_success_interactive () { + q_to_cr >expected <<-EOF + $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) + HEAD is now at $(git rev-parse --short feature-branch) third commit + Rebasing (1/2)QRebasing (2/2)QApplied autostash. + Successfully rebased and updated refs/heads/rebased-feature-branch. + EOF +} + +create_expected_success_merge () { + cat >expected <<-EOF + $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) + HEAD is now at $(git rev-parse --short feature-branch) third commit + First, rewinding head to replay your work on top of it... + Merging unrelated-onto-branch with HEAD~1 + Merging: + $(git rev-parse --short unrelated-onto-branch) unrelated commit + $(git rev-parse --short feature-branch^) second commit + found 1 common ancestor: + $(git rev-parse --short feature-branch~2) initial commit + [detached HEAD $(git rev-parse --short rebased-feature-branch~1)] second commit + Author: A U Thor <author@example.com> + Date: Thu Apr 7 15:14:13 2005 -0700 + 2 files changed, 2 insertions(+) + create mode 100644 file1 + create mode 100644 file2 + Committed: 0001 second commit + Merging unrelated-onto-branch with HEAD~0 + Merging: + $(git rev-parse --short rebased-feature-branch~1) second commit + $(git rev-parse --short feature-branch) third commit + found 1 common ancestor: + $(git rev-parse --short feature-branch~1) second commit + [detached HEAD $(git rev-parse --short rebased-feature-branch)] third commit + Author: A U Thor <author@example.com> + Date: Thu Apr 7 15:15:13 2005 -0700 + 1 file changed, 1 insertion(+) + create mode 100644 file3 + Committed: 0002 third commit + All done. + Applied autostash. + EOF +} + +create_expected_failure_am () { + cat >expected <<-EOF + $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) + HEAD is now at $(git rev-parse --short feature-branch) third commit + First, rewinding head to replay your work on top of it... + Applying: second commit + Applying: third commit + Applying autostash resulted in conflicts. + Your changes are safe in the stash. + You can run "git stash pop" or "git stash drop" at any time. + EOF +} + +create_expected_failure_interactive () { + q_to_cr >expected <<-EOF + $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) + HEAD is now at $(git rev-parse --short feature-branch) third commit + Rebasing (1/2)QRebasing (2/2)QApplying autostash resulted in conflicts. + Your changes are safe in the stash. + You can run "git stash pop" or "git stash drop" at any time. + Successfully rebased and updated refs/heads/rebased-feature-branch. + EOF +} + +create_expected_failure_merge () { + cat >expected <<-EOF + $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) + HEAD is now at $(git rev-parse --short feature-branch) third commit + First, rewinding head to replay your work on top of it... + Merging unrelated-onto-branch with HEAD~1 + Merging: + $(git rev-parse --short unrelated-onto-branch) unrelated commit + $(git rev-parse --short feature-branch^) second commit + found 1 common ancestor: + $(git rev-parse --short feature-branch~2) initial commit + [detached HEAD $(git rev-parse --short rebased-feature-branch~1)] second commit + Author: A U Thor <author@example.com> + Date: Thu Apr 7 15:14:13 2005 -0700 + 2 files changed, 2 insertions(+) + create mode 100644 file1 + create mode 100644 file2 + Committed: 0001 second commit + Merging unrelated-onto-branch with HEAD~0 + Merging: + $(git rev-parse --short rebased-feature-branch~1) second commit + $(git rev-parse --short feature-branch) third commit + found 1 common ancestor: + $(git rev-parse --short feature-branch~1) second commit + [detached HEAD $(git rev-parse --short rebased-feature-branch)] third commit + Author: A U Thor <author@example.com> + Date: Thu Apr 7 15:15:13 2005 -0700 + 1 file changed, 1 insertion(+) + create mode 100644 file3 + Committed: 0002 third commit + All done. + Applying autostash resulted in conflicts. + Your changes are safe in the stash. + You can run "git stash pop" or "git stash drop" at any time. + EOF +} + +testrebase () { type=$1 dotest=$2 @@ -51,14 +167,20 @@ testrebase() { test_config rebase.autostash true && git reset --hard && git checkout -b rebased-feature-branch feature-branch && - test_when_finished git branch -D rebased-feature-branch && echo dirty >>file3 && - git rebase$type unrelated-onto-branch && + git rebase$type unrelated-onto-branch >actual 2>&1 && grep unrelated file4 && grep dirty file3 && git checkout feature-branch ' + test_expect_success "rebase$type --autostash: check output" ' + test_when_finished git branch -D rebased-feature-branch && + suffix=${type#\ --} && suffix=${suffix:-am} && + create_expected_success_$suffix && + test_i18ncmp expected actual + ' + test_expect_success "rebase$type: dirty index, non-conflicting rebase" ' test_config rebase.autostash true && git reset --hard && @@ -137,10 +259,9 @@ testrebase() { test_config rebase.autostash true && git reset --hard && git checkout -b rebased-feature-branch feature-branch && - test_when_finished git branch -D rebased-feature-branch && echo dirty >file4 && git add file4 && - git rebase$type unrelated-onto-branch && + git rebase$type unrelated-onto-branch >actual 2>&1 && test_path_is_missing $dotest && git reset --hard && grep unrelated file4 && @@ -149,6 +270,13 @@ testrebase() { git stash pop && grep dirty file4 ' + + test_expect_success "rebase$type: check output with conflicting stash" ' + test_when_finished git branch -D rebased-feature-branch && + suffix=${type#\ --} && suffix=${suffix:-am} && + create_expected_failure_$suffix && + test_i18ncmp expected actual + ' } test_expect_success "rebase: fast-forward rebase" ' diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index c268298eaf..5cdd76dfa7 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -13,7 +13,9 @@ test_expect_success setup ' echo modified >file && git diff --stat -p >patch-0.txt && chmod +x file && - git diff --stat -p >patch-1.txt + git diff --stat -p >patch-1.txt && + sed "s/^\(new mode \).*/\1/" <patch-1.txt >patch-empty-mode.txt && + sed "s/^\(new mode \).*/\1garbage/" <patch-1.txt >patch-bogus-mode.txt ' test_expect_success FILEMODE 'same mode (no index)' ' @@ -59,4 +61,16 @@ test_expect_success FILEMODE 'mode update (index only)' ' git ls-files -s file | grep "^100755" ' +test_expect_success FILEMODE 'empty mode is rejected' ' + git reset --hard && + test_must_fail git apply patch-empty-mode.txt 2>err && + test_i18ngrep "invalid mode" err +' + +test_expect_success FILEMODE 'bogus mode is rejected' ' + git reset --hard && + test_must_fail git apply patch-bogus-mode.txt 2>err && + test_i18ngrep "invalid mode" err +' + test_done diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh index 2ecb4216b7..c5ed3b17c4 100755 --- a/t/t4133-apply-filenames.sh +++ b/t/t4133-apply-filenames.sh @@ -35,4 +35,28 @@ test_expect_success 'apply diff with inconsistent filenames in headers' ' test_i18ngrep "inconsistent old filename" err ' +test_expect_success 'apply diff with new filename missing from headers' ' + cat >missing_new_filename.diff <<-\EOF && + diff --git a/f b/f + index 0000000..d00491f + --- a/f + @@ -0,0 +1 @@ + +1 + EOF + test_must_fail git apply missing_new_filename.diff 2>err && + test_i18ngrep "lacks filename information" err +' + +test_expect_success 'apply diff with old filename missing from headers' ' + cat >missing_old_filename.diff <<-\EOF && + diff --git a/f b/f + index d00491f..0000000 + +++ b/f + @@ -1 +0,0 @@ + -1 + EOF + test_must_fail git apply missing_old_filename.diff 2>err && + test_i18ngrep "lacks filename information" err +' + test_done diff --git a/t/t4136-apply-check.sh b/t/t4136-apply-check.sh index 4b0a374b63..6d92872318 100755 --- a/t/t4136-apply-check.sh +++ b/t/t4136-apply-check.sh @@ -29,4 +29,22 @@ test_expect_success 'apply exits non-zero with no-op patch' ' test_must_fail git apply --check input ' +test_expect_success 'invalid combination: create and copy' ' + test_must_fail git apply --check - <<-\EOF + diff --git a/1 b/2 + new file mode 100644 + copy from 1 + copy to 2 + EOF +' + +test_expect_success 'invalid combination: create and rename' ' + test_must_fail git apply --check - <<-\EOF + diff --git a/1 b/2 + new file mode 100644 + rename from 1 + rename to 2 + EOF +' + test_done diff --git a/t/t4213-log-tabexpand.sh b/t/t4213-log-tabexpand.sh index e01a8f6ac9..7f90f58c03 100755 --- a/t/t4213-log-tabexpand.sh +++ b/t/t4213-log-tabexpand.sh @@ -37,7 +37,7 @@ count_expand () # Prefix the output with the command line arguments, and # replace SP with a dot both in the expecte and actual output - # so that test_cmp would show the differene together with the + # so that test_cmp would show the difference together with the # breakage in a way easier to consume by the debugging user. { echo "git show -s $*" diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh index 5bcb288f5c..464ffdd147 100755 --- a/t/t5534-push-signed.sh +++ b/t/t5534-push-signed.sh @@ -119,8 +119,11 @@ test_expect_success GPG 'signed push sends push certificate' ' sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert ) >expect && - grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert && - grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert && + noop=$(git rev-parse noop) && + ff=$(git rev-parse ff) && + noff=$(git rev-parse noff) && + grep "$noop $ff refs/heads/ff" dst/push-cert && + grep "$noop $noff refs/heads/noff" dst/push-cert && test_cmp expect dst/push-cert-status ' @@ -200,8 +203,11 @@ test_expect_success GPG 'fail without key and heed user.signingkey' ' sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert ) >expect && - grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert && - grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert && + noop=$(git rev-parse noop) && + ff=$(git rev-parse ff) && + noff=$(git rev-parse noff) && + grep "$noop $ff refs/heads/ff" dst/push-cert && + grep "$noop $noff refs/heads/noff" dst/push-cert && test_cmp expect dst/push-cert-status ' diff --git a/t/t5614-clone-submodules-shallow.sh b/t/t5614-clone-submodules-shallow.sh index a87d329656..e4e6ea4d52 100755 --- a/t/t5614-clone-submodules-shallow.sh +++ b/t/t5614-clone-submodules-shallow.sh @@ -71,7 +71,7 @@ test_expect_success 'clone follows shallow recommendation' ' test_when_finished "rm -rf super_clone" && git config -f .gitmodules submodule.sub.shallow true && git add .gitmodules && - git commit -m "recommed shallow for sub" && + git commit -m "recommend shallow for sub" && git clone --recurse-submodules --no-local "file://$pwd/." super_clone && ( cd super_clone && @@ -105,7 +105,7 @@ test_expect_success 'clone follows non shallow recommendation' ' test_when_finished "rm -rf super_clone" && git config -f .gitmodules submodule.sub.shallow false && git add .gitmodules && - git commit -m "recommed non shallow for sub" && + git commit -m "recommend non shallow for sub" && git clone --recurse-submodules --no-local "file://$pwd/." super_clone && ( cd super_clone && diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 16952e44fc..aa74eb8f0d 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -244,7 +244,7 @@ test_expect_success 'setup and absorb a submodule' ' test_cmp expect out ' -test_expect_success 'describe chokes on severly broken submodules' ' +test_expect_success 'describe chokes on severely broken submodules' ' mv .git/modules/sub1/ .git/modules/sub_moved && test_must_fail git describe --dirty ' diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh index e2bbb449b6..ce74c12da2 100755 --- a/t/t7412-submodule-absorbgitdirs.sh +++ b/t/t7412-submodule-absorbgitdirs.sh @@ -33,7 +33,7 @@ test_expect_success 'absorb the git dir' ' test_cmp expect.2 actual.2 ' -test_expect_success 'absorbing does not fail for deinitalized submodules' ' +test_expect_success 'absorbing does not fail for deinitialized submodules' ' test_when_finished "git submodule update --init" && git submodule deinit --all && git submodule absorbgitdirs && diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh index 9c785b07ec..c8e7e98331 100755 --- a/t/t7413-submodule-is-active.sh +++ b/t/t7413-submodule-is-active.sh @@ -2,7 +2,7 @@ test_description='Test submodule--helper is-active -This test verifies that `git submodue--helper is-active` correclty identifies +This test verifies that `git submodue--helper is-active` correctly identifies submodules which are "active" and interesting to the user. ' diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 116885a260..5739d3ed23 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -329,4 +329,27 @@ test_expect_success 'invalid message options when using --fixup' ' test_must_fail git commit --fixup HEAD~1 -F log ' +cat >expected-template <<EOF + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# +# Author: A U Thor <author@example.com> +# +# On branch commit-template-check +# Changes to be committed: +# new file: commit-template-check +# +# Untracked files not listed +EOF + +test_expect_success 'new line found before status message in commit template' ' + git checkout -b commit-template-check && + git reset --hard HEAD && + touch commit-template-check && + git add commit-template-check && + GIT_EDITOR="cat >editor-input" git commit --untracked-files=no --allow-empty-message && + test_i18ncmp expected-template editor-input +' + test_done diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 0b6da7ae1f..fa61b1a4ee 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -18,7 +18,7 @@ test_expect_success 'initial status' ' echo bongo bongo >file && git add file && git status >actual && - test_i18ngrep "Initial commit" actual + test_i18ngrep "No commits yet" actual ' test_expect_success 'fail initial amend' ' diff --git a/t/t7508-status.sh b/t/t7508-status.sh index f7fe22a195..43d19a9b22 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -1640,4 +1640,34 @@ test_expect_success 'no additionnal info if no stash entries' ' test_cmp expected_without_stash actual ' +test_expect_success '"No commits yet" should be noted in status output' ' + git checkout --orphan empty-branch-1 && + git status >output && + test_i18ngrep "No commits yet" output +' + +test_expect_success '"No commits yet" should not be noted in status output' ' + git checkout --orphan empty-branch-2 && + test_commit test-commit-1 && + git status >output && + test_i18ngrep ! "No commits yet" output +' + +test_expect_success '"Initial commit" should be noted in commit template' ' + git checkout --orphan empty-branch-3 && + touch to_be_committed_1 && + git add to_be_committed_1 && + git commit --dry-run >output && + test_i18ngrep "Initial commit" output +' + +test_expect_success '"Initial commit" should not be noted in commit template' ' + git checkout --orphan empty-branch-4 && + test_commit test-commit-2 && + touch to_be_committed_2 && + git add to_be_committed_2 && + git commit --dry-run >output && + test_i18ngrep ! "Initial commit" output +' + test_done diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 2e0ba3ebd8..67b8c50a5a 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -2822,7 +2822,7 @@ test_expect_success 'S: filemodify with garbage after sha1 must fail' ' # # notemodify, three ways to say dataref # -test_expect_success 'S: notemodify with garabge after mark dataref must fail' ' +test_expect_success 'S: notemodify with garbage after mark dataref must fail' ' test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && commit refs/heads/S committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE diff --git a/t/t9700/test.pl b/t/t9700/test.pl index 1b75c91965..34cd01366f 100755 --- a/t/t9700/test.pl +++ b/t/t9700/test.pl @@ -133,6 +133,13 @@ close TEMPFILE3; unlink $tmpfile3; chdir($abs_repo_dir); +# unquoting paths +is(Git::unquote_path('abc'), 'abc', 'unquote unquoted path'); +is(Git::unquote_path('"abc def"'), 'abc def', 'unquote simple quoted path'); +is(Git::unquote_path('"abc\"\\\\ \a\b\t\n\v\f\r\001\040"'), + "abc\"\\ \x07\x08\x09\x0a\x0b\x0c\x0d\x01 ", + 'unquote escape sequences'); + printf "1..%d\n", Test::More->builder->current_test; my $is_passing = eval { Test::More->is_passing }; diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 5ee124332a..db622c3555 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -216,6 +216,11 @@ test_chmod () { git update-index --add "--chmod=$@" } +# Get the modebits from a file. +test_modebits () { + ls -l "$1" | sed -e 's|^\(..........\).*|\1|' +} + # Unset a configuration variable, but don't fail if it doesn't exist. test_unconfig () { config_dir= diff --git a/unicode_width.h b/unicode_width.h index 02207be4fc..6dee2c77ce 100644 --- a/unicode_width.h +++ b/unicode_width.h @@ -51,6 +51,7 @@ static const struct interval zero_width[] = { { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, +{ 0x0AFA, 0x0AFF }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, @@ -73,7 +74,8 @@ static const struct interval zero_width[] = { { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, -{ 0x0D01, 0x0D01 }, +{ 0x0D00, 0x0D01 }, +{ 0x0D3B, 0x0D3C }, { 0x0D41, 0x0D44 }, { 0x0D4D, 0x0D4D }, { 0x0D62, 0x0D63 }, @@ -158,7 +160,7 @@ static const struct interval zero_width[] = { { 0x1CED, 0x1CED }, { 0x1CF4, 0x1CF4 }, { 0x1CF8, 0x1CF9 }, -{ 0x1DC0, 0x1DF5 }, +{ 0x1DC0, 0x1DF9 }, { 0x1DFB, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, @@ -262,6 +264,15 @@ static const struct interval zero_width[] = { { 0x1171D, 0x1171F }, { 0x11722, 0x11725 }, { 0x11727, 0x1172B }, +{ 0x11A01, 0x11A06 }, +{ 0x11A09, 0x11A0A }, +{ 0x11A33, 0x11A38 }, +{ 0x11A3B, 0x11A3E }, +{ 0x11A47, 0x11A47 }, +{ 0x11A51, 0x11A56 }, +{ 0x11A59, 0x11A5B }, +{ 0x11A8A, 0x11A96 }, +{ 0x11A98, 0x11A99 }, { 0x11C30, 0x11C36 }, { 0x11C38, 0x11C3D }, { 0x11C3F, 0x11C3F }, @@ -269,6 +280,11 @@ static const struct interval zero_width[] = { { 0x11CAA, 0x11CB0 }, { 0x11CB2, 0x11CB3 }, { 0x11CB5, 0x11CB6 }, +{ 0x11D31, 0x11D36 }, +{ 0x11D3A, 0x11D3A }, +{ 0x11D3C, 0x11D3D }, +{ 0x11D3F, 0x11D45 }, +{ 0x11D47, 0x11D47 }, { 0x16AF0, 0x16AF4 }, { 0x16B30, 0x16B36 }, { 0x16F8F, 0x16F92 }, @@ -339,7 +355,7 @@ static const struct interval double_width[] = { { 0x3000, 0x303E }, { 0x3041, 0x3096 }, { 0x3099, 0x30FF }, -{ 0x3105, 0x312D }, +{ 0x3105, 0x312E }, { 0x3131, 0x318E }, { 0x3190, 0x31BA }, { 0x31C0, 0x31E3 }, @@ -358,10 +374,11 @@ static const struct interval double_width[] = { { 0xFE68, 0xFE6B }, { 0xFF01, 0xFF60 }, { 0xFFE0, 0xFFE6 }, -{ 0x16FE0, 0x16FE0 }, +{ 0x16FE0, 0x16FE1 }, { 0x17000, 0x187EC }, { 0x18800, 0x18AF2 }, -{ 0x1B000, 0x1B001 }, +{ 0x1B000, 0x1B11E }, +{ 0x1B170, 0x1B2FB }, { 0x1F004, 0x1F004 }, { 0x1F0CF, 0x1F0CF }, { 0x1F18E, 0x1F18E }, @@ -370,6 +387,7 @@ static const struct interval double_width[] = { { 0x1F210, 0x1F23B }, { 0x1F240, 0x1F248 }, { 0x1F250, 0x1F251 }, +{ 0x1F260, 0x1F265 }, { 0x1F300, 0x1F320 }, { 0x1F32D, 0x1F335 }, { 0x1F337, 0x1F37C }, @@ -392,15 +410,13 @@ static const struct interval double_width[] = { { 0x1F6CC, 0x1F6CC }, { 0x1F6D0, 0x1F6D2 }, { 0x1F6EB, 0x1F6EC }, -{ 0x1F6F4, 0x1F6F6 }, -{ 0x1F910, 0x1F91E }, -{ 0x1F920, 0x1F927 }, -{ 0x1F930, 0x1F930 }, -{ 0x1F933, 0x1F93E }, -{ 0x1F940, 0x1F94B }, -{ 0x1F950, 0x1F95E }, -{ 0x1F980, 0x1F991 }, +{ 0x1F6F4, 0x1F6F8 }, +{ 0x1F910, 0x1F93E }, +{ 0x1F940, 0x1F94C }, +{ 0x1F950, 0x1F96B }, +{ 0x1F980, 0x1F997 }, { 0x1F9C0, 0x1F9C0 }, +{ 0x1F9D0, 0x1F9E6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD } }; diff --git a/urlmatch.c b/urlmatch.c index 4bbde924e8..3e42bd7504 100644 --- a/urlmatch.c +++ b/urlmatch.c @@ -42,12 +42,12 @@ static int append_normalized_escapes(struct strbuf *buf, from_len--; if (ch == '%') { - if (from_len < 2 || - !isxdigit(from[0]) || - !isxdigit(from[1])) + if (from_len < 2) return 0; - ch = hexval(*from++) << 4; - ch |= hexval(*from++); + ch = hex2chr(from); + if (ch < 0) + return 0; + from += 2; from_len -= 2; was_esc = 1; } @@ -44,7 +44,23 @@ static void warn_builtin(const char *warn, va_list params) static int die_is_recursing_builtin(void) { static int dying; - return dying++; + /* + * Just an arbitrary number X where "a < x < b" where "a" is + * "maximum number of pthreads we'll ever plausibly spawn" and + * "b" is "something less than Inf", since the point is to + * prevent infinite recursion. + */ + static const int recursion_limit = 1024; + + dying++; + if (dying > recursion_limit) { + return 1; + } else if (dying == 2) { + warning("die() called many times. Recursion error or racy threaded death!"); + return 0; + } else { + return 0; + } } /* If we are in a dlopen()ed .so write to a global variable would segfault diff --git a/wildmatch.c b/wildmatch.c index 57c8765805..d074c1be10 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -272,8 +272,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags) } /* Match the "pattern" against the "text" string. */ -int wildmatch(const char *pattern, const char *text, - unsigned int flags, struct wildopts *wo) +int wildmatch(const char *pattern, const char *text, unsigned int flags) { return dowild((const uchar*)pattern, (const uchar*)text, flags); } diff --git a/wildmatch.h b/wildmatch.h index 4090c8f4bb..b8c826aa68 100644 --- a/wildmatch.h +++ b/wildmatch.h @@ -10,9 +10,5 @@ #define WM_ABORT_ALL -1 #define WM_ABORT_TO_STARSTAR -2 -struct wildopts; - -int wildmatch(const char *pattern, const char *text, - unsigned int flags, - struct wildopts *wo); +int wildmatch(const char *pattern, const char *text, unsigned int flags); #endif diff --git a/worktree.c b/worktree.c index 2801c6d52b..e28ffbeb09 100644 --- a/worktree.c +++ b/worktree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "repository.h" #include "refs.h" #include "strbuf.h" #include "worktree.h" @@ -76,7 +77,7 @@ static struct worktree *get_linked_worktree(const char *id) if (!id) die("Missing linked worktree name"); - strbuf_git_common_path(&path, "worktrees/%s/gitdir", id); + strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id); if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0) /* invalid gitdir file */ goto done; diff --git a/wt-status.c b/wt-status.c index 7992a73902..77c27c5113 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1601,7 +1601,10 @@ static void wt_longstatus_print(struct wt_status *s) if (s->is_initial) { status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); - status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit")); + status_printf_ln(s, color(WT_STATUS_HEADER, s), + s->commit_template + ? _("Initial commit") + : _("No commits yet")); status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", ""); } @@ -1760,6 +1763,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s); const char *base; + char *short_base; const char *branch_name; int num_ours, num_theirs; int upstream_is_gone = 0; @@ -1773,7 +1777,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) #define LABEL(string) (s->no_gettext ? (string) : _(string)) if (s->is_initial) - color_fprintf(s->fp, header_color, LABEL(N_("Initial commit on "))); + color_fprintf(s->fp, header_color, LABEL(N_("No commits yet on "))); if (!strcmp(s->branch, "HEAD")) { color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s", @@ -1794,10 +1798,10 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) upstream_is_gone = 1; } - base = shorten_unambiguous_ref(base, 0); + short_base = shorten_unambiguous_ref(base, 0); color_fprintf(s->fp, header_color, "..."); - color_fprintf(s->fp, branch_color_remote, "%s", base); - free((char *)base); + color_fprintf(s->fp, branch_color_remote, "%s", short_base); + free(short_base); if (!upstream_is_gone && !num_ours && !num_theirs) goto conclude; diff --git a/wt-status.h b/wt-status.h index d8ae2e590d..64f4d33ea1 100644 --- a/wt-status.h +++ b/wt-status.h @@ -76,6 +76,7 @@ struct wt_status { char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN]; unsigned colopts; int null_termination; + int commit_template; int show_branch; int show_stash; int hints; |