From: Junio C Hamano Date: Tue, 12 Feb 2008 00:46:20 +0000 (-0800) Subject: Merge branch 'lt/in-core-index' X-Git-Tag: v1.5.5-rc0~232 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/e0197c9aae39b0f1ba6c21d1f6d0bae5de03a44d?hp=9cb76b8cdc8ac62a77080595f6443613fd64bab3 Merge branch 'lt/in-core-index' * lt/in-core-index: lazy index hashing Create pathname-based hash-table lookup into index read-cache.c: introduce is_racy_timestamp() helper read-cache.c: fix a couple more CE_REMOVE conversion Also use unpack_trees() in do_diff_cache() Make run_diff_index() use unpack_trees(), not read_tree() Avoid running lstat(2) on the same cache entry. index: be careful when handling long names Make on-disk index representation separate from in-core one --- diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..6b9c715d21 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* whitespace=!indent,trail,space +*.[ch] whitespace diff --git a/Documentation/.gitattributes b/Documentation/.gitattributes new file mode 100644 index 0000000000..ddb030137d --- /dev/null +++ b/Documentation/.gitattributes @@ -0,0 +1 @@ +*.txt whitespace diff --git a/Documentation/Makefile b/Documentation/Makefile index 7a325462ee..43781fb248 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -45,6 +45,7 @@ man7dir=$(mandir)/man7 ASCIIDOC=asciidoc ASCIIDOC_EXTRA = +MANPAGE_XSL = callouts.xsl INSTALL?=install RM ?= rm -f DOC_REF = origin/man @@ -65,6 +66,7 @@ ASCIIDOC_EXTRA += -a asciidoc7compatible endif ifdef DOCBOOK_XSL_172 ASCIIDOC_EXTRA += -a docbook-xsl-172 +MANPAGE_XSL = manpage-1.72.xsl endif # @@ -159,7 +161,7 @@ $(MAN_HTML): %.html : %.txt %.1 %.5 %.7 : %.xml $(RM) $@ - xmlto -m callouts.xsl man $< + xmlto -m $(MANPAGE_XSL) man $< %.xml : %.txt $(RM) $@+ $@ diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes-1.5.3.3.txt index 2a7bfdd5cc..d213846951 100644 --- a/Documentation/RelNotes-1.5.3.3.txt +++ b/Documentation/RelNotes-1.5.3.3.txt @@ -12,7 +12,7 @@ Fixes since v1.5.3.2 * The default shell on some FreeBSD did not execute the argument parsing code correctly and made git unusable. - * git-svn incorrectly spawned pager even when the user user + * git-svn incorrectly spawned pager even when the user explicitly asked not to. * sample post-receive hook overquoted the envelope sender diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index 9c864c9def..f1323b6174 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -10,22 +10,32 @@ Removal * As git-commit and git-status have been rewritten, "git runstatus" helper script lost all its users and has been removed. - * Curl library older than 7.10 is not supported by "git http-push", - as it does not work without CURLM. + +Temporarily disabled +-------------------- + + * "git http-push" is known not to work well with cURL library older + than 7.16, and we had reports of repository corruption. It is + disabled on such platforms for now. Unfortunately, 1.5.3.8 shares + the same issue. In other words, this does not mean you will be + fine if you stick to an older git release. For now, please do not + use http-push from older git with cURL older than 7.16 if you + value your data. A proper fix will hopefully materialize in + later versions. Deprecation notices ------------------- - * The next feature release of git (this change is scheduled for v1.6.0) - will by default install dashed form of commands (e.g. "git-commit") - outside of users' normal $PATH, and will install only selected - commands ("git" itself, and "gitk") in $PATH. This implies: + * From v1.6.0, git will by default install dashed form of commands + (e.g. "git-commit") outside of users' normal $PATH, and will install + only selected commands ("git" itself, and "gitk") in $PATH. This + implies: - Using dashed forms of git commands (e.g. "git-commit") from the command line has been informally deprecated since early 2006, but now it officially is, and will be removed in the future. Use - dashless forms (e.g. "git commit") instead. + dash-less forms (e.g. "git commit") instead. - Using dashed forms from your scripts, without first prepending the return value from "git --exec-path" to the scripts' PATH, has been @@ -34,8 +44,8 @@ Deprecation notices - Use of dashed forms with "PATH=$(git --exec-path):$PATH; export PATH" early in your script is not deprecated with this change. - Users are strongly encouraged to adjust their habits and scripts now - to prepare for this. + Users are strongly encouraged to adjust their habits and scripts now + to prepare for this change. * The post-receive hook was introduced in March 2007 to supersede the post-update hook, primarily to overcome the command line length @@ -70,7 +80,7 @@ Updates since v1.5.3 * Comes with much improved gitk, with i18n. - * Comes with "git gui" 0.9.1 with i18n. + * Comes with git-gui 0.9.2 with i18n. * gitk is now merged as a subdirectory of git.git project, in preparation for its i18n. @@ -156,7 +166,7 @@ Updates since v1.5.3 command line in the generated log message, when told to cherry-pick a commit by naming a tag that points at it. It does not anymore. - * "git for-each-ref" learned %(xxxdate:) syntax to show the + * "git for-each-ref" learned %(xxxdate:) syntax to show the various date fields in different formats. * "git gc --auto" is a low-impact way to automatically run a variant of @@ -211,7 +221,7 @@ Updates since v1.5.3 * "git pull --rebase" is a different way to integrate what you fetched into your current branch. - * "git fast-export" produces datastream that can be fed to fast-import + * "git fast-export" produces data-stream that can be fed to fast-import to reproduce the history recorded in a git repository. * "git add -i" takes pathspecs to limit the set of files to work on. @@ -232,8 +242,8 @@ Updates since v1.5.3 from its first parent. * "git commit" used to unconditionally strip comment lines that - began with '#' and removed excess blank lines. This - behaviour has been made configurable. + began with '#' and removed excess blank lines. This behavior has + been made configurable. * "git commit" has been rewritten in C. @@ -317,7 +327,7 @@ Updates since v1.5.3 * "git status" from a subdirectory now shows relative paths, which makes copy-and-pasting for git-checkout/git-add/git-rm easier. The - traditional behaviour to show the full path relative to the top of + traditional behavior to show the full path relative to the top of the work tree can be had by setting status.relativepaths configuration variable to false. @@ -332,7 +342,7 @@ Updates since v1.5.3 * "git help" learned "-w" option to show documentation in browsers. - * In addition there are quite a few internal clean-ups. Notably + * In addition there are quite a few internal clean-ups. Notably: - many fork/exec have been replaced with run-command API, brought from the msysgit effort. @@ -341,6 +351,7 @@ Updates since v1.5.3 - enhancement and more use of the strbuf API. + * Makefile tweaks to support HP-UX is in. Fixes since v1.5.3 ------------------ @@ -354,7 +365,7 @@ series. * The way "git diff --check" behaves is much more consistent with the way "git apply --whitespace=warn" works. - * "git svn" talking with the SVN over http will correctly quote branch + * "git svn" talking with the SVN over HTTP will correctly quote branch and project names. * "git config" did not work correctly on platforms that define @@ -364,9 +375,3 @@ series. documentation; a workaround has been implemented. * "git diff --color-words" colored context lines in a wrong color. - --- -exec >/var/tmp/1 -O=v1.5.4-rc4 -echo O=`git describe refs/heads/master` -git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index ea1007bfb0..c11bb7d36c 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -52,7 +52,7 @@ of lines before or after the line given by . When is not specified, the command annotates the changes starting backwards from the working tree copy. This flag makes the command pretend as if the working - tree copy has the contents of he named file (specify + tree copy has the contents of the named file (specify `-` to make the command read from the standard input). -M||:: diff --git a/Documentation/config.txt b/Documentation/config.txt index 877eda960d..f9bdb164e0 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -333,7 +333,7 @@ branch.autosetupmerge:: so that linkgit:git-pull[1] will appropriately merge from that remote branch. Note that even if this option is not set, this behavior can be chosen per-branch using the `--track` - and `--no-track` options. This option defaults to false. + and `--no-track` options. This option defaults to true. branch..remote:: When in branch , it tells `git fetch` which remote to fetch. @@ -367,6 +367,11 @@ branch..rebase:: it unless you understand the implications (see linkgit:git-rebase[1] for details). +browser..path:: + Override the path for the given tool that may be used to + browse HTML help (see '-w' option in linkgit:git-help[1]) or a + working repository in gitweb (see linkgit:git-instaweb[1]). + clean.requireForce:: A boolean to make git-clean do nothing unless given -f or -n. Defaults to true. @@ -761,6 +766,12 @@ pack.indexVersion:: whenever the corresponding pack is larger than 2 GB. Otherwise the default is 1. +pack.packSizeLimit: + The default maximum size of a pack. This setting only affects + packing to a file, i.e. the git:// protocol is unaffected. It + can be overridden by the `\--max-pack-size` option of + linkgit:git-repack[1]. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 61e48ccf02..b675911480 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -23,11 +23,15 @@ fetches is a descendant of ``. This option overrides that check. +ifdef::git-pull[] +\--no-tags:: +endif::git-pull[] +ifndef::git-pull[] -n, \--no-tags:: - By default, `git-fetch` fetches tags that point at - objects that are downloaded from the remote repository - and stores them locally. This option disables this - automatic tag following. +endif::git-pull[] + By default, tags that point at objects that are downloaded + from the remote repository are fetched and stored locally. + This option disables this automatic tag following. -t, \--tags:: Most of the tags are fetched automatically as branch diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index f920c04cc0..7e8874acaa 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -34,11 +34,11 @@ Note that this will create the new branch, but it will not switch the working tree to it; use "git checkout " to switch to the new branch. -When a local branch is started off a remote branch, git can setup the +When a local branch is started off a remote branch, git sets up the branch so that linkgit:git-pull[1] will appropriately merge from that -remote branch. If this behavior is desired, it is possible to make it -the default using the global `branch.autosetupmerge` configuration -flag. Otherwise, it can be chosen per-branch using the `--track` +remote branch. If this behavior is not desired, it is possible to +disable it using the global `branch.autosetupmerge` configuration +flag. That setting can be overridden by using the `--track` and `--no-track` options. With a '-m' or '-M' option, will be renamed to . @@ -108,10 +108,11 @@ OPTIONS Set up configuration so that git-pull will automatically retrieve data from the remote branch. Use this if you always pull from the same remote branch into the new branch, or if you - don't want to use "git pull " explicitly. Set the - branch.autosetupmerge configuration variable to true if you + don't want to use "git pull " explicitly. + This behavior is the default. Set the + branch.autosetupmerge configuration variable to false if you want git-checkout and git-branch to always behave as if - '--track' were given. + '--no-track' were given. --no-track:: When a branch is created off a remote branch, diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 584359ff3f..b4cfa044bb 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -52,10 +52,11 @@ OPTIONS set up configuration so that git-pull will automatically retrieve data from the remote branch. Use this if you always pull from the same remote branch into the new branch, or if you - don't want to use "git pull " explicitly. Set the - branch.autosetupmerge configuration variable to true if you + don't want to use "git pull " explicitly. + This behavior is the default. Set the + branch.autosetupmerge configuration variable to false if you want git-checkout and git-branch to always behave as if - '--track' were given. + '--no-track' were given. --no-track:: When -b is given and a branch is created off a remote branch, diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index fdccbd4cbe..2341881614 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -62,6 +62,14 @@ OPTIONS .git/objects/info/alternates to share the objects with the source repository. The resulting repository starts out without any object of its own. + *NOTE*: this is a possibly dangerous operation; do *not* use + it unless you understand what it does. If you clone your + repository using this option, then delete branches in the + source repository and then run linkgit:git-gc[1] using the + '--prune' option in the source repository, it may remove + objects which are referenced by the cloned repository. + + --reference :: If the reference repository is on the local machine diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index e4d0e47529..fa161718dd 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -130,7 +130,7 @@ See also <>. -z, --null:: For all options that output values and/or keys, always - end values with with the null character (instead of a + end values with the null character (instead of a newline). Use newline instead as a delimiter between key and value. This allows for secure parsing of the output without getting confused e.g. by values that diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index d75e401343..d3e99931d7 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -183,7 +183,7 @@ access method and requested operation. That means that even if you offer only read access (e.g. by using the pserver method), git-cvsserver should have write access to the database to work reliably (otherwise you need to make sure -that the database if up-to-date all the time git-cvsserver is run). +that the database is up-to-date any time git-cvsserver is executed). By default it uses SQLite databases in the git directory, named `gitcvs..sqlite`. Note that the SQLite backend creates @@ -204,7 +204,7 @@ about `DBI->connect()`. gitcvs.dbname:: Database name. The exact meaning depends on the - used database driver, for SQLite this is a filename. + selected database driver, for SQLite this is a filename. Supports variable substitution (see below). May not contain semicolons (`;`). Default: '%Ggitcvs.%m.sqlite' @@ -215,7 +215,7 @@ gitcvs.dbdriver:: with 'DBD::SQLite', reported to work with 'DBD::Pg', and reported *not* to work with 'DBD::mysql'. Please regard this as an experimental feature. May not - contain double colons (`:`). + contain colons (`:`). Default: 'SQLite' gitcvs.dbuser:: diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index 0742152b81..1c3dfb40c6 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -51,6 +51,10 @@ OPTIONS being employed to standard error. The tag name will still be printed to standard out. +--match :: + Only consider tags matching the given pattern (can be used to avoid + leaking private tags made from the repository). + EXAMPLES -------- diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index 09904c75c4..fb77ca3a57 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -58,6 +58,16 @@ is available in PATH. + Note that the script tries, as much as possible, to display the HTML page in a new tab on an already opened browser. ++ +The following browsers are currently supported by 'git-help--browse': ++ +* firefox (this is the default under X Window when not using KDE) +* iceweasel +* konqueror (this is the default under KDE) +* w3m (this is the default outside X Window) +* links +* lynx +* dillo CONFIGURATION VARIABLES ----------------------- diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt index cca77f10d2..0b82722342 100644 --- a/Documentation/git-http-push.txt +++ b/Documentation/git-http-push.txt @@ -15,6 +15,9 @@ DESCRIPTION Sends missing objects to remote repository, and updates the remote branch. +*NOTE*: This command is temporarily disabled if your cURL +library is older than 7.16, as the combination has been reported +not to work and sometimes corrupts repository. OPTIONS ------- diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index 74cc7c1cb8..8353be186f 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -99,7 +99,8 @@ base-name:: --max-pack-size=:: Maximum size of each output packfile, expressed in MiB. If specified, multiple packfiles may be created. - The default is unlimited. + The default is unlimited, unless the config variable + `pack.packSizeLimit` is set. --incremental:: This flag causes an object already in a pack ignored diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 77fdaf146e..179bdfc69d 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -25,6 +25,7 @@ OPTIONS ------- include::merge-options.txt[] +:git-pull: 1 include::fetch-options.txt[] include::pull-fetch-param.txt[] @@ -34,11 +35,15 @@ include::urls-remotes.txt[] include::merge-strategies.txt[] \--rebase:: - Instead of a merge, perform a rebase after fetching. - *NOTE:* This is a potentially _dangerous_ mode of operation. - It rewrites history, which does not bode well when you - published that history already. Do *not* use this option - unless you have read linkgit:git-rebase[1] carefully. + Instead of a merge, perform a rebase after fetching. If + there is a remote ref for the upstream branch, and this branch + was rebased since last fetched, the rebase uses that information + to avoid rebasing non-local changes. ++ +*NOTE:* This is a potentially _dangerous_ mode of operation. +It rewrites history, which does not bode well when you +published that history already. Do *not* use this option +unless you have read linkgit:git-rebase[1] carefully. \--no-rebase:: Override earlier \--rebase. diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 10f6fa58bf..2cbd1f764b 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git-remote' -'git-remote' add [-t ] [-m ] [-f] [--mirror] +'git-remote' add [-t ] [-m ] [-f] [--mirror] 'git-remote' rm 'git-remote' show 'git-remote' prune diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 5d9c36985f..f02f6bbb49 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -229,13 +229,13 @@ blobs contained in a commit. * A colon, optionally followed by a stage number (0 to 3) and a colon, followed by a path; this names a blob object in the index at the given path. Missing stage number (and the colon - that follows it) names an stage 0 entry. During a merge, stage + that follows it) names a stage 0 entry. During a merge, stage 1 is the common ancestor, stage 2 is the target branch's version (typically the current branch), and stage 3 is the version from the branch being merged. -Here is an illustration, by Jon Loeliger. Both node B and C are -a commit parents of commit node A. Parent commits are ordered +Here is an illustration, by Jon Loeliger. Both commit nodes B +and C are parents of commit node A. Parent commits are ordered left-to-right. G H I J @@ -291,7 +291,7 @@ and its parent commits exists. `r1{caret}@` notation means all parents of `r1`. `r1{caret}!` includes commit `r1` but excludes its all parents. -Here are a handful examples: +Here are a handful of examples: D G H D D F G H I J D F diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 4b8ec8a200..0554f2b374 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -137,6 +137,8 @@ The --cc option must be repeated for each user you want on the cc list. Specify the primary recipient of the emails generated. Generally, this will be the upstream maintainer of the project involved. + Default is the value of the 'sendemail.to' configuration value; + if that is unspecified, this will be prompted for. + The --to option must be repeated for each user you want on the to list. diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 9889806a5e..cd0dc1bd9d 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git-stash' (list | show [] | apply [] | clear) -'git-stash' [save] [message...] +'git-stash' [save []] DESCRIPTION ----------- @@ -36,11 +36,12 @@ is also possible). OPTIONS ------- -save:: +save []:: Save your local modifications to a new 'stash', and run `git-reset --hard` to revert them. This is the default action when no - subcommand is given. + subcommand is given. The part is optional and gives + the description along with the stashed state. list:: diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index cffc6d48fb..e818e6e789 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -9,8 +9,9 @@ git-submodule - Initialize, update or inspect submodules SYNOPSIS -------- [verse] -'git-submodule' [--quiet] [-b branch] add [] -'git-submodule' [--quiet] [--cached] [status|init|update] [--] [...] +'git-submodule' [--quiet] add [-b branch] [--] [] +'git-submodule' [--quiet] status [--cached] [--] [...] +'git-submodule' [--quiet] [init|update] [--] [...] COMMANDS diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index e1a1d46a9f..340f1be02a 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -161,6 +161,13 @@ New features: + Any other arguments are passed directly to `git log' +'blame':: + Show what revision and author last modified each line of a file. This is + identical to `git blame', but SVN revision numbers are shown instead of git + commit hashes. ++ +All arguments are passed directly to `git blame'. + -- 'find-rev':: When given an SVN revision number of the form 'rN', returns the @@ -456,10 +463,13 @@ have each person clone that repository with 'git clone': ------------------------------------------------------------------------ # Do the initial import on a server ssh server "cd /pub && git-svn clone http://svn.foo.org/project -# Clone locally - git clone server:/pub/project -# Tell git-svn which branch contains the Subversion commits - git update-ref refs/remotes/git-svn origin/master +# Clone locally - make sure the refs/remotes/ space matches the server + mkdir project + cd project + git-init + git remote add origin server:/pub/project + git config --add remote.origin.fetch=+refs/remotes/*:refs/remotes/* + git fetch # Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server) git-svn init http://svn.foo.org/project # Pull the latest changes from Subversion diff --git a/Documentation/git.txt b/Documentation/git.txt index 4ece40d1eb..17aee93ec5 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,6 +43,11 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: +* link:v1.5.4/git.html[documentation for release 1.5.4] + +* release notes for + link:RelNotes-1.5.4.txt[1.5.4]. + * link:v1.5.3.8/git.html[documentation for release 1.5.3.8] * release notes for diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl new file mode 100644 index 0000000000..fe3cd72d6f --- /dev/null +++ b/Documentation/manpage-1.72.xsl @@ -0,0 +1,17 @@ + + + + + + + ⌂sp + + + + + + + ⌂br + + + diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt index 5030d9f2f8..6bdf034b3a 100644 --- a/Documentation/technical/racy-git.txt +++ b/Documentation/technical/racy-git.txt @@ -184,7 +184,7 @@ In a large project where raciness avoidance cost really matters, however, the initial computation of all object names in the index takes more than one second, and the index file is written out after all that happens. Therefore the timestamp of the -index file will be more than one seconds later than the the +index file will be more than one seconds later than the youngest file in the working tree. This means that in these cases there actually will not be any racily clean entry in the resulting index. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 2ba142a11e..38a3273974 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.4-rc3.GIT +DEF_VER=v1.5.4.GIT LF=' ' diff --git a/INSTALL b/INSTALL index f1eb4049b9..6f3bcb4525 100644 --- a/INSTALL +++ b/INSTALL @@ -79,8 +79,8 @@ Issues of note: - "perl" and POSIX-compliant shells are needed to use most of the barebone Porcelainish scripts. - - "cpio" is used by git-merge for saving and restoring the index, - and by git-clone when doing a local (possibly hardlinked) clone. + - "cpio" is used by git-clone when doing a local (possibly + hardlinked) clone. - Some platform specific issues are dealt with Makefile rules, but depending on your specific installation, you may not diff --git a/Makefile b/Makefile index 5aac0c0c87..92341c4bbe 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,8 @@ all:: # # Define NO_MKDTEMP if you don't have mkdtemp in the C library. # +# Define NO_SYS_SELECT_H if you don't have sys/select.h. +# # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. # Enable it on Windows. By default, symrefs are still used. # @@ -503,6 +505,17 @@ ifeq ($(uname_S),IRIX64) # for now, build 32-bit version BASIC_LDFLAGS += -L/usr/lib32 endif +ifeq ($(uname_S),HP-UX) + NO_IPV6=YesPlease + NO_SETENV=YesPlease + NO_STRCASESTR=YesPlease + NO_MEMMEM = YesPlease + NO_STRLCPY = YesPlease + NO_MKDTEMP = YesPlease + NO_UNSETENV = YesPlease + NO_HSTRERROR = YesPlease + NO_SYS_SELECT_H = YesPlease +endif ifneq (,$(findstring arm,$(uname_M))) ARM_SHA1 = YesPlease endif @@ -635,6 +648,9 @@ ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV COMPAT_OBJS += compat/unsetenv.o endif +ifdef NO_SYS_SELECT_H + BASIC_CFLAGS += -DNO_SYS_SELECT_H +endif ifdef NO_MMAP COMPAT_CFLAGS += -DNO_MMAP COMPAT_OBJS += compat/mmap.o @@ -928,7 +944,7 @@ git-%$X: %.o $(GITLIBS) git-imap-send$X: imap-send.o $(LIB_FILE) -http.o http-walker.o http-push.o: http.h +http.o http-walker.o http-push.o transport.o: http.h git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ diff --git a/archive-tar.c b/archive-tar.c index e1bced5609..30aa2e23fd 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -222,7 +222,7 @@ static void write_global_extended_header(const unsigned char *sha1) static int git_tar_config(const char *var, const char *value) { if (!strcmp(var, "tar.umask")) { - if (!strcmp(value, "user")) { + if (value && !strcmp(value, "user")) { tar_umask = umask(0); umask(tar_umask); } else { diff --git a/attr.c b/attr.c index 741db3b468..64b77b1663 100644 --- a/attr.c +++ b/attr.c @@ -406,7 +406,7 @@ static void debug_info(const char *what, struct attr_stack *elem) { fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()"); } -static void debug_set(const char *what, const char *match, struct git_attr *attr, void *v) +static void debug_set(const char *what, const char *match, struct git_attr *attr, const void *v) { const char *value = v; @@ -543,10 +543,10 @@ static int path_matches(const char *pathname, int pathlen, if (*pattern == '/') pattern++; if (pathlen < baselen || - (baselen && pathname[baselen - 1] != '/') || + (baselen && pathname[baselen] != '/') || strncmp(pathname, base, baselen)) return 0; - return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; + return fnmatch(pattern, pathname + baselen + 1, FNM_PATHNAME) == 0; } static int fill_one(const char *what, struct match_attr *a, int rem) diff --git a/builtin-apply.c b/builtin-apply.c index 30d86f2197..46dad5b2a1 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2746,6 +2746,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) static int git_apply_config(const char *var, const char *value) { if (!strcmp(var, "apply.whitespace")) { + if (!value) + return config_error_nonbool(var); apply_default_whitespace = xstrdup(value); return 0; } diff --git a/builtin-branch.c b/builtin-branch.c index 089cae5929..e414c88983 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -70,12 +70,15 @@ static int git_branch_config(const char *var, const char *value) } if (!prefixcmp(var, "color.branch.")) { int slot = parse_branch_color_slot(var, 13); + if (!value) + return config_error_nonbool(var); color_parse(value, var, branch_colors[slot]); return 0; } - if (!strcmp(var, "branch.autosetupmerge")) - branch_track = git_config_bool(var, value); - + if (!strcmp(var, "branch.autosetupmerge")) { + branch_track = git_config_bool(var, value); + return 0; + } return git_default_config(var, value); } diff --git a/builtin-commit.c b/builtin-commit.c index c63ff826fc..717eb18da0 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -122,19 +122,23 @@ static void rollback_index_files(void) } } -static void commit_index_files(void) +static int commit_index_files(void) { + int err = 0; + switch (commit_style) { case COMMIT_AS_IS: break; /* nothing to do */ case COMMIT_NORMAL: - commit_lock_file(&index_lock); + err = commit_lock_file(&index_lock); break; case COMMIT_PARTIAL: - commit_lock_file(&index_lock); + err = commit_lock_file(&index_lock); rollback_lock_file(&false_lock); break; } + + return err; } /* @@ -597,7 +601,7 @@ static int parse_and_validate_options(int argc, const char *argv[], if (get_sha1(use_message, sha1)) die("could not lookup commit %s", use_message); - commit = lookup_commit(sha1); + commit = lookup_commit_reference(sha1); if (!commit || parse_commit(commit)) die("could not parse commit %s", use_message); @@ -739,6 +743,8 @@ static void print_summary(const char *prefix, const unsigned char *sha1) int git_commit_config(const char *k, const char *v) { if (!strcmp(k, "commit.template")) { + if (!v) + return config_error_nonbool(v); template_file = xstrdup(v); return 0; } @@ -925,8 +931,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix) unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); + unlink(git_path("SQUASH_MSG")); - commit_index_files(); + if (commit_index_files()) + die ("Repository has been updated, but unable to write\n" + "new_index file. Check that disk is not full or quota is\n" + "not exceeded, and then \"git reset HEAD\" to recover."); rerere(); run_hook(get_index_file(), "post-commit", NULL); diff --git a/builtin-config.c b/builtin-config.c index e4a12e3166..077d8ef2df 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -168,6 +168,8 @@ static char parsed_color[COLOR_MAXLEN]; static int git_get_color_config(const char *var, const char *value) { if (!strcmp(var, get_color_slot)) { + if (!value) + config_error_nonbool(var); color_parse(value, var, parsed_color); get_color_found = 1; } diff --git a/builtin-describe.c b/builtin-describe.c index 7a148a2c26..3428483134 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -19,6 +19,7 @@ static int all; /* Default to annotated tags only */ static int tags; /* But allow any tags if --tags is specified */ static int abbrev = DEFAULT_ABBREV; static int max_candidates = 10; +const char *pattern = NULL; struct commit_name { int prio; /* annotated tag = 2, tag = 1, head = 0 */ @@ -57,9 +58,11 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void * Otherwise only annotated tags are used. */ if (!prefixcmp(path, "refs/tags/")) { - if (object->type == OBJ_TAG) + if (object->type == OBJ_TAG) { prio = 2; - else + if (pattern && fnmatch(pattern, path + 10, 0)) + prio = 0; + } else prio = 1; } else @@ -253,7 +256,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"), OPT__ABBREV(&abbrev), OPT_INTEGER(0, "candidates", &max_candidates, - "consider most recent tags (default: 10)"), + "consider most recent tags (default: 10)"), + OPT_STRING(0, "match", &pattern, "pattern", + "only consider tags matching "), OPT_END(), }; @@ -266,12 +271,19 @@ int cmd_describe(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; if (contains) { - const char **args = xmalloc((4 + argc) * sizeof(char*)); + const char **args = xmalloc((6 + argc) * sizeof(char*)); int i = 0; args[i++] = "name-rev"; args[i++] = "--name-only"; - if (!all) + args[i++] = "--no-undefined"; + if (!all) { args[i++] = "--tags"; + if (pattern) { + char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1); + sprintf(s, "--refs=refs/tags/%s", pattern); + args[i++] = s; + } + } memcpy(args + i, argv, argc * sizeof(char*)); args[i + argc] = NULL; return cmd_name_rev(i + argc, args, prefix); diff --git a/builtin-fsck.c b/builtin-fsck.c index 6fc9525e04..cc7524be80 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -360,6 +360,9 @@ static int fsck_commit(struct commit *commit) fprintf(stderr, "Checking commit %s\n", sha1_to_hex(commit->object.sha1)); + if (!commit->date) + return objerror(&commit->object, "invalid author/committer line"); + if (memcmp(buffer, "tree ", 5)) return objerror(&commit->object, "invalid format - expected 'tree' line"); if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n') @@ -378,9 +381,6 @@ static int fsck_commit(struct commit *commit) return objerror(&commit->object, "could not load commit's tree %s", tree_sha1); if (!commit->parents && show_root) printf("root %s\n", sha1_to_hex(commit->object.sha1)); - if (!commit->date) - printf("bad commit date in %s\n", - sha1_to_hex(commit->object.sha1)); return 0; } diff --git a/builtin-gc.c b/builtin-gc.c index ac34788c89..ad4a75eedd 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -37,7 +37,7 @@ static const char *argv_rerere[] = {"rerere", "gc", NULL}; static int gc_config(const char *var, const char *value) { if (!strcmp(var, "gc.packrefs")) { - if (!strcmp(value, "notbare")) + if (value && !strcmp(value, "notbare")) pack_refs = -1; else pack_refs = git_config_bool(var, value); diff --git a/builtin-init-db.c b/builtin-init-db.c index e1393b8d1e..5d7cdda933 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -141,9 +141,9 @@ static void copy_templates(const char *git_dir, int len, const char *template_di */ template_dir = DEFAULT_GIT_TEMPLATE_DIR; if (!is_absolute_path(template_dir)) { - const char *exec_path = git_exec_path(); - template_dir = prefix_path(exec_path, strlen(exec_path), - template_dir); + struct strbuf d = STRBUF_INIT; + strbuf_addf(&d, "%s/%s", git_exec_path(), template_dir); + template_dir = strbuf_detach(&d, NULL); } } strcpy(template_path, template_dir); diff --git a/builtin-log.c b/builtin-log.c index dcc9f81793..99d69f0791 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -219,7 +219,7 @@ static int git_log_config(const char *var, const char *value) { if (!strcmp(var, "format.subjectprefix")) { if (!value) - die("format.subjectprefix without value"); + config_error_nonbool(var); fmt_patch_subject_prefix = xstrdup(value); return 0; } @@ -432,7 +432,7 @@ static int git_format_config(const char *var, const char *value) } if (!strcmp(var, "format.suffix")) { if (!value) - die("format.suffix without value"); + return config_error_nonbool(var); fmt_patch_suffix = xstrdup(value); return 0; } @@ -440,11 +440,10 @@ static int git_format_config(const char *var, const char *value) return 0; } if (!strcmp(var, "format.numbered")) { - if (!strcasecmp(value, "auto")) { + if (value && !strcasecmp(value, "auto")) { auto_number = 1; return 0; } - numbered = git_config_bool(var, value); return 0; } diff --git a/builtin-name-rev.c b/builtin-name-rev.c index a0c89a827b..f22c8b5f5d 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -125,18 +125,18 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void } /* returns a static buffer */ -static const char* get_rev_name(struct object *o) +static const char *get_rev_name(struct object *o) { static char buffer[1024]; struct rev_name *n; struct commit *c; if (o->type != OBJ_COMMIT) - return "undefined"; + return NULL; c = (struct commit *) o; n = c->util; if (!n) - return "undefined"; + return NULL; if (!n->generation) return n->tip_name; @@ -159,7 +159,7 @@ static char const * const name_rev_usage[] = { int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = { 0, 0, NULL }; - int all = 0, transform_stdin = 0; + int all = 0, transform_stdin = 0, allow_undefined = 1; struct name_ref_data data = { 0, 0, NULL }; struct option opts[] = { OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"), @@ -169,6 +169,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_GROUP(""), OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"), OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"), + OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"), OPT_END(), }; @@ -226,7 +227,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) else if (++forty == 40 && !ishex(*(p+1))) { unsigned char sha1[40]; - const char *name = "undefined"; + const char *name = NULL; char c = *(p+1); forty = 0; @@ -240,11 +241,10 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) } *(p+1) = c; - if (!strcmp(name, "undefined")) + if (!name) continue; - fwrite(p_start, p - p_start + 1, 1, - stdout); + fwrite(p_start, p - p_start + 1, 1, stdout); printf(" (%s)", name); p_start = p + 1; } @@ -260,18 +260,32 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) max = get_max_object_index(); for (i = 0; i < max; i++) { struct object * obj = get_indexed_object(i); + const char *name; if (!obj) continue; if (!data.name_only) printf("%s ", sha1_to_hex(obj->sha1)); - printf("%s\n", get_rev_name(obj)); + name = get_rev_name(obj); + if (name) + printf("%s\n", name); + else if (allow_undefined) + printf("undefined\n"); + else + die("cannot describe '%s'", sha1_to_hex(obj->sha1)); } } else { int i; for (i = 0; i < revs.nr; i++) { + const char *name; if (!data.name_only) printf("%s ", revs.objects[i].name); - printf("%s\n", get_rev_name(revs.objects[i].item)); + name = get_rev_name(revs.objects[i].item); + if (name) + printf("%s\n", name); + else if (allow_undefined) + printf("undefined\n"); + else + die("cannot describe '%s'", sha1_to_hex(revs.objects[i].item->sha1)); } } diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index ec10238e4a..acb05554d4 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -68,7 +68,7 @@ static int allow_ofs_delta; static const char *base_name; static int progress = 1; static int window = 10; -static uint32_t pack_size_limit; +static uint32_t pack_size_limit, pack_size_limit_cfg; static int depth = 50; static int delta_search_threads = 1; static int pack_to_stdout; @@ -1464,7 +1464,7 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n) return m; } -static unsigned long free_unpacked(struct unpacked *n) +static unsigned long free_unpacked_data(struct unpacked *n) { unsigned long freed_mem = sizeof_delta_index(n->index); free_delta_index(n->index); @@ -1474,6 +1474,12 @@ static unsigned long free_unpacked(struct unpacked *n) free(n->data); n->data = NULL; } + return freed_mem; +} + +static unsigned long free_unpacked(struct unpacked *n) +{ + unsigned long freed_mem = free_unpacked_data(n); n->entry = NULL; n->depth = 0; return freed_mem; @@ -1514,7 +1520,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, mem_usage > window_memory_limit && count > 1) { uint32_t tail = (idx + window - count) % window; - mem_usage -= free_unpacked(array + tail); + mem_usage -= free_unpacked_data(array + tail); count--; } @@ -1547,6 +1553,9 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, if (!m->entry) break; ret = try_delta(n, m, max_depth, &mem_usage); + if (window_memory_limit && + mem_usage > window_memory_limit) + mem_usage -= free_unpacked_data(m); if (ret < 0) break; else if (ret > 0) @@ -1672,7 +1681,8 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, p[i].data_ready = 0; /* try to split chunks on "path" boundaries */ - while (sub_size < list_size && list[sub_size]->hash && + while (sub_size && sub_size < list_size && + list[sub_size]->hash && list[sub_size]->hash == list[sub_size-1]->hash) sub_size++; @@ -1866,6 +1876,10 @@ static int git_pack_config(const char *k, const char *v) die("bad pack.indexversion=%d", pack_idx_default_version); return 0; } + if (!strcmp(k, "pack.packsizelimit")) { + pack_size_limit_cfg = git_config_ulong(k, v); + return 0; + } return git_default_config(k, v); } @@ -2095,6 +2109,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } if (!prefixcmp(arg, "--max-pack-size=")) { char *end; + pack_size_limit_cfg = 0; pack_size_limit = strtoul(arg+16, &end, 0) * 1024 * 1024; if (!arg[16] || *end) usage(pack_usage); @@ -2219,6 +2234,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (pack_to_stdout != !base_name) usage(pack_usage); + if (!pack_to_stdout && !pack_size_limit) + pack_size_limit = pack_size_limit_cfg; + if (pack_to_stdout && pack_size_limit) die("--max-pack-size cannot be used to build a pack for transfer."); diff --git a/builtin-prune.c b/builtin-prune.c index b5e768421b..bb8ead92cf 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -83,6 +83,44 @@ static void prune_object_dir(const char *path) } } +/* + * Write errors (particularly out of space) can result in + * failed temporary packs (and more rarely indexes and other + * files begining with "tmp_") accumulating in the + * object directory. + */ +static void remove_temporary_files(void) +{ + DIR *dir; + struct dirent *de; + char* dirname=get_object_directory(); + + dir = opendir(dirname); + if (!dir) { + fprintf(stderr, "Unable to open object directory %s\n", + dirname); + return; + } + while ((de = readdir(dir)) != NULL) { + if (!prefixcmp(de->d_name, "tmp_")) { + char name[PATH_MAX]; + int c = snprintf(name, PATH_MAX, "%s/%s", + dirname, de->d_name); + if (c < 0 || c >= PATH_MAX) + continue; + if (expire) { + struct stat st; + if (stat(name, &st) != 0 || st.st_mtime >= expire) + continue; + } + printf("Removing stale temporary file %s\n", name); + if (!show_only) + unlink(name); + } + } + closedir(dir); +} + int cmd_prune(int argc, const char **argv, const char *prefix) { int i; @@ -115,5 +153,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix) sync(); prune_packed_objects(show_only); + remove_temporary_files(); return 0; } diff --git a/builtin-reflog.c b/builtin-reflog.c index ce093cad78..4836ec951b 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -34,6 +34,16 @@ struct expire_reflog_cb { struct cmd_reflog_expire_cb *cmd; }; +struct collected_reflog { + unsigned char sha1[20]; + char reflog[FLEX_ARRAY]; +}; +struct collect_reflog_cb { + struct collected_reflog **e; + int alloc; + int nr; +}; + #define INCOMPLETE (1u<<10) #define STUDYING (1u<<11) @@ -281,15 +291,35 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, return status; } +static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data) +{ + struct collected_reflog *e; + struct collect_reflog_cb *cb = cb_data; + size_t namelen = strlen(ref); + + e = xmalloc(sizeof(*e) + namelen + 1); + hashcpy(e->sha1, sha1); + memcpy(e->reflog, ref, namelen + 1); + ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); + cb->e[cb->nr++] = e; + return 0; +} + static int reflog_expire_config(const char *var, const char *value) { - if (!strcmp(var, "gc.reflogexpire")) + if (!strcmp(var, "gc.reflogexpire")) { + if (!value) + config_error_nonbool(var); default_reflog_expire = approxidate(value); - else if (!strcmp(var, "gc.reflogexpireunreachable")) + return 0; + } + if (!strcmp(var, "gc.reflogexpireunreachable")) { + if (!value) + config_error_nonbool(var); default_reflog_expire_unreachable = approxidate(value); - else - return git_default_config(var, value); - return 0; + return 0; + } + return git_default_config(var, value); } static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) @@ -349,8 +379,20 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) putchar('\n'); } - if (do_all) - status |= for_each_reflog(expire_reflog, &cb); + if (do_all) { + struct collect_reflog_cb collected; + int i; + + memset(&collected, 0, sizeof(collected)); + for_each_reflog(collect_reflog, &collected); + for (i = 0; i < collected.nr; i++) { + struct collected_reflog *e = collected.e[i]; + status |= expire_reflog(e->reflog, e->sha1, 0, &cb); + free(e); + } + free(collected.e); + } + while (i < argc) { const char *ref = argv[i++]; unsigned char sha1[20]; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 6dc835d30a..019abd3527 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -536,6 +536,8 @@ static void append_one_rev(const char *av) static int git_show_branch_config(const char *var, const char *value) { if (!strcmp(var, "showbranch.default")) { + if (!value) + return config_error_nonbool(var); if (default_alloc <= default_num + 1) { default_alloc = default_alloc * 3 / 2 + 20; default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc); diff --git a/builtin-tag.c b/builtin-tag.c index 03e70155fc..4a4a88c10b 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -258,7 +258,7 @@ static int git_tag_config(const char *var, const char *value) { if (!strcmp(var, "user.signingkey")) { if (!value) - die("user.signingkey without value"); + return config_error_nonbool(value); set_signingkey(value); return 0; } diff --git a/cache.h b/cache.h index e4aeff07d1..3867ba7ff5 100644 --- a/cache.h +++ b/cache.h @@ -630,6 +630,7 @@ extern int git_config_set_multivar(const char *, const char *, const char *, int extern int git_config_rename_section(const char *, const char *); extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value); +extern int config_error_nonbool(const char *); #define MAX_GITNAME (1000) extern char git_default_email[MAX_GITNAME]; diff --git a/color.c b/color.c index 7f66c29fae..cb70340420 100644 --- a/color.c +++ b/color.c @@ -17,7 +17,7 @@ static int parse_color(const char *name, int len) return i - 1; } i = strtol(name, &end, 10); - if (*name && !*end && i >= -1 && i <= 255) + if (end - name == len && i >= -1 && i <= 255) return i; return -2; } diff --git a/config.c b/config.c index 526a3f4294..3e72778e94 100644 --- a/config.c +++ b/config.c @@ -408,21 +408,29 @@ int git_default_config(const char *var, const char *value) } if (!strcmp(var, "user.name")) { + if (!value) + return config_error_nonbool(var); strlcpy(git_default_name, value, sizeof(git_default_name)); return 0; } if (!strcmp(var, "user.email")) { + if (!value) + return config_error_nonbool(var); strlcpy(git_default_email, value, sizeof(git_default_email)); return 0; } if (!strcmp(var, "i18n.commitencoding")) { + if (!value) + return config_error_nonbool(var); git_commit_encoding = xstrdup(value); return 0; } if (!strcmp(var, "i18n.logoutputencoding")) { + if (!value) + return config_error_nonbool(var); git_log_output_encoding = xstrdup(value); return 0; } @@ -434,23 +442,29 @@ int git_default_config(const char *var, const char *value) } if (!strcmp(var, "core.pager")) { + if (!value) + return config_error_nonbool(var); pager_program = xstrdup(value); return 0; } if (!strcmp(var, "core.editor")) { + if (!value) + return config_error_nonbool(var); editor_program = xstrdup(value); return 0; } if (!strcmp(var, "core.excludesfile")) { if (!value) - die("core.excludesfile without value"); + return config_error_nonbool(var); excludes_file = xstrdup(value); return 0; } if (!strcmp(var, "core.whitespace")) { + if (!value) + return config_error_nonbool(var); whitespace_rule_cfg = parse_whitespace_rule(value); return 0; } @@ -484,9 +498,9 @@ const char *git_etc_gitconfig(void) system_wide = ETC_GITCONFIG; if (!is_absolute_path(system_wide)) { /* interpret path relative to exec-dir */ - const char *exec_path = git_exec_path(); - system_wide = prefix_path(exec_path, strlen(exec_path), - system_wide); + struct strbuf d = STRBUF_INIT; + strbuf_addf(&d, "%s/%s", git_exec_path(), system_wide); + system_wide = strbuf_detach(&d, NULL); } } return system_wide; @@ -701,12 +715,17 @@ static ssize_t find_beginning_of_line(const char* contents, size_t size, size_t equal_offset = size, bracket_offset = size; ssize_t offset; +contline: for (offset = offset_-2; offset > 0 && contents[offset] != '\n'; offset--) switch (contents[offset]) { case '=': equal_offset = offset; break; case ']': bracket_offset = offset; break; } + if (offset > 0 && contents[offset-1] == '\\') { + offset_ = offset; + goto contline; + } if (bracket_offset < equal_offset) { *found_bracket = 1; offset = bracket_offset+1; @@ -1074,3 +1093,12 @@ int git_config_rename_section(const char *old_name, const char *new_name) free(config_filename); return ret; } + +/* + * Call this to report error for your variable that should not + * get a boolean value (i.e. "[my] var" means "true"). + */ +int config_error_nonbool(const char *var) +{ + return error("Missing value for '%s'", var); +} diff --git a/config.mak.in b/config.mak.in index 40b14d985a..ee6c33df03 100644 --- a/config.mak.in +++ b/config.mak.in @@ -30,6 +30,7 @@ NO_CURL=@NO_CURL@ NO_EXPAT=@NO_EXPAT@ NEEDS_LIBICONV=@NEEDS_LIBICONV@ NEEDS_SOCKET=@NEEDS_SOCKET@ +NO_SYS_SELECT_H=@NO_SYS_SELECT_H@ NO_D_INO_IN_DIRENT=@NO_D_INO_IN_DIRENT@ NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@ NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@ diff --git a/configure.ac b/configure.ac index af177fdb4d..85d7ef570d 100644 --- a/configure.ac +++ b/configure.ac @@ -235,6 +235,12 @@ test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket" ## Checks for header files. AC_MSG_NOTICE([CHECKS for header files]) # +# Define NO_SYS_SELECT_H if you don't have sys/select.h. +AC_CHECK_HEADER([sys/select.h], +[NO_SYS_SELECT_H=], +[NO_SYS_SELECT_H=UnfortunatelyYes]) +AC_SUBST(NO_SYS_SELECT_H) +# # Define OLD_ICONV if your library has an old iconv(), where the second # (input buffer pointer) parameter is declared with type (const char **). AC_DEFUN([OLDICONVTEST_SRC], [[ diff --git a/connect.c b/connect.c index 3aefd4ace5..71597d4920 100644 --- a/connect.c +++ b/connect.c @@ -370,6 +370,8 @@ static int git_proxy_command_options(const char *var, const char *value) if (git_proxy_command) return 0; + if (!value) + return config_error_nonbool(var); /* [core] * ;# matches www.kernel.org as well * gitproxy = netcatter-1 for kernel.org diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0d33f9a3dc..4ea727b143 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -64,12 +64,41 @@ __gitdir () __git_ps1 () { - local b="$(git symbolic-ref HEAD 2>/dev/null)" - if [ -n "$b" ]; then + local g="$(git rev-parse --git-dir 2>/dev/null)" + if [ -n "$g" ]; then + local r + local b + if [ -d "$g/../.dotest" ] + then + r="|AM/REBASE" + b="$(git symbolic-ref HEAD 2>/dev/null)" + elif [ -f "$g/.dotest-merge/interactive" ] + then + r="|REBASE-i" + b="$(cat $g/.dotest-merge/head-name)" + elif [ -d "$g/.dotest-merge" ] + then + r="|REBASE-m" + b="$(cat $g/.dotest-merge/head-name)" + elif [ -f "$g/MERGE_HEAD" ] + then + r="|MERGING" + b="$(git symbolic-ref HEAD 2>/dev/null)" + else + if [ -f $g/BISECT_LOG ] + then + r="|BISECTING" + fi + if ! b="$(git symbolic-ref HEAD 2>/dev/null)" + then + b="$(cut -c1-7 $g/HEAD)..." + fi + fi + if [ -n "$1" ]; then - printf "$1" "${b##refs/heads/}" + printf "$1" "${b##refs/heads/}$r" else - printf " (%s)" "${b##refs/heads/}" + printf " (%s)" "${b##refs/heads/}$r" fi fi } diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index bb671d561e..9f92cd250b 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -105,6 +105,13 @@ selected element from l." (setq ,l (remove e ,l)) e)) +(defvar git-blame-log-oneline-format + "format:[%cr] %cn: %s" + "*Formatting option used for describing current line in the minibuffer. + +This option is used to pass to git log --pretty= command-line option, +and describe which commit the current line was made.") + (defvar git-blame-dark-colors (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") "*List of colors (format #RGB) to use in a dark environment. @@ -371,7 +378,8 @@ See also function `git-blame-mode'." (defun git-describe-commit (hash) (with-temp-buffer (call-process "git" nil t nil - "log" "-1" "--pretty=oneline" + "log" "-1" + (concat "--pretty=" git-blame-log-oneline-format) hash) (buffer-substring (point-min) (1- (point-max))))) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d8a06381f4..a8bf0ef883 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -35,7 +35,6 @@ ;; ;; TODO ;; - portability to XEmacs -;; - better handling of subprocess errors ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -191,6 +190,18 @@ if there is already one that displays the same directory." (append (git-get-env-strings env) (list "git") args)) (apply #'call-process "git" nil buffer nil args))) +(defun git-call-process-display-error (&rest args) + "Wrapper for call-process that displays error messages." + (let* ((dir default-directory) + (buffer (get-buffer-create "*Git Command Output*")) + (ok (with-current-buffer buffer + (let ((default-directory dir) + (buffer-read-only nil)) + (erase-buffer) + (eq 0 (apply 'call-process "git" nil (list buffer t) nil args)))))) + (unless ok (display-message-or-buffer buffer)) + ok)) + (defun git-call-process-env-string (env &rest args) "Wrapper for call-process that sets environment strings, and returns the process output as a string." @@ -377,7 +388,7 @@ and returns the process output as a string." (when reason (push reason args) (push "-m" args)) - (eq 0 (apply #'git-call-process-env nil nil "update-ref" args)))) + (apply 'git-call-process-display-error "update-ref" args))) (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." @@ -558,12 +569,15 @@ and returns the process output as a string." (?\100 " (type change file -> subproject)") (?\120 " (type change symlink -> subproject)") (t " (subproject)"))) + (?\110 nil) ;; directory (internal, not a real git state) (?\000 ;; deleted or unknown (case old-type (?\120 " (symlink)") (?\160 " (subproject)"))) (t (format " (unknown type %o)" new-type))))) - (if str (propertize str 'face 'git-status-face) ""))) + (cond (str (propertize str 'face 'git-status-face)) + ((eq new-type ?\110) "/") + (t "")))) (defun git-rename-as-string (info) "Return a string describing the copy or rename associated with INFO, or an empty string if none." @@ -666,9 +680,11 @@ Return the list of files that haven't been handled." (with-temp-buffer (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) - (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1) (let ((name (match-string 1))) - (push (git-create-fileinfo default-state name) infolist) + (push (git-create-fileinfo default-state name 0 + (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0)) + infolist) (setq files (delete name files))))) (git-insert-info-list status infolist) files)) @@ -713,7 +729,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-files-with-excludes (status files default-state &rest options) "Run git-ls-files on FILES with appropriate --exclude-from options." (let ((exclude-files (git-get-exclude-files))) - (apply #'git-run-ls-files status files default-state + (apply #'git-run-ls-files status files default-state "--directory" (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) @@ -735,6 +751,27 @@ Return the list of files that haven't been handled." (git-refresh-files) (git-refresh-ewoc-hf git-status))) +(defun git-mark-files (status files) + "Mark all the specified FILES, and unmark the others." + (setq files (sort files #'string-lessp)) + (let ((file (and files (pop files))) + (node (ewoc-nth status 0))) + (while node + (let ((info (ewoc-data node))) + (if (and file (string-equal (git-fileinfo->name info) file)) + (progn + (unless (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) t) + (setf (git-fileinfo->needs-refresh info) t)) + (setq file (pop files)) + (setq node (ewoc-next status node))) + (when (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) nil) + (setf (git-fileinfo->needs-refresh info) t)) + (if (and file (string-lessp file (git-fileinfo->name info))) + (setq file (pop files)) + (setq node (ewoc-next status node)))))))) + (defun git-marked-files () "Return a list of all marked files, or if none a list containing just the file at cursor position." (unless git-status (error "Not in git-status buffer.")) @@ -840,16 +877,17 @@ Return the list of files that haven't been handled." (if (or (not (string-equal tree head-tree)) (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) (let ((commit (git-commit-tree buffer tree head))) - (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) - (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) - (with-current-buffer buffer (erase-buffer)) - (git-update-status-files (git-get-filenames files) 'uptodate) - (git-call-process-env nil nil "rerere") - (git-call-process-env nil nil "gc" "--auto") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) - (message "Committed %s." commit) - (git-run-hook "post-commit" nil)) + (when commit + (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) + (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) + (with-current-buffer buffer (erase-buffer)) + (git-update-status-files (git-get-filenames files) 'uptodate) + (git-call-process-env nil nil "rerere") + (git-call-process-env nil nil "gc" "--auto") + (git-refresh-files) + (git-refresh-ewoc-hf git-status) + (message "Committed %s." commit) + (git-run-hook "post-commit" nil))) (message "Commit aborted.")))) (message "No files to commit."))) (delete-file index-file)))))) @@ -957,11 +995,12 @@ Return the list of files that haven't been handled." "Add marked file(s) to the index cache." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) + ;; FIXME: add support for directories (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate) - (git-success-message "Added" files))) + (when (apply 'git-call-process-display-error "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Added" files)))) (defun git-ignore-file () "Add marked file(s) to the ignore list." @@ -983,16 +1022,19 @@ Return the list of files that haven't been handled." (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) (progn (dolist (name files) - (when (file-exists-p name) (delete-file name))) - (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) - (git-update-status-files files nil) - (git-success-message "Removed" files)) + (ignore-errors + (if (file-directory-p name) + (delete-directory name) + (delete-file name)))) + (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files) + (git-update-status-files files nil) + (git-success-message "Removed" files))) (message "Aborting")))) (defun git-revert-file () "Revert changes to the marked file(s)." (interactive) - (let ((files (git-marked-files)) + (let ((files (git-marked-files-state 'added 'deleted 'modified 'unmerged)) added modified) (when (and files (yes-or-no-p @@ -1003,21 +1045,31 @@ Return the list of files that haven't been handled." ('deleted (push (git-fileinfo->name info) modified)) ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) - (when added - (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) - (when modified - (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) - (git-update-status-files (append added modified) 'uptodate) - (git-success-message "Reverted" (git-get-filenames files))))) + ;; check if a buffer contains one of the files and isn't saved + (dolist (file modified) + (let ((buffer (get-file-buffer file))) + (when (and buffer (buffer-modified-p buffer)) + (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer))))) + (let ((ok (and + (or (not added) + (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) + (or (not modified) + (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) + (git-update-status-files (append added modified) 'uptodate) + (when ok + (dolist (file modified) + (let ((buffer (get-file-buffer file))) + (when buffer (with-current-buffer buffer (revert-buffer t t t))))) + (git-success-message "Reverted" (git-get-filenames files))))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-call-process-env nil nil "update-index" "--" files) - (git-update-status-files files 'uptodate) - (git-success-message "Resolved" files)))) + (when (apply 'git-call-process-display-error "update-index" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Resolved" files))))) (defun git-remove-handled () "Remove handled files from the status list." @@ -1063,6 +1115,16 @@ Return the list of files that haven't been handled." (message "Inserting unknown files...done")) (git-remove-handled))) +(defun git-expand-directory (info) + "Expand the directory represented by INFO to list its files." + (when (eq (lsh (git-fileinfo->new-perm info) -9) ?\110) + (let ((dir (git-fileinfo->name info))) + (git-set-filenames-state git-status (list dir) nil) + (git-run-ls-files-with-excludes git-status (list (concat dir "/")) 'unknown "-o") + (git-refresh-files) + (git-refresh-ewoc-hf git-status) + t))) + (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." (let ((dir default-directory)) @@ -1199,7 +1261,8 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (when (re-search-forward "\n+\\'" nil t) (replace-match "\n" t t)) - (when sign-off (git-append-sign-off committer-name committer-email))))) + (when sign-off (git-append-sign-off committer-name committer-email))) + buffer)) (defun git-commit-file () "Commit the marked file(s), asking for a commit message." @@ -1232,14 +1295,61 @@ Return the list of files that haven't been handled." (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) +(defun git-setup-commit-buffer (commit) + "Setup the commit buffer with the contents of COMMIT." + (let (author-name author-email subject date msg) + (with-temp-buffer + (let ((coding-system (git-get-logoutput-coding-system))) + (git-call-process-env t nil "log" "-1" commit) + (goto-char (point-min)) + (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (when (re-search-forward "^Date: *\\(.*\\)$" nil t) + (setq date (match-string 1))) + (while (re-search-forward "^ \\(.*\\)$" nil t) + (push (match-string 1) msg)) + (setq msg (nreverse msg)) + (setq subject (pop msg)) + (while (and msg (zerop (length (car msg))) (pop msg))))) + (git-setup-log-buffer (get-buffer-create "*git-commit*") + author-name author-email subject date + (mapconcat #'identity msg "\n")))) + +(defun git-get-commit-files (commit) + "Retrieve the list of files modified by COMMIT." + (let (files) + (with-temp-buffer + (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (goto-char (point-min)) + (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (push (match-string 1) files))) + files)) + +(defun git-amend-commit () + "Undo the last commit on HEAD, and set things up to commit an +amended version of it." + (interactive) + (unless git-status (error "Not in git-status buffer.")) + (when (git-empty-db-p) (error "No commit to amend.")) + (let* ((commit (git-rev-parse "HEAD")) + (files (git-get-commit-files commit))) + (when (git-call-process-display-error "reset" "--soft" "HEAD^") + (git-update-status-files (copy-sequence files) 'uptodate) + (git-mark-files git-status files) + (git-refresh-files) + (git-setup-commit-buffer commit) + (git-commit-file)))) + (defun git-find-file () "Visit the current file in its own buffer." (interactive) (unless git-status (error "Not in git-status buffer.")) (let ((info (ewoc-data (ewoc-locate git-status)))) - (find-file (git-fileinfo->name info)) - (when (eq 'unmerged (git-fileinfo->state info)) - (smerge-mode 1)))) + (unless (git-expand-directory info) + (find-file (git-fileinfo->name info)) + (when (eq 'unmerged (git-fileinfo->state info)) + (smerge-mode 1))))) (defun git-find-file-other-window () "Visit the current file in its own buffer in another window." @@ -1309,6 +1419,7 @@ Return the list of files that haven't been handled." (unless git-status-mode-map (let ((map (make-keymap)) + (commit-map (make-sparse-keymap)) (diff-map (make-sparse-keymap)) (toggle-map (make-sparse-keymap))) (suppress-keymap map) @@ -1317,6 +1428,7 @@ Return the list of files that haven't been handled." (define-key map " " 'git-next-file) (define-key map "a" 'git-add-file) (define-key map "c" 'git-commit-file) + (define-key map "\C-c" commit-map) (define-key map "d" diff-map) (define-key map "=" 'git-diff-file) (define-key map "f" 'git-find-file) @@ -1342,6 +1454,8 @@ Return the list of files that haven't been handled." (define-key map "x" 'git-remove-handled) (define-key map "\C-?" 'git-unmark-file-up) (define-key map "\M-\C-?" 'git-unmark-all) + ; the commit submap + (define-key commit-map "\C-a" 'git-amend-commit) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) (define-key diff-map "c" 'git-diff-file-combined) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c80a6da252..781a0cbbbc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -469,9 +469,7 @@ class P4Submit(Command): optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("--log-substitutions", dest="substFile"), - optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), - optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." @@ -479,12 +477,10 @@ class P4Submit(Command): self.firstTime = True self.reset = False self.interactive = True - self.dryRun = False self.substFile = "" self.firstTime = True self.origin = "" self.directSubmit = False - self.trustMeLikeAFool = False self.detectRename = False self.verbose = False self.isWindows = (platform.system() == "Windows") @@ -681,57 +677,30 @@ class P4Submit(Command): separatorLine += "\r" separatorLine += "\n" - response = "e" - if self.trustMeLikeAFool: - response = "y" - - firstIteration = True - while response == "e": - if not firstIteration: - response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ") - firstIteration = False - if response == "e": - [handle, fileName] = tempfile.mkstemp() - tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) - tmpFile.close() - defaultEditor = "vi" - if platform.system() == "Windows": - defaultEditor = "notepad" - editor = os.environ.get("EDITOR", defaultEditor); - system(editor + " " + fileName) - tmpFile = open(fileName, "rb") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - if self.isWindows: - submitTemplate = submitTemplate.replace("\r\n", "\n") - - if response == "y" or response == "yes": - if self.dryRun: - print submitTemplate - raw_input("Press return to continue...") - else: - if self.directSubmit: - print "Submitting to git first" - os.chdir(self.oldWorkingDirectory) - write_pipe("git commit -a -F -", submitTemplate) - os.chdir(self.clientPath) - - write_pipe("p4 submit -i", submitTemplate) - elif response == "s": - for f in editedFiles: - system("p4 revert \"%s\"" % f); - for f in filesToAdd: - system("p4 revert \"%s\"" % f); - system("rm %s" %f) - for f in filesToDelete: - system("p4 delete \"%s\"" % f); - return - else: - print "Not submitting!" - self.interactive = False + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate + separatorLine + diff) + tmpFile.close() + defaultEditor = "vi" + if platform.system() == "Windows": + defaultEditor = "notepad" + editor = os.environ.get("EDITOR", defaultEditor); + system(editor + " " + fileName) + tmpFile = open(fileName, "rb") + message = tmpFile.read() + tmpFile.close() + os.remove(fileName) + submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") + + if self.directSubmit: + print "Submitting to git first" + os.chdir(self.oldWorkingDirectory) + write_pipe("git commit -a -F -", submitTemplate) + os.chdir(self.clientPath) + + write_pipe("p4 submit -i", submitTemplate) else: fileName = "submit.txt" file = open(fileName, "w+") @@ -828,10 +797,8 @@ class P4Submit(Command): sync = P4Sync() sync.run([]) - response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ") - if response == "y" or response == "yes": - rebase = P4Rebase() - rebase.rebase() + rebase = P4Rebase() + rebase.rebase() os.remove(self.configFile) return True @@ -964,9 +931,13 @@ class P4Sync(Command): stat = filedata[j] j += 1 text = '' - while j < len(filedata) and filedata[j]['code'] in ('text', - 'binary'): - text += filedata[j]['data'] + while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): + tmp = filedata[j]['data'] + if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): + tmp = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', tmp) + elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): + tmp = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', tmp) + text += tmp j += 1 @@ -1640,6 +1611,11 @@ class P4Rebase(Command): return self.rebase() def rebase(self): + if os.system("git update-index --refresh") != 0: + die("Some files in your working directory are modified and different than what is in your index. You can use git update-index to bring the index up-to-date or stash away all your changes with git stash."); + if len(read_pipe("git diff-index HEAD --")) > 0: + die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash."); + [upstream, settings] = findUpstreamBranchPoint() if len(upstream) == 0: die("Cannot find upstream branchpoint for rebase") @@ -1670,7 +1646,7 @@ class P4Clone(P4Sync): depotPath = args[0] depotDir = re.sub("(@[^@]*)$", "", depotPath) depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"\.\.\.$", "", depotDir) depotDir = re.sub(r"/$", "", depotDir) return os.path.split(depotDir)[1] diff --git a/convert.c b/convert.c index 80f114b2e2..552707e8e6 100644 --- a/convert.c +++ b/convert.c @@ -326,14 +326,14 @@ static int read_convert_config(const char *var, const char *value) if (!strcmp("smudge", ep)) { if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); drv->smudge = strdup(value); return 0; } if (!strcmp("clean", ep)) { if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); drv->clean = strdup(value); return 0; } diff --git a/diff.c b/diff.c index d464fe3b20..cd8bc4dcc3 100644 --- a/diff.c +++ b/diff.c @@ -158,6 +158,8 @@ int git_diff_ui_config(const char *var, const char *value) return 0; } if (!strcmp(var, "diff.external")) { + if (!value) + return config_error_nonbool(var); external_diff_cmd_cfg = xstrdup(value); return 0; } @@ -165,8 +167,11 @@ int git_diff_ui_config(const char *var, const char *value) const char *ep = strrchr(var, '.'); if (ep != var + 4) { - if (!strcmp(ep, ".command")) + if (!strcmp(ep, ".command")) { + if (!value) + return config_error_nonbool(var); return parse_lldiff_command(var, ep, value); + } } } @@ -177,6 +182,8 @@ int git_diff_basic_config(const char *var, const char *value) { if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); + if (!value) + return config_error_nonbool(var); color_parse(value, var, diff_colors[slot]); return 0; } @@ -184,8 +191,11 @@ int git_diff_basic_config(const char *var, const char *value) if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); if (ep != var + 4) { - if (!strcmp(ep, ".funcname")) + if (!strcmp(ep, ".funcname")) { + if (!value) + return config_error_nonbool(var); return parse_funcname_pattern(var, ep, value); + } } } diff --git a/fast-import.c b/fast-import.c index 45b4edf36b..a523b171e2 100644 --- a/fast-import.c +++ b/fast-import.c @@ -275,6 +275,8 @@ struct recent_command static unsigned long max_depth = 10; static off_t max_packsize = (1LL << 32) - 1; static int force_update; +static int pack_compression_level = Z_DEFAULT_COMPRESSION; +static int pack_compression_seen; /* Stats and misc. counters */ static uintmax_t alloc_count; @@ -1038,7 +1040,7 @@ static int store_object( delta = NULL; memset(&s, 0, sizeof(s)); - deflateInit(&s, zlib_compression_level); + deflateInit(&s, pack_compression_level); if (delta) { s.next_in = delta; s.avail_in = deltalen; @@ -1066,7 +1068,7 @@ static int store_object( delta = NULL; memset(&s, 0, sizeof(s)); - deflateInit(&s, zlib_compression_level); + deflateInit(&s, pack_compression_level); s.next_in = (void *)dat->buf; s.avail_in = dat->len; s.avail_out = deflateBound(&s, s.avail_in); @@ -1123,6 +1125,24 @@ static int store_object( return 0; } +/* All calls must be guarded by find_object() or find_mark() to + * ensure the 'struct object_entry' passed was written by this + * process instance. We unpack the entry by the offset, avoiding + * the need for the corresponding .idx file. This unpacking rule + * works because we only use OBJ_REF_DELTA within the packfiles + * created by fast-import. + * + * oe must not be NULL. Such an oe usually comes from giving + * an unknown SHA-1 to find_object() or an undefined mark to + * find_mark(). Callers must test for this condition and use + * the standard read_sha1_file() when it happens. + * + * oe->pack_id must not be MAX_PACK_ID. Such an oe is usually from + * find_mark(), where the mark was reloaded from an existing marks + * file and is referencing an object that this fast-import process + * instance did not write out to a packfile. Callers must test for + * this condition and use read_sha1_file() instead. + */ static void *gfi_unpack_entry( struct object_entry *oe, unsigned long *sizep) @@ -1130,7 +1150,22 @@ static void *gfi_unpack_entry( enum object_type type; struct packed_git *p = all_packs[oe->pack_id]; if (p == pack_data && p->pack_size < (pack_size + 20)) { + /* The object is stored in the packfile we are writing to + * and we have modified it since the last time we scanned + * back to read a previously written object. If an old + * window covered [p->pack_size, p->pack_size + 20) its + * data is stale and is not valid. Closing all windows + * and updating the packfile length ensures we can read + * the newly written data. + */ close_pack_windows(p); + + /* We have to offer 20 bytes additional on the end of + * the packfile as the core unpacker code assumes the + * footer is present at the file end and must promise + * at least 20 bytes within any window it maps. But + * we don't actually create the footer here. + */ p->pack_size = pack_size + 20; } return unpack_entry(p, oe->offset, &type, sizep); @@ -2282,6 +2317,27 @@ static void import_marks(const char *input_file) fclose(f); } +static int git_pack_config(const char *k, const char *v) +{ + if (!strcmp(k, "pack.depth")) { + max_depth = git_config_int(k, v); + if (max_depth > MAX_DEPTH) + max_depth = MAX_DEPTH; + return 0; + } + if (!strcmp(k, "pack.compression")) { + int level = git_config_int(k, v); + if (level == -1) + level = Z_DEFAULT_COMPRESSION; + else if (level < 0 || level > Z_BEST_COMPRESSION) + die("bad pack compression level %d", level); + pack_compression_level = level; + pack_compression_seen = 1; + return 0; + } + return git_default_config(k, v); +} + static const char fast_import_usage[] = "git-fast-import [--date-format=f] [--max-pack-size=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file]"; @@ -2289,7 +2345,10 @@ int main(int argc, const char **argv) { unsigned int i, show_stats = 1; - git_config(git_default_config); + git_config(git_pack_config); + if (!pack_compression_seen && core_compression_seen) + pack_compression_level = core_compression_level; + alloc_objects(object_entry_alloc); strbuf_init(&command_buf, 0); atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*)); diff --git a/git-am.sh b/git-am.sh index 5f0f241ad0..2ecebc45a9 100755 --- a/git-am.sh +++ b/git-am.sh @@ -14,7 +14,7 @@ b,binary pass --allo-binary-replacement to git-apply 3,3way allow fall back on 3way merging if needed s,signoff add a Signed-off-by line to the commit message u,utf8 recode into utf8 (default) -k,keep pass -k flagg to git-mailinfo +k,keep pass -k flag to git-mailinfo whitespace= pass it through git-apply C= pass it through git-apply p= pass it through git-apply diff --git a/git-bisect.sh b/git-bisect.sh index 5385249890..393fa35584 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -26,6 +26,9 @@ OPTIONS_SPEC= . git-sh-setup require_work_tree +_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' +_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" + sq() { @@PERL@@ -e ' for (@ARGV) { @@ -60,7 +63,8 @@ bisect_start() { # top-of-line master first! # head=$(GIT_DIR="$GIT_DIR" git symbolic-ref HEAD) || - die "Bad HEAD - I need a symbolic ref" + head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) || + die "Bad HEAD - I need a HEAD" case "$head" in refs/heads/bisect) if [ -s "$GIT_DIR/head-name" ]; then @@ -70,7 +74,7 @@ bisect_start() { fi git checkout $branch || exit ;; - refs/heads/*) + refs/heads/*|$_x40) [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree" echo "${head#refs/heads/}" >"$GIT_DIR/head-name" ;; diff --git a/git-checkout.sh b/git-checkout.sh index 5621c69d86..bd74d701a1 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -71,7 +71,8 @@ while test $# != 0; do done arg="$1" -if rev=$(git rev-parse --verify "$arg^0" 2>/dev/null) +rev=$(git rev-parse --verify "$arg" 2>/dev/null) +if rev=$(git rev-parse --verify "$rev^0" 2>/dev/null) then [ -z "$rev" ] && die "unknown flag $arg" new_name="$arg" @@ -82,11 +83,11 @@ then fi new="$rev" shift -elif rev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null) +elif rev=$(git rev-parse --verify "$rev^{tree}" 2>/dev/null) then # checking out selected paths from a tree-ish. new="$rev" - new_name="$arg^{tree}" + new_name="$rev^{tree}" shift fi [ "$1" = "--" ] && shift diff --git a/git-compat-util.h b/git-compat-util.h index b6ef5442b7..4df90cb34e 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -68,7 +68,9 @@ #include #include #include +#ifndef NO_SYS_SELECT_H #include +#endif #include #include #include diff --git a/git-cvsserver.perl b/git-cvsserver.perl index ecded3b9cb..afe3d0b7fe 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2543,8 +2543,15 @@ sub update if ($parent eq $lastpicked) { next; } - my $base = safe_pipe_capture('git-merge-base', + my $base = eval { + safe_pipe_capture('git-merge-base', $lastpicked, $parent); + }; + # The two branches may not be related at all, + # in which case merge base simply fails to find + # any, but that's Ok. + next if ($@); + chomp $base; if ($base) { my @merged; diff --git a/git-filter-branch.sh b/git-filter-branch.sh index ebf05ca600..ff716cabb0 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -114,7 +114,6 @@ orig_namespace=refs/original/ force= while : do - test $# = 0 && usage case "$1" in --) shift @@ -189,6 +188,9 @@ cd "$tempdir/t" && workdir="$(pwd)" || die "" +# Remove tempdir on exit +trap 'cd ../..; rm -rf "$tempdir"' 0 + # Make sure refs/original is empty git for-each-ref > "$tempdir"/backup-refs while read sha1 type name @@ -210,7 +212,7 @@ GIT_WORK_TREE=. export GIT_DIR GIT_WORK_TREE # The refs should be updated if their heads were rewritten -git rev-parse --no-flags --revs-only --symbolic-full-name "$@" | +git rev-parse --no-flags --revs-only --symbolic-full-name --default HEAD "$@" | sed -e '/^^/d' >"$tempdir"/heads test -s "$tempdir"/heads || @@ -406,6 +408,8 @@ fi cd ../.. rm -rf "$tempdir" +trap - 0 + unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE test -z "$ORIG_GIT_DIR" || GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR test -z "$ORIG_GIT_WORK_TREE" || GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" && diff --git a/git-gui/Makefile b/git-gui/Makefile index 1baf4b0861..34438cdf5c 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -67,7 +67,7 @@ ifndef V QUIET_GEN = $(QUIET)echo ' ' GEN '$@' && QUIET_INDEX = $(QUIET)echo ' ' INDEX $(dir $@) && QUIET_MSGFMT0 = $(QUIET)printf ' MSGFMT %12s ' $@ && v=` - QUIET_MSGFMT1 = 2>&1` && echo "$$v" | sed -e 's/fuzzy translations/fuzzy/' | sed -e 's/ messages//g' + QUIET_MSGFMT1 = 2>&1` && echo "$$v" | sed -e 's/fuzzy translations/fuzzy/' | sed -e 's/ messages*//g' QUIET_2DEVNULL = 2>/dev/null INSTALL_D0 = dir= @@ -198,6 +198,9 @@ ifdef NO_MSGFMT MSGFMT ?= $(TCL_PATH) po/po2msg.sh else MSGFMT ?= msgfmt + ifeq ($(shell $(MSGFMT) >/dev/null 2>&1 || echo $$?),127) + MSGFMT := $(TCL_PATH) po/po2msg.sh + endif endif msgsdir = $(gg_libdir)/msgs diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index fcb2ab2fb7..f42e461fd4 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -438,6 +438,34 @@ proc git_write {args} { return [open [concat $opt $cmdp $args] w] } +proc githook_read {hook_name args} { + set pchook [gitdir hooks $hook_name] + lappend args 2>@1 + + # On Cygwin [file executable] might lie so we need to ask + # the shell if the hook is executable. Yes that's annoying. + # + if {[is_Cygwin]} { + upvar #0 _sh interp + if {![info exists interp]} { + set interp [_which sh] + } + if {$interp eq {}} { + error "hook execution requires sh (not in PATH)" + } + + set scr {if test -x "$1";then exec "$@";fi} + set sh_c [list | $interp -c $scr $interp $pchook] + return [_open_stdout_stderr [concat $sh_c $args]] + } + + if {[file executable $pchook]} { + return [_open_stdout_stderr [concat [list | $pchook] $args]] + } + + return {} +} + proc sq {value} { regsub -all ' $value "'\\''" value return "'$value'" diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl index a063c5bc49..c8821c1463 100644 --- a/git-gui/lib/choose_rev.tcl +++ b/git-gui/lib/choose_rev.tcl @@ -451,7 +451,8 @@ method _sb_set {sb orient first last} { focus $old_focus } } - $sb set $first $last + + catch {$sb set $first $last} } method _show_tooltip {pos} { diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 1c0586c409..947b201c32 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -192,45 +192,52 @@ A good commit message has the following format: return } - # -- Run the pre-commit hook. + # -- Build the message file. # - set pchook [gitdir hooks pre-commit] + set msg_p [gitdir GITGUI_EDITMSG] + set msg_wt [open $msg_p w] + fconfigure $msg_wt -translation lf + if {[catch {set enc $repo_config(i18n.commitencoding)}]} { + set enc utf-8 + } + set use_enc [tcl_encoding $enc] + if {$use_enc ne {}} { + fconfigure $msg_wt -encoding $use_enc + } else { + puts stderr [mc "warning: Tcl does not support encoding '%s'." $enc] + fconfigure $msg_wt -encoding utf-8 + } + puts $msg_wt $msg + close $msg_wt - # On Cygwin [file executable] might lie so we need to ask - # the shell if the hook is executable. Yes that's annoying. + # -- Run the pre-commit hook. # - if {[is_Cygwin] && [file isfile $pchook]} { - set pchook [list sh -c [concat \ - "if test -x \"$pchook\";" \ - "then exec \"$pchook\" 2>&1;" \ - "fi"]] - } elseif {[file executable $pchook]} { - set pchook [list $pchook |& cat] - } else { - commit_writetree $curHEAD $msg + set fd_ph [githook_read pre-commit] + if {$fd_ph eq {}} { + commit_commitmsg $curHEAD $msg_p return } ui_status {Calling pre-commit hook...} set pch_error {} - set fd_ph [open "| $pchook" r] fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} fileevent $fd_ph readable \ - [list commit_prehook_wait $fd_ph $curHEAD $msg] + [list commit_prehook_wait $fd_ph $curHEAD $msg_p] } -proc commit_prehook_wait {fd_ph curHEAD msg} { +proc commit_prehook_wait {fd_ph curHEAD msg_p} { global pch_error append pch_error [read $fd_ph] fconfigure $fd_ph -blocking 1 if {[eof $fd_ph]} { if {[catch {close $fd_ph}]} { + catch {file delete $msg_p} ui_status {Commit declined by pre-commit hook.} hook_failed_popup pre-commit $pch_error unlock_index } else { - commit_writetree $curHEAD $msg + commit_commitmsg $curHEAD $msg_p } set pch_error {} return @@ -238,14 +245,52 @@ proc commit_prehook_wait {fd_ph curHEAD msg} { fconfigure $fd_ph -blocking 0 } -proc commit_writetree {curHEAD msg} { +proc commit_commitmsg {curHEAD msg_p} { + global pch_error + + # -- Run the commit-msg hook. + # + set fd_ph [githook_read commit-msg $msg_p] + if {$fd_ph eq {}} { + commit_writetree $curHEAD $msg_p + return + } + + ui_status {Calling commit-msg hook...} + set pch_error {} + fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fileevent $fd_ph readable \ + [list commit_commitmsg_wait $fd_ph $curHEAD $msg_p] +} + +proc commit_commitmsg_wait {fd_ph curHEAD msg_p} { + global pch_error + + append pch_error [read $fd_ph] + fconfigure $fd_ph -blocking 1 + if {[eof $fd_ph]} { + if {[catch {close $fd_ph}]} { + catch {file delete $msg_p} + ui_status {Commit declined by commit-msg hook.} + hook_failed_popup commit-msg $pch_error + unlock_index + } else { + commit_writetree $curHEAD $msg_p + } + set pch_error {} + return + } + fconfigure $fd_ph -blocking 0 +} + +proc commit_writetree {curHEAD msg_p} { ui_status {Committing changes...} set fd_wt [git_read write-tree] fileevent $fd_wt readable \ - [list commit_committree $fd_wt $curHEAD $msg] + [list commit_committree $fd_wt $curHEAD $msg_p] } -proc commit_committree {fd_wt curHEAD msg} { +proc commit_committree {fd_wt curHEAD msg_p} { global HEAD PARENT MERGE_HEAD commit_type global current_branch global ui_comm selected_commit_type @@ -254,6 +299,7 @@ proc commit_committree {fd_wt curHEAD msg} { gets $fd_wt tree_id if {[catch {close $fd_wt} err]} { + catch {file delete $msg_p} error_popup [strcat [mc "write-tree failed:"] "\n\n$err"] ui_status {Commit failed.} unlock_index @@ -276,6 +322,7 @@ proc commit_committree {fd_wt curHEAD msg} { } if {$tree_id eq $old_tree} { + catch {file delete $msg_p} info_popup [mc "No changes to commit. No files were modified by this commit and it was not a merge commit. @@ -288,24 +335,6 @@ A rescan will be automatically started now. } } - # -- Build the message. - # - set msg_p [gitdir COMMIT_EDITMSG] - set msg_wt [open $msg_p w] - fconfigure $msg_wt -translation lf - if {[catch {set enc $repo_config(i18n.commitencoding)}]} { - set enc utf-8 - } - set use_enc [tcl_encoding $enc] - if {$use_enc ne {}} { - fconfigure $msg_wt -encoding $use_enc - } else { - puts stderr [mc "warning: Tcl does not support encoding '%s'." $enc] - fconfigure $msg_wt -encoding utf-8 - } - puts $msg_wt $msg - close $msg_wt - # -- Create the commit. # set cmd [list commit-tree $tree_id] @@ -314,6 +343,7 @@ A rescan will be automatically started now. } lappend cmd <$msg_p if {[catch {set cmt_id [eval git $cmd]} err]} { + catch {file delete $msg_p} error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"] ui_status {Commit failed.} unlock_index @@ -326,16 +356,14 @@ A rescan will be automatically started now. if {$commit_type ne {normal}} { append reflogm " ($commit_type)" } - set i [string first "\n" $msg] - if {$i >= 0} { - set subject [string range $msg 0 [expr {$i - 1}]] - } else { - set subject $msg - } + set msg_fd [open $msg_p r] + gets $msg_fd subject + close $msg_fd append reflogm {: } $subject if {[catch { git update-ref -m $reflogm HEAD $cmt_id $curHEAD } err]} { + catch {file delete $msg_p} error_popup [strcat [mc "update-ref failed:"] "\n\n$err"] ui_status {Commit failed.} unlock_index @@ -363,17 +391,13 @@ A rescan will be automatically started now. # -- Run the post-commit hook. # - set pchook [gitdir hooks post-commit] - if {[is_Cygwin] && [file isfile $pchook]} { - set pchook [list sh -c [concat \ - "if test -x \"$pchook\";" \ - "then exec \"$pchook\";" \ - "fi"]] - } elseif {![file executable $pchook]} { - set pchook {} - } - if {$pchook ne {}} { - catch {exec $pchook &} + set fd_ph [githook_read post-commit] + if {$fd_ph ne {}} { + upvar #0 pch_error$cmt_id pc_err + set pc_err {} + fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fileevent $fd_ph readable \ + [list commit_postcommit_wait $fd_ph $cmt_id] } $ui_comm delete 0.0 end @@ -429,3 +453,18 @@ A rescan will be automatically started now. reshow_diff ui_status [mc "Created commit %s: %s" [string range $cmt_id 0 7] $subject] } + +proc commit_postcommit_wait {fd_ph cmt_id} { + upvar #0 pch_error$cmt_id pch_error + + append pch_error [read $fd_ph] + fconfigure $fd_ph -blocking 1 + if {[eof $fd_ph]} { + if {[catch {close $fd_ph}]} { + hook_failed_popup post-commit $pch_error 0 + } + unset pch_error + return + } + fconfigure $fd_ph -blocking 0 +} diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl index 13565b7ab0..0fdd7531da 100644 --- a/git-gui/lib/error.tcl +++ b/git-gui/lib/error.tcl @@ -62,7 +62,7 @@ proc ask_popup {msg} { eval $cmd } -proc hook_failed_popup {hook msg} { +proc hook_failed_popup {hook msg {is_fatal 1}} { set w .hookfail toplevel $w @@ -77,14 +77,16 @@ proc hook_failed_popup {hook msg} { -width 80 -height 10 \ -font font_diff \ -yscrollcommand [list $w.m.sby set] - label $w.m.l2 \ - -text [mc "You must correct the above errors before committing."] \ - -anchor w \ - -justify left \ - -font font_uibold scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x - pack $w.m.l2 -side bottom -fill x + if {$is_fatal} { + label $w.m.l2 \ + -text [mc "You must correct the above errors before committing."] \ + -anchor w \ + -justify left \ + -font font_uibold + pack $w.m.l2 -side bottom -fill x + } pack $w.m.sby -side right -fill y pack $w.m.t -side left -fill both -expand 1 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10 @@ -99,6 +101,6 @@ proc hook_failed_popup {hook msg} { bind $w "grab $w; focus $w" bind $w "destroy $w" - wm title $w [append "[appname] ([reponame]): " [mc "error"]] + wm title $w [strcat "[appname] ([reponame]): " [mc "error"]] tkwait window $w } diff --git a/git-gui/po/glossary/fr.po b/git-gui/po/glossary/fr.po index bb2feaf137..27c006abb2 100644 --- a/git-gui/po/glossary/fr.po +++ b/git-gui/po/glossary/fr.po @@ -34,7 +34,7 @@ msgstr "branche" #. "" msgid "branch [verb]" -msgstr "créer une branche" +msgstr "créer une branche" #. "" msgid "checkout [noun]" @@ -58,7 +58,7 @@ msgstr "commiter" #. "" msgid "diff [noun]" -msgstr "différence" +msgstr "différence" #. "" msgid "diff [verb]" @@ -70,11 +70,11 @@ msgstr "fusion par avance rapide" #. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too." msgid "fetch" -msgstr "récupérer" +msgstr "récupérer" #. "A collection of files. The index is a stored version of your working tree." msgid "index (in git-gui: staging area)" -msgstr "pré-commit" +msgstr "pré-commit" #. "A successful merge results in the creation of a new commit representing the result of the merge." msgid "merge [noun]" @@ -106,15 +106,15 @@ msgstr "refaire" #. "An other repository ('remote'). One might have a set of remotes whose branches one tracks." msgid "remote" -msgstr "référentiel distant" +msgstr "référentiel distant" #. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" msgid "repository" -msgstr "référentiel" +msgstr "référentiel" #. "" msgid "reset" -msgstr "réinitialiser" +msgstr "réinitialiser" #. "" msgid "revert" @@ -122,7 +122,7 @@ msgstr "inverser" #. "A particular state of files and directories which was stored in the object database." msgid "revision" -msgstr "révision" +msgstr "révision" #. "" msgid "sign off" @@ -130,11 +130,11 @@ msgstr "signer" #. "" msgid "staging area" -msgstr "pré-commit" +msgstr "pré-commit" #. "" msgid "status" -msgstr "état" +msgstr "état" #. "A ref pointing to a tag or commit object" msgid "tag [noun]" @@ -150,15 +150,15 @@ msgstr "branche de suivi" #. "" msgid "undo" -msgstr "défaire" +msgstr "défaire" #. "" msgid "update" -msgstr "mise à jour" +msgstr "mise à jour" #. "" msgid "verify" -msgstr "vérifier" +msgstr "vérifier" #. "The tree of actual checked out files." msgid "working copy, working tree" diff --git a/git-gui/po/po2msg.sh b/git-gui/po/po2msg.sh index c63248e375..b7c4bf3fdf 100644 --- a/git-gui/po/po2msg.sh +++ b/git-gui/po/po2msg.sh @@ -127,7 +127,26 @@ foreach file $files { } if {$show_statistics} { - puts [concat "$translated_count translated messages, " \ - "$fuzzy_count fuzzy ones, " \ - "$not_translated_count untranslated ones."] + set str "" + + append str "$translated_count translated message" + if {$translated_count != 1} { + append str s + } + + if {$fuzzy_count > 1} { + append str ", $fuzzy_count fuzzy translation" + if {$fuzzy_count != 1} { + append str s + } + } + if {$not_translated_count > 0} { + append str ", $not_translated_count untranslated message" + if {$not_translated_count != 1} { + append str s + } + } + + append str . + puts $str } diff --git a/git-instaweb.sh b/git-instaweb.sh index ad0723ccc6..3e4452bc4b 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -274,6 +274,14 @@ webrick) ;; esac +init_browser_path() { + browser_path="`git config browser.$1.path`" + test -z "$browser_path" && browser_path="$1" +} + start_httpd url=http://127.0.0.1:$port -test -n "$browser" && "$browser" $url || echo $url +test -n "$browser" && { + init_browser_path "$browser" + "$browser_path" $url +} || echo $url diff --git a/git-pull.sh b/git-pull.sh index fa97b0f356..46da0f4ca2 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -106,6 +106,15 @@ error_on_no_merge_candidates () { exit 1 } +test true = "$rebase" && { + . git-parse-remote && + origin="$1" + test -z "$origin" && origin=$(get_default_remote) + reflist="$(get_remote_refs_for_fetch "$@" 2>/dev/null | + sed "s|refs/heads/\(.*\):|\1|")" && + oldremoteref="$(git rev-parse --verify \ + "refs/remotes/$origin/$reflist" 2>/dev/null)" +} orig_head=$(git rev-parse --verify HEAD 2>/dev/null) git-fetch --update-head-ok "$@" || exit 1 @@ -164,6 +173,7 @@ then fi merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit -test true = "$rebase" && exec git-rebase $merge_head +test true = "$rebase" && + exec git-rebase --onto $merge_head ${oldremoteref:-$merge_head} exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \ "$merge_name" HEAD $merge_head diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 402ff3782c..fb12b03b20 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -432,7 +432,7 @@ do shift ;; esac ;; - --merge) + -m|--merge) # we use merge anyway ;; -C*) diff --git a/git-relink.perl b/git-relink.perl index f6b4f6a2f8..15fb932021 100755 --- a/git-relink.perl +++ b/git-relink.perl @@ -40,7 +40,7 @@ opendir(D,$master_dir . "objects/") or die "Failed to open $master_dir/objects/ : $!"; -my @hashdirs = grep !/^\.{1,2}$/, readdir(D); +my @hashdirs = grep { ($_ eq 'pack') || /^[0-9a-f]{2}$/ } readdir(D); foreach my $repo (@dirs) { $linked = 0; diff --git a/git-remote.perl b/git-remote.perl index d13e4c1fea..5cd69513cf 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -1,5 +1,6 @@ #!/usr/bin/perl -w +use strict; use Git; my $git = Git->repository(); @@ -296,12 +297,13 @@ sub add_remote { sub update_remote { my ($name) = @_; + my @remotes; my $conf = $git->config("remotes." . $name); if (defined($conf)) { @remotes = split(' ', $conf); } elsif ($name eq 'default') { - undef @remotes; + @remotes = (); for (sort keys %$remote) { my $do_fetch = $git->config_bool("remote." . $_ . ".skipDefaultUpdate"); @@ -341,7 +343,7 @@ sub rm_remote { my @refs = $git->command('for-each-ref', '--format=%(refname) %(objectname)', "refs/remotes/$name"); for (@refs) { - ($ref, $object) = split; + my ($ref, $object) = split; $git->command(qw(update-ref -d), $ref, $object); } return 0; @@ -352,7 +354,7 @@ sub add_usage { exit(1); } -local $VERBOSE = 0; +my $VERBOSE = 0; @ARGV = grep { if ($_ eq '-v' or $_ eq '--verbose') { $VERBOSE=1; @@ -395,7 +397,7 @@ sub add_usage { update_remote("default"); exit(1); } - for ($i = 1; $i < @ARGV; $i++) { + for (my $i = 1; $i < @ARGV; $i++) { update_remote($ARGV[$i]); } } diff --git a/git-send-email.perl b/git-send-email.perl index 6c72952fcc..a1a9d14b00 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -462,7 +462,7 @@ sub expand_aliases { exit(0); } - @files = ($compose_filename . ".final"); + @files = ($compose_filename . ".final", @files); } # Variables we set as part of the loop over files diff --git a/git-submodule.sh b/git-submodule.sh index ad9fe628fd..a6aaf40b0a 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -9,11 +9,8 @@ OPTIONS_SPEC= . git-sh-setup require_work_tree -add= +command= branch= -init= -update= -status= quiet= cached= @@ -86,9 +83,9 @@ module_name() # # Clone a submodule # -# Prior to calling, modules_update checks that a possibly existing +# Prior to calling, cmd_update checks that a possibly existing # path is not a git repository. -# Likewise, module_add checks that path does not exist at all, +# Likewise, cmd_add checks that path does not exist at all, # since it is the location of a new submodule. # module_clone() @@ -121,8 +118,34 @@ module_clone() # # optional branch is stored in global branch variable # -module_add() +cmd_add() { + # parse $args after "submodule ... add". + while test $# -ne 0 + do + case "$1" in + -b | --branch) + case "$2" in '') usage ;; esac + branch=$2 + shift + ;; + -q|--quiet) + quiet=1 + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift + done + repo=$1 path=$2 @@ -174,8 +197,29 @@ module_add() # # $@ = requested paths (default to all) # -modules_init() +cmd_init() { + # parse $args after "submodule ... init". + while test $# -ne 0 + do + case "$1" in + -q|--quiet) + quiet=1 + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift + done + git ls-files --stage -- "$@" | grep -e '^160000 ' | while read mode sha1 stage path do @@ -207,8 +251,29 @@ modules_init() # # $@ = requested paths (default to all) # -modules_update() +cmd_update() { + # parse $args after "submodule ... update". + while test $# -ne 0 + do + case "$1" in + -q|--quiet) + quiet=1 + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift + done + git ls-files --stage -- "$@" | grep -e '^160000 ' | while read mode sha1 stage path do @@ -266,8 +331,32 @@ set_name_rev () { # # $@ = requested paths (default to all) # -modules_list() +cmd_status() { + # parse $args after "submodule ... status". + while test $# -ne 0 + do + case "$1" in + -q|--quiet) + quiet=1 + ;; + --cached) + cached=1 + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift + done + git ls-files --stage -- "$@" | grep -e '^160000 ' | while read mode sha1 stage path do @@ -293,20 +382,17 @@ modules_list() done } -while test $# != 0 +# This loop parses the command line arguments to find the +# subcommand name to dispatch. Parsing of the subcommand specific +# options are primarily done by the subcommand implementations. +# Subcommand specific options such as --branch and --cached are +# parsed here as well, for backward compatibility. + +while test $# != 0 && test -z "$command" do case "$1" in - add) - add=1 - ;; - init) - init=1 - ;; - update) - update=1 - ;; - status) - status=1 + add | init | update | status) + command=$1 ;; -q|--quiet) quiet=1 @@ -335,30 +421,19 @@ do shift done -case "$add,$branch" in -1,*) - ;; -,) - ;; -,*) +# No command word defaults to "status" +test -n "$command" || command=status + +# "-b branch" is accepted only by "add" +if test -n "$branch" && test "$command" != add +then usage - ;; -esac - -case "$add,$init,$update,$status,$cached" in -1,,,,) - module_add "$@" - ;; -,1,,,) - modules_init "$@" - ;; -,,1,,) - modules_update "$@" - ;; -,,,*,*) - modules_list "$@" - ;; -*) +fi + +# "--cached" is accepted only by "status" +if test -n "$cached" && test "$command" != status +then usage - ;; -esac +fi + +"cmd_$command" "$@" diff --git a/git-svn.perl b/git-svn.perl index 9f2b587b25..05fb3582d9 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -186,6 +186,9 @@ BEGIN "Show info about the latest SVN revision on the current branch", { 'url' => \$_url, } ], + 'blame' => [ \&Git::SVN::Log::cmd_blame, + "Show what revision and author last modified each line of a file", + {} ], ); my $cmd; @@ -1247,7 +1250,8 @@ package Git::SVN; use File::Copy qw/copy/; use IPC::Open3; -my $_repack_nr; +my ($_gc_nr, $_gc_period); + # properties that we do not log: my %SKIP_PROP; BEGIN { @@ -1408,10 +1412,9 @@ sub read_all_remotes { } sub init_vars { - if (defined $_repack) { - $_repack = 1000 if ($_repack <= 0); - $_repack_nr = $_repack; - $_repack_flags ||= '-d'; + $_gc_nr = $_gc_period = 1000; + if (defined $_repack || defined $_repack_flags) { + warn "Repack options are obsolete; they have no effect.\n"; } } @@ -2098,6 +2101,10 @@ sub restore_commit_header_env { } } +sub gc { + command_noisy('gc', '--auto'); +}; + sub do_git_commit { my ($self, $log_entry) = @_; my $lr = $self->last_rev; @@ -2151,12 +2158,9 @@ sub do_git_commit { 0, $self->svm_uuid); } print " = $commit ($self->{ref_id})\n"; - if (defined $_repack && (--$_repack_nr == 0)) { - $_repack_nr = $_repack; - # repack doesn't use any arguments with spaces in them, does it? - print "Running git repack $_repack_flags ...\n"; - command_noisy('repack', split(/\s+/, $_repack_flags)); - print "Done repacking\n"; + if (--$_gc_nr == 0) { + $_gc_nr = $_gc_period; + gc(); } return $commit; } @@ -2228,7 +2232,12 @@ sub find_parent_branch { # just grow a tail if we're not unique enough :x $ref_id .= '-' while find_ref($ref_id); print STDERR "Initializing parent: $ref_id\n"; - $gs = Git::SVN->init($new_url, '', $ref_id, $ref_id, 1); + my ($u, $p) = ($new_url, ''); + if ($u =~ s#^\Q$url\E(/|$)##) { + $p = $u; + $u = $url; + } + $gs = Git::SVN->init($u, $p, $self->{repo_id}, $ref_id, 1); } my ($r0, $parent) = $gs->find_rev_before($r, 1); if (!defined $r0 || !defined $parent) { @@ -3985,6 +3994,7 @@ sub gs_fetch_loop_common { $max += $inc; $max = $head if ($max > $head); } + Git::SVN::gc(); } sub match_globs { @@ -4441,6 +4451,24 @@ sub cmd_show_log { print commit_log_separator unless $incremental || $oneline; } +sub cmd_blame { + my $path = shift; + + config_pager(); + run_pager(); + + my ($fh, $ctx) = command_output_pipe('blame', @_, $path); + while (my $line = <$fh>) { + if ($line =~ /^\^?([[:xdigit:]]+)\s/) { + my (undef, $rev, undef) = ::cmt_metadata($1); + $rev = sprintf('%-10s', $rev); + $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/; + } + print $line; + } + command_close_pipe($fh, $ctx); +} + package Git::SVN::Migration; # these version numbers do NOT correspond to actual version numbers # of git nor git-svn. They are just relative. diff --git a/git.c b/git.c index 15fec8974a..0cb86884d7 100644 --- a/git.c +++ b/git.c @@ -93,6 +93,8 @@ static char *alias_string; static int git_alias_config(const char *var, const char *value) { if (!prefixcmp(var, "alias.") && !strcmp(var + 6, alias_command)) { + if (!value) + return config_error_nonbool(var); alias_string = xstrdup(value); } return 0; diff --git a/git.spec.in b/git.spec.in index 659f058819..3f9f88815b 100644 --- a/git.spec.in +++ b/git.spec.in @@ -8,7 +8,7 @@ License: GPL Group: Development/Tools URL: http://kernel.org/pub/software/scm/git/ Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz -BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel %{!?_without_docs:, xmlto, asciidoc > 6.0.3} +BuildRequires: zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, gettext %{!?_without_docs:, xmlto, asciidoc > 6.0.3} BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: git-core = %{version}-%{release} @@ -181,6 +181,9 @@ rm -rf $RPM_BUILD_ROOT %{!?_without_docs: %doc Documentation/technical} %changelog +* Sun Feb 03 2008 James Bowes +- Add a BuildRequires for gettext + * Fri Jan 11 2008 Junio C Hamano - Include gitk message files diff --git a/gitweb/README b/gitweb/README index b28f59f574..4c8bedf744 100644 --- a/gitweb/README +++ b/gitweb/README @@ -129,17 +129,30 @@ descriptions. Gitweb config file variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can set, among others, the following variables in gitweb config files: +You can set, among others, the following variables in gitweb config files +(with the exception of $projectroot and $projects_list this list does +not include variables usually directly set during build): * $GIT Cure git executable to use. By default set to "$GIT_BINDIR/git", which in turn is by default set to "$(bindir)/git". If you use git from binary package, set this to "/usr/bin/git". This can just be "git" if your webserver has a sensible PATH. If you have multiple git versions - installed it is / can be used to choose which one to use. + installed it can be used to choose which one to use. * $version Gitweb version, set automatically when creating gitweb.cgi from gitweb.perl. You might want to modify it if you are running modified gitweb. + * $projectroot + Absolute filesystem path which will be prepended to project path; + the path to repository is $projectroot/$project. Set to + $GITWEB_PROJECTROOT during installation. This variable have to be + set correctly for gitweb to find repositories. + * $projects_list + Source of projects list, either directory to scan, or text file + with list of repositories (in the " SPC + " format). Set to $GITWEB_LIST + during installation. If empty, $projectroot is used to scan for + repositories. * $my_url, $my_uri URL and absolute URL of gitweb script; you might need to set those variables if you are using 'pathinfo' feature: see also below. diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6256641ace..5e88637b5e 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1606,7 +1606,7 @@ sub git_get_project_description { my $path = shift; $git_dir = "$projectroot/$path"; - open my $fd, "$projectroot/$path/description" + open my $fd, "$git_dir/description" or return git_get_project_config('description'); my $descr = <$fd>; close $fd; @@ -1620,7 +1620,7 @@ sub git_get_project_url_list { my $path = shift; $git_dir = "$projectroot/$path"; - open my $fd, "$projectroot/$path/cloneurl" + open my $fd, "$git_dir/cloneurl" or return wantarray ? @{ config_to_multi(git_get_project_config('url')) } : config_to_multi(git_get_project_config('url')); @@ -5048,16 +5048,15 @@ sub git_commitdiff { -expires => $expires, -content_disposition => 'inline; filename="' . "$filename" . '"'); my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'}); - print <self_url() . "\n\n"; foreach my $line (@{$co{'comment'}}) { - print "$line\n"; + print to_utf8($line) . "\n"; } print "---\n\n"; } @@ -5566,7 +5565,7 @@ sub git_feed { or next; # print element (entry, item) - my $co_url = href(-full=>1, action=>"commit", hash=>$commit); + my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit); if ($format eq 'rss') { print "\n" . "" . esc_html($co{'title'}) . "\n" . diff --git a/help.c b/help.c index 1302a61c83..95e7640fed 100644 --- a/help.c +++ b/help.c @@ -40,6 +40,8 @@ static void parse_help_format(const char *format) static int git_help_config(const char *var, const char *value) { if (!strcmp(var, "help.format")) { + if (!value) + return config_error_nonbool(var); help_default_format = xstrdup(value); return 0; } diff --git a/http.c b/http.c index d2c11aee90..5925d07478 100644 --- a/http.c +++ b/http.c @@ -101,16 +101,18 @@ static int http_options(const char *var, const char *value) if (!strcmp("http.sslcert", var)) { if (ssl_cert == NULL) { - ssl_cert = xmalloc(strlen(value)+1); - strcpy(ssl_cert, value); + if (!value) + return config_error_nonbool(var); + ssl_cert = xstrdup(value); } return 0; } #if LIBCURL_VERSION_NUM >= 0x070902 if (!strcmp("http.sslkey", var)) { if (ssl_key == NULL) { - ssl_key = xmalloc(strlen(value)+1); - strcpy(ssl_key, value); + if (!value) + return config_error_nonbool(var); + ssl_key = xstrdup(value); } return 0; } @@ -118,16 +120,18 @@ static int http_options(const char *var, const char *value) #if LIBCURL_VERSION_NUM >= 0x070908 if (!strcmp("http.sslcapath", var)) { if (ssl_capath == NULL) { - ssl_capath = xmalloc(strlen(value)+1); - strcpy(ssl_capath, value); + if (!value) + return config_error_nonbool(var); + ssl_capath = xstrdup(value); } return 0; } #endif if (!strcmp("http.sslcainfo", var)) { if (ssl_cainfo == NULL) { - ssl_cainfo = xmalloc(strlen(value)+1); - strcpy(ssl_cainfo, value); + if (!value) + return config_error_nonbool(var); + ssl_cainfo = xstrdup(value); } return 0; } @@ -157,8 +161,9 @@ static int http_options(const char *var, const char *value) } if (!strcmp("http.proxy", var)) { if (curl_http_proxy == NULL) { - curl_http_proxy = xmalloc(strlen(value)+1); - strcpy(curl_http_proxy, value); + if (!value) + return config_error_nonbool(var); + curl_http_proxy = xstrdup(value); } return 0; } diff --git a/http.h b/http.h index aeba9301f8..9bab2c8821 100644 --- a/http.h +++ b/http.h @@ -8,6 +8,14 @@ #include "strbuf.h" +/* + * We detect based on the cURL version if multi-transfer is + * usable in this implementation and define this symbol accordingly. + * This is not something Makefile should set nor users should pass + * via CFLAGS. + */ +#undef USE_CURL_MULTI + #if LIBCURL_VERSION_NUM >= 0x071000 #define USE_CURL_MULTI #define DEFAULT_MAX_REQUESTS 5 diff --git a/imap-send.c b/imap-send.c index a429a76a63..9025d9aa3e 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1254,6 +1254,10 @@ git_imap_config(const char *key, const char *val) if (strncmp( key, imap_key, sizeof imap_key - 1 )) return 0; + + if (!val) + return config_error_nonbool(key); + key += sizeof imap_key - 1; if (!strcmp( "folder", key )) { diff --git a/merge-recursive.c b/merge-recursive.c index bdf03b1f1f..dd52342539 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -844,8 +844,9 @@ static int read_merge_config(const char *var, const char *value) int namelen; if (!strcmp(var, "merge.default")) { - if (value) - default_ll_merge = strdup(value); + if (!value) + return config_error_nonbool(var); + default_ll_merge = strdup(value); return 0; } @@ -878,14 +879,14 @@ static int read_merge_config(const char *var, const char *value) if (!strcmp("name", ep)) { if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); fn->description = strdup(value); return 0; } if (!strcmp("driver", ep)) { if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); /* * merge..driver specifies the command line: * @@ -908,7 +909,7 @@ static int read_merge_config(const char *var, const char *value) if (!strcmp("recursive", ep)) { if (!value) - return error("%s: lacks value", var); + return config_error_nonbool(var); fn->recursive = strdup(value); return 0; } diff --git a/object.c b/object.c index 5a5ebe27b0..50b6528001 100644 --- a/object.c +++ b/object.c @@ -140,7 +140,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t if (type == OBJ_BLOB) { struct blob *blob = lookup_blob(sha1); if (blob) { - parse_blob_buffer(blob, buffer, size); + if (parse_blob_buffer(blob, buffer, size)) + return NULL; obj = &blob->object; } } else if (type == OBJ_TREE) { @@ -148,14 +149,16 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t if (tree) { obj = &tree->object; if (!tree->object.parsed) { - parse_tree_buffer(tree, buffer, size); + if (parse_tree_buffer(tree, buffer, size)) + return NULL; eaten = 1; } } } else if (type == OBJ_COMMIT) { struct commit *commit = lookup_commit(sha1); if (commit) { - parse_commit_buffer(commit, buffer, size); + if (parse_commit_buffer(commit, buffer, size)) + return NULL; if (!commit->buffer) { commit->buffer = buffer; eaten = 1; @@ -165,7 +168,8 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t } else if (type == OBJ_TAG) { struct tag *tag = lookup_tag(sha1); if (tag) { - parse_tag_buffer(tag, buffer, size); + if (parse_tag_buffer(tag, buffer, size)) + return NULL; obj = &tag->object; } } else { diff --git a/parse-options.c b/parse-options.c index 7a08a0c64f..d9562ba504 100644 --- a/parse-options.c +++ b/parse-options.c @@ -216,6 +216,26 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, return error("unknown option `%s'", arg); } +void check_typos(const char *arg, const struct option *options) +{ + if (strlen(arg) < 3) + return; + + if (!prefixcmp(arg, "no-")) { + error ("did you mean `--%s` (with two dashes ?)", arg); + exit(129); + } + + for (; options->type != OPTION_END; options++) { + if (!options->long_name) + continue; + if (!prefixcmp(options->long_name, arg)) { + error ("did you mean `--%s` (with two dashes ?)", arg); + exit(129); + } + } +} + static NORETURN void usage_with_options_internal(const char * const *, const struct option *, int); @@ -235,12 +255,18 @@ int parse_options(int argc, const char **argv, const struct option *options, if (arg[1] != '-') { args.opt = arg + 1; - do { + if (*args.opt == 'h') + usage_with_options(usagestr, options); + if (parse_short_opt(&args, options) < 0) + usage_with_options(usagestr, options); + if (args.opt) + check_typos(arg + 1, options); + while (args.opt) { if (*args.opt == 'h') usage_with_options(usagestr, options); if (parse_short_opt(&args, options) < 0) usage_with_options(usagestr, options); - } while (args.opt); + } continue; } diff --git a/remote.c b/remote.c index 0e006804ef..20abbc07ac 100644 --- a/remote.c +++ b/remote.c @@ -222,15 +222,18 @@ static int handle_config(const char *key, const char *value) subkey = strrchr(name, '.'); if (!subkey) return 0; - if (!value) - return 0; branch = make_branch(name, subkey - name); if (!strcmp(subkey, ".remote")) { + if (!value) + return config_error_nonbool(key); branch->remote_name = xstrdup(value); if (branch == current_branch) default_remote_name = branch->remote_name; - } else if (!strcmp(subkey, ".merge")) + } else if (!strcmp(subkey, ".merge")) { + if (!value) + return config_error_nonbool(key); add_merge(branch, xstrdup(value)); + } return 0; } if (prefixcmp(key, "remote.")) diff --git a/setup.c b/setup.c index adede16a4d..4509598d57 100644 --- a/setup.c +++ b/setup.c @@ -372,6 +372,8 @@ int check_repository_format_version(const char *var, const char *value) if (is_bare_repository_cfg == 1) inside_work_tree = -1; } else if (strcmp(var, "core.worktree") == 0) { + if (!value) + return config_error_nonbool(var); if (git_work_tree_cfg) free(git_work_tree_cfg); git_work_tree_cfg = xstrdup(value); diff --git a/t/.gitattributes b/t/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/README b/t/README index 36f2517617..73ed11bfe2 100644 --- a/t/README +++ b/t/README @@ -160,14 +160,12 @@ library for your script to use. - test_expect_failure