diff options
118 files changed, 2682 insertions, 650 deletions
diff --git a/.gitignore b/.gitignore index 87b833c9d8..3dd6ef7d25 100644 --- a/.gitignore +++ b/.gitignore @@ -177,6 +177,7 @@ /test-sha1 /test-sigchain /test-string-pool +/test-subprocess /test-svn-fe /test-treap /common-cmds.h diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 1b1c45df5c..ba2006d892 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -157,7 +157,7 @@ Writing Documentation: --sort=<key> --abbrev[=<n>] - Possibility of multiple occurences is indicated by three dots: + Possibility of multiple occurrences is indicated by three dots: <file>... (One or more of <file>.) diff --git a/Documentation/RelNotes/1.7.3.5.txt b/Documentation/RelNotes/1.7.3.5.txt new file mode 100644 index 0000000000..40f3ba5795 --- /dev/null +++ b/Documentation/RelNotes/1.7.3.5.txt @@ -0,0 +1,34 @@ +Git 1.7.3.5 Release Notes +========================= + + * The xfuncname pattern used by "git diff" and "git grep" to show the + last notable line in context were broken for python and ruby for a long + time. + + * "git merge" into an unborn branch removed an untracked file "foo" from + the working tree when merged branch had "foo" (this fix was already in + 1.7.3.3 but was omitted from the release notes by mistake). + + * "git status -s" did not quote unprintable characters in paths as + documented. + + * "git am --abort" used to always reset to the commit at the beginning of + the last "am" invocation that has stopped, losing any unrelated commits + that may have been made since then. Now it refrains from doing so and + instead issues a warning. + + * "git blame" incorrectly reused bogusly cached result of textconv + filter for files from the working tree. + + * "git commit" used to abort after the user edited the log message + when the committer information was not correctly set up. It now + aborts before starting the editor. + + * "git commit --date=invalid" used to silently ignore the incorrectly + specified date; it is now diagnosed as an error. + + * "git rebase --skip" to skip the last commit in a series used to fail + to run post-rewrite hook and to copy notes from old commits that have + successfully been rebased so far. Now it do (backmerge ef88ad2). + + * "gitweb" tried to show a wrong feed logo when none was specified. diff --git a/Documentation/RelNotes/1.7.4.txt b/Documentation/RelNotes/1.7.4.txt index ace061f5fe..d5bca731b5 100644 --- a/Documentation/RelNotes/1.7.4.txt +++ b/Documentation/RelNotes/1.7.4.txt @@ -1,5 +1,5 @@ -Git v1.7.4 Release Notes (draft) -================================ +Git v1.7.4 Release Notes +======================== Updates since v1.7.3 -------------------- @@ -8,15 +8,14 @@ Updates since v1.7.3 docbook-xsl >= 1.73. If you have older versions, you can set ASCIIDOC7 and ASCIIDOC_ROFF, respectively. - * The option parsers of various commands that create new branch (or + * The option parsers of various commands that create new branches (or rename existing ones to a new name) were too loose and users were - allowed to call a branch with a name that begins with a dash by - creative abuse of their command line options, which only lead to - burn themselves. The name of a branch cannot begin with a dash - now. + allowed to give a branch a name that begins with a dash by creative + abuse of their command line options, which only led to burning + themselves. The name of a branch cannot begin with a dash now. * System-wide fallback default attributes can be stored in - /etc/gitattributes; core.attributesfile configuration variable can + /etc/gitattributes; the core.attributesfile configuration variable can be used to customize the path to this file. * The thread structure generated by "git send-email" has changed @@ -26,33 +25,39 @@ Updates since v1.7.3 cover letter of the previous series; this has been changed to make the patches in the new series replies to the new cover letter. - * Bash completion script in contrib/ has been adjusted to be also - usable by zsh. + * The Bash completion script in contrib/ has been adjusted to be usable with + Bash 4 (options with '=value' didn't complete). It has been also made + usable with zsh. * Different pagers can be chosen depending on which subcommand is - being run under the pager, using "pager.<subcommand>" variable. + being run under the pager, using the "pager.<subcommand>" variable. - * The hardcoded tab-width of 8 used in whitespace breakage checks is now + * The hardcoded tab-width of 8 that is used in whitespace breakage checks is now configurable via the attributes mechanism. * Support of case insensitive filesystems (i.e. "core.ignorecase") has been improved. For example, the gitignore mechanism didn't pay attention - to the case insensitivity. + to case insensitivity. - * The <tree>:<path> syntax to name a blob in a tree, and :<path> - syntax to name a blob in the index (e.g. "master:Makefile", + * The <tree>:<path> syntax for naming a blob in a tree, and the :<path> + syntax for naming a blob in the index (e.g. "master:Makefile", ":hello.c") have been extended. You can start <path> with "./" to implicitly have the (sub)directory you are in prefixed to the lookup. Similarly, ":../Makefile" from a subdirectory would mean "the Makefile of the parent directory in the index". - * "git blame" learned --show-email option to display the e-mail + * "git blame" learned the --show-email option to display the e-mail addresses instead of the names of authors. - * "git commit" learned --fixup and --squash options to help later invocation - of the interactive rebase. + * "git commit" learned the --fixup and --squash options to help later invocation + of interactive rebase. - * "git daemon" can be built in MinGW environment. + * Command line options to "git cvsimport" whose names are in capital + letters (-A, -M, -R and -S) can now be specified as the default in + the .git/config file by their longer names (cvsimport.authorsFile, + cvsimport.mergeRegex, cvsimport.trackRevisions, cvsimport.ignorePaths). + + * "git daemon" can be built in the MinGW environment. * "git daemon" can take more than one --listen option to listen to multiple addresses. @@ -60,13 +65,13 @@ Updates since v1.7.3 * "git describe --exact-match" was optimized not to read commit objects unnecessarily. - * "git diff" and "git grep" learned how functions and subroutines - in Fortran look like. + * "git diff" and "git grep" learned what functions and subroutines + in Fortran, Pascal and Perl look like. - * "git fetch" learned "--recurse-submodules" option. + * "git fetch" learned the "--recurse-submodules" option. - * "git mergetool" tells vim/gvim to show three-way diff by default - (use vimdiff2/gvimdiff2 as the tool name for old behaviour). + * "git mergetool" tells vim/gvim to show a three-way diff by default + (use vimdiff2/gvimdiff2 as the tool name for old behavior). * "git log -G<pattern>" limits the output to commits whose change has added or deleted lines that match the given pattern. @@ -76,8 +81,8 @@ Updates since v1.7.3 use the new --empty option to be more explicit instead. * "git repack -f" does not spend cycles to recompress objects in the - non-delta representation anymore (use -F if you really mean it when - e.g. you changed the compression level). + non-delta representation anymore (use -F if you really mean it + e.g. after you changed the core.compression variable setting). * "git merge --log" used to limit the resulting merge log to 20 entries; this is now customizable by giving e.g. "--log=47". @@ -86,33 +91,42 @@ Updates since v1.7.3 directory in one branch while a new file is created in place of that directory in the other branch. - * "git rebase --autosquash" can use SHA-1 object names to name which - commit to fix up (e.g. "fixup! e83c5163"). + * "git merge" learned the "--abort" option, synonymous to + "git reset --merge" when a merge is in progress. + + * "git notes" learned the "merge" subcommand to merge notes refs. + In addition to the default manual conflict resolution, there are + also several notes merge strategies for automatically resolving + notes merge conflicts. + + * "git rebase --autosquash" can use SHA-1 object names to name the + commit which is to be fixed up (e.g. "fixup! e83c5163"). - * The default "recursive" merge strategy learned --rename-threshold + * The default "recursive" merge strategy learned the --rename-threshold option to influence the rename detection, similar to the -M option - of "git diff". E.g. "git merge -Xrename-threshold=50% ..." to use - this. + of "git diff". From the "git merge" frontend, the "-X<strategy option>" + interface, e.g. "git merge -Xrename-threshold=50% ...", can be used + to trigger this. * The "recursive" strategy also learned to ignore various whitespace changes; the most notable is -Xignore-space-at-eol. * "git send-email" learned "--to-cmd", similar to "--cc-cmd", to read - recipient list from a command output. + the recipient list from a command output. * "git send-email" learned to read and use "To:" from its input files. * you can extend "git shell", which is often used on boxes that allow - git-only login over ssh as login shell, with custom set of + git-only login over ssh as login shell, with a custom set of commands. * The current branch name in "git status" output can be colored differently - from the generic header color by setting "color.status.branch" variable. + from the generic header color by setting the "color.status.branch" variable. * "git submodule sync" updates metainformation for all submodules, not just the ones that have been checked out. - * gitweb can use custom 'highlight' command with its configuration file. + * gitweb can use a custom 'highlight' command with its configuration file. * other gitweb updates. @@ -123,26 +137,20 @@ Also contains various documentation updates. Fixes since v1.7.3 ------------------ -All of the fixes in v1.7.3.X maintenance series are included in this +All of the fixes in the v1.7.3.X maintenance series are included in this release, unless otherwise noted. * "git log --author=me --author=her" did not find commits written by me or by her; instead it looked for commits written by me and by her, which is impossible. - * "git merge" into an unborn branch removed an untracked file "foo" - from the working tree when merged branch had "foo" (2caf20c..172b642). - * "git push --progress" shows progress indicators now. + * "git rebase -i" showed a confusing error message when given a + branch name that does not exist. + * "git repack" places its temporary packs under $GIT_OBJECT_DIRECTORY/pack instead of $GIT_OBJECT_DIRECTORY/ to avoid cross directory renames. * "git submodule update --recursive --other-flags" passes flags down to its subinvocations. - ---- -exec >/var/tmp/1 -O=v1.7.3.4-567-g38a5932 -echo O=$(git describe master) -git shortlog --no-merges ^maint ^$O master diff --git a/Documentation/config.txt b/Documentation/config.txt index 54597f1897..c5e183516a 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -317,24 +317,26 @@ false), while all other repositories are assumed to be bare (bare = true). core.worktree:: - Set the path to the root of the work tree. + Set the path to the root of the working tree. This can be overridden by the GIT_WORK_TREE environment - variable and the '--work-tree' command line option. It can be - an absolute path or a relative path to the .git directory, - either specified by --git-dir or GIT_DIR, or automatically - discovered. - If --git-dir or GIT_DIR are specified but none of + variable and the '--work-tree' command line option. + The value can an absolute path or relative to the path to + the .git directory, which is either specified by --git-dir + or GIT_DIR, or automatically discovered. + If --git-dir or GIT_DIR is specified but none of --work-tree, GIT_WORK_TREE and core.worktree is specified, - the current working directory is regarded as the root of the - work tree. + the current working directory is regarded as the top level + of your working tree. + Note that this variable is honored even when set in a configuration -file in a ".git" subdirectory of a directory, and its value differs +file in a ".git" subdirectory of a directory and its value differs from the latter directory (e.g. "/path/to/.git/config" has core.worktree set to "/different/path"), which is most likely a -misconfiguration. Running git commands in "/path/to" directory will +misconfiguration. Running git commands in the "/path/to" directory will still use "/different/path" as the root of the work tree and can cause -great confusion to the users. +confusion unless you know what you are doing (e.g. you are creating a +read-only snapshot of the same index to a location different from the +repository's usual working tree). core.logAllRefUpdates:: Enable the reflog. Updates to a ref <ref> is logged to the file @@ -899,7 +901,7 @@ diff.wordRegex:: fetch.recurseSubmodules:: A boolean value which changes the behavior for fetch and pull, the - default is to not recursively fetch populated sumodules unless + default is to not recursively fetch populated submodules unless configured otherwise. fetch.unpackLimit:: @@ -1818,7 +1820,7 @@ submodule.<name>.update:: submodule.<name>.fetchRecurseSubmodules:: This option can be used to enable/disable recursive fetching of this - submodule. It can be overriden by using the --[no-]recurse-submodules + submodule. It can be overridden by using the --[no-]recurse-submodules command line option to "git fetch" and "git pull". This setting will override that from in the linkgit:gitmodules[5] file. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 695696da1b..f37276e5ad 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -64,13 +64,11 @@ ifndef::git-pull[] downloaded. The default behavior for a remote may be specified with the remote.<name>.tagopt setting. See linkgit:git-config[1]. -endif::git-pull[] --[no-]recurse-submodules:: This option controls if new commits of all populated submodules should be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]). -ifndef::git-pull[] --submodule-prefix=<path>:: Prepend <path> to paths printed in informative messages such as "Fetching submodule foo". This option is used diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 54aaaeb41b..a03448f923 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -333,7 +333,7 @@ likely to introduce confusing changes to the index. There are also more complex operations that can be performed. But beware that because the patch is applied only to the index and not the working tree, the working tree will appear to "undo" the change in the index. -For example, introducing a a new line into the index that is in neither +For example, introducing a new line into the index that is in neither the HEAD nor the working tree will stage the new line for commit, but the line will appear to be reverted in the working tree. diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt index 4163a1bcb1..bf5037ab2a 100644 --- a/Documentation/git-archive.txt +++ b/Documentation/git-archive.txt @@ -116,7 +116,7 @@ Note that attributes are by default taken from the `.gitattributes` files in the tree that is being archived. If you want to tweak the way the output is generated after the fact (e.g. you committed without adding an appropriate export-ignore in its `.gitattributes`), adjust the checked out -`.gitattributes` file as necessary and use `--work-tree-attributes` +`.gitattributes` file as necessary and use `--worktree-attributes` option. Alternatively you can keep necessary attributes that should apply while archiving any tree in your `$GIT_DIR/info/attributes` file. diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt index 6266a3a602..299007b206 100644 --- a/Documentation/git-bundle.txt +++ b/Documentation/git-bundle.txt @@ -59,7 +59,7 @@ unbundle <file>:: <git-rev-list-args>:: A list of arguments, acceptable to 'git rev-parse' and - 'git rev-list' (and containg a named ref, see SPECIFYING REFERENCES + 'git rev-list' (and containing a named ref, see SPECIFYING REFERENCES below), that specifies the specific objects and references to transport. For example, `master{tilde}10..master` causes the current master reference to be packaged along with all objects diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 73008705eb..749d68a72b 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -79,6 +79,16 @@ effect to your index in a row. cherry-pick'ed commit, then a fast forward to this commit will be performed. +--strategy=<strategy>:: + Use the given merge strategy. Should only be used once. + See the MERGE STRATEGIES section in linkgit:git-merge[1] + for details. + +-X<option>:: +--strategy-option=<option>:: + Pass the merge strategy-specific option through to the + merge strategy. See linkgit:git-merge[1] for details. + EXAMPLES -------- git cherry-pick master:: @@ -120,6 +130,28 @@ git rev-list --reverse master \-- README | git cherry-pick -n --stdin:: so the result can be inspected and made into a single new commit if suitable. +The following sequence attempts to backport a patch, bails out because +the code the patch applies to has changed too much, and then tries +again, this time exercising more care about matching up context lines. + +------------ +$ git cherry-pick topic^ <1> +$ git diff <2> +$ git reset --merge ORIG_HEAD <3> +$ git cherry-pick -Xpatience topic^ <4> +------------ +<1> apply the change that would be shown by `git show topic^`. +In this example, the patch does not apply cleanly, so +information about the conflict is written to the index and +working tree and no new commit results. +<2> summarize changes to be reconciled +<3> cancel the cherry-pick. In other words, return to the +pre-cherry-pick state, preserving any local modifications you had in +the working tree. +<4> try to apply the change introduced by `topic^` again, +spending extra time to avoid mistakes based on incorrectly matching +context lines. + Author ------ Written by Junio C Hamano <gitster@pobox.com> diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index f6ac847507..49105102db 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -38,6 +38,8 @@ directories. This behavior can be forced by --no-index. commit relative to the named <commit>. Typically you would want comparison with the latest commit, so if you do not give <commit>, it defaults to HEAD. + If HEAD does not exist (e.g. unborned branches) and + <commit> is not given, it shows all staged changes. --staged is a synonym of --cached. 'git diff' [--options] <commit> [--] [<path>...]:: diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index f56dfcabb9..02bb49886c 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -534,9 +534,6 @@ start with double quote (`"`). If an `LF` or double quote must be encoded into `<path>` shell-style quoting should be used, e.g. `"path/with\n and \" in it"`. -Additionally, in `040000` mode, `<path>` may also be an empty string -(`""`) to specify the root of the tree. - The value of `<path>` must be in canonical form. That is it must not: * contain an empty directory component (e.g. `foo//bar` is invalid), @@ -545,6 +542,8 @@ The value of `<path>` must be in canonical form. That is it must not: * contain the special component `.` or `..` (e.g. `foo/./bar` and `foo/../bar` are invalid). +The root of the tree can be represented by an empty string as `<path>`. + It is recommended that `<path>` always be encoded using UTF-8. `filedelete` @@ -905,7 +904,7 @@ The `<dataref>` can be either a mark reference (`:<idnum>`) set previously or a full 40-byte SHA-1 of a Git blob, preexisting or ready to be written. -output uses the same format as `git cat-file --batch`: +Output uses the same format as `git cat-file --batch`: ==== <sha1> SP 'blob' SP <size> LF @@ -949,6 +948,13 @@ cat-blob:: rather than wasting time on the early part of an import before the unsupported command is detected. +notes:: + Require that the backend support the 'notemodify' (N) + subcommand to the 'commit' command. + Versions of fast-import not supporting notes will exit + with a message indicating so. + + `option` ~~~~~~~~ Processes the specified option so that git fast-import behaves in a diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt index 40dba8c0a9..75adf7a502 100644 --- a/Documentation/git-fmt-merge-msg.txt +++ b/Documentation/git-fmt-merge-msg.txt @@ -57,7 +57,7 @@ merge.log:: In addition to branch names, populate the log message with at most the specified number of one-line descriptions from the actual commits that are being merged. Defaults to false, and - true is a synoym for 20. + true is a synonym for 20. merge.summary:: Synonym to `merge.log`; this is deprecated and will be removed in diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 30466917da..b33e6be872 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -84,6 +84,15 @@ must be given before the options meant for 'git fetch'. --verbose:: Pass --verbose to git-fetch and git-merge. +--[no-]recurse-submodules:: + This option controls if new commits of all populated submodules should + be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]). + That might be necessary to get the data needed for merging submodule + commits, a feature git learned in 1.7.3. Notice that the result of a + merge will not be checked out in the submodule, "git submodule update" + has to be called afterwards to bring the work tree up to date with the + merge result. + Options related to merging ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index e88e9c2d55..634423a69e 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -416,13 +416,6 @@ turn `core.sparseCheckout` on in order to have sparse checkout support. -BUGS ----- -In order to match a directory with $GIT_DIR/info/sparse-checkout, -trailing slash must be used. The form without trailing slash, while -works with .gitignore, does not work with sparse checkout. - - SEE ALSO -------- linkgit:git-write-tree[1]; linkgit:git-ls-files[1]; diff --git a/Documentation/git-remote-ext.txt b/Documentation/git-remote-ext.txt index f4fbf67209..2d65cfefd5 100644 --- a/Documentation/git-remote-ext.txt +++ b/Documentation/git-remote-ext.txt @@ -15,12 +15,12 @@ This remote helper uses the specified 'program' to connect to a remote git server. Data written to stdin of this specified 'program' is assumed -to be sent to git:// server, git-upload-pack, git-receive-pack +to be sent to a git:// server, git-upload-pack, git-receive-pack or git-upload-archive (depending on situation), and data read from stdout of this program is assumed to be received from the same service. -Command and arguments are separated by unescaped space. +Command and arguments are separated by an unescaped space. The following sequences have a special meaning: @@ -39,19 +39,19 @@ The following sequences have a special meaning: git-upload-pack, or git-upload-archive) of the service git wants to invoke. -'%G' (must be first characters in argument):: +'%G' (must be the first characters in an argument):: This argument will not be passed to 'program'. Instead, it - will cause helper to start by sending git:// service request to - remote side with service field set to approiate value and - repository field set to rest of the argument. Default is not to send - such request. + will cause the helper to start by sending git:// service requests to + the remote side with the service field set to an appropriate value and + the repository field set to rest of the argument. Default is not to send + such a request. + This is useful if remote side is git:// server accessed over some tunnel. '%V' (must be first characters in argument):: This argument will not be passed to 'program'. Instead it sets - the vhost field in git:// service request (to rest of the argument). + the vhost field in the git:// service request (to rest of the argument). Default is not to send vhost in such request (if sent). ENVIRONMENT VARIABLES: diff --git a/Documentation/git-remote-fd.txt b/Documentation/git-remote-fd.txt index abc49441be..4aecd4d187 100644 --- a/Documentation/git-remote-fd.txt +++ b/Documentation/git-remote-fd.txt @@ -11,20 +11,20 @@ SYNOPSIS DESCRIPTION ----------- -This helper uses specified file descriptors to connect to remote git server. +This helper uses specified file descriptors to connect to a remote git server. This is not meant for end users but for programs and scripts calling git fetch, push or archive. -If only <infd> is given, it is assumed to be bidirectional socket connected +If only <infd> is given, it is assumed to be a bidirectional socket connected to remote git server (git-upload-pack, git-receive-pack or git-upload-achive). If both <infd> and <outfd> are given, they are assumed -to be pipes connected to remote git server (<infd> being the inbound pipe +to be pipes connected to a remote git server (<infd> being the inbound pipe and <outfd> being the outbound pipe. It is assumed that any handshaking procedures have already been completed (such as sending service request for git://) before this helper is started. -<anything> can be any string. It is ignored. It is meant for provoding +<anything> can be any string. It is ignored. It is meant for providing information to user in the URL in case that URL is displayed in some context. diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index fd72976371..927ecee2f2 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -76,15 +76,10 @@ In other words, --merge does something like a 'git read-tree -u -m <commit>', but carries forward unmerged index entries. --keep:: - Resets the index, updates files in the working tree that are - different between <commit> and HEAD, but keeps those - which are different between HEAD and the working tree (i.e. - which have local changes). + Resets index entries and updates files in the working tree that are + different between <commit> and HEAD. If a file that is different between <commit> and HEAD has local changes, reset is aborted. -+ -In other words, --keep does a 2-way merge between <commit> and HEAD followed by -'git reset --mixed <commit>'. -- If you want to undo a commit other than the latest on a branch, diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 4a27643c1e..ff23cb0219 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -136,7 +136,12 @@ appending `/{asterisk}`. directory (typically a sequence of "../", or an empty string). --git-dir:: - Show `$GIT_DIR` if defined else show the path to the .git directory. + Show `$GIT_DIR` if defined. Otherwise show the path to + the .git directory, relative to the current directory. ++ +If `$GIT_DIR` is not defined and the current directory +is not detected to lie in a git repository or work tree +print a message to stderr and exit with nonzero status. --is-inside-git-dir:: When the current working directory is below the repository diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 752fc88e76..45be851750 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -80,6 +80,16 @@ effect to your index in a row. --signoff:: Add Signed-off-by line at the end of the commit message. +--strategy=<strategy>:: + Use the given merge strategy. Should only be used once. + See the MERGE STRATEGIES section in linkgit:git-merge[1] + for details. + +-X<option>:: +--strategy-option=<option>:: + Pass the merge strategy-specific option through to the + merge strategy. See linkgit:git-merge[1] for details. + EXAMPLES -------- git revert HEAD~3:: diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 139d314ba5..0ade2ce54e 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -613,7 +613,7 @@ old references to SVN revision numbers in existing documentation, bug reports and archives. If you plan to eventually migrate from SVN to git and are certain about dropping SVN history, consider linkgit:git-filter-branch[1] instead. filter-branch also allows -reformating of metadata for ease-of-reading and rewriting authorship +reformatting of metadata for ease-of-reading and rewriting authorship info for non-"svn.authorsFile" users. svn.useSvmProps:: @@ -729,8 +729,11 @@ have each person clone that repository with 'git clone': cd project git init git remote add origin server:/pub/project - git config --add remote.origin.fetch '+refs/remotes/*:refs/remotes/*' + git config --replace-all remote.origin.fetch '+refs/remotes/*:refs/remotes/*' git fetch +# Prevent fetch/pull from remote git server in the future, +# we only want to use git svn for future updates + git config --remove-section remote.origin # Create a local branch from one of the branches just fetched git checkout -b master FETCH_HEAD # Initialize 'git svn' locally (be sure to use the same URL and -T/-b/-t options as were used on server) diff --git a/Documentation/git.txt b/Documentation/git.txt index 012837145e..e968ed4aa0 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -44,9 +44,15 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.7.3.4/git.html[documentation for release 1.7.3.4] +* link:v1.7.4/git.html[documentation for release 1.7.4] * release notes for + link:RelNotes/1.7.4.txt[1.7.4]. + +* link:v1.7.3.5/git.html[documentation for release 1.7.3.5] + +* release notes for + link:RelNotes/1.7.3.5.txt[1.7.3.5], link:RelNotes/1.7.3.4.txt[1.7.3.4], link:RelNotes/1.7.3.3.txt[1.7.3.3], link:RelNotes/1.7.3.2.txt[1.7.3.2], @@ -290,17 +296,12 @@ help ...`. path or relative path to current working directory. --work-tree=<path>:: - Set the path to the working tree. The value will not be - used in combination with repositories found automatically in - a .git directory (i.e. $GIT_DIR is not set). + Set the path to the working tree. It can be an absolute path + or a path relative to the current working directory. This can also be controlled by setting the GIT_WORK_TREE environment variable and the core.worktree configuration - variable. It can be an absolute path or relative path to - the directory specified by --git-dir or GIT_DIR. - Note: If --git-dir or GIT_DIR are specified but none of - --work-tree, GIT_WORK_TREE and core.worktree is specified, - the current working directory is regarded as the top directory - of your working tree. + variable (see core.worktree in linkgit:git-config[1] for a + more detailed discussion). --bare:: Treat the repository as a bare repository. If GIT_DIR diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 5a7f936429..7e7e12168e 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -335,6 +335,16 @@ input that is already correctly indented. In this case, the lack of a smudge filter means that the clean filter _must_ accept its own output without modifying it. +Sequence "%f" on the filter command line is replaced with the name of +the file the filter is working on. A filter might use this in keyword +substitution. For example: + +------------------------ +[filter "p4"] + clean = git-p4-filter --clean %f + smudge = git-p4-filter --smudge %f +------------------------ + Interaction between checkin/checkout attributes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -494,6 +504,8 @@ patterns are available: - `pascal` suitable for source code in the Pascal/Delphi language. +- `perl` suitable for source code in the Perl language. + - `php` suitable for source code in the PHP language. - `python` suitable for source code in the Python language. @@ -581,6 +593,39 @@ and now produces better output), you can remove the cache manually with `git update-ref -d refs/notes/textconv/jpg` (where "jpg" is the name of the diff driver, as in the example above). +Marking files as binary +^^^^^^^^^^^^^^^^^^^^^^^ + +Git usually guesses correctly whether a blob contains text or binary +data by examining the beginning of the contents. However, sometimes you +may want to override its decision, either because a blob contains binary +data later in the file, or because the content, while technically +composed of text characters, is opaque to a human reader. For example, +many postscript files contain only ascii characters, but produce noisy +and meaningless diffs. + +The simplest way to mark a file as binary is to unset the diff +attribute in the `.gitattributes` file: + +------------------------ +*.ps -diff +------------------------ + +This will cause git to generate `Binary files differ` (or a binary +patch, if binary patches are enabled) instead of a regular diff. + +However, one may also want to specify other diff driver attributes. For +example, you might want to use `textconv` to convert postscript files to +an ascii representation for human viewing, but otherwise treat them as +binary files. You cannot specify both `-diff` and `diff=ps` attributes. +The solution is to use the `diff.*.binary` config option: + +------------------------ +[diff "ps"] + textconv = ps2ascii + binary = true +------------------------ + Performing a three-way merge ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 7183aa9abb..28edefa202 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -350,10 +350,6 @@ rebase:: The commits are guaranteed to be listed in the order that they were processed by rebase. -There is no default 'post-rewrite' hook, but see the -`post-receive-copy-notes` script in `contrib/hooks` for an example -that copies your git-notes to the rewritten commits. - GIT --- diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index 6c93202e73..68977943e7 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -49,7 +49,7 @@ submodule.<name>.fetchRecurseSubmodules:: submodule. If this option is also present in the submodules entry in .git/config of the superproject, the setting there will override the one found in .gitmodules. - Both settings can be overriden on the command line by using the + Both settings can be overridden on the command line by using the "--[no-]recurse-submodules" option to "git fetch" and "git pull".. submodule.<name>.ignore:: diff --git a/Documentation/howto/using-merge-subtree.txt b/Documentation/howto/using-merge-subtree.txt index 0953a50b69..2933056120 100644 --- a/Documentation/howto/using-merge-subtree.txt +++ b/Documentation/howto/using-merge-subtree.txt @@ -71,5 +71,5 @@ Additional tips relevant parts of your tree. - Please note that if the other project merges from you, then it will - connects its history to yours, which can be something they don't want + connect its history to yours, which can be something they don't want to. diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt index 92772e7c4e..1e5c22c5e5 100644 --- a/Documentation/merge-config.txt +++ b/Documentation/merge-config.txt @@ -10,7 +10,7 @@ merge.log:: In addition to branch names, populate the log message with at most the specified number of one-line descriptions from the actual commits that are being merged. Defaults to false, and - true is a synoym for 20. + true is a synonym for 20. merge.renameLimit:: The number of files to consider when performing rename detection diff --git a/Documentation/technical/api-sigchain.txt b/Documentation/technical/api-sigchain.txt index 535cdff164..9e1189ef01 100644 --- a/Documentation/technical/api-sigchain.txt +++ b/Documentation/technical/api-sigchain.txt @@ -32,7 +32,7 @@ and installation code should look something like: } ------------------------------------------ -Handlers are given the typdef of sigchain_fun. This is the same type +Handlers are given the typedef of sigchain_fun. This is the same type that is given to signal() or sigaction(). It is perfectly reasonable to push SIG_DFL or SIG_IGN onto the stack. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index d441d88d6f..92fe7a59db 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.7.3.GIT +DEF_VER=v1.7.4 LF=' ' @@ -431,6 +431,7 @@ TEST_PROGRAMS_NEED_X += test-run-command TEST_PROGRAMS_NEED_X += test-sha1 TEST_PROGRAMS_NEED_X += test-sigchain TEST_PROGRAMS_NEED_X += test-string-pool +TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe TEST_PROGRAMS_NEED_X += test-treap TEST_PROGRAMS_NEED_X += test-index-version @@ -1004,6 +1005,7 @@ ifeq ($(uname_S),IRIX) # issue, comment out the NO_MMAP statement. NO_MMAP = YesPlease NO_REGEX = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease SNPRINTF_RETURNS_BOGUS = YesPlease SHELL_PATH = /usr/gnu/bin/bash NEEDS_LIBGEN = YesPlease @@ -1023,6 +1025,7 @@ ifeq ($(uname_S),IRIX64) # issue, comment out the NO_MMAP statement. NO_MMAP = YesPlease NO_REGEX = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease SNPRINTF_RETURNS_BOGUS = YesPlease SHELL_PATH=/usr/gnu/bin/bash NEEDS_LIBGEN = YesPlease diff --git a/builtin/commit.c b/builtin/commit.c index 6c09857a60..03cff5af63 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -45,9 +45,9 @@ static const char implicit_ident_advice[] = " git config --global user.name \"Your Name\"\n" " git config --global user.email you@example.com\n" "\n" -"If the identity used for this commit is wrong, you can fix it with:\n" +"After doing this, you may fix the identity used for this commit with:\n" "\n" -" git commit --amend --author='Your Name <you@example.com>'\n"; +" git commit --amend --reset-author\n"; static const char empty_amend_advice[] = "You asked to amend the most recent commit, but doing so would make\n" @@ -70,7 +70,6 @@ static const char *logfile, *force_author; static const char *template_file; static char *edit_message, *use_message; static char *fixup_message, *squash_message; -static char *author_name, *author_email, *author_date; static int all, edit_flag, also, interactive, only, amend, signoff; static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int no_post_rewrite, allow_empty_message; @@ -462,7 +461,7 @@ static int is_a_merge(const unsigned char *sha1) static const char sign_off_header[] = "Signed-off-by: "; -static void determine_author_info(void) +static void determine_author_info(struct strbuf *author_ident) { char *name, *email, *date; @@ -506,10 +505,8 @@ static void determine_author_info(void) if (force_date) date = force_date; - - author_name = name; - author_email = email; - author_date = date; + strbuf_addstr(author_ident, fmt_ident(name, email, date, + IDENT_ERROR_ON_NO_NAME)); } static int ends_rfc2822_footer(struct strbuf *sb) @@ -553,10 +550,21 @@ static int ends_rfc2822_footer(struct strbuf *sb) return 1; } +static char *cut_ident_timestamp_part(char *string) +{ + char *ket = strrchr(string, '>'); + if (!ket || ket[1] != ' ') + die("Malformed ident string: '%s'", string); + *++ket = '\0'; + return ket; +} + static int prepare_to_commit(const char *index_file, const char *prefix, - struct wt_status *s) + struct wt_status *s, + struct strbuf *author_ident) { struct stat statbuf; + struct strbuf committer_ident = STRBUF_INIT; int commitable, saved_color_setting; struct strbuf sb = STRBUF_INIT; char *buffer; @@ -679,14 +687,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix, strbuf_release(&sb); - determine_author_info(); + /* This checks and barfs if author is badly specified */ + determine_author_info(author_ident); /* This checks if committer ident is explicitly given */ - git_committer_info(0); + strbuf_addstr(&committer_ident, git_committer_info(0)); if (use_editor && include_status) { - char *author_ident; - const char *committer_ident; - + char *ai_tmp, *ci_tmp; if (in_merge) fprintf(fp, "#\n" @@ -714,23 +721,21 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (only_include_assumed) fprintf(fp, "# %s\n", only_include_assumed); - author_ident = xstrdup(fmt_name(author_name, author_email)); - committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"), - getenv("GIT_COMMITTER_EMAIL")); - if (strcmp(author_ident, committer_ident)) + ai_tmp = cut_ident_timestamp_part(author_ident->buf); + ci_tmp = cut_ident_timestamp_part(committer_ident.buf); + if (strcmp(author_ident->buf, committer_ident.buf)) fprintf(fp, "%s" "# Author: %s\n", ident_shown++ ? "" : "#\n", - author_ident); - free(author_ident); + author_ident->buf); if (!user_ident_sufficiently_given()) fprintf(fp, "%s" "# Committer: %s\n", ident_shown++ ? "" : "#\n", - committer_ident); + committer_ident.buf); if (ident_shown) fprintf(fp, "#\n"); @@ -739,6 +744,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix, s->use_color = 0; commitable = run_status(fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; + + *ai_tmp = ' '; + *ci_tmp = ' '; } else { unsigned char sha1[20]; const char *parent = "HEAD"; @@ -754,6 +762,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, else commitable = index_differs_from(parent, 0); } + strbuf_release(&committer_ident); fclose(fp); @@ -1278,6 +1287,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1, int cmd_commit(int argc, const char **argv, const char *prefix) { struct strbuf sb = STRBUF_INIT; + struct strbuf author_ident = STRBUF_INIT; const char *index_file, *reflog_msg; char *nl, *p; unsigned char commit_sha1[20]; @@ -1308,7 +1318,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ - if (!prepare_to_commit(index_file, prefix, &s)) { + if (!prepare_to_commit(index_file, prefix, &s, &author_ident)) { rollback_index_files(); return 1; } @@ -1387,11 +1397,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1, - fmt_ident(author_name, author_email, author_date, - IDENT_ERROR_ON_NO_NAME))) { + author_ident.buf)) { rollback_index_files(); die("failed to write commit object"); } + strbuf_release(&author_ident); ref_lock = lock_any_ref_for_update("HEAD", initial_commit ? NULL : head_sha1, diff --git a/builtin/describe.c b/builtin/describe.c index a0f52c1b72..342129fdbd 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -218,7 +218,7 @@ static unsigned long finish_depth_computation( struct commit *p = parents->item; parse_commit(p); if (!(p->object.flags & SEEN)) - insert_by_date(p, list); + commit_list_insert_by_date(p, list); p->object.flags |= c->object.flags; parents = parents->next; } @@ -334,7 +334,7 @@ static void describe(const char *arg, int last_one) struct commit *p = parents->item; parse_commit(p); if (!(p->object.flags & SEEN)) - insert_by_date(p, &list); + commit_list_insert_by_date(p, &list); p->object.flags |= c->object.flags; parents = parents->next; } @@ -362,7 +362,7 @@ static void describe(const char *arg, int last_one) qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt); if (gave_up_on) { - insert_by_date(gave_up_on, &list); + commit_list_insert_by_date(gave_up_on, &list); seen_commits--; } seen_commits += finish_depth_computation(&list, &all_matches[0]); diff --git a/builtin/diff.c b/builtin/diff.c index 945e7583a8..42822cd537 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -330,8 +330,11 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); - if (!rev.pending.nr) - die("No HEAD commit to compare with (yet)"); + if (!rev.pending.nr) { + struct tree *tree; + tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN); + add_pending_object(&rev, &tree->object, "HEAD"); + } break; } } diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index dbd8b7bcc8..b999413934 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -47,7 +47,7 @@ static void rev_list_push(struct commit *commit, int mark) if (parse_commit(commit)) return; - insert_by_date(commit, &rev_list); + commit_list_insert_by_date(commit, &rev_list); if (!(commit->object.flags & COMMON)) non_common_revs++; @@ -436,7 +436,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag, if (o && o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; commit->object.flags |= COMPLETE; - insert_by_date(commit, &complete); + commit_list_insert_by_date(commit, &complete); } return 0; } diff --git a/builtin/init-db.c b/builtin/init-db.c index 9d4886c716..e3af9eaa87 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -414,6 +414,7 @@ static const char *const init_db_usage[] = { int cmd_init_db(int argc, const char **argv, const char *prefix) { const char *git_dir; + const char *work_tree; const char *template_dir = NULL; unsigned int flags = 0; const struct option init_db_options[] = { @@ -480,8 +481,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) * without --bare. Catch the error early. */ git_dir = getenv(GIT_DIR_ENVIRONMENT); - if ((!git_dir || is_bare_repository_cfg == 1) - && getenv(GIT_WORK_TREE_ENVIRONMENT)) + work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT); + if ((!git_dir || is_bare_repository_cfg == 1) && work_tree) die("%s (or --work-tree=<directory>) not allowed without " "specifying %s (or --git-dir=<directory>)", GIT_WORK_TREE_ENVIRONMENT, @@ -510,10 +511,18 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) if (!getcwd(git_work_tree_cfg, PATH_MAX)) die_errno ("Cannot access current working directory"); } + if (work_tree) + set_git_work_tree(make_absolute_path(work_tree)); + else + set_git_work_tree(git_work_tree_cfg); if (access(get_git_work_tree(), X_OK)) die_errno ("Cannot access work tree '%s'", get_git_work_tree()); } + else { + if (work_tree) + set_git_work_tree(make_absolute_path(work_tree)); + } set_git_dir(make_absolute_path(git_dir)); diff --git a/builtin/merge.c b/builtin/merge.c index 42fff387e6..9403747b6a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -582,7 +582,8 @@ static void write_tree_trivial(unsigned char *sha1) die("git write-tree failed to write a tree"); } -int try_merge_command(const char *strategy, struct commit_list *common, +int try_merge_command(const char *strategy, size_t xopts_nr, + const char **xopts, struct commit_list *common, const char *head_arg, struct commit_list *remotes) { const char **args; @@ -680,7 +681,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, rollback_lock_file(lock); return clean ? 0 : 1; } else { - return try_merge_command(strategy, common, head_arg, remoteheads); + return try_merge_command(strategy, xopts_nr, xopts, + common, head_arg, remoteheads); } } diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index 1f773171cb..ea71977c83 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -212,16 +212,16 @@ static int command_loop(const char *child) char buffer[MAXCOMMAND]; while (1) { - size_t length; + size_t i; if (!fgets(buffer, MAXCOMMAND - 1, stdin)) { if (ferror(stdin)) die("Comammand input error"); exit(0); } /* Strip end of line characters. */ - length = strlen(buffer); - while (isspace((unsigned char)buffer[length - 1])) - buffer[--length] = 0; + i = strlen(buffer); + while (i > 0 && isspace(buffer[i - 1])) + buffer[--i] = 0; if (!strcmp(buffer, "capabilities")) { printf("*connect\n\n"); diff --git a/builtin/revert.c b/builtin/revert.c index bb6e9e83b7..dc1b702edc 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -44,7 +44,11 @@ static const char **commit_argv; static int allow_rerere_auto; static const char *me; + +/* Merge strategy. */ static const char *strategy; +static const char **xopts; +static size_t xopts_nr, xopts_alloc; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -55,6 +59,17 @@ static const char * const *revert_or_cherry_pick_usage(void) return action == REVERT ? revert_usage : cherry_pick_usage; } +static int option_parse_x(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + return 0; + + ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); + xopts[xopts_nr++] = xstrdup(arg); + return 0; +} + static void parse_args(int argc, const char **argv) { const char * const * usage_str = revert_or_cherry_pick_usage(); @@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv) OPT_INTEGER('m', "mainline", &mainline, "parent number"), OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"), + OPT_CALLBACK('X', "strategy-option", &xopts, "option", + "option for merge strategy", option_parse_x), OPT_END(), OPT_END(), OPT_END(), @@ -311,18 +328,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next, struct merge_options o; struct tree *result, *next_tree, *base_tree, *head_tree; int clean, index_fd; + const char **xopt; static struct lock_file index_lock; index_fd = hold_locked_index(&index_lock, 1); read_cache(); - /* - * NEEDSWORK: cherry-picking between branches with - * different end-of-line normalization is a pain; - * plumb in an option to set o.renormalize? - * (or better: arbitrary -X options) - */ init_merge_options(&o); o.ancestor = base ? base_label : "(empty tree)"; o.branch1 = "HEAD"; @@ -332,6 +344,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next, next_tree = next ? next->tree : empty_tree(); base_tree = base ? base->tree : empty_tree(); + for (xopt = xopts; xopt != xopts + xopts_nr; xopt++) + parse_merge_opt(&o, *xopt); + clean = merge_trees(&o, head_tree, next_tree, base_tree, &result); @@ -503,7 +518,7 @@ static int do_pick_commit(void) commit_list_insert(base, &common); commit_list_insert(next, &remotes); - res = try_merge_command(strategy, common, + res = try_merge_command(strategy, xopts_nr, xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 8663ccaa99..da695815e2 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -243,7 +243,7 @@ static void join_revs(struct commit_list **list_p, if (mark_seen(p, seen_p) && !still_interesting) extra--; p->object.flags |= flags; - insert_by_date(p, list_p); + commit_list_insert_by_date(p, list_p); } } @@ -859,7 +859,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) */ commit->object.flags |= flag; if (commit->object.flags == flag) - insert_by_date(commit, &list); + commit_list_insert_by_date(commit, &list); rev[num_rev] = commit; } for (i = 0; i < num_rev; i++) @@ -868,7 +868,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (0 <= extra) join_revs(&list, &seen, num_rev, extra); - sort_by_date(&seen); + commit_list_sort_by_date(&seen); if (merge_base) return show_merge_base(seen, num_rev); @@ -200,7 +200,7 @@ int create_bundle(struct bundle_header *header, const char *path, int bundle_fd = -1; int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); - const char **argv_pack = xmalloc(5 * sizeof(const char *)); + const char **argv_pack = xmalloc(6 * sizeof(const char *)); int i, ref_count = 0; char buffer[1024]; struct rev_info revs; @@ -346,7 +346,8 @@ int create_bundle(struct bundle_header *header, const char *path, argv_pack[1] = "--all-progress-implied"; argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; - argv_pack[4] = NULL; + argv_pack[4] = "--delta-base-offset"; + argv_pack[5] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_pack; rls.in = -1; @@ -170,26 +170,26 @@ struct cache_entry { * * In-memory only flags */ -#define CE_UPDATE (0x10000) -#define CE_REMOVE (0x20000) -#define CE_UPTODATE (0x40000) -#define CE_ADDED (0x80000) +#define CE_UPDATE (1 << 16) +#define CE_REMOVE (1 << 17) +#define CE_UPTODATE (1 << 18) +#define CE_ADDED (1 << 19) -#define CE_HASHED (0x100000) -#define CE_UNHASHED (0x200000) -#define CE_CONFLICTED (0x800000) +#define CE_HASHED (1 << 20) +#define CE_UNHASHED (1 << 21) +#define CE_WT_REMOVE (1 << 22) /* remove in work directory */ +#define CE_CONFLICTED (1 << 23) -#define CE_WT_REMOVE (0x400000) /* remove in work directory */ - -#define CE_UNPACKED (0x1000000) +#define CE_UNPACKED (1 << 24) +#define CE_NEW_SKIP_WORKTREE (1 << 25) /* * Extended on-disk flags */ -#define CE_INTENT_TO_ADD 0x20000000 -#define CE_SKIP_WORKTREE 0x40000000 +#define CE_INTENT_TO_ADD (1 << 29) +#define CE_SKIP_WORKTREE (1 << 30) /* CE_EXTENDED2 is for future extension */ -#define CE_EXTENDED2 0x80000000 +#define CE_EXTENDED2 (1 << 31) #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE) @@ -987,6 +987,7 @@ extern int git_config_parse_parameter(const char *text); extern int git_config_parse_environment(void); extern int git_config_from_parameters(config_fn_t fn, void *data); extern int git_config(config_fn_t fn, void *); +extern int git_config_early(config_fn_t fn, void *, const char *repo_config); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); @@ -1066,6 +1067,7 @@ __attribute__((format (printf, 1, 2))) extern void trace_printf(const char *format, ...); __attribute__((format (printf, 2, 3))) extern void trace_argv_printf(const char **argv, const char *format, ...); +extern void trace_repo_setup(const char *prefix); /* convert.c */ /* returns 1 if *dst was used */ @@ -245,10 +245,10 @@ int unregister_shallow(const unsigned char *sha1) return 0; } -int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) +int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size) { - char *tail = buffer; - char *bufptr = buffer; + const char *tail = buffer; + const char *bufptr = buffer; unsigned char parent[20]; struct commit_list **pptr; struct commit_graft *graft; @@ -374,7 +374,7 @@ void free_commit_list(struct commit_list *list) } } -struct commit_list * insert_by_date(struct commit *item, struct commit_list **list) +struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list) { struct commit_list **pp = list; struct commit_list *p; @@ -388,11 +388,11 @@ struct commit_list * insert_by_date(struct commit *item, struct commit_list **li } -void sort_by_date(struct commit_list **list) +void commit_list_sort_by_date(struct commit_list **list) { struct commit_list *ret = NULL; while (*list) { - insert_by_date((*list)->item, &ret); + commit_list_insert_by_date((*list)->item, &ret); *list = (*list)->next; } *list = ret; @@ -412,7 +412,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list, struct commit *commit = parents->item; if (!parse_commit(commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; - insert_by_date(commit, list); + commit_list_insert_by_date(commit, list); } parents = parents->next; } @@ -501,7 +501,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) /* process the list in topological order */ if (!lifo) - sort_by_date(&work); + commit_list_sort_by_date(&work); pptr = list; *list = NULL; @@ -527,7 +527,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) */ if (--parent->indegree == 1) { if (!lifo) - insert_by_date(parent, &work); + commit_list_insert_by_date(parent, &work); else commit_list_insert(parent, &work); } @@ -587,10 +587,10 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co } one->object.flags |= PARENT1; - insert_by_date(one, &list); + commit_list_insert_by_date(one, &list); for (i = 0; i < n; i++) { twos[i]->object.flags |= PARENT2; - insert_by_date(twos[i], &list); + commit_list_insert_by_date(twos[i], &list); } while (interesting(list)) { @@ -608,7 +608,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co if (flags == (PARENT1 | PARENT2)) { if (!(commit->object.flags & RESULT)) { commit->object.flags |= RESULT; - insert_by_date(commit, &result); + commit_list_insert_by_date(commit, &result); } /* Mark parents of a found merge stale */ flags |= STALE; @@ -622,7 +622,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co if (parse_commit(p)) return NULL; p->object.flags |= flags; - insert_by_date(p, &list); + commit_list_insert_by_date(p, &list); } } @@ -632,7 +632,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co while (list) { struct commit_list *next = list->next; if (!(list->item->object.flags & STALE)) - insert_by_date(list->item, &result); + commit_list_insert_by_date(list->item, &result); free(list); list = next; } @@ -725,7 +725,7 @@ struct commit_list *get_merge_bases_many(struct commit *one, result = NULL; for (i = 0; i < cnt; i++) { if (rslt[i]) - insert_by_date(rslt[i], &result); + commit_list_insert_by_date(rslt[i], &result); } free(rslt); return result; @@ -38,21 +38,21 @@ struct commit *lookup_commit_reference_gently(const unsigned char *sha1, int quiet); struct commit *lookup_commit_reference_by_name(const char *name); -int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size); - +int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size); int parse_commit(struct commit *item); /* Find beginning and length of commit subject. */ int find_commit_subject(const char *commit_buffer, const char **subject); -struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p); +struct commit_list *commit_list_insert(struct commit *item, + struct commit_list **list); unsigned commit_list_count(const struct commit_list *l); -struct commit_list * insert_by_date(struct commit *item, struct commit_list **list); +struct commit_list *commit_list_insert_by_date(struct commit *item, + struct commit_list **list); +void commit_list_sort_by_date(struct commit_list **list); void free_commit_list(struct commit_list *list); -void sort_by_date(struct commit_list **list); - /* Commit formats */ enum cmit_fmt { CMIT_FMT_RAW, @@ -852,10 +852,9 @@ int git_config_from_parameters(config_fn_t fn, void *data) return 0; } -int git_config(config_fn_t fn, void *data) +int git_config_early(config_fn_t fn, void *data, const char *repo_config) { int ret = 0, found = 0; - char *repo_config = NULL; const char *home = NULL; /* Setting $GIT_CONFIG makes git read _only_ the given config file. */ @@ -877,12 +876,10 @@ int git_config(config_fn_t fn, void *data) free(user_config); } - repo_config = git_pathdup("config"); - if (!access(repo_config, R_OK)) { + if (repo_config && !access(repo_config, R_OK)) { ret += git_config_from_file(fn, repo_config, data); found += 1; } - free(repo_config); ret += git_config_from_parameters(fn, data); if (config_parameters) @@ -891,6 +888,18 @@ int git_config(config_fn_t fn, void *data) return ret == 0 ? found : ret; } +int git_config(config_fn_t fn, void *data) +{ + char *repo_config = NULL; + int ret; + + repo_config = git_pathdup("config"); + ret = git_config_early(fn, data, repo_config); + if (repo_config) + free(repo_config); + return ret; +} + /* * Find all the stuff for git_config_set() below. */ diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 803da09a12..893b7716ca 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -327,11 +327,168 @@ __gitcomp_1 () done } +# The following function is based on code from: +# +# bash_completion - programmable completion functions for bash 3.2+ +# +# Copyright © 2006-2008, Ian Macdonald <ian@caliban.org> +# © 2009-2010, Bash Completion Maintainers +# <bash-completion-devel@lists.alioth.debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The latest version of this software can be obtained here: +# +# http://bash-completion.alioth.debian.org/ +# +# RELEASE: 2.x + +# This function can be used to access a tokenized list of words +# on the command line: +# +# __git_reassemble_comp_words_by_ref '=:' +# if test "${words_[cword_-1]}" = -w +# then +# ... +# fi +# +# The argument should be a collection of characters from the list of +# word completion separators (COMP_WORDBREAKS) to treat as ordinary +# characters. +# +# This is roughly equivalent to going back in time and setting +# COMP_WORDBREAKS to exclude those characters. The intent is to +# make option types like --date=<type> and <rev>:<path> easy to +# recognize by treating each shell word as a single token. +# +# It is best not to set COMP_WORDBREAKS directly because the value is +# shared with other completion scripts. By the time the completion +# function gets called, COMP_WORDS has already been populated so local +# changes to COMP_WORDBREAKS have no effect. +# +# Output: words_, cword_, cur_. + +__git_reassemble_comp_words_by_ref() +{ + local exclude i j first + # Which word separators to exclude? + exclude="${1//[^$COMP_WORDBREAKS]}" + cword_=$COMP_CWORD + if [ -z "$exclude" ]; then + words_=("${COMP_WORDS[@]}") + return + fi + # List of word completion separators has shrunk; + # re-assemble words to complete. + for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do + # Append each nonempty word consisting of just + # word separator characters to the current word. + first=t + while + [ $i -gt 0 ] && + [ -n "${COMP_WORDS[$i]}" ] && + # word consists of excluded word separators + [ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ] + do + # Attach to the previous token, + # unless the previous token is the command name. + if [ $j -ge 2 ] && [ -n "$first" ]; then + ((j--)) + fi + first= + words_[$j]=${words_[j]}${COMP_WORDS[i]} + if [ $i = $COMP_CWORD ]; then + cword_=$j + fi + if (($i < ${#COMP_WORDS[@]} - 1)); then + ((i++)) + else + # Done. + return + fi + done + words_[$j]=${words_[j]}${COMP_WORDS[i]} + if [ $i = $COMP_CWORD ]; then + cword_=$j + fi + done +} + +if ! type _get_comp_words_by_ref >/dev/null 2>&1; then +if [[ -z ${ZSH_VERSION:+set} ]]; then +_get_comp_words_by_ref () +{ + local exclude cur_ words_ cword_ + if [ "$1" = "-n" ]; then + exclude=$2 + shift 2 + fi + __git_reassemble_comp_words_by_ref "$exclude" + cur_=${words_[cword_]} + while [ $# -gt 0 ]; do + case "$1" in + cur) + cur=$cur_ + ;; + prev) + prev=${words_[$cword_-1]} + ;; + words) + words=("${words_[@]}") + ;; + cword) + cword=$cword_ + ;; + esac + shift + done +} +else +_get_comp_words_by_ref () +{ + while [ $# -gt 0 ]; do + case "$1" in + cur) + cur=${COMP_WORDS[COMP_CWORD]} + ;; + prev) + prev=${COMP_WORDS[COMP_CWORD-1]} + ;; + words) + words=("${COMP_WORDS[@]}") + ;; + cword) + cword=$COMP_CWORD + ;; + -n) + # assume COMP_WORDBREAKS is already set sanely + shift + ;; + esac + shift + done +} +fi +fi + # __gitcomp accepts 1, 2, 3, or 4 arguments # generates completion reply with compgen __gitcomp () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur if [ $# -gt 2 ]; then cur="$3" fi @@ -392,7 +549,8 @@ __git_tags () __git_refs () { local i is_hash=y dir="$(__gitdir "${1-}")" track="${2-}" - local cur="${COMP_WORDS[COMP_CWORD]}" format refs + local cur format refs + _get_comp_words_by_ref -n =: cur if [ -d "$dir" ]; then case "$cur" in refs|refs/*) @@ -506,7 +664,8 @@ __git_compute_merge_strategies () __git_complete_file () { - local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}" + local pfx ls ref cur + _get_comp_words_by_ref -n =: cur case "$cur" in ?*:*) ref="${cur%%:*}" @@ -554,7 +713,8 @@ __git_complete_file () __git_complete_revlist () { - local pfx cur="${COMP_WORDS[COMP_CWORD]}" + local pfx cur + _get_comp_words_by_ref -n =: cur case "$cur" in *...*) pfx="${cur%...*}..." @@ -574,11 +734,12 @@ __git_complete_revlist () __git_complete_remote_or_refspec () { - local cmd="${COMP_WORDS[1]}" - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur words cword + _get_comp_words_by_ref -n =: cur words cword + local cmd="${words[1]}" local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" + while [ $c -lt $cword ]; do + i="${words[c]}" case "$i" in --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; --all) @@ -646,13 +807,14 @@ __git_complete_remote_or_refspec () __git_complete_strategy () { + local cur prev + _get_comp_words_by_ref -n =: cur prev __git_compute_merge_strategies - case "${COMP_WORDS[COMP_CWORD-1]}" in + case "$prev" in -s|--strategy) __gitcomp "$__git_merge_strategies" return 0 esac - local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --strategy=*) __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}" @@ -824,10 +986,10 @@ __git_aliased_command () # __git_find_on_cmdline requires 1 argument __git_find_on_cmdline () { - local word subcommand c=1 - - while [ $c -lt $COMP_CWORD ]; do - word="${COMP_WORDS[c]}" + local word subcommand c=1 words cword + _get_comp_words_by_ref -n =: words cword + while [ $c -lt $cword ]; do + word="${words[c]}" for subcommand in $1; do if [ "$subcommand" = "$word" ]; then echo "$subcommand" @@ -840,9 +1002,10 @@ __git_find_on_cmdline () __git_has_doubledash () { - local c=1 - while [ $c -lt $COMP_CWORD ]; do - if [ "--" = "${COMP_WORDS[c]}" ]; then + local c=1 words cword + _get_comp_words_by_ref -n =: words cword + while [ $c -lt $cword ]; do + if [ "--" = "${words[c]}" ]; then return 0 fi c=$((++c)) @@ -854,7 +1017,8 @@ __git_whitespacelist="nowarn warn error error-all fix" _git_am () { - local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + local cur dir="$(__gitdir)" + _get_comp_words_by_ref -n =: cur if [ -d "$dir"/rebase-apply ]; then __gitcomp "--skip --continue --resolved --abort" return @@ -878,7 +1042,8 @@ _git_am () _git_apply () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --whitespace=*) __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" @@ -901,7 +1066,8 @@ _git_add () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -915,7 +1081,8 @@ _git_add () _git_archive () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --format=*) __gitcomp "$(git archive --list)" "" "${cur##--format=}" @@ -963,10 +1130,11 @@ _git_bisect () _git_branch () { - local i c=1 only_local_ref="n" has_r="n" + local i c=1 only_local_ref="n" has_r="n" cur words cword - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" + _get_comp_words_by_ref -n =: cur words cword + while [ $c -lt $cword ]; do + i="${words[c]}" case "$i" in -d|-m) only_local_ref="y" ;; -r) has_r="y" ;; @@ -974,7 +1142,7 @@ _git_branch () c=$((++c)) done - case "${COMP_WORDS[COMP_CWORD]}" in + case "$cur" in --*) __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev @@ -994,8 +1162,10 @@ _git_branch () _git_bundle () { - local cmd="${COMP_WORDS[2]}" - case "$COMP_CWORD" in + local words cword + _get_comp_words_by_ref -n =: words cword + local cmd="${words[2]}" + case "$cword" in 2) __gitcomp "create list-heads verify unbundle" ;; @@ -1016,7 +1186,8 @@ _git_checkout () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --conflict=*) __gitcomp "diff3 merge" "" "${cur##--conflict=}" @@ -1046,7 +1217,8 @@ _git_cherry () _git_cherry_pick () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--edit --no-commit" @@ -1061,7 +1233,8 @@ _git_clean () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--dry-run --quiet" @@ -1073,7 +1246,8 @@ _git_clean () _git_clone () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -1100,7 +1274,8 @@ _git_commit () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --cleanup=*) __gitcomp "default strip verbatim whitespace @@ -1135,7 +1310,8 @@ _git_commit () _git_describe () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -1167,7 +1343,8 @@ _git_diff () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex @@ -1188,7 +1365,8 @@ _git_difftool () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --tool=*) __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}" @@ -1213,7 +1391,8 @@ __git_fetch_options=" _git_fetch () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "$__git_fetch_options" @@ -1225,7 +1404,8 @@ _git_fetch () _git_format_patch () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --thread=*) __gitcomp " @@ -1257,7 +1437,8 @@ _git_format_patch () _git_fsck () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -1272,7 +1453,8 @@ _git_fsck () _git_gc () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--prune --aggressive" @@ -1291,7 +1473,8 @@ _git_grep () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -1314,7 +1497,8 @@ _git_grep () _git_help () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--all --info --man --web" @@ -1332,7 +1516,8 @@ _git_help () _git_init () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --shared=*) __gitcomp " @@ -1352,7 +1537,8 @@ _git_ls_files () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--cached --deleted --modified --others --ignored @@ -1406,12 +1592,13 @@ _git_log () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" local g="$(git rev-parse --git-dir 2>/dev/null)" local merge="" if [ -f "$g/MERGE_HEAD" ]; then merge="--merge" fi + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --pretty=*) __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) @@ -1465,7 +1652,8 @@ _git_merge () { __git_complete_strategy && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "$__git_merge_options" @@ -1476,7 +1664,8 @@ _git_merge () _git_mergetool () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --tool=*) __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}" @@ -1497,7 +1686,8 @@ _git_merge_base () _git_mv () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--dry-run" @@ -1516,14 +1706,15 @@ _git_notes () { local subcommands='add append copy edit list prune remove show' local subcommand="$(__git_find_on_cmdline "$subcommands")" - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur words cword + _get_comp_words_by_ref -n =: cur words cword case "$subcommand,$cur" in ,--*) __gitcomp '--ref' ;; ,*) - case "${COMP_WORDS[COMP_CWORD-1]}" in + case "${words[cword-1]}" in --ref) __gitcomp "$(__git_refs)" ;; @@ -1551,7 +1742,7 @@ _git_notes () prune,*) ;; *) - case "${COMP_WORDS[COMP_CWORD-1]}" in + case "${words[cword-1]}" in -m|-F) ;; *) @@ -1566,7 +1757,8 @@ _git_pull () { __git_complete_strategy && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -1582,8 +1774,9 @@ _git_pull () _git_push () { - local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[COMP_CWORD-1]}" in + local cur prev + _get_comp_words_by_ref -n =: cur prev + case "$prev" in --repo) __gitcomp "$(__git_remotes)" return @@ -1606,7 +1799,9 @@ _git_push () _git_rebase () { - local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + local dir="$(__gitdir)" + local cur + _get_comp_words_by_ref -n =: cur if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then __gitcomp "--continue --skip --abort" return @@ -1648,7 +1843,8 @@ __git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all" _git_send_email () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --confirm=*) __gitcomp " @@ -1690,9 +1886,11 @@ _git_stage () __git_config_get_set_variables () { - local prevword word config_file= c=$COMP_CWORD + local words cword + _get_comp_words_by_ref -n =: words cword + local prevword word config_file= c=$cword while [ $c -gt 1 ]; do - word="${COMP_WORDS[c]}" + word="${words[c]}" case "$word" in --global|--system|--file=*) config_file="$word" @@ -1720,9 +1918,9 @@ __git_config_get_set_variables () _git_config () { - local cur="${COMP_WORDS[COMP_CWORD]}" - local prv="${COMP_WORDS[COMP_CWORD-1]}" - case "$prv" in + local cur prev + _get_comp_words_by_ref -n =: cur prev + case "$prev" in branch.*.remote) __gitcomp "$(__git_remotes)" return @@ -1732,13 +1930,13 @@ _git_config () return ;; remote.*.fetch) - local remote="${prv#remote.}" + local remote="${prev#remote.}" remote="${remote%.fetch}" __gitcomp "$(__git_refs_remotes "$remote")" return ;; remote.*.push) - local remote="${prv#remote.}" + local remote="${prev#remote.}" remote="${remote%.push}" __gitcomp "$(git --git-dir="$(__gitdir)" \ for-each-ref --format='%(refname):%(refname)' \ @@ -2191,7 +2389,8 @@ _git_reset () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--merge --mixed --hard --soft --patch" @@ -2203,7 +2402,8 @@ _git_reset () _git_revert () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--edit --mainline --no-edit --no-commit --signoff" @@ -2217,7 +2417,8 @@ _git_rm () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--cached --dry-run --ignore-unmatch --quiet" @@ -2231,7 +2432,8 @@ _git_shortlog () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -2249,7 +2451,8 @@ _git_show () { __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --pretty=*) __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) @@ -2273,7 +2476,8 @@ _git_show () _git_show_branch () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " @@ -2290,7 +2494,8 @@ _git_show_branch () _git_stash () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur local save_opts='--keep-index --no-keep-index --quiet --patch' local subcommands='save list show apply clear drop pop create branch' local subcommand="$(__git_find_on_cmdline "$subcommands")" @@ -2335,7 +2540,8 @@ _git_submodule () local subcommands="add status init update summary foreach sync" if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp "--quiet --cached" @@ -2379,7 +2585,8 @@ _git_svn () --edit --rmdir --find-copies-harder --copy-similarity= " - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur + _get_comp_words_by_ref -n =: cur case "$subcommand,$cur" in fetch,--*) __gitcomp "--revision= --fetch-all $fc_opts" @@ -2451,8 +2658,10 @@ _git_svn () _git_tag () { local i c=1 f=0 - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" + local words cword prev + _get_comp_words_by_ref -n =: words cword prev + while [ $c -lt $cword ]; do + i="${words[c]}" case "$i" in -d|-v) __gitcomp "$(__git_tags)" @@ -2465,7 +2674,7 @@ _git_tag () c=$((++c)) done - case "${COMP_WORDS[COMP_CWORD-1]}" in + case "$prev" in -m|-F) COMPREPLY=() ;; @@ -2496,8 +2705,10 @@ _git () setopt KSH_TYPESET fi - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" + local cur words cword + _get_comp_words_by_ref -n =: cur words cword + while [ $c -lt $cword ]; do + i="${words[c]}" case "$i" in --git-dir=*) __git_dir="${i#--git-dir=}" ;; --bare) __git_dir="." ;; @@ -2509,7 +2720,7 @@ _git () done if [ -z "$command" ]; then - case "${COMP_WORDS[COMP_CWORD]}" in + case "$cur" in --*) __gitcomp " --paginate --no-pager @@ -2547,12 +2758,13 @@ _gitk () __git_has_doubledash && return - local cur="${COMP_WORDS[COMP_CWORD]}" + local cur local g="$(__gitdir)" local merge="" if [ -f "$g/MERGE_HEAD" ]; then merge="--merge" fi + _get_comp_words_by_ref -n =: cur case "$cur" in --*) __gitcomp " diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh index 60a05a8b97..6bf155cbdb 100755 --- a/contrib/examples/git-revert.sh +++ b/contrib/examples/git-revert.sh @@ -26,6 +26,7 @@ require_work_tree cd_to_toplevel no_commit= +xopt= while case "$#" in 0) break ;; esac do case "$1" in @@ -44,6 +45,16 @@ do -x|--i-really-want-to-expose-my-private-commit-object-name) replay= ;; + -X?*) + xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")" + ;; + --strategy-option=*) + xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")" + ;; + -X|--strategy-option) + shift + xopt="$xopt$(git rev-parse --sq-quote "--$1")" + ;; -*) usage ;; @@ -159,7 +170,7 @@ export GITHEAD_$head GITHEAD_$next # and $prev on top of us (when reverting), or the change between # $prev and $commit on top of us (when cherry-picking or replaying). -git-merge-recursive $base -- $head $next && +eval "git merge-recursive $xopt $base -- $head $next" && result=$(git-write-tree 2>/dev/null) || { mv -f .msg "$GIT_DIR/MERGE_MSG" { diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index f99ea95850..21989fc6ab 100755 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -709,7 +709,7 @@ if [ -z "$GIT_DIR" ]; then exit 1 fi -projectdesc=$(sed -ne '1p' "$GIT_DIR/description") +projectdesc=$(sed -ne '1p' "$GIT_DIR/description" 2>/dev/null) # Check if the description is unchanged from it's default, and shorten it to # a more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null @@ -1,6 +1,7 @@ #include "cache.h" #include "attr.h" #include "run-command.h" +#include "quote.h" /* * convert.c - convert a file when checking it out and checking it in. @@ -318,6 +319,7 @@ struct filter_params { const char *src; unsigned long size; const char *cmd; + const char *path; }; static int filter_buffer(int in, int out, void *data) @@ -330,7 +332,23 @@ static int filter_buffer(int in, int out, void *data) int write_err, status; const char *argv[] = { NULL, NULL }; - argv[0] = params->cmd; + /* apply % substitution to cmd */ + struct strbuf cmd = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + struct strbuf_expand_dict_entry dict[] = { + { "f", NULL, }, + { NULL, NULL, }, + }; + + /* quote the path to preserve spaces, etc. */ + sq_quote_buf(&path, params->path); + dict[0].value = path.buf; + + /* expand all %f with the quoted path */ + strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict); + strbuf_release(&path); + + argv[0] = cmd.buf; memset(&child_process, 0, sizeof(child_process)); child_process.argv = argv; @@ -350,6 +368,8 @@ static int filter_buffer(int in, int out, void *data) status = finish_command(&child_process); if (status) error("external filter %s failed %d", params->cmd, status); + + strbuf_release(&cmd); return (write_err || status); } @@ -377,6 +397,7 @@ static int apply_filter(const char *path, const char *src, size_t len, params.src = src; params.size = len; params.cmd = cmd; + params.path = path; fflush(NULL); if (start_async(&async)) @@ -1226,9 +1226,10 @@ int main(int argc, char **argv) /* prepare argv for serving-processes */ cld_argv = xmalloc(sizeof (char *) * (argc + 2)); - for (i = 0; i < argc; ++i) - cld_argv[i] = argv[i]; - cld_argv[argc] = "--serve"; + cld_argv[0] = argv[0]; /* git-daemon */ + cld_argv[1] = "--serve"; + for (i = 1; i < argc; ++i) + cld_argv[i+1] = argv[i]; cld_argv[argc+1] = NULL; return serve(&listen_addr, listen_port, cred); @@ -253,6 +253,18 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size) return data; } +void free_excludes(struct exclude_list *el) +{ + int i; + + for (i = 0; i < el->nr; i++) + free(el->excludes[i]); + free(el->excludes); + + el->nr = 0; + el->excludes = NULL; +} + int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, @@ -389,13 +401,6 @@ int excluded_from_list(const char *pathname, int to_exclude = x->to_exclude; if (x->flags & EXC_FLAG_MUSTBEDIR) { - if (!dtype) { - if (!prefixcmp(pathname, exclude) && - pathname[x->patternlen] == '/') - return to_exclude; - else - continue; - } if (*dtype == DT_UNKNOWN) *dtype = get_dtype(NULL, pathname, pathlen); if (*dtype != DT_DIR) @@ -78,6 +78,7 @@ extern int add_excludes_from_file_to_list(const char *fname, const char *base, i extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which); +extern void free_excludes(struct exclude_list *el); extern int file_exists(const char *); extern char *get_relative_cwd(char *buffer, int size, const char *dir); diff --git a/environment.c b/environment.c index c79f2a9b56..9564475f42 100644 --- a/environment.c +++ b/environment.c @@ -139,30 +139,20 @@ static int git_work_tree_initialized; */ void set_git_work_tree(const char *new_work_tree) { - if (is_bare_repository_cfg >= 0) - die("cannot set work tree after initialization"); + if (git_work_tree_initialized) { + new_work_tree = make_absolute_path(new_work_tree); + if (strcmp(new_work_tree, work_tree)) + die("internal error: work tree has already been set\n" + "Current worktree: %s\nNew worktree: %s", + work_tree, new_work_tree); + return; + } git_work_tree_initialized = 1; - free(work_tree); work_tree = xstrdup(make_absolute_path(new_work_tree)); - is_bare_repository_cfg = 0; } const char *get_git_work_tree(void) { - if (!git_work_tree_initialized) { - work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT); - /* core.bare = true overrides implicit and config work tree */ - if (!work_tree && is_bare_repository_cfg < 1) { - work_tree = git_work_tree_cfg; - /* make_absolute_path also normalizes the path */ - if (work_tree && !is_absolute_path(work_tree)) - work_tree = xstrdup(make_absolute_path(git_path("%s", work_tree))); - } else if (work_tree) - work_tree = xstrdup(make_absolute_path(work_tree)); - git_work_tree_initialized = 1; - if (work_tree) - is_bare_repository_cfg = 0; - } return work_tree; } diff --git a/exec_cmd.c b/exec_cmd.c index bf225706ee..38545e8bfd 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -3,7 +3,6 @@ #include "quote.h" #define MAX_ARGS 32 -extern char **environ; static const char *argv_exec_path; static const char *argv0_path; diff --git a/fast-import.c b/fast-import.c index 785776086c..970d8470ed 100644 --- a/fast-import.c +++ b/fast-import.c @@ -2231,6 +2231,12 @@ static void file_change_m(struct branch *b) p = uq.buf; } + /* Git does not track empty, non-toplevel directories. */ + if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) { + tree_content_remove(&b->branch_tree, p, NULL); + return; + } + if (S_ISGITLINK(mode)) { if (inline_data) die("Git links cannot be specified 'inline': %s", @@ -2985,6 +2991,8 @@ static int parse_one_feature(const char *feature, int from_stream) relative_marks_paths = 0; } else if (!prefixcmp(feature, "force")) { force_update = 1; + } else if (!strcmp(feature, "notes")) { + ; /* do nothing; we have the feature */ } else { return 0; } @@ -68,9 +68,31 @@ sq () { stop_here () { echo "$1" >"$dotest/next" + git rev-parse --verify -q HEAD >"$dotest/abort-safety" exit 1 } +safe_to_abort () { + if test -f "$dotest/dirtyindex" + then + return 1 + fi + + if ! test -s "$dotest/abort-safety" + then + return 0 + fi + + abort_safety=$(cat "$dotest/abort-safety") + if test "z$(git rev-parse --verify -q HEAD)" = "z$abort_safety" + then + return 0 + fi + echo >&2 "You seem to have moved HEAD since the last 'am' failure." + echo >&2 "Not rewinding to ORIG_HEAD" + return 1 +} + stop_here_user_resolve () { if [ -n "$resolvemsg" ]; then printf '%s\n' "$resolvemsg" @@ -419,10 +441,11 @@ then exec git rebase --abort fi git rerere clear - test -f "$dotest/dirtyindex" || { + if safe_to_abort + then git read-tree --reset -u HEAD ORIG_HEAD git reset ORIG_HEAD - } + fi rm -fr "$dotest" exit ;; esac @@ -554,13 +577,6 @@ then resume= fi -if test "$this" -gt "$last" -then - say Nothing to do. - rm -fr "$dotest" - exit -fi - while test "$this" -le "$last" do msgnum=`printf "%0${prec}d" $this` diff --git a/git-cvsimport.perl b/git-cvsimport.perl index d27abfe7f3..8e683e5478 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -90,23 +90,40 @@ sub write_author_info($) { } # convert getopts specs for use by git config +my %longmap = ( + 'A:' => 'authors-file', + 'M:' => 'merge-regex', + 'P:' => undef, + 'R' => 'track-revisions', + 'S:' => 'ignore-paths', +); + sub read_repo_config { - # Split the string between characters, unless there is a ':' - # So "abc:de" becomes ["a", "b", "c:", "d", "e"] + # Split the string between characters, unless there is a ':' + # So "abc:de" becomes ["a", "b", "c:", "d", "e"] my @opts = split(/ *(?!:)/, shift); foreach my $o (@opts) { my $key = $o; $key =~ s/://g; my $arg = 'git config'; $arg .= ' --bool' if ($o !~ /:$/); - - chomp(my $tmp = `$arg --get cvsimport.$key`); + my $ckey = $key; + + if (exists $longmap{$o}) { + # An uppercase option like -R cannot be + # expressed in the configuration, as the + # variable names are downcased. + $ckey = $longmap{$o}; + next if (! defined $ckey); + $ckey =~ s/-//g; + } + chomp(my $tmp = `$arg --get cvsimport.$ckey`); if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) { - no strict 'refs'; - my $opt_name = "opt_" . $key; - if (!$$opt_name) { - $$opt_name = $tmp; - } + no strict 'refs'; + my $opt_name = "opt_" . $key; + if (!$$opt_name) { + $$opt_name = $tmp; + } } } } diff --git a/git-difftool.perl b/git-difftool.perl index e95e4ad973..ced1615e21 100755 --- a/git-difftool.perl +++ b/git-difftool.perl @@ -52,6 +52,7 @@ sub generate_command my @command = (exe('git'), 'diff'); my $skip_next = 0; my $idx = -1; + my $prompt = ''; for my $arg (@ARGV) { $idx++; if ($skip_next) { @@ -89,13 +90,11 @@ sub generate_command next; } if ($arg eq '-y' || $arg eq '--no-prompt') { - $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; - delete $ENV{GIT_DIFFTOOL_PROMPT}; + $prompt = 'no'; next; } if ($arg eq '--prompt') { - $ENV{GIT_DIFFTOOL_PROMPT} = 'true'; - delete $ENV{GIT_DIFFTOOL_NO_PROMPT}; + $prompt = 'yes'; next; } if ($arg eq '-h' || $arg eq '--help') { @@ -103,6 +102,11 @@ sub generate_command } push @command, $arg; } + if ($prompt eq 'yes') { + $ENV{GIT_DIFFTOOL_PROMPT} = 'true'; + } elsif ($prompt eq 'no') { + $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; + } return @command } diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index a5ffd9a31e..5873ba4bc3 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -894,7 +894,7 @@ first and then run 'git rebase --continue' again." if test ! -z "$1" then - output git checkout "$1" || + output git checkout "$1" -- || die "Could not checkout $1" fi @@ -1021,7 +1021,7 @@ first and then run 'git rebase --continue' again." # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message -# x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails +# x, exec = run command (the rest of the line) using shell # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. diff --git a/git-rebase.sh b/git-rebase.sh index d8e1903026..cbb0ea90ed 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -482,6 +482,7 @@ case "$#" in then head_name="detached HEAD" else + echo >&2 "fatal: no such branch: $1" usage fi ;; @@ -513,7 +514,7 @@ then if test -z "$force_rebase" then # Lazily switch to the target branch if needed... - test -z "$switch_to" || git checkout "$switch_to" + test -z "$switch_to" || git checkout "$switch_to" -- say "Current branch $branch_name is up to date." exit 0 else diff --git a/git-submodule.sh b/git-submodule.sh index c21b77aee5..8b90589717 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -37,12 +37,24 @@ resolve_relative_url () die "remote ($remote) does not have a url defined in .git/config" url="$1" remoteurl=${remoteurl%/} + sep=/ while test -n "$url" do case "$url" in ../*) url="${url#../}" - remoteurl="${remoteurl%/*}" + case "$remoteurl" in + */*) + remoteurl="${remoteurl%/*}" + ;; + *:*) + remoteurl="${remoteurl%:*}" + sep=: + ;; + *) + die "cannot strip one component off url '$remoteurl'" + ;; + esac ;; ./*) url="${url#./}" @@ -51,7 +63,7 @@ resolve_relative_url () break;; esac done - echo "$remoteurl/${url%/}" + echo "$remoteurl$sep${url%/}" } # @@ -275,6 +275,10 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) use_pager = check_pager_config(p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; + + if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) && + startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */ + trace_repo_setup(prefix); } commit_pager_choice(); diff --git a/gitk-git/gitk b/gitk-git/gitk index e82c6bfede..e82c6bfede 100644..100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk diff --git a/gitweb/INSTALL b/gitweb/INSTALL index 823053173c..4964a679b3 100644 --- a/gitweb/INSTALL +++ b/gitweb/INSTALL @@ -237,6 +237,12 @@ Requirements - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename. - web server +The following optional Perl modules are required for extra features + - Digest::MD5 - for gravatar support + - CGI::Fast and FCGI - for running gitweb as FastCGI script + - HTML::TagCloud - for fancy tag cloud in project list view + - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds + Example web server configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 47796180d2..0779f12d61 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -250,13 +250,14 @@ our %highlight_ext = ( # main extensions, defining name of syntax; # see files in /usr/share/highlight/langDefs/ directory map { $_ => $_ } - qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl), + qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl sql make), # alternate extensions, see /etc/highlight/filetypes.conf 'h' => 'c', + map { $_ => 'sh' } qw(bash zsh ksh), map { $_ => 'cpp' } qw(cxx c++ cc), - map { $_ => 'php' } qw(php3 php4), + map { $_ => 'php' } qw(php3 php4 php5 phps), map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi' - 'mak' => 'make', + map { $_ => 'make'} qw(mak mk), map { $_ => 'xml' } qw(xhtml html htm), ); @@ -3464,11 +3465,10 @@ sub run_highlighter { my ($fd, $highlight, $syntax) = @_; return $fd unless ($highlight && defined $syntax); - close $fd - or die_error(404, "Reading blob failed"); + close $fd; open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ". quote_command($highlight_bin). - " --xhtml --fragment --syntax $syntax |" + " --fragment --syntax $syntax |" or die_error(500, "Couldn't open file or run syntax highlighter"); return $fd; } @@ -3601,10 +3601,15 @@ EOF insert_file($site_header); } - print "<div class=\"page_header\">\n" . - $cgi->a({-href => esc_url($logo_url), - -title => $logo_label}, - qq(<img src=").esc_url($logo).qq(" width="72" height="27" alt="git" class="logo"/>)); + print "<div class=\"page_header\">\n"; + if (defined $logo) { + print $cgi->a({-href => esc_url($logo_url), + -title => $logo_label}, + $cgi->img({-src => esc_url($logo), + -width => 72, -height => 27, + -alt => "git", + -class => "logo"})); + } print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / "; if (defined $project) { print $cgi->a({-href => href(action=>"summary")}, esc_html($project)); @@ -7169,7 +7174,7 @@ XML if (defined $favicon) { print "<icon>" . esc_url($favicon) . "</icon>\n"; } - if (defined $logo_url) { + if (defined $logo) { # not twice as wide as tall: 72 x 27 pixels print "<logo>" . esc_url($logo) . "</logo>\n"; } @@ -217,8 +217,10 @@ const char *fmt_ident(const char *name, const char *email, } strcpy(date, git_default_date); - if (!name_addr_only && date_str) - parse_date(date_str, date, sizeof(date)); + if (!name_addr_only && date_str && date_str[0]) { + if (parse_date(date_str, date, sizeof(date)) < 0) + die("invalid date format: %s", date_str); + } i = copy(buffer, sizeof(buffer), 0, name); i = add_raw(buffer, sizeof(buffer), i, " <"); diff --git a/ll-merge.c b/ll-merge.c index 007dd3e4d3..6ce512efc4 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -351,16 +351,13 @@ int ll_merge(mmbuffer_t *result_buf, const struct ll_merge_options *opts) { static struct git_attr_check check[2]; + static const struct ll_merge_options default_opts; const char *ll_driver_name = NULL; int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; const struct ll_merge_driver *driver; - if (!opts) { - struct ll_merge_options default_opts = {0}; - return ll_merge(result_buf, path, ancestor, ancestor_label, - ours, our_label, theirs, their_label, - &default_opts); - } + if (!opts) + opts = &default_opts; if (opts->renormalize) { normalize_file(ancestor, path); diff --git a/merge-recursive.h b/merge-recursive.h index c8135b0ec7..981ed6ac94 100644 --- a/merge-recursive.h +++ b/merge-recursive.h @@ -57,6 +57,8 @@ struct tree *write_tree_from_memory(struct merge_options *o); int parse_merge_opt(struct merge_options *out, const char *s); /* builtin/merge.c */ -int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes); +int try_merge_command(const char *strategy, size_t xopts_nr, + const char **xopts, struct commit_list *common, + const char *head_arg, struct commit_list *remotes); #endif @@ -1,8 +1,7 @@ #ifndef QUOTE_H #define QUOTE_H -#include <stddef.h> -#include <stdio.h> +struct strbuf; /* Help to copy the thing properly quoted for the shell safety. * any single quote is replaced with '\'', any exclamation point diff --git a/revision.c b/revision.c index ded881263b..7b9eaefae4 100644 --- a/revision.c +++ b/revision.c @@ -444,15 +444,15 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) commit->object.flags |= TREESAME; } -static void insert_by_date_cached(struct commit *p, struct commit_list **head, +static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head, struct commit_list *cached_base, struct commit_list **cache) { struct commit_list *new_entry; if (cached_base && p->date < cached_base->item->date) - new_entry = insert_by_date(p, &cached_base->next); + new_entry = commit_list_insert_by_date(p, &cached_base->next); else - new_entry = insert_by_date(p, head); + new_entry = commit_list_insert_by_date(p, head); if (cache && (!*cache || p->date < (*cache)->item->date)) *cache = new_entry; @@ -494,7 +494,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, if (p->object.flags & SEEN) continue; p->object.flags |= SEEN; - insert_by_date_cached(p, list, cached_base, cache_ptr); + commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr); } return 0; } @@ -521,7 +521,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, p->object.flags |= left_flag; if (!(p->object.flags & SEEN)) { p->object.flags |= SEEN; - insert_by_date_cached(p, list, cached_base, cache_ptr); + commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr); } if (revs->first_parent_only) break; @@ -1891,7 +1891,7 @@ int prepare_revision_walk(struct rev_info *revs) if (commit) { if (!(commit->object.flags & SEEN)) { commit->object.flags |= SEEN; - insert_by_date(commit, &revs->commits); + commit_list_insert_by_date(commit, &revs->commits); } } e++; diff --git a/run-command.c b/run-command.c index 2a1041ef65..f91e446c86 100644 --- a/run-command.c +++ b/run-command.c @@ -194,6 +194,7 @@ fail_pipe: } trace_argv_printf(cmd->argv, "trace: run_command:"); + fflush(NULL); #ifndef WIN32 { @@ -201,7 +202,6 @@ fail_pipe: if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; - fflush(NULL); cmd->pid = fork(); if (!cmd->pid) { /* @@ -208,24 +208,6 @@ int is_inside_work_tree(void) return inside_work_tree; } -/* - * set_work_tree() is only ever called if you set GIT_DIR explicitly. - * The old behaviour (which we retain here) is to set the work tree root - * to the cwd, unless overridden by the config, the command line, or - * GIT_WORK_TREE. - */ -static const char *set_work_tree(const char *dir) -{ - char buffer[PATH_MAX + 1]; - - if (!getcwd(buffer, sizeof(buffer))) - die ("Could not get the current working directory"); - git_work_tree_cfg = xstrdup(buffer); - inside_work_tree = 1; - - return NULL; -} - void setup_work_tree(void) { const char *work_tree, *git_dir; @@ -239,13 +221,33 @@ void setup_work_tree(void) git_dir = make_absolute_path(git_dir); if (!work_tree || chdir(work_tree)) die("This operation must be run in a work tree"); + + /* + * Make sure subsequent git processes find correct worktree + * if $GIT_WORK_TREE is set relative + */ + if (getenv(GIT_WORK_TREE_ENVIRONMENT)) + setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1); + set_git_dir(make_relative_path(git_dir, work_tree)); initialized = 1; } -static int check_repository_format_gently(int *nongit_ok) +static int check_repository_format_gently(const char *gitdir, int *nongit_ok) { - git_config(check_repository_format_version, NULL); + char repo_config[PATH_MAX+1]; + + /* + * git_config() can't be used here because it calls git_pathdup() + * to get $GIT_CONFIG/config. That call will make setup_git_env() + * set git_dir to ".git". + * + * We are in gitdir setup, no git dir has been found useable yet. + * Use a gentler version of git_config() to check if this repo + * is a good one. + */ + snprintf(repo_config, PATH_MAX, "%s/config", gitdir); + git_config_early(check_repository_format_version, NULL, repo_config); if (GIT_REPO_VERSION < repository_format_version) { if (!nongit_ok) die ("Expected git repo version <= %d, found %d", @@ -314,64 +316,124 @@ const char *read_gitfile_gently(const char *path) } static const char *setup_explicit_git_dir(const char *gitdirenv, - const char *work_tree_env, int *nongit_ok) + char *cwd, int len, + int *nongit_ok) { - static char buffer[1024 + 1]; - const char *retval; + const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); + const char *worktree; + char *gitfile; if (PATH_MAX - 40 < strlen(gitdirenv)) die("'$%s' too big", GIT_DIR_ENVIRONMENT); + + gitfile = (char*)read_gitfile_gently(gitdirenv); + if (gitfile) { + gitfile = xstrdup(gitfile); + gitdirenv = gitfile; + } + if (!is_git_directory(gitdirenv)) { if (nongit_ok) { *nongit_ok = 1; + free(gitfile); return NULL; } die("Not a git repository: '%s'", gitdirenv); } - if (!work_tree_env) { - retval = set_work_tree(gitdirenv); - /* config may override worktree */ - if (check_repository_format_gently(nongit_ok)) - return NULL; - return retval; + + if (check_repository_format_gently(gitdirenv, nongit_ok)) { + free(gitfile); + return NULL; } - if (check_repository_format_gently(nongit_ok)) + + /* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */ + if (work_tree_env) + set_git_work_tree(work_tree_env); + else if (is_bare_repository_cfg > 0) { + if (git_work_tree_cfg) /* #22.2, #30 */ + die("core.bare and core.worktree do not make sense"); + + /* #18, #26 */ + set_git_dir(gitdirenv); + free(gitfile); return NULL; - retval = get_relative_cwd(buffer, sizeof(buffer) - 1, - get_git_work_tree()); - if (!retval || !*retval) + } + else if (git_work_tree_cfg) { /* #6, #14 */ + if (is_absolute_path(git_work_tree_cfg)) + set_git_work_tree(git_work_tree_cfg); + else { + char core_worktree[PATH_MAX]; + if (chdir(gitdirenv)) + die_errno("Could not chdir to '%s'", gitdirenv); + if (chdir(git_work_tree_cfg)) + die_errno("Could not chdir to '%s'", git_work_tree_cfg); + if (!getcwd(core_worktree, PATH_MAX)) + die_errno("Could not get directory '%s'", git_work_tree_cfg); + if (chdir(cwd)) + die_errno("Could not come back to cwd"); + set_git_work_tree(core_worktree); + } + } + else /* #2, #10 */ + set_git_work_tree("."); + + /* set_git_work_tree() must have been called by now */ + worktree = get_git_work_tree(); + + /* both get_git_work_tree() and cwd are already normalized */ + if (!strcmp(cwd, worktree)) { /* cwd == worktree */ + set_git_dir(gitdirenv); + free(gitfile); return NULL; - set_git_dir(make_absolute_path(gitdirenv)); - if (chdir(work_tree_env) < 0) - die_errno ("Could not chdir to '%s'", work_tree_env); - strcat(buffer, "/"); - return retval; -} + } -static int cwd_contains_git_dir(const char **gitfile_dirp) -{ - const char *gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); - *gitfile_dirp = gitfile_dir; - if (gitfile_dir) { - if (set_git_dir(gitfile_dir)) - die("Repository setup failed"); - return 1; + if (!prefixcmp(cwd, worktree) && + cwd[strlen(worktree)] == '/') { /* cwd inside worktree */ + set_git_dir(make_absolute_path(gitdirenv)); + if (chdir(worktree)) + die_errno("Could not chdir to '%s'", worktree); + cwd[len++] = '/'; + cwd[len] = '\0'; + free(gitfile); + return cwd + strlen(worktree) + 1; } - return is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT); + + /* cwd outside worktree */ + set_git_dir(gitdirenv); + free(gitfile); + return NULL; } -static const char *setup_discovered_git_dir(const char *work_tree_env, - int offset, int len, char *cwd, int *nongit_ok) +static const char *setup_discovered_git_dir(const char *gitdir, + char *cwd, int offset, int len, + int *nongit_ok) { - int root_len; + if (check_repository_format_gently(gitdir, nongit_ok)) + return NULL; - inside_git_dir = 0; - if (!work_tree_env) - inside_work_tree = 1; - root_len = offset_1st_component(cwd); - git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len); - if (check_repository_format_gently(nongit_ok)) + /* --work-tree is set without --git-dir; use discovered one */ + if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { + if (offset != len && !is_absolute_path(gitdir)) + gitdir = xstrdup(make_absolute_path(gitdir)); + if (chdir(cwd)) + die_errno("Could not come back to cwd"); + return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok); + } + + /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */ + if (is_bare_repository_cfg > 0) { + set_git_dir(offset == len ? gitdir : make_absolute_path(gitdir)); + if (chdir(cwd)) + die_errno("Could not come back to cwd"); return NULL; + } + + /* #0, #1, #5, #8, #9, #12, #13 */ + set_git_work_tree("."); + if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT)) + set_git_dir(gitdir); + inside_git_dir = 0; + inside_work_tree = 1; if (offset == len) return NULL; @@ -382,23 +444,35 @@ static const char *setup_discovered_git_dir(const char *work_tree_env, return cwd + offset; } -static const char *setup_bare_git_dir(const char *work_tree_env, - int offset, int len, char *cwd, int *nongit_ok) +/* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */ +static const char *setup_bare_git_dir(char *cwd, int offset, int len, int *nongit_ok) { int root_len; + if (check_repository_format_gently(".", nongit_ok)) + return NULL; + + /* --work-tree is set without --git-dir; use discovered one */ + if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { + const char *gitdir; + + gitdir = offset == len ? "." : xmemdupz(cwd, offset); + if (chdir(cwd)) + die_errno("Could not come back to cwd"); + return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok); + } + inside_git_dir = 1; - if (!work_tree_env) - inside_work_tree = 0; + inside_work_tree = 0; if (offset != len) { if (chdir(cwd)) die_errno("Cannot come back to cwd"); root_len = offset_1st_component(cwd); cwd[offset > root_len ? offset : root_len] = '\0'; set_git_dir(cwd); - } else + } + else set_git_dir("."); - check_repository_format_gently(nongit_ok); return NULL; } @@ -428,11 +502,10 @@ static dev_t get_device_or_die(const char *path, const char *prefix) */ static const char *setup_git_directory_gently_1(int *nongit_ok) { - const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); static char cwd[PATH_MAX+1]; - const char *gitdirenv; - const char *gitfile_dir; + const char *gitdirenv, *ret; + char *gitfile; int len, offset, ceil_offset; dev_t current_device = 0; int one_filesystem = 1; @@ -445,6 +518,10 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) if (nongit_ok) *nongit_ok = 0; + if (!getcwd(cwd, sizeof(cwd)-1)) + die_errno("Unable to read current working directory"); + offset = len = strlen(cwd); + /* * If GIT_DIR is set explicitly, we're not going * to do any discovery, but we still do repository @@ -452,10 +529,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) */ gitdirenv = getenv(GIT_DIR_ENVIRONMENT); if (gitdirenv) - return setup_explicit_git_dir(gitdirenv, work_tree_env, nongit_ok); - - if (!getcwd(cwd, sizeof(cwd)-1)) - die_errno("Unable to read current working directory"); + return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs); if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) @@ -472,17 +546,30 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) * - ../../.git/ * etc. */ - offset = len = strlen(cwd); one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0); if (one_filesystem) current_device = get_device_or_die(".", NULL); for (;;) { - if (cwd_contains_git_dir(&gitfile_dir)) - return setup_discovered_git_dir(work_tree_env, offset, - len, cwd, nongit_ok); + gitfile = (char*)read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); + if (gitfile) + gitdirenv = gitfile = xstrdup(gitfile); + else { + if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) + gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT; + } + + if (gitdirenv) { + ret = setup_discovered_git_dir(gitdirenv, + cwd, offset, len, + nongit_ok); + free(gitfile); + return ret; + } + free(gitfile); + if (is_git_directory(".")) - return setup_bare_git_dir(work_tree_env, offset, - len, cwd, nongit_ok); + return setup_bare_git_dir(cwd, offset, len, nongit_ok); + while (--offset > ceil_offset && cwd[offset] != '/'); if (offset <= ceil_offset) return setup_nongit(cwd, nongit_ok); @@ -592,7 +679,7 @@ int check_repository_format_version(const char *var, const char *value, void *cb int check_repository_format(void) { - return check_repository_format_gently(NULL); + return check_repository_format_gently(get_git_dir(), NULL); } /* @@ -603,19 +690,5 @@ int check_repository_format(void) */ const char *setup_git_directory(void) { - const char *retval = setup_git_directory_gently(NULL); - - /* If the work tree is not the default one, recompute prefix */ - if (inside_work_tree < 0) { - static char buffer[PATH_MAX + 1]; - char *rel; - if (retval && chdir(retval)) - die_errno ("Could not jump back into original cwd"); - rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree()); - if (rel && *rel && chdir(get_git_work_tree())) - die_errno ("Could not jump to working directory"); - return rel && *rel ? strcat(rel, "/") : NULL; - } - - return retval; + return setup_git_directory_gently(NULL); } diff --git a/sha1_file.c b/sha1_file.c index 1cafdfa617..0b830c8642 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -37,6 +37,41 @@ const unsigned char null_sha1[20]; static int git_open_noatime(const char *name, struct packed_git *p); +/* + * This is meant to hold a *small* number of objects that you would + * want read_sha1_file() to be able to return, but yet you do not want + * to write them into the object store (e.g. a browse-only + * application). + */ +static struct cached_object { + unsigned char sha1[20]; + enum object_type type; + void *buf; + unsigned long size; +} *cached_objects; +static int cached_object_nr, cached_object_alloc; + +static struct cached_object empty_tree = { + EMPTY_TREE_SHA1_BIN, + OBJ_TREE, + "", + 0 +}; + +static struct cached_object *find_cached_object(const unsigned char *sha1) +{ + int i; + struct cached_object *co = cached_objects; + + for (i = 0; i < cached_object_nr; i++, co++) { + if (!hashcmp(co->sha1, sha1)) + return co; + } + if (!hashcmp(sha1, empty_tree.sha1)) + return &empty_tree; + return NULL; +} + int safe_create_leading_directories(char *path) { char *pos = path + offset_1st_component(path); @@ -1985,9 +2020,17 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) { + struct cached_object *co; struct pack_entry e; int status; + co = find_cached_object(sha1); + if (co) { + if (sizep) + *sizep = co->size; + return co->type; + } + if (!find_pack_entry(sha1, &e)) { /* Most likely it's a loose object. */ status = sha1_loose_object_info(sha1, sizep); @@ -2033,41 +2076,6 @@ static void *read_packed_sha1(const unsigned char *sha1, return data; } -/* - * This is meant to hold a *small* number of objects that you would - * want read_sha1_file() to be able to return, but yet you do not want - * to write them into the object store (e.g. a browse-only - * application). - */ -static struct cached_object { - unsigned char sha1[20]; - enum object_type type; - void *buf; - unsigned long size; -} *cached_objects; -static int cached_object_nr, cached_object_alloc; - -static struct cached_object empty_tree = { - EMPTY_TREE_SHA1_BIN, - OBJ_TREE, - "", - 0 -}; - -static struct cached_object *find_cached_object(const unsigned char *sha1) -{ - int i; - struct cached_object *co = cached_objects; - - for (i = 0; i < cached_object_nr; i++, co++) { - if (!hashcmp(co->sha1, sha1)) - return co; - } - if (!hashcmp(sha1, empty_tree.sha1)) - return &empty_tree; - return NULL; -} - int pretend_sha1_file(void *buf, unsigned long len, enum object_type type, unsigned char *sha1) { @@ -2141,7 +2149,7 @@ void *read_sha1_file_repl(const unsigned char *sha1, return data; } - if (errno != ENOENT) + if (errno && errno != ENOENT) die_errno("failed to read object %s", sha1_to_hex(sha1)); /* die if we replaced an object with one that does not exist */ diff --git a/sha1_name.c b/sha1_name.c index ceb9cdd860..709ff2eee6 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -707,7 +707,7 @@ static int handle_one_ref(const char *path, } if (object->type != OBJ_COMMIT) return 0; - insert_by_date((struct commit *)object, list); + commit_list_insert_by_date((struct commit *)object, list); return 0; } @@ -283,6 +283,12 @@ Do: Tests that are likely to smoke out future regressions are better than tests that just inflate the coverage metrics. + - When a test checks for an absolute path that a git command generated, + construct the expected value using $(pwd) rather than $PWD, + $TEST_DIRECTORY, or $TRASH_DIRECTORY. It makes a difference on + Windows, where the shell (MSYS bash) mangles absolute path names. + For details, see the commit message of 4114156ae9. + Don't: - exit() within a <script> part. diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 92d6d31942..199f22c231 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -68,41 +68,46 @@ svn_cmd () { svn "$orig_svncmd" --config-dir "$svnconf" "$@" } -for d in \ - "$SVN_HTTPD_PATH" \ - /usr/sbin/apache2 \ - /usr/sbin/httpd \ -; do - if test -f "$d" +prepare_httpd () { + for d in \ + "$SVN_HTTPD_PATH" \ + /usr/sbin/apache2 \ + /usr/sbin/httpd \ + ; do + if test -f "$d" + then + SVN_HTTPD_PATH="$d" + break + fi + done + if test -z "$SVN_HTTPD_PATH" then - SVN_HTTPD_PATH="$d" - break + echo >&2 '*** error: Apache not found' + return 1 fi -done -for d in \ - "$SVN_HTTPD_MODULE_PATH" \ - /usr/lib/apache2/modules \ - /usr/libexec/apache2 \ -; do - if test -d "$d" + for d in \ + "$SVN_HTTPD_MODULE_PATH" \ + /usr/lib/apache2/modules \ + /usr/libexec/apache2 \ + ; do + if test -d "$d" + then + SVN_HTTPD_MODULE_PATH="$d" + break + fi + done + if test -z "$SVN_HTTPD_MODULE_PATH" then - SVN_HTTPD_MODULE_PATH="$d" - break + echo >&2 '*** error: Apache module dir not found' + return 1 fi -done - -start_httpd () { - repo_base_path="$1" - if test -z "$SVN_HTTPD_PORT" + if test ! -f "$SVN_HTTPD_MODULE_PATH/mod_dav_svn.so" then - echo >&2 'SVN_HTTPD_PORT is not defined!' - return - fi - if test -z "$repo_base_path" - then - repo_base_path=svn + echo >&2 '*** error: Apache module "mod_dav_svn" not found' + return 1 fi + repo_base_path="${1-svn}" mkdir "$GIT_DIR"/logs cat > "$GIT_DIR/httpd.conf" <<EOF @@ -119,12 +124,24 @@ LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so SVNPath "$rawsvnrepo" </Location> EOF +} + +start_httpd () { + if test -z "$SVN_HTTPD_PORT" + then + echo >&2 'SVN_HTTPD_PORT is not defined!' + return + fi + + prepare_httpd "$1" || return 1 + "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path" } stop_httpd () { test -z "$SVN_HTTPD_PORT" && return + test ! -f "$GIT_DIR/httpd.conf" && return "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop } diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 2f7002a5e5..8deec75c3a 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -80,11 +80,11 @@ EOF chmod +x passing-todo.sh && ./passing-todo.sh >out 2>err && ! test -s err && -cat >expect <<EOF && -ok 1 - pretend we have fixed a known breakage # TODO known breakage -# fixed 1 known breakage(s) -# passed all 1 test(s) -1..1 +sed -e 's/^> //' >expect <<EOF && +> ok 1 - pretend we have fixed a known breakage # TODO known breakage +> # fixed 1 known breakage(s) +> # passed all 1 test(s) +> 1..1 EOF test_cmp expect out) " @@ -164,19 +164,19 @@ EOF test_must_fail ./failing-cleanup.sh >out 2>err && ! test -s err && ! test -f \"trash directory.failing-cleanup/clean-after-failure\" && -sed -e 's/Z$//' >expect <<\EOF && -not ok - 1 tests clean up even after a failure -# Z -# touch clean-after-failure && -# test_when_finished rm clean-after-failure && -# (exit 1) -# Z -not ok - 2 failure to clean up causes the test to fail -# Z -# test_when_finished \"(exit 2)\" -# Z -# failed 2 among 2 test(s) -1..2 +sed -e 's/Z$//' -e 's/^> //' >expect <<\EOF && +> not ok - 1 tests clean up even after a failure +> # Z +> # touch clean-after-failure && +> # test_when_finished rm clean-after-failure && +> # (exit 1) +> # Z +> not ok - 2 failure to clean up causes the test to fail +> # Z +> # test_when_finished \"(exit 2)\" +> # Z +> # failed 2 among 2 test(s) +> 1..2 EOF test_cmp expect out) " diff --git a/t/t0001-init.sh b/t/t0001-init.sh index d44194c35f..f684993211 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -33,6 +33,62 @@ test_expect_success 'plain' ' check_config plain/.git false unset ' +test_expect_success 'plain nested in bare' ' + ( + sane_unset GIT_DIR GIT_WORK_TREE && + git init --bare bare-ancestor.git && + cd bare-ancestor.git && + mkdir plain-nested && + cd plain-nested && + git init + ) && + check_config bare-ancestor.git/plain-nested/.git false unset +' + +test_expect_success 'plain through aliased command, outside any git repo' ' + ( + sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG_NOGLOBAL && + HOME=$(pwd)/alias-config && + export HOME && + mkdir alias-config && + echo "[alias] aliasedinit = init" >alias-config/.gitconfig && + + GIT_CEILING_DIRECTORIES=$(pwd) && + export GIT_CEILING_DIRECTORIES && + + mkdir plain-aliased && + cd plain-aliased && + git aliasedinit + ) && + check_config plain-aliased/.git false unset +' + +test_expect_failure 'plain nested through aliased command' ' + ( + sane_unset GIT_DIR GIT_WORK_TREE && + git init plain-ancestor-aliased && + cd plain-ancestor-aliased && + echo "[alias] aliasedinit = init" >>.git/config && + mkdir plain-nested && + cd plain-nested && + git aliasedinit + ) && + check_config plain-ancestor-aliased/plain-nested/.git false unset +' + +test_expect_failure 'plain nested in bare through aliased command' ' + ( + sane_unset GIT_DIR GIT_WORK_TREE && + git init --bare bare-ancestor-aliased.git && + cd bare-ancestor-aliased.git && + echo "[alias] aliasedinit = init" >>config && + mkdir plain-nested && + cd plain-nested && + git aliasedinit + ) && + check_config bare-ancestor-aliased.git/plain-nested/.git false unset +' + test_expect_success 'plain with GIT_WORK_TREE' ' if ( sane_unset GIT_DIR && diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 828e35baf7..9078b84ae6 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -93,4 +93,47 @@ test_expect_success expanded_in_repo ' cmp expanded-keywords expected-output ' +# The use of %f in a filter definition is expanded to the path to +# the filename being smudged or cleaned. It must be shell escaped. +# First, set up some interesting file names and pet them in +# .gitattributes. +test_expect_success 'filter shell-escaped filenames' ' + cat >argc.sh <<-EOF && + #!$SHELL_PATH + cat >/dev/null + echo argc: \$# "\$@" + EOF + normal=name-no-magic && + special="name with '\''sq'\'' and \$x" && + echo some test text >"$normal" && + echo some test text >"$special" && + git add "$normal" "$special" && + git commit -q -m "add files" && + echo "name* filter=argc" >.gitattributes && + + # delete the files and check them out again, using a smudge filter + # that will count the args and echo the command-line back to us + git config filter.argc.smudge "sh ./argc.sh %f" && + rm "$normal" "$special" && + git checkout -- "$normal" "$special" && + + # make sure argc.sh counted the right number of args + echo "argc: 1 $normal" >expect && + test_cmp expect "$normal" && + echo "argc: 1 $special" >expect && + test_cmp expect "$special" && + + # do the same thing, but with more args in the filter expression + git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" && + rm "$normal" "$special" && + git checkout -- "$normal" "$special" && + + # make sure argc.sh counted the right number of args + echo "argc: 2 $normal --my-extra-arg" >expect && + test_cmp expect "$normal" && + echo "argc: 2 $special --my-extra-arg" >expect && + test_cmp expect "$special" && + : +' + test_done diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh index 0ef11bccb4..de84e35c43 100755 --- a/t/t1011-read-tree-sparse-checkout.sh +++ b/t/t1011-read-tree-sparse-checkout.sh @@ -94,12 +94,20 @@ test_expect_success 'match directories with trailing slash' ' test -f sub/added ' -test_expect_failure 'match directories without trailing slash' ' - echo init.t >.git/info/sparse-checkout && +test_expect_success 'match directories without trailing slash' ' echo sub >>.git/info/sparse-checkout && git read-tree -m -u HEAD && git ls-files -t >result && - test_cmp expected.swt result && + test_cmp expected.swt-noinit result && + test ! -f init.t && + test -f sub/added +' + +test_expect_success 'match directory pattern' ' + echo "s?b" >>.git/info/sparse-checkout && + git read-tree -m -u HEAD && + git ls-files -t >result && + test_cmp expected.swt-noinit result && test ! -f init.t && test -f sub/added ' diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index a3ac33801a..1fd187c5eb 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -110,6 +110,14 @@ test_expect_success 'read-tree' ' ) ' +test_expect_success 'alias expansion' ' + ( + git config alias.ss status && + cd dir && + git status && + git ss + ) +' test_expect_success 'no file/rev ambiguity check inside .git' ' git commit -a -m 1 && ( diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh index 2c8f01f668..da6252b117 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-worktree.sh @@ -340,4 +340,11 @@ test_expect_success 'make_relative_path handles double slashes in GIT_DIR' ' git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file ' +test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' ' + GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work \ + test-subprocess --setup-work-tree rev-parse --show-toplevel >actual && + echo "$(pwd)/repo.git/work" >expected && + test_cmp expected actual +' + test_done diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh new file mode 100755 index 0000000000..15101d5e03 --- /dev/null +++ b/t/t1510-repo-setup.sh @@ -0,0 +1,776 @@ +#!/bin/sh + +test_description="Tests of cwd/prefix/worktree/gitdir setup in all cases + +A few rules for repo setup: + +1. GIT_DIR is relative to user's cwd. --git-dir is equivalent to + GIT_DIR. + +2. .git file is relative to parent directory. .git file is basically + symlink in disguise. The directory where .git file points to will + become new git_dir. + +3. core.worktree is relative to git_dir. + +4. GIT_WORK_TREE is relative to user's cwd. --work-tree is + equivalent to GIT_WORK_TREE. + +5. GIT_WORK_TREE/core.worktree was originally meant to work only if + GIT_DIR is set, but earlier git didn't enforce it, and some scripts + depend on the implementation that happened to first discover .git by + going up from the users $cwd and then using the specified working tree + that may or may not have any relation to where .git was found in. This + historical behaviour must be kept. + +6. Effective GIT_WORK_TREE overrides core.worktree and core.bare + +7. Effective core.worktree conflicts with core.bare + +8. If GIT_DIR is set but neither worktree nor bare setting is given, + original cwd becomes worktree. + +9. If .git discovery is done inside a repo, the repo becomes a bare + repo. .git discovery is performed if GIT_DIR is not set. + +10. If no worktree is available, cwd remains unchanged, prefix is + NULL. + +11. When user's cwd is outside worktree, cwd remains unchanged, + prefix is NULL. +" +. ./test-lib.sh + +here=$(pwd) + +test_repo () { + ( + cd "$1" && + if test -n "$2" + then + GIT_DIR="$2" && + export GIT_DIR + fi && + if test -n "$3" + then + GIT_WORK_TREE="$3" && + export GIT_WORK_TREE + fi && + rm -f trace && + GIT_TRACE="$(pwd)/trace" git symbolic-ref HEAD >/dev/null && + grep '^setup: ' trace >result && + test_cmp expected result + ) +} + +maybe_config () { + file=$1 var=$2 value=$3 && + if test "$value" != unset + then + git config --file="$file" "$var" "$value" + fi +} + +setup_repo () { + name=$1 worktreecfg=$2 gitfile=$3 barecfg=$4 && + sane_unset GIT_DIR GIT_WORK_TREE && + + git init "$name" && + maybe_config "$name/.git/config" core.worktree "$worktreecfg" && + maybe_config "$name/.git/config" core.bare "$barecfg" && + mkdir -p "$name/sub/sub" && + + if test "${gitfile:+set}" + then + mv "$name/.git" "$name.git" && + echo "gitdir: ../$name.git" >"$name/.git" + fi +} + +maybe_set () { + var=$1 value=$2 && + if test "$value" != unset + then + eval "$var=\$value" && + export $var + fi +} + +setup_env () { + worktreenv=$1 gitdirenv=$2 && + sane_unset GIT_DIR GIT_WORK_TREE && + maybe_set GIT_DIR "$gitdirenv" && + maybe_set GIT_WORK_TREE "$worktreeenv" +} + +expect () { + cat >"$1/expected" <<-EOF + setup: git_dir: $2 + setup: worktree: $3 + setup: cwd: $4 + setup: prefix: $5 + EOF +} + +try_case () { + name=$1 worktreeenv=$2 gitdirenv=$3 && + setup_env "$worktreeenv" "$gitdirenv" && + expect "$name" "$4" "$5" "$6" "$7" && + test_repo "$name" +} + +run_wt_tests () { + N=$1 gitfile=$2 + + absgit="$here/$N/.git" + dotgit=.git + dotdotgit=../../.git + + if test "$gitfile" + then + absgit="$here/$N.git" + dotgit=$absgit dotdotgit=$absgit + fi + + test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR at toplevel" ' + try_case $N "$here/$N" .git \ + "$dotgit" "$here/$N" "$here/$N" "(null)" && + try_case $N . .git \ + "$dotgit" "$here/$N" "$here/$N" "(null)" && + try_case $N "$here/$N" "$here/$N/.git" \ + "$absgit" "$here/$N" "$here/$N" "(null)" && + try_case $N . "$here/$N/.git" \ + "$absgit" "$here/$N" "$here/$N" "(null)" + ' + + test_expect_success "#$N: explicit GIT_WORK_TREE and GIT_DIR in subdir" ' + try_case $N/sub/sub "$here/$N" ../../.git \ + "$absgit" "$here/$N" "$here/$N" sub/sub/ && + try_case $N/sub/sub ../.. ../../.git \ + "$absgit" "$here/$N" "$here/$N" sub/sub/ && + try_case $N/sub/sub "$here/$N" "$here/$N/.git" \ + "$absgit" "$here/$N" "$here/$N" sub/sub/ && + try_case $N/sub/sub ../.. "$here/$N/.git" \ + "$absgit" "$here/$N" "$here/$N" sub/sub/ + ' + + test_expect_success "#$N: explicit GIT_WORK_TREE from parent of worktree" ' + try_case $N "$here/$N/wt" .git \ + "$dotgit" "$here/$N/wt" "$here/$N" "(null)" && + try_case $N wt .git \ + "$dotgit" "$here/$N/wt" "$here/$N" "(null)" && + try_case $N wt "$here/$N/.git" \ + "$absgit" "$here/$N/wt" "$here/$N" "(null)" && + try_case $N "$here/$N/wt" "$here/$N/.git" \ + "$absgit" "$here/$N/wt" "$here/$N" "(null)" + ' + + test_expect_success "#$N: explicit GIT_WORK_TREE from nephew of worktree" ' + try_case $N/sub/sub "$here/$N/wt" ../../.git \ + "$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" && + try_case $N/sub/sub ../../wt ../../.git \ + "$dotdotgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" && + try_case $N/sub/sub ../../wt "$here/$N/.git" \ + "$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" && + try_case $N/sub/sub "$here/$N/wt" "$here/$N/.git" \ + "$absgit" "$here/$N/wt" "$here/$N/sub/sub" "(null)" + ' + + test_expect_success "#$N: chdir_to_toplevel uses worktree, not git dir" ' + try_case $N "$here" .git \ + "$absgit" "$here" "$here" $N/ && + try_case $N .. .git \ + "$absgit" "$here" "$here" $N/ && + try_case $N .. "$here/$N/.git" \ + "$absgit" "$here" "$here" $N/ && + try_case $N "$here" "$here/$N/.git" \ + "$absgit" "$here" "$here" $N/ + ' + + test_expect_success "#$N: chdir_to_toplevel uses worktree (from subdir)" ' + try_case $N/sub/sub "$here" ../../.git \ + "$absgit" "$here" "$here" $N/sub/sub/ && + try_case $N/sub/sub ../../.. ../../.git \ + "$absgit" "$here" "$here" $N/sub/sub/ && + try_case $N/sub/sub ../../../ "$here/$N/.git" \ + "$absgit" "$here" "$here" $N/sub/sub/ && + try_case $N/sub/sub "$here" "$here/$N/.git" \ + "$absgit" "$here" "$here" $N/sub/sub/ + ' +} + +# try_repo #c GIT_WORK_TREE GIT_DIR core.worktree .gitfile? core.bare \ +# (git dir) (work tree) (cwd) (prefix) \ <-- at toplevel +# (git dir) (work tree) (cwd) (prefix) <-- from subdir +try_repo () { + name=$1 worktreeenv=$2 gitdirenv=$3 && + setup_repo "$name" "$4" "$5" "$6" && + shift 6 && + try_case "$name" "$worktreeenv" "$gitdirenv" \ + "$1" "$2" "$3" "$4" && + shift 4 && + case "$gitdirenv" in + /* | ?:/* | unset) ;; + *) + gitdirenv=../$gitdirenv ;; + esac && + try_case "$name/sub" "$worktreeenv" "$gitdirenv" \ + "$1" "$2" "$3" "$4" +} + +# Bit 0 = GIT_WORK_TREE +# Bit 1 = GIT_DIR +# Bit 2 = core.worktree +# Bit 3 = .git is a file +# Bit 4 = bare repo +# Case# = encoding of the above 5 bits + +test_expect_success '#0: nonbare repo, no explicit configuration' ' + try_repo 0 unset unset unset "" unset \ + .git "$here/0" "$here/0" "(null)" \ + .git "$here/0" "$here/0" sub/ 2>message && + ! test -s message +' + +test_expect_success '#1: GIT_WORK_TREE without explicit GIT_DIR is accepted' ' + mkdir -p wt && + try_repo 1 "$here" unset unset "" unset \ + "$here/1/.git" "$here" "$here" 1/ \ + "$here/1/.git" "$here" "$here" 1/sub/ 2>message && + ! test -s message +' + +test_expect_success '#2: worktree defaults to cwd with explicit GIT_DIR' ' + try_repo 2 unset "$here/2/.git" unset "" unset \ + "$here/2/.git" "$here/2" "$here/2" "(null)" \ + "$here/2/.git" "$here/2/sub" "$here/2/sub" "(null)" +' + +test_expect_success '#2b: relative GIT_DIR' ' + try_repo 2b unset ".git" unset "" unset \ + ".git" "$here/2b" "$here/2b" "(null)" \ + "../.git" "$here/2b/sub" "$here/2b/sub" "(null)" +' + +test_expect_success '#3: setup' ' + setup_repo 3 unset "" unset && + mkdir -p 3/sub/sub 3/wt/sub +' +run_wt_tests 3 + +test_expect_success '#4: core.worktree without GIT_DIR set is accepted' ' + setup_repo 4 ../sub "" unset && + mkdir -p 4/sub sub && + try_case 4 unset unset \ + .git "$here/4/sub" "$here/4" "(null)" \ + "$here/4/.git" "$here/4/sub" "$here/4/sub" "(null)" 2>message && + ! test -s message +' + +test_expect_success '#5: core.worktree + GIT_WORK_TREE is accepted' ' + # or: you cannot intimidate away the lack of GIT_DIR setting + try_repo 5 "$here" unset "$here/5" "" unset \ + "$here/5/.git" "$here" "$here" 5/ \ + "$here/5/.git" "$here" "$here" 5/sub/ 2>message && + try_repo 5a .. unset "$here/5a" "" unset \ + "$here/5a/.git" "$here" "$here" 5a/ \ + "$here/5a/.git" "$here/5a" "$here/5a" sub/ && + ! test -s message +' + +test_expect_success '#6: setting GIT_DIR brings core.worktree to life' ' + setup_repo 6 "$here/6" "" unset && + try_case 6 unset .git \ + .git "$here/6" "$here/6" "(null)" && + try_case 6 unset "$here/6/.git" \ + "$here/6/.git" "$here/6" "$here/6" "(null)" && + try_case 6/sub/sub unset ../../.git \ + "$here/6/.git" "$here/6" "$here/6" sub/sub/ && + try_case 6/sub/sub unset "$here/6/.git" \ + "$here/6/.git" "$here/6" "$here/6" sub/sub/ +' + +test_expect_success '#6b: GIT_DIR set, core.worktree relative' ' + setup_repo 6b .. "" unset && + try_case 6b unset .git \ + .git "$here/6b" "$here/6b" "(null)" && + try_case 6b unset "$here/6b/.git" \ + "$here/6b/.git" "$here/6b" "$here/6b" "(null)" && + try_case 6b/sub/sub unset ../../.git \ + "$here/6b/.git" "$here/6b" "$here/6b" sub/sub/ && + try_case 6b/sub/sub unset "$here/6b/.git" \ + "$here/6b/.git" "$here/6b" "$here/6b" sub/sub/ +' + +test_expect_success '#6c: GIT_DIR set, core.worktree=../wt (absolute)' ' + setup_repo 6c "$here/6c/wt" "" unset && + mkdir -p 6c/wt/sub && + + try_case 6c unset .git \ + .git "$here/6c/wt" "$here/6c" "(null)" && + try_case 6c unset "$here/6c/.git" \ + "$here/6c/.git" "$here/6c/wt" "$here/6c" "(null)" && + try_case 6c/sub/sub unset ../../.git \ + ../../.git "$here/6c/wt" "$here/6c/sub/sub" "(null)" && + try_case 6c/sub/sub unset "$here/6c/.git" \ + "$here/6c/.git" "$here/6c/wt" "$here/6c/sub/sub" "(null)" +' + +test_expect_success '#6d: GIT_DIR set, core.worktree=../wt (relative)' ' + setup_repo 6d "$here/6d/wt" "" unset && + mkdir -p 6d/wt/sub && + + try_case 6d unset .git \ + .git "$here/6d/wt" "$here/6d" "(null)" && + try_case 6d unset "$here/6d/.git" \ + "$here/6d/.git" "$here/6d/wt" "$here/6d" "(null)" && + try_case 6d/sub/sub unset ../../.git \ + ../../.git "$here/6d/wt" "$here/6d/sub/sub" "(null)" && + try_case 6d/sub/sub unset "$here/6d/.git" \ + "$here/6d/.git" "$here/6d/wt" "$here/6d/sub/sub" "(null)" +' + +test_expect_success '#6e: GIT_DIR set, core.worktree=../.. (absolute)' ' + setup_repo 6e "$here" "" unset && + try_case 6e unset .git \ + "$here/6e/.git" "$here" "$here" 6e/ && + try_case 6e unset "$here/6e/.git" \ + "$here/6e/.git" "$here" "$here" 6e/ && + try_case 6e/sub/sub unset ../../.git \ + "$here/6e/.git" "$here" "$here" 6e/sub/sub/ && + try_case 6e/sub/sub unset "$here/6e/.git" \ + "$here/6e/.git" "$here" "$here" 6e/sub/sub/ +' + +test_expect_success '#6f: GIT_DIR set, core.worktree=../.. (relative)' ' + setup_repo 6f ../../ "" unset && + try_case 6f unset .git \ + "$here/6f/.git" "$here" "$here" 6f/ && + try_case 6f unset "$here/6f/.git" \ + "$here/6f/.git" "$here" "$here" 6f/ && + try_case 6f/sub/sub unset ../../.git \ + "$here/6f/.git" "$here" "$here" 6f/sub/sub/ && + try_case 6f/sub/sub unset "$here/6f/.git" \ + "$here/6f/.git" "$here" "$here" 6f/sub/sub/ +' + +# case #7: GIT_WORK_TREE overrides core.worktree. +test_expect_success '#7: setup' ' + setup_repo 7 non-existent "" unset && + mkdir -p 7/sub/sub 7/wt/sub +' +run_wt_tests 7 + +test_expect_success '#8: gitfile, easy case' ' + try_repo 8 unset unset unset gitfile unset \ + "$here/8.git" "$here/8" "$here/8" "(null)" \ + "$here/8.git" "$here/8" "$here/8" sub/ +' + +test_expect_success '#9: GIT_WORK_TREE accepted with gitfile' ' + mkdir -p 9/wt && + try_repo 9 wt unset unset gitfile unset \ + "$here/9.git" "$here/9/wt" "$here/9" "(null)" \ + "$here/9.git" "$here/9/sub/wt" "$here/9/sub" "(null)" 2>message && + ! test -s message +' + +test_expect_success '#10: GIT_DIR can point to gitfile' ' + try_repo 10 unset "$here/10/.git" unset gitfile unset \ + "$here/10.git" "$here/10" "$here/10" "(null)" \ + "$here/10.git" "$here/10/sub" "$here/10/sub" "(null)" +' + +test_expect_success '#10b: relative GIT_DIR can point to gitfile' ' + try_repo 10b unset .git unset gitfile unset \ + "$here/10b.git" "$here/10b" "$here/10b" "(null)" \ + "$here/10b.git" "$here/10b/sub" "$here/10b/sub" "(null)" +' + +# case #11: GIT_WORK_TREE works, gitfile case. +test_expect_success '#11: setup' ' + setup_repo 11 unset gitfile unset && + mkdir -p 11/sub/sub 11/wt/sub +' +run_wt_tests 11 gitfile + +test_expect_success '#12: core.worktree with gitfile is accepted' ' + try_repo 12 unset unset "$here/12" gitfile unset \ + "$here/12.git" "$here/12" "$here/12" "(null)" \ + "$here/12.git" "$here/12" "$here/12" sub/ 2>message && + ! test -s message +' + +test_expect_success '#13: core.worktree+GIT_WORK_TREE accepted (with gitfile)' ' + # or: you cannot intimidate away the lack of GIT_DIR setting + try_repo 13 non-existent-too unset non-existent gitfile unset \ + "$here/13.git" "$here/13/non-existent-too" "$here/13" "(null)" \ + "$here/13.git" "$here/13/sub/non-existent-too" "$here/13/sub" "(null)" 2>message && + ! test -s message +' + +# case #14. +# If this were more table-driven, it could share code with case #6. + +test_expect_success '#14: core.worktree with GIT_DIR pointing to gitfile' ' + setup_repo 14 "$here/14" gitfile unset && + try_case 14 unset .git \ + "$here/14.git" "$here/14" "$here/14" "(null)" && + try_case 14 unset "$here/14/.git" \ + "$here/14.git" "$here/14" "$here/14" "(null)" && + try_case 14/sub/sub unset ../../.git \ + "$here/14.git" "$here/14" "$here/14" sub/sub/ && + try_case 14/sub/sub unset "$here/14/.git" \ + "$here/14.git" "$here/14" "$here/14" sub/sub/ && + + setup_repo 14c "$here/14c/wt" gitfile unset && + mkdir -p 14c/wt/sub && + + try_case 14c unset .git \ + "$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" && + try_case 14c unset "$here/14c/.git" \ + "$here/14c.git" "$here/14c/wt" "$here/14c" "(null)" && + try_case 14c/sub/sub unset ../../.git \ + "$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" && + try_case 14c/sub/sub unset "$here/14c/.git" \ + "$here/14c.git" "$here/14c/wt" "$here/14c/sub/sub" "(null)" && + + setup_repo 14d "$here/14d/wt" gitfile unset && + mkdir -p 14d/wt/sub && + + try_case 14d unset .git \ + "$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" && + try_case 14d unset "$here/14d/.git" \ + "$here/14d.git" "$here/14d/wt" "$here/14d" "(null)" && + try_case 14d/sub/sub unset ../../.git \ + "$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" && + try_case 14d/sub/sub unset "$here/14d/.git" \ + "$here/14d.git" "$here/14d/wt" "$here/14d/sub/sub" "(null)" && + + setup_repo 14e "$here" gitfile unset && + try_case 14e unset .git \ + "$here/14e.git" "$here" "$here" 14e/ && + try_case 14e unset "$here/14e/.git" \ + "$here/14e.git" "$here" "$here" 14e/ && + try_case 14e/sub/sub unset ../../.git \ + "$here/14e.git" "$here" "$here" 14e/sub/sub/ && + try_case 14e/sub/sub unset "$here/14e/.git" \ + "$here/14e.git" "$here" "$here" 14e/sub/sub/ +' + +test_expect_success '#14b: core.worktree is relative to actual git dir' ' + setup_repo 14b ../14b gitfile unset && + try_case 14b unset .git \ + "$here/14b.git" "$here/14b" "$here/14b" "(null)" && + try_case 14b unset "$here/14b/.git" \ + "$here/14b.git" "$here/14b" "$here/14b" "(null)" && + try_case 14b/sub/sub unset ../../.git \ + "$here/14b.git" "$here/14b" "$here/14b" sub/sub/ && + try_case 14b/sub/sub unset "$here/14b/.git" \ + "$here/14b.git" "$here/14b" "$here/14b" sub/sub/ && + + setup_repo 14f ../ gitfile unset && + try_case 14f unset .git \ + "$here/14f.git" "$here" "$here" 14f/ && + try_case 14f unset "$here/14f/.git" \ + "$here/14f.git" "$here" "$here" 14f/ && + try_case 14f/sub/sub unset ../../.git \ + "$here/14f.git" "$here" "$here" 14f/sub/sub/ && + try_case 14f/sub/sub unset "$here/14f/.git" \ + "$here/14f.git" "$here" "$here" 14f/sub/sub/ +' + +# case #15: GIT_WORK_TREE overrides core.worktree (gitfile case). +test_expect_success '#15: setup' ' + setup_repo 15 non-existent gitfile unset && + mkdir -p 15/sub/sub 15/wt/sub +' +run_wt_tests 15 gitfile + +test_expect_success '#16a: implicitly bare repo (cwd inside .git dir)' ' + setup_repo 16a unset "" unset && + mkdir -p 16a/.git/wt/sub && + + try_case 16a/.git unset unset \ + . "(null)" "$here/16a/.git" "(null)" && + try_case 16a/.git/wt unset unset \ + "$here/16a/.git" "(null)" "$here/16a/.git/wt" "(null)" && + try_case 16a/.git/wt/sub unset unset \ + "$here/16a/.git" "(null)" "$here/16a/.git/wt/sub" "(null)" +' + +test_expect_success '#16b: bare .git (cwd inside .git dir)' ' + setup_repo 16b unset "" true && + mkdir -p 16b/.git/wt/sub && + + try_case 16b/.git unset unset \ + . "(null)" "$here/16b/.git" "(null)" && + try_case 16b/.git/wt unset unset \ + "$here/16b/.git" "(null)" "$here/16b/.git/wt" "(null)" && + try_case 16b/.git/wt/sub unset unset \ + "$here/16b/.git" "(null)" "$here/16b/.git/wt/sub" "(null)" +' + +test_expect_success '#16c: bare .git has no worktree' ' + try_repo 16c unset unset unset "" true \ + .git "(null)" "$here/16c" "(null)" \ + "$here/16c/.git" "(null)" "$here/16c/sub" "(null)" +' + +test_expect_success '#17: GIT_WORK_TREE without explicit GIT_DIR is accepted (bare case)' ' + # Just like #16. + setup_repo 17a unset "" true && + setup_repo 17b unset "" true && + mkdir -p 17a/.git/wt/sub && + mkdir -p 17b/.git/wt/sub && + + try_case 17a/.git "$here/17a" unset \ + "$here/17a/.git" "$here/17a" "$here/17a" .git/ \ + 2>message && + try_case 17a/.git/wt "$here/17a" unset \ + "$here/17a/.git" "$here/17a" "$here/17a" .git/wt/ && + try_case 17a/.git/wt/sub "$here/17a" unset \ + "$here/17a/.git" "$here/17a" "$here/17a" .git/wt/sub/ && + + try_case 17b/.git "$here/17b" unset \ + "$here/17b/.git" "$here/17b" "$here/17b" .git/ && + try_case 17b/.git/wt "$here/17b" unset \ + "$here/17b/.git" "$here/17b" "$here/17b" .git/wt/ && + try_case 17b/.git/wt/sub "$here/17b" unset \ + "$here/17b/.git" "$here/17b" "$here/17b" .git/wt/sub/ && + + try_repo 17c "$here/17c" unset unset "" true \ + .git "$here/17c" "$here/17c" "(null)" \ + "$here/17c/.git" "$here/17c" "$here/17c" sub/ 2>message && + ! test -s message +' + +test_expect_success '#18: bare .git named by GIT_DIR has no worktree' ' + try_repo 18 unset .git unset "" true \ + .git "(null)" "$here/18" "(null)" \ + ../.git "(null)" "$here/18/sub" "(null)" && + try_repo 18b unset "$here/18b/.git" unset "" true \ + "$here/18b/.git" "(null)" "$here/18b" "(null)" \ + "$here/18b/.git" "(null)" "$here/18b/sub" "(null)" +' + +# Case #19: GIT_DIR + GIT_WORK_TREE suppresses bareness. +test_expect_success '#19: setup' ' + setup_repo 19 unset "" true && + mkdir -p 19/sub/sub 19/wt/sub +' +run_wt_tests 19 + +test_expect_success '#20a: core.worktree without GIT_DIR accepted (inside .git)' ' + # Unlike case #16a. + setup_repo 20a "$here/20a" "" unset && + mkdir -p 20a/.git/wt/sub && + try_case 20a/.git unset unset \ + "$here/20a/.git" "$here/20a" "$here/20a" .git/ 2>message && + try_case 20a/.git/wt unset unset \ + "$here/20a/.git" "$here/20a" "$here/20a" .git/wt/ && + try_case 20a/.git/wt/sub unset unset \ + "$here/20a/.git" "$here/20a" "$here/20a" .git/wt/sub/ && + ! test -s message +' + +test_expect_success '#20b/c: core.worktree and core.bare conflict' ' + setup_repo 20b non-existent "" true && + mkdir -p 20b/.git/wt/sub && + ( + cd 20b/.git && + test_must_fail git symbolic-ref HEAD >/dev/null + ) 2>message && + grep "core.bare and core.worktree" message +' + +# Case #21: core.worktree/GIT_WORK_TREE overrides core.bare' ' +test_expect_success '#21: setup, core.worktree warns before overriding core.bare' ' + setup_repo 21 non-existent "" unset && + mkdir -p 21/.git/wt/sub && + ( + cd 21/.git && + GIT_WORK_TREE="$here/21" && + export GIT_WORK_TREE && + git symbolic-ref HEAD >/dev/null + ) 2>message && + ! test -s message + +' +run_wt_tests 21 + +test_expect_success '#22a: core.worktree = GIT_DIR = .git dir' ' + # like case #6. + + setup_repo 22a "$here/22a/.git" "" unset && + setup_repo 22ab . "" unset + mkdir -p 22a/.git/sub 22a/sub && + mkdir -p 22ab/.git/sub 22ab/sub && + try_case 22a/.git unset . \ + . "$here/22a/.git" "$here/22a/.git" "(null)" && + try_case 22a/.git unset "$here/22a/.git" \ + "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" "(null)" && + try_case 22a/.git/sub unset .. \ + "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ && + try_case 22a/.git/sub unset "$here/22a/.git" \ + "$here/22a/.git" "$here/22a/.git" "$here/22a/.git" sub/ && + + try_case 22ab/.git unset . \ + . "$here/22ab/.git" "$here/22ab/.git" "(null)" && + try_case 22ab/.git unset "$here/22ab/.git" \ + "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)" && + try_case 22ab/.git/sub unset .. \ + "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" sub/ && + try_case 22ab/.git unset "$here/22ab/.git" \ + "$here/22ab/.git" "$here/22ab/.git" "$here/22ab/.git" "(null)" +' + +test_expect_success '#22b: core.worktree child of .git, GIT_DIR=.git' ' + setup_repo 22b "$here/22b/.git/wt" "" unset && + setup_repo 22bb wt "" unset && + mkdir -p 22b/.git/sub 22b/sub 22b/.git/wt/sub 22b/wt/sub && + mkdir -p 22bb/.git/sub 22bb/sub 22bb/.git/wt 22bb/wt && + + try_case 22b/.git unset . \ + . "$here/22b/.git/wt" "$here/22b/.git" "(null)" && + try_case 22b/.git unset "$here/22b/.git" \ + "$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git" "(null)" && + try_case 22b/.git/sub unset .. \ + .. "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" && + try_case 22b/.git/sub unset "$here/22b/.git" \ + "$here/22b/.git" "$here/22b/.git/wt" "$here/22b/.git/sub" "(null)" && + + try_case 22bb/.git unset . \ + . "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" && + try_case 22bb/.git unset "$here/22bb/.git" \ + "$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git" "(null)" && + try_case 22bb/.git/sub unset .. \ + .. "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)" && + try_case 22bb/.git/sub unset "$here/22bb/.git" \ + "$here/22bb/.git" "$here/22bb/.git/wt" "$here/22bb/.git/sub" "(null)" +' + +test_expect_success '#22c: core.worktree = .git/.., GIT_DIR=.git' ' + setup_repo 22c "$here/22c" "" unset && + setup_repo 22cb .. "" unset && + mkdir -p 22c/.git/sub 22c/sub && + mkdir -p 22cb/.git/sub 22cb/sub && + + try_case 22c/.git unset . \ + "$here/22c/.git" "$here/22c" "$here/22c" .git/ && + try_case 22c/.git unset "$here/22c/.git" \ + "$here/22c/.git" "$here/22c" "$here/22c" .git/ && + try_case 22c/.git/sub unset .. \ + "$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ && + try_case 22c/.git/sub unset "$here/22c/.git" \ + "$here/22c/.git" "$here/22c" "$here/22c" .git/sub/ && + + try_case 22cb/.git unset . \ + "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ && + try_case 22cb/.git unset "$here/22cb/.git" \ + "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/ && + try_case 22cb/.git/sub unset .. \ + "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/ && + try_case 22cb/.git/sub unset "$here/22cb/.git" \ + "$here/22cb/.git" "$here/22cb" "$here/22cb" .git/sub/ +' + +test_expect_success '#22.2: core.worktree and core.bare conflict' ' + setup_repo 22 "$here/22" "" true && + ( + cd 22/.git && + GIT_DIR=. && + export GIT_DIR && + test_must_fail git symbolic-ref HEAD 2>result + ) && + ( + cd 22 && + GIT_DIR=.git && + export GIT_DIR && + test_must_fail git symbolic-ref HEAD 2>result + ) && + grep "core.bare and core.worktree" 22/.git/result && + grep "core.bare and core.worktree" 22/result +' + +# Case #23: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses bareness. +test_expect_success '#23: setup' ' + setup_repo 23 non-existent "" true && + mkdir -p 23/sub/sub 23/wt/sub +' +run_wt_tests 23 + +test_expect_success '#24: bare repo has no worktree (gitfile case)' ' + try_repo 24 unset unset unset gitfile true \ + "$here/24.git" "(null)" "$here/24" "(null)" \ + "$here/24.git" "(null)" "$here/24/sub" "(null)" +' + +test_expect_success '#25: GIT_WORK_TREE accepted if GIT_DIR unset (bare gitfile case)' ' + try_repo 25 "$here/25" unset unset gitfile true \ + "$here/25.git" "$here/25" "$here/25" "(null)" \ + "$here/25.git" "$here/25" "$here/25" "sub/" 2>message && + ! test -s message +' + +test_expect_success '#26: bare repo has no worktree (GIT_DIR -> gitfile case)' ' + try_repo 26 unset "$here/26/.git" unset gitfile true \ + "$here/26.git" "(null)" "$here/26" "(null)" \ + "$here/26.git" "(null)" "$here/26/sub" "(null)" && + try_repo 26b unset .git unset gitfile true \ + "$here/26b.git" "(null)" "$here/26b" "(null)" \ + "$here/26b.git" "(null)" "$here/26b/sub" "(null)" +' + +# Case #27: GIT_DIR + GIT_WORK_TREE suppresses bareness (with gitfile). +test_expect_success '#27: setup' ' + setup_repo 27 unset gitfile true && + mkdir -p 27/sub/sub 27/wt/sub +' +run_wt_tests 27 gitfile + +test_expect_success '#28: core.worktree and core.bare conflict (gitfile case)' ' + setup_repo 28 "$here/28" gitfile true && + ( + cd 28 && + test_must_fail git symbolic-ref HEAD + ) 2>message && + ! grep "^warning:" message && + grep "core.bare and core.worktree" message +' + +# Case #29: GIT_WORK_TREE(+core.worktree) overrides core.bare (gitfile case). +test_expect_success '#29: setup' ' + setup_repo 29 non-existent gitfile true && + mkdir -p 29/sub/sub 29/wt/sub + ( + cd 29 && + GIT_WORK_TREE="$here/29" && + export GIT_WORK_TREE && + git symbolic-ref HEAD >/dev/null + ) 2>message && + ! test -s message +' +run_wt_tests 29 gitfile + +test_expect_success '#30: core.worktree and core.bare conflict (gitfile version)' ' + # Just like case #22. + setup_repo 30 "$here/30" gitfile true && + ( + cd 30 && + GIT_DIR=.git && + export GIT_DIR && + test_must_fail git symbolic-ref HEAD 2>result + ) && + grep "core.bare and core.worktree" 30/result +' + +# Case #31: GIT_DIR + GIT_WORK_TREE(+core.worktree) suppresses +# bareness (gitfile version). +test_expect_success '#31: setup' ' + setup_repo 31 non-existent gitfile true && + mkdir -p 31/sub/sub 31/wt/sub +' +run_wt_tests 31 gitfile + +test_done diff --git a/t/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-options.sh index 2293797553..2b17311cb0 100755 --- a/t/t3032-merge-recursive-options.sh +++ b/t/t3032-merge-recursive-options.sh @@ -13,16 +13,19 @@ test_description='merge-recursive options . ./test-lib.sh +test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b +test_have_prereq MINGW && export GREP_OPTIONS=-U + test_expect_success 'setup' ' conflict_hunks () { - sed -n -e " - /^<<<</ b inconflict + sed $SED_OPTIONS -n -e " + /^<<<</ b conflict b - : inconflict + : conflict p /^>>>>/ b n - b inconflict + b conflict " "$@" } && @@ -107,6 +110,20 @@ test_expect_success '--ignore-space-change makes merge succeed' ' git merge-recursive --ignore-space-change HEAD^ -- HEAD remote ' +test_expect_success 'naive cherry-pick fails' ' + git read-tree --reset -u HEAD && + test_must_fail git cherry-pick --no-commit remote && + git read-tree --reset -u HEAD && + test_must_fail git cherry-pick remote && + test_must_fail git update-index --refresh && + grep "<<<<<<" text.txt +' + +test_expect_success '-Xignore-space-change makes cherry-pick succeed' ' + git read-tree --reset -u HEAD && + git cherry-pick --no-commit -Xignore-space-change remote +' + test_expect_success '--ignore-space-change: our w/s-only change wins' ' q_to_cr <<-\EOF >expected && justice and holiness and is the nurse of his age and theQ diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh index dc2e04a016..1921ca3a73 100755 --- a/t/t3301-notes.sh +++ b/t/t3301-notes.sh @@ -1067,7 +1067,7 @@ test_expect_success 'git notes copy diagnoses too many or too few parameters' ' test_expect_success 'git notes get-ref (no overrides)' ' git config --unset core.notesRef && - unset GIT_NOTES_REF && + sane_unset GIT_NOTES_REF && test "$(git notes get-ref)" = "refs/notes/commits" ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index d3a3bd2679..7d8147bb93 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -71,8 +71,9 @@ test_expect_success 'setup' ' # "exec" commands are ran with the user shell by default, but this may # be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work # to create a file. Unseting SHELL avoids such non-portable behavior -# in tests. +# in tests. It must be exported for it to take effect where needed. SHELL= +export SHELL test_expect_success 'rebase -i with the exec command' ' git checkout master && diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh index 948ca1bce6..df921d1f33 100755 --- a/t/t3509-cherry-pick-merge-df.sh +++ b/t/t3509-cherry-pick-merge-df.sh @@ -3,12 +3,14 @@ test_description='Test cherry-pick with directory/file conflicts' . ./test-lib.sh -test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' ' +test_expect_success 'Initialize repository' ' mkdir a && >a/f && git add a && - git commit -m a && + git commit -m a +' +test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' ' mkdir b && ln -s ../a b/a && git add b && diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index b26cabd571..cd093bd347 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -96,7 +96,7 @@ test_expect_success FUNNYNAMES \ "git rm -f 'space embedded' 'tab embedded' 'newline embedded'" -test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' ' +test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' ' chmod a-w . && test_must_fail git rm -f baz && chmod 775 . diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 9a66520588..b8f81d07c3 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -290,4 +290,15 @@ test_expect_success 'log -S requires an argument' ' test_must_fail git log -S ' +test_expect_success 'diff --cached on unborn branch' ' + echo ref: refs/heads/unborn >.git/HEAD && + git diff --cached >result && + test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached" result +' + +test_expect_success 'diff --cached -- file on unborn branch' ' + git diff --cached -- file0 >result && + test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached_--_file0" result +' + test_done diff --git a/t/t4013/diff.diff_--cached b/t/t4013/diff.diff_--cached new file mode 100644 index 0000000000..ff16e83e7c --- /dev/null +++ b/t/t4013/diff.diff_--cached @@ -0,0 +1,38 @@ +diff --git a/dir/sub b/dir/sub +new file mode 100644 +index 0000000..992913c +--- /dev/null ++++ b/dir/sub +@@ -0,0 +1,8 @@ ++A ++B ++C ++D ++E ++F ++1 ++2 +diff --git a/file0 b/file0 +new file mode 100644 +index 0000000..10a8a9f +--- /dev/null ++++ b/file0 +@@ -0,0 +1,9 @@ ++1 ++2 ++3 ++4 ++5 ++6 ++A ++B ++C +diff --git a/file1 b/file1 +new file mode 100644 +index 0000000..b1e6722 +--- /dev/null ++++ b/file1 +@@ -0,0 +1,3 @@ ++A ++B ++C diff --git a/t/t4013/diff.diff_--cached_--_file0 b/t/t4013/diff.diff_--cached_--_file0 new file mode 100644 index 0000000000..b9bb858a03 --- /dev/null +++ b/t/t4013/diff.diff_--cached_--_file0 @@ -0,0 +1,15 @@ +diff --git a/file0 b/file0 +new file mode 100644 +index 0000000..10a8a9f +--- /dev/null ++++ b/file0 +@@ -0,0 +1,9 @@ ++1 ++2 ++3 ++4 ++5 ++6 ++A ++B ++C diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index 0a61b57b5f..3646930623 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -32,7 +32,7 @@ EOF sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java -builtin_patterns="bibtex cpp csharp fortran html java objc pascal php python ruby tex" +builtin_patterns="bibtex cpp csharp fortran html java objc pascal perl php python ruby tex" for p in $builtin_patterns do test_expect_success "builtin $p pattern compiles" ' diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index 579c9e6105..a33d510bf6 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh @@ -6,6 +6,7 @@ test_description='git apply -p handling.' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh test_expect_success setup ' mkdir sub && @@ -62,8 +63,12 @@ test_expect_success 'apply (-p2) diff, mode change only' ' old mode 100644 new mode 100755 EOF - chmod 644 file1 && - git apply -p2 patch.chmod && + test_chmod -x file1 && + git apply --index -p2 patch.chmod && + case $(git ls-files -s file1) in 100755*) : good;; *) false;; esac +' + +test_expect_success FILEMODE 'file mode was changed' ' test -x file1 ' diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh index b55c411788..c95c4ccc39 100755 --- a/t/t4151-am-abort.sh +++ b/t/t4151-am-abort.sh @@ -62,4 +62,13 @@ do done +test_expect_success 'am --abort will keep the local commits intact' ' + test_must_fail git am 0004-*.patch && + test_commit unrelated && + git rev-parse HEAD >expect && + git am --abort && + git rev-parse HEAD >actual && + test_cmp expect actual +' + test_done diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh index 552da65a61..baa670cea5 100755 --- a/t/t5407-post-rewrite-hook.sh +++ b/t/t5407-post-rewrite-hook.sh @@ -10,7 +10,11 @@ test_expect_success 'setup' ' test_commit A foo A && test_commit B foo B && test_commit C foo C && - test_commit D foo D + test_commit D foo D && + git checkout A^0 && + test_commit E bar E && + test_commit F foo F && + git checkout master ' mkdir .git/hooks @@ -79,6 +83,18 @@ EOF verify_hook_input ' +test_expect_success 'git rebase --skip the last one' ' + git reset --hard F && + clear_hook_input && + test_must_fail git rebase --onto D A && + git rebase --skip && + echo rebase >expected.args && + cat >expected.data <<EOF && +$(git rev-parse E) $(git rev-parse HEAD) +EOF + verify_hook_input +' + test_expect_success 'git rebase -m' ' git reset --hard D && clear_hook_input && diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 884a5e567c..a5f458533f 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -124,7 +124,7 @@ test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setti ( cd downstream && git fetch --recurse-submodules >../actual.out 2>../actual.err && - git config -f --unset .gitmodules submodule.submodule.fetchRecurseSubmodules true && + git config --unset -f .gitmodules submodule.submodule.fetchRecurseSubmodules && git config --unset submodule.submodule.fetchRecurseSubmodules ) && test_cmp expect.out actual.out && diff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh index 460bf741b5..d9c2d386dd 100755 --- a/t/t6038-merge-text-auto.sh +++ b/t/t6038-merge-text-auto.sh @@ -14,7 +14,7 @@ test_description='CRLF merge conflict across text=auto change . ./test-lib.sh -test_have_prereq MINGW && SED_OPTIONS=-b +test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b test_expect_success setup ' git config core.autocrlf false && diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 2c49db9f62..874279e32d 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -446,4 +446,42 @@ test_expect_success 'add should fail when path is used by an existing directory' ) ' +test_expect_success 'set up for relative path tests' ' + mkdir reltest && + ( + cd reltest && + git init && + mkdir sub && + ( + cd sub && + git init && + test_commit foo + ) && + git add sub && + git config -f .gitmodules submodule.sub.path sub && + git config -f .gitmodules submodule.sub.url ../subrepo && + cp .git/config pristine-.git-config + ) +' + +test_expect_success 'relative path works with URL' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + git config remote.origin.url ssh://hostname/repo && + git submodule init && + test "$(git config submodule.sub.url)" = ssh://hostname/subrepo + ) +' + +test_expect_success 'relative path works with user@host:path' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + git config remote.origin.url user@host:repo && + git submodule init && + test "$(git config submodule.sub.url)" = user@host:subrepo + ) +' + test_done diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index d8ad25036f..e5be13c271 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -238,6 +238,10 @@ test_expect_success 'ensure "status --cached --recursive" preserves the --cached ) && git submodule status --cached --recursive -- nested1 > ../actual ) && + if test_have_prereq MINGW + then + dos2unix actual + fi && test_cmp expect actual ' diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 8297cb4f1e..8980738c75 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -230,6 +230,10 @@ test_expect_success 'amend commit to fix date' ' ' +test_expect_success 'commit complains about bogus date' ' + test_must_fail git commit --amend --date=10.11.2010 +' + test_expect_success 'sign off (1)' ' echo 1 >positive && diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 5e48318013..579ddb7572 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -265,7 +265,7 @@ test_expect_success $PREREQ 'Author From: in message body' ' --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - sed "1,/^\$/d" < msgtxt1 > msgbody1 + sed "1,/^\$/d" < msgtxt1 > msgbody1 && grep "From: A <author@example.com>" msgbody1 ' @@ -276,7 +276,7 @@ test_expect_success $PREREQ 'Author From: not in message body' ' --to=nobody@example.com \ --smtp-server="$(pwd)/fake.sendmail" \ $patches && - sed "1,/^\$/d" < msgtxt1 > msgbody1 + sed "1,/^\$/d" < msgtxt1 > msgbody1 && ! grep "From: A <author@example.com>" msgbody1 ' @@ -298,7 +298,7 @@ test_expect_success $PREREQ 'Invalid In-Reply-To' ' --in-reply-to=" " \ --smtp-server="$(pwd)/fake.sendmail" \ $patches \ - 2>errors + 2>errors && ! grep "^In-Reply-To: < *>" msgtxt1 ' @@ -319,7 +319,7 @@ test_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' ' git send-email \ --from="Example <nobody@example.com>" \ --to=nobody@example.com \ - --no-chain-reply-to \ + --nochain-reply-to \ --in-reply-to="$(cat expect)" \ --smtp-server="$(pwd)/fake.sendmail" \ $patches $patches $patches \ @@ -617,7 +617,7 @@ EOF " test_expect_success $PREREQ '--suppress-cc=sob' ' - git config --unset sendemail.cccmd + test_might_fail git config --unset sendemail.cccmd && test_suppression sob ' @@ -1135,7 +1135,7 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' ' # Note that the patches in this test are deliberately out of order; we # want to make sure it works even if the cover-letter is not in the # first mail. -test_expect_success 'refusing to send cover letter template' ' +test_expect_success $PREREQ 'refusing to send cover letter template' ' clean_fake_sendmail && rm -fr outdir && git format-patch --cover-letter -2 -o outdir && @@ -1151,7 +1151,7 @@ test_expect_success 'refusing to send cover letter template' ' test -z "$(ls msgtxt*)" ' -test_expect_success '--force sends cover letter template anyway' ' +test_expect_success $PREREQ '--force sends cover letter template anyway' ' clean_fake_sendmail && rm -fr outdir && git format-patch --cover-letter -2 -o outdir && diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh index faf9092967..88a9751dd3 100755 --- a/t/t9010-svn-fe.sh +++ b/t/t9010-svn-fe.sh @@ -4,45 +4,55 @@ test_description='check svn dumpfile importer' . ./test-lib.sh -if ! svnadmin -h >/dev/null 2>&1 -then - skip_all='skipping svn-fe tests, svn not available' - test_done -fi - -svnconf=$PWD/svnconf -export svnconf - -svn_cmd () { - subcommand=$1 && - shift && - mkdir -p "$svnconf" && - svn "$subcommand" --config-dir "$svnconf" "$@" +reinit_git () { + rm -fr .git && + git init } -test_dump () { - label=$1 - dump=$2 - test_expect_success "$dump" ' - svnadmin create "$label-svn" && - svnadmin load "$label-svn" < "$TEST_DIRECTORY/$dump" && - svn_cmd export "file://$PWD/$label-svn" "$label-svnco" && - git init "$label-git" && - test-svn-fe "$TEST_DIRECTORY/$dump" >"$label.fe" && - ( - cd "$label-git" && - git fast-import < ../"$label.fe" - ) && - ( - cd "$label-svnco" && - git init && - git add . && - git fetch "../$label-git" master && - git diff --exit-code FETCH_HEAD - ) - ' -} +>empty + +test_expect_success 'empty dump' ' + reinit_git && + echo "SVN-fs-dump-format-version: 2" >input && + test-svn-fe input >stream && + git fast-import <stream +' + +test_expect_success 'v3 dumps not supported' ' + reinit_git && + echo "SVN-fs-dump-format-version: 3" >input && + test_must_fail test-svn-fe input >stream && + test_cmp empty stream +' + +test_expect_success 'set up svn repo' ' + svnconf=$PWD/svnconf && + mkdir -p "$svnconf" && -test_dump simple t9135/svn.dump + if + svnadmin -h >/dev/null 2>&1 && + svnadmin create simple-svn && + svnadmin load simple-svn <"$TEST_DIRECTORY/t9135/svn.dump" && + svn export --config-dir "$svnconf" "file://$PWD/simple-svn" simple-svnco + then + test_set_prereq SVNREPO + fi +' + +test_expect_success SVNREPO 't9135/svn.dump' ' + git init simple-git && + test-svn-fe "$TEST_DIRECTORY/t9135/svn.dump" >simple.fe && + ( + cd simple-git && + git fast-import <../simple.fe + ) && + ( + cd simple-svnco && + git init && + git add . && + git fetch ../simple-git master && + git diff --exit-code FETCH_HEAD + ) +' test_done diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh index 1236accd99..e21ee5f663 100755 --- a/t/t9142-git-svn-shallow-clone.sh +++ b/t/t9142-git-svn-shallow-clone.sh @@ -17,11 +17,10 @@ test_expect_success 'setup test repository' ' > foo && svn_cmd add foo && svn_cmd commit -m "add foo" - ) + ) && + start_httpd ' -start_httpd - test_expect_success 'clone trunk with "-r HEAD"' ' git svn clone -r HEAD "$svnrepo/trunk" g && ( cd g && git rev-parse --symbolic --verify HEAD ) diff --git a/t/t9157-git-svn-fetch-merge.sh b/t/t9157-git-svn-fetch-merge.sh index da582c5382..991d2aa1be 100755 --- a/t/t9157-git-svn-fetch-merge.sh +++ b/t/t9157-git-svn-fetch-merge.sh @@ -6,6 +6,14 @@ test_description='git svn merge detection' . ./lib-git-svn.sh +svn_ver="$(svn --version --quiet)" +case $svn_ver in +0.* | 1.[0-4].*) + skip_all="skipping git-svn test - SVN too old ($svn_ver)" + test_done + ;; +esac + test_expect_success 'initialize source svn repo' ' svn_cmd mkdir -m x "$svnrepo"/trunk && svn_cmd mkdir -m x "$svnrepo"/branches && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 222d1059ef..986bc14d58 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -920,6 +920,48 @@ test_expect_success \ compare_diff_raw expect actual' test_expect_success \ + 'N: delete directory by copying' \ + 'cat >expect <<-\EOF && + OBJID + :100644 000000 OBJID OBJID D foo/bar/qux + OBJID + :000000 100644 OBJID OBJID A foo/bar/baz + :000000 100644 OBJID OBJID A foo/bar/qux + EOF + empty_tree=$(git mktree </dev/null) && + cat >input <<-INPUT_END && + commit refs/heads/N-delete + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + collect data to be deleted + COMMIT + + deleteall + M 100644 inline foo/bar/baz + data <<DATA_END + hello + DATA_END + C "foo/bar/baz" "foo/bar/qux" + C "foo/bar/baz" "foo/bar/quux/1" + C "foo/bar/baz" "foo/bar/quuux" + M 040000 $empty_tree foo/bar/quux + M 040000 $empty_tree foo/bar/quuux + + commit refs/heads/N-delete + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + delete subdirectory + COMMIT + + M 040000 $empty_tree foo/bar/qux + INPUT_END + git fast-import <input && + git rev-list N-delete | + git diff-tree -r --stdin --root --always | + sed -e "s/$_x40/OBJID/g" >actual && + test_cmp expect actual' + +test_expect_success \ 'N: modify copied tree' \ 'cat >expect <<-\EOF && :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5 diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh index 7cf8cd8a2f..463254c727 100755 --- a/t/t9301-fast-import-notes.sh +++ b/t/t9301-fast-import-notes.sh @@ -120,6 +120,7 @@ test_expect_success 'add notes with simple M command' ' test_tick cat >input <<INPUT_END +feature notes commit refs/notes/test committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE data <<COMMIT diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh index 2487da1296..18825aff89 100755 --- a/t/t9501-gitweb-standalone-http-status.sh +++ b/t/t9501-gitweb-standalone-http-status.sh @@ -16,7 +16,7 @@ code and message.' # snapshot settings test_expect_success 'setup' " - test_commit 'SnapshotTests' 'i can has snapshot?' + test_commit 'SnapshotTests' 'i can has snapshot' " diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 432b82e3d5..4c384ff023 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -89,7 +89,8 @@ EOF test_expect_success PERL 'update git module' ' (cd module-git && - git cvsimport -a -R -z 0 module && + git config cvsimport.trackRevisions true && + git cvsimport -a -z 0 module && git merge origin ) && test_cmp module-cvs/o_fortuna module-git/o_fortuna @@ -117,7 +118,8 @@ test_expect_success PERL 'cvsimport.module config works' ' (cd module-git && git config cvsimport.module module && - git cvsimport -a -R -z0 && + git config cvsimport.trackRevisions true && + git cvsimport -a -z0 && git merge origin ) && test_cmp module-cvs/tick module-git/tick @@ -137,6 +139,7 @@ test_expect_success PERL 'import from a CVS working tree' ' $CVS co -d import-from-wt module && (cd import-from-wt && + git config cvsimport.trackRevisions false && git cvsimport -a -z0 && echo 1 >expect && git log -1 --pretty=format:%s%n >actual && diff --git a/t/test-lib.sh b/t/test-lib.sh index 48fa516004..0fdc541a7c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -70,6 +70,9 @@ unset GIT_NOTES_REF unset GIT_NOTES_DISPLAY_REF unset GIT_NOTES_REWRITE_REF unset GIT_NOTES_REWRITE_MODE +unset GIT_REFLOG_ACTION +unset GIT_CHERRY_PICK_HELP +unset GIT_QUIET GIT_MERGE_VERBOSITY=5 export GIT_MERGE_VERBOSITY export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME @@ -260,7 +263,7 @@ test_decode_color () { if (n == 47) return "BWHITE"; } { - while (match($0, /\x1b\[[0-9;]*m/) != 0) { + while (match($0, /\033\[[0-9;]*m/) != 0) { printf "%s<", substr($0, 1, RSTART-1); codes = substr($0, RSTART+2, RLENGTH-3); if (length(codes) == 0) @@ -1057,6 +1060,13 @@ case $(uname -s) in # backslashes in pathspec are converted to '/' # exec does not inherit the PID test_set_prereq MINGW + test_set_prereq SED_STRIPS_CR + ;; +*CYGWIN*) + test_set_prereq POSIXPERM + test_set_prereq EXECKEEPSPID + test_set_prereq NOT_MINGW + test_set_prereq SED_STRIPS_CR ;; *) test_set_prereq POSIXPERM @@ -56,7 +56,7 @@ static unsigned long parse_tag_date(const char *buf, const char *tail) return strtoul(dateptr, NULL, 10); } -int parse_tag_buffer(struct tag *item, void *data, unsigned long size) +int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) { unsigned char sha1[20]; char type[20]; @@ -13,7 +13,7 @@ struct tag { }; extern struct tag *lookup_tag(const unsigned char *sha1); -extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size); +extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long size); extern int parse_tag(struct tag *item); extern struct object *deref_tag(struct object *, const char *, int); extern size_t parse_signature(const char *buf, unsigned long size); diff --git a/test-subprocess.c b/test-subprocess.c new file mode 100644 index 0000000000..667d3e5079 --- /dev/null +++ b/test-subprocess.c @@ -0,0 +1,21 @@ +#include "cache.h" +#include "run-command.h" + +int main(int argc, char **argv) +{ + const char *prefix; + struct child_process cp; + int nogit = 0; + + prefix = setup_git_directory_gently(&nogit); + if (nogit) + die("No git repo found"); + if (!strcmp(argv[1], "--setup-work-tree")) { + setup_work_tree(); + argv++; + } + memset(&cp, 0, sizeof(cp)); + cp.git_cmd = 1; + cp.argv = (const char **)argv+1; + return run_command(&cp); +} @@ -127,3 +127,52 @@ void trace_argv_printf(const char **argv, const char *fmt, ...) if (need_close) close(fd); } + +static const char *quote_crnl(const char *path) +{ + static char new_path[PATH_MAX]; + const char *p2 = path; + char *p1 = new_path; + + if (!path) + return NULL; + + while (*p2) { + switch (*p2) { + case '\\': *p1++ = '\\'; *p1++ = '\\'; break; + case '\n': *p1++ = '\\'; *p1++ = 'n'; break; + case '\r': *p1++ = '\\'; *p1++ = 'r'; break; + default: + *p1++ = *p2; + } + p2++; + } + *p1 = '\0'; + return new_path; +} + +/* FIXME: move prefix to startup_info struct and get rid of this arg */ +void trace_repo_setup(const char *prefix) +{ + const char *git_work_tree; + char cwd[PATH_MAX]; + char *trace = getenv("GIT_TRACE"); + + if (!trace || !strcmp(trace, "") || + !strcmp(trace, "0") || !strcasecmp(trace, "false")) + return; + + if (!getcwd(cwd, PATH_MAX)) + die("Unable to get current working directory"); + + if (!(git_work_tree = get_git_work_tree())) + git_work_tree = "(null)"; + + if (!prefix) + prefix = "(null)"; + + trace_printf("setup: git_dir: %s\n", quote_crnl(get_git_dir())); + trace_printf("setup: worktree: %s\n", quote_crnl(git_work_tree)); + trace_printf("setup: cwd: %s\n", quote_crnl(cwd)); + trace_printf("setup: prefix: %s\n", quote_crnl(prefix)); +} diff --git a/unpack-trees.c b/unpack-trees.c index d5a453079a..1ca41b1a69 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -231,20 +231,11 @@ static int check_updates(struct unpack_trees_options *o) static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o); static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o); -static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o) -{ - const char *basename; - - basename = strrchr(ce->name, '/'); - basename = basename ? basename+1 : ce->name; - return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0; -} - static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o) { int was_skip_worktree = ce_skip_worktree(ce); - if (!ce_stage(ce) && will_have_skip_worktree(ce, o)) + if (ce->ce_flags & CE_NEW_SKIP_WORKTREE) ce->ce_flags |= CE_SKIP_WORKTREE; else ce->ce_flags &= ~CE_SKIP_WORKTREE; @@ -319,7 +310,7 @@ static void mark_all_ce_unused(struct index_state *index) { int i; for (i = 0; i < index->cache_nr; i++) - index->cache[i]->ce_flags &= ~CE_UNPACKED; + index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE); } static int locate_in_src_index(struct cache_entry *ce, @@ -820,9 +811,177 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str return mask; } +/* Whole directory matching */ +static int clear_ce_flags_dir(struct cache_entry **cache, int nr, + char *prefix, int prefix_len, + char *basename, + int select_mask, int clear_mask, + struct exclude_list *el) +{ + struct cache_entry **cache_end = cache + nr; + int dtype = DT_DIR; + int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el); + + prefix[prefix_len++] = '/'; + + /* included, no clearing for any entries under this directory */ + if (!ret) { + for (; cache != cache_end; cache++) { + struct cache_entry *ce = *cache; + if (strncmp(ce->name, prefix, prefix_len)) + break; + } + return nr - (cache_end - cache); + } + + /* excluded, clear all selected entries under this directory. */ + if (ret == 1) { + for (; cache != cache_end; cache++) { + struct cache_entry *ce = *cache; + if (select_mask && !(ce->ce_flags & select_mask)) + continue; + if (strncmp(ce->name, prefix, prefix_len)) + break; + ce->ce_flags &= ~clear_mask; + } + return nr - (cache_end - cache); + } + + return 0; +} + +/* + * Traverse the index, find every entry that matches according to + * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the + * number of traversed entries. + * + * If select_mask is non-zero, only entries whose ce_flags has on of + * those bits enabled are traversed. + * + * cache : pointer to an index entry + * prefix_len : an offset to its path + * + * The current path ("prefix") including the trailing '/' is + * cache[0]->name[0..(prefix_len-1)] + * Top level path has prefix_len zero. + */ +static int clear_ce_flags_1(struct cache_entry **cache, int nr, + char *prefix, int prefix_len, + int select_mask, int clear_mask, + struct exclude_list *el) +{ + struct cache_entry **cache_end = cache + nr; + + /* + * Process all entries that have the given prefix and meet + * select_mask condition + */ + while(cache != cache_end) { + struct cache_entry *ce = *cache; + const char *name, *slash; + int len, dtype; + + if (select_mask && !(ce->ce_flags & select_mask)) { + cache++; + continue; + } + + if (prefix_len && strncmp(ce->name, prefix, prefix_len)) + break; + + name = ce->name + prefix_len; + slash = strchr(name, '/'); + + /* If it's a directory, try whole directory match first */ + if (slash) { + int processed; + + len = slash - name; + memcpy(prefix + prefix_len, name, len); + + /* + * terminate the string (no trailing slash), + * clear_c_f_dir needs it + */ + prefix[prefix_len + len] = '\0'; + processed = clear_ce_flags_dir(cache, cache_end - cache, + prefix, prefix_len + len, + prefix + prefix_len, + select_mask, clear_mask, + el); + + /* clear_c_f_dir eats a whole dir already? */ + if (processed) { + cache += processed; + continue; + } + + prefix[prefix_len + len++] = '/'; + cache += clear_ce_flags_1(cache, cache_end - cache, + prefix, prefix_len + len, + select_mask, clear_mask, el); + continue; + } + + /* Non-directory */ + dtype = ce_to_dtype(ce); + if (excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el) > 0) + ce->ce_flags &= ~clear_mask; + cache++; + } + return nr - (cache_end - cache); +} + +static int clear_ce_flags(struct cache_entry **cache, int nr, + int select_mask, int clear_mask, + struct exclude_list *el) +{ + char prefix[PATH_MAX]; + return clear_ce_flags_1(cache, nr, + prefix, 0, + select_mask, clear_mask, + el); +} + +/* + * Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout + */ +static void mark_new_skip_worktree(struct exclude_list *el, + struct index_state *the_index, + int select_flag, int skip_wt_flag) +{ + int i; + + /* + * 1. Pretend the narrowest worktree: only unmerged entries + * are checked out + */ + for (i = 0; i < the_index->cache_nr; i++) { + struct cache_entry *ce = the_index->cache[i]; + + if (select_flag && !(ce->ce_flags & select_flag)) + continue; + + if (!ce_stage(ce)) + ce->ce_flags |= skip_wt_flag; + else + ce->ce_flags &= ~skip_wt_flag; + } + + /* + * 2. Widen worktree according to sparse-checkout file. + * Matched entries will have skip_wt_flag cleared (i.e. "in") + */ + clear_ce_flags(the_index->cache, the_index->cache_nr, + select_flag, skip_wt_flag, el); +} + +static int verify_absent(struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *); /* * N-way merge "len" trees. Returns 0 on success, -1 on failure to manipulate the * resulting index, -2 on failure to reflect the changes to the work tree. + * + * CE_ADDED, CE_UNPACKED and CE_NEW_SKIP_WORKTREE are used internally */ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) { @@ -855,6 +1014,12 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->merge_size = len; mark_all_ce_unused(o->src_index); + /* + * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries + */ + if (!o->skip_sparse_checkout) + mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE); + if (!dfc) dfc = xcalloc(1, cache_entry_size(0)); o->df_conflict_entry = dfc; @@ -908,9 +1073,29 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options if (!o->skip_sparse_checkout) { int empty_worktree = 1; - for (i = 0;i < o->result.cache_nr;i++) { + + /* + * Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1 + * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE + * so apply_sparse_checkout() won't attempt to remove it from worktree + */ + mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE); + + for (i = 0; i < o->result.cache_nr; i++) { struct cache_entry *ce = o->result.cache[i]; + /* + * Entries marked with CE_ADDED in merged_entry() do not have + * verify_absent() check (the check is effectively disabled + * because CE_NEW_SKIP_WORKTREE is set unconditionally). + * + * Do the real check now because we have had + * correct CE_NEW_SKIP_WORKTREE + */ + if (ce->ce_flags & CE_ADDED && + verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) + return -1; + if (apply_sparse_checkout(ce, o)) { ret = -1; goto done; @@ -931,11 +1116,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o->dst_index = o->result; done: - for (i = 0;i < el.nr;i++) - free(el.excludes[i]); - if (el.excludes) - free(el.excludes); - + free_excludes(&el); return ret; return_failed: @@ -1003,7 +1184,7 @@ static int verify_uptodate_1(struct cache_entry *ce, static int verify_uptodate(struct cache_entry *ce, struct unpack_trees_options *o) { - if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o)) + if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) return 0; return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE); } @@ -1209,7 +1390,7 @@ static int verify_absent(struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { - if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o)) + if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE)) return 0; return verify_absent_1(ce, error_type, o); } @@ -1231,10 +1412,23 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old, int update = CE_UPDATE; if (!old) { + /* + * New index entries. In sparse checkout, the following + * verify_absent() will be delayed until after + * traverse_trees() finishes in unpack_trees(), then: + * + * - CE_NEW_SKIP_WORKTREE will be computed correctly + * - verify_absent() be called again, this time with + * correct CE_NEW_SKIP_WORKTREE + * + * verify_absent() call here does nothing in sparse + * checkout (i.e. o->skip_sparse_checkout == 0) + */ + update |= CE_ADDED; + merge->ce_flags |= CE_NEW_SKIP_WORKTREE; + if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) return -1; - if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o)) - update |= CE_SKIP_WORKTREE; invalidate_ce_path(merge, o); } else if (!(old->ce_flags & CE_CONFLICTED)) { /* @@ -1250,8 +1444,8 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old, } else { if (verify_uptodate(old, o)) return -1; - if (ce_skip_worktree(old)) - update |= CE_SKIP_WORKTREE; + /* Migrate old flags over */ + update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE); invalidate_ce_path(old, o); } } else { diff --git a/upload-pack.c b/upload-pack.c index f05e4229d0..b40a43f27d 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -366,7 +366,7 @@ static int reachable(struct commit *want) { struct commit_list *work = NULL; - insert_by_date(want, &work); + commit_list_insert_by_date(want, &work); while (work) { struct commit_list *list = work->next; struct commit *commit = work->item; @@ -387,7 +387,7 @@ static int reachable(struct commit *want) for (list = commit->parents; list; list = list->next) { struct commit *parent = list->item; if (!(parent->object.flags & REACHABLE)) - insert_by_date(parent, &work); + commit_list_insert_by_date(parent, &work); } } want->object.flags |= REACHABLE; diff --git a/userdiff.c b/userdiff.c index 2d5453697a..9ebf231ea5 100644 --- a/userdiff.c +++ b/userdiff.c @@ -52,7 +52,7 @@ PATTERNS("objc", "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->" "|[^[:space:]]|[\x80-\xff]+"), PATTERNS("pascal", - "^((procedure|function|constructor|destructor|interface|" + "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|" "implementation|initialization|finalization)[ \t]*.*)$" "\n" "^(.*=[ \t]*(class|record).*)$", @@ -61,6 +61,23 @@ PATTERNS("pascal", "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+" "|<>|<=|>=|:=|\\.\\." "|[^[:space:]]|[\x80-\xff]+"), +PATTERNS("perl", + "^[ \t]*package .*;\n" + "^[ \t]*sub .* \\{\n" + "^[A-Z]+ \\{\n" /* BEGIN, END, ... */ + "^=head[0-9] ", /* POD */ + /* -- */ + "[[:alpha:]_'][[:alnum:]_']*" + "|0[xb]?[0-9a-fA-F_]*" + /* taking care not to interpret 3..5 as (3.)(.5) */ + "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?" + "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::" + "|&&=|\\|\\|=|//=|\\*\\*=" + "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?" + "|[-+*/%.^&<>=!|]=" + "|=~|!~" + "|<<|<>|<=>|>>" + "|[^[:space:]]"), PATTERNS("php", "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n" "^[\t ]*(class.*)$", diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c index 53d0215d2d..2ad2c307dd 100644 --- a/vcs-svn/svndump.c +++ b/vcs-svn/svndump.c @@ -51,14 +51,14 @@ static struct { } rev_ctx; static struct { - uint32_t uuid, url; + uint32_t version, uuid, url; } dump_ctx; static struct { uint32_t svn_log, svn_author, svn_date, svn_executable, svn_special, uuid, revision_number, node_path, node_kind, node_action, node_copyfrom_path, node_copyfrom_rev, text_content_length, - prop_content_length, content_length; + prop_content_length, content_length, svn_fs_dump_format_version; } keys; static void reset_node_ctx(char *fname) @@ -85,6 +85,7 @@ static void reset_rev_ctx(uint32_t revision) static void reset_dump_ctx(uint32_t url) { dump_ctx.url = url; + dump_ctx.version = 1; dump_ctx.uuid = ~0; } @@ -105,6 +106,7 @@ static void init_keys(void) keys.text_content_length = pool_intern("Text-content-length"); keys.prop_content_length = pool_intern("Prop-content-length"); keys.content_length = pool_intern("Content-length"); + keys.svn_fs_dump_format_version = pool_intern("SVN-fs-dump-format-version"); } static void read_props(void) @@ -206,7 +208,12 @@ void svndump_read(const char *url) *val++ = '\0'; key = pool_intern(t); - if (key == keys.uuid) { + if (key == keys.svn_fs_dump_format_version) { + dump_ctx.version = atoi(val); + if (dump_ctx.version > 2) + die("expected svn dump format version <= 2, found %"PRIu32, + dump_ctx.version); + } else if (key == keys.uuid) { dump_ctx.uuid = pool_intern(val); } else if (key == keys.revision_number) { if (active_ctx == NODE_CTX) @@ -207,7 +207,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag, struct commit *commit = lookup_commit_reference_gently(sha1, 1); if (commit) { commit->object.flags |= COMPLETE; - insert_by_date(commit, &complete); + commit_list_insert_by_date(commit, &complete); } return 0; } diff --git a/xdiff-interface.c b/xdiff-interface.c index e1e054e4d9..164581f87f 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -212,8 +212,10 @@ int read_mmfile(mmfile_t *ptr, const char *filename) return error("Could not open %s", filename); sz = xsize_t(st.st_size); ptr->ptr = xmalloc(sz ? sz : 1); - if (sz && fread(ptr->ptr, sz, 1, f) != 1) + if (sz && fread(ptr->ptr, sz, 1, f) != 1) { + fclose(f); return error("Could not read %s", filename); + } fclose(f); ptr->size = sz; return 0; |