diff options
120 files changed, 3084 insertions, 631 deletions
diff --git a/.gitignore b/.gitignore index 7f8421dcd0..165b256e2e 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,6 @@ git-gc git-get-tar-commit-id git-grep git-hash-object -git-help--browse git-http-fetch git-http-push git-imap-send @@ -136,6 +135,7 @@ git-upload-pack git-var git-verify-pack git-verify-tag +git-web--browse git-whatchanged git-write-tree git-core-*/?* @@ -17,6 +17,7 @@ H. Peter Anvin <hpa@bonde.sc.orionmulti.com> H. Peter Anvin <hpa@tazenda.sc.orionmulti.com> H. Peter Anvin <hpa@trantor.hos.anvin.org> Horst H. von Brand <vonbrand@inf.utfsm.cl> +Jay Soffian <jaysoffian+git@gmail.com> Joachim Berdal Haga <cjhaga@fys.uio.no> Jon Loeliger <jdl@freescale.com> Jon Seymour <jon@blackcubes.dyndns.org> @@ -43,6 +44,7 @@ Steven Grimm <koreth@midwinter.com> Theodore Ts'o <tytso@mit.edu> Tony Luck <tony.luck@intel.com> Uwe Kleine-König <Uwe_Zeisberger@digi.com> +Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com> Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de> Uwe Kleine-König <uzeisberger@io.fsforth.de> Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de> diff --git a/Documentation/RelNotes-1.5.4.1.txt b/Documentation/RelNotes-1.5.4.1.txt new file mode 100644 index 0000000000..d4e44b8b09 --- /dev/null +++ b/Documentation/RelNotes-1.5.4.1.txt @@ -0,0 +1,17 @@ +GIT v1.5.4.1 Release Notes +========================== + +Fixes since v1.5.4 +------------------ + + * "git-commit -C $tag" used to work but rewrite in C done in + 1.5.4 broke it. + + * An entry in the .gitattributes file that names a pattern in a + subdirectory of the directory it is in did not match + correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should + match "a/b/foo.c" but it didn't). + + * Customized color specification was parsed incorrectly when + numeric color values are used. This was fixed in 1.5.4.1. + diff --git a/Documentation/RelNotes-1.5.4.2.txt b/Documentation/RelNotes-1.5.4.2.txt new file mode 100644 index 0000000000..21d0df59fb --- /dev/null +++ b/Documentation/RelNotes-1.5.4.2.txt @@ -0,0 +1,43 @@ +GIT v1.5.4.2 Release Notes +========================== + +Fixes since v1.5.4 +------------------ + + * The configuration parser was not prepared to see string + valued variables misspelled as boolean and segfaulted. + + * Temporary files left behind due to interrupted object + transfers were not cleaned up with "git prune". + + * "git config --unset" was confused when the unset variables + were spelled with continuation lines in the config file. + + * The merge message detection in "git cvsimport" did not catch + a message that began with "Merge...". + + * "git status" suggests "git rm --cached" for unstaging the + earlier "git add" before the initial commit. + + * "git status" output was incorrect during a partial commit. + + * "git bisect" refused to start when the HEAD was detached. + + * "git bisect" allowed a wildcard character in the commit + message expanded while writing its log file. + + * Manual pages were not formatted correctly with docbook xsl + 1.72; added a workaround. + + * "git-commit -C $tag" used to work but rewrite in C done in + 1.5.4 broke it. This was fixed in 1.5.4.1. + + * An entry in the .gitattributes file that names a pattern in a + subdirectory of the directory it is in did not match + correctly (e.g. pattern "b/*.c" in "a/.gitattributes" should + match "a/b/foo.c" but it didn't). This was fixed in 1.5.4.1. + + * Customized color specification was parsed incorrectly when + numeric color values are used. This was fixed in 1.5.4.1. + + * http transport misbehaved when linked with curl-gnutls. diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt new file mode 100644 index 0000000000..c8b4f72c23 --- /dev/null +++ b/Documentation/RelNotes-1.5.5.txt @@ -0,0 +1,78 @@ +GIT v1.5.5 Release Notes +======================== + +Updates since v1.5.4 +-------------------- + +(performance) + + * On platforms with suboptimal qsort(3) implementation, there + is an option to use more reasonable substitute we ship with + our software. + + * New configuration variable "pack.packsizelimit" can be used + in place of command line option --max-pack-size. + + * "git fetch" over the native git protocol used to make a + connection to find out the set of current remote refs and + another to actually download the pack data. We now use only + one connection for these tasks. + + * "git commit" does not run lstat(2) more than necessary + anymore. + +(usability, bells and whistles) + + * You can be warned when core.autocrlf conversion is applied in + such a way that results in an irreversible conversion. + + * A pattern "foo/" in .gitignore file now matches a directory + "foo". Pattern "foo" also matches as before. + + * "git describe" learned to limit the tags to be used for + naming with --match option. + + * "git describe --contains" now barfs when the named commit + cannot be described. + + * bash completion's prompt helper function can talk about + operation in-progress (e.g. merge, rebase, etc.). + + * "git commit" learned a new hook "prepare-commit-msg" that can + inspect what is going to be committed and prepare the commit + log message template to be edited. + + * "git gui" learned an auto-spell checking. + + * "git send-email" learned to prompt for passwords + interactively. + + * "git send-email" learned an easier way to suppress CC + recipients. + + * Various "git cvsimport", "git cvsexportcommit", "git svn" and + "git p4" improvements. + +(internal) + + * Duplicated code between git-help and git-instaweb that + launches user's preferred browser has been refactored. + + * It is now easier to write test scripts that records known + breakages. + + +Fixes since v1.5.4 +------------------ + +All of the fixes in v1.5.4 maintenance series are included in +this release, unless otherwise noted. + + +--- +exec >/var/tmp/1 +O=v1.5.4 +O=v1.5.4.2-122-g7cb97da +echo O=`git describe refs/heads/master` +git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint + diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index de08d094e3..0e155c936c 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -34,9 +34,9 @@ Checklist (and a short version for the impatient): - if your name is not writable in ASCII, make sure that you send off a message in the correct encoding. - send the patch to the list (git@vger.kernel.org) and the - maintainer (gitster@pobox.com). If you use - git-send-email(1), please test it first by sending - email to yourself. + maintainer (gitster@pobox.com) if (and only if) the patch + is ready for inclusion. If you use git-send-email(1), + please test it first by sending email to yourself. Long version: @@ -112,7 +112,12 @@ lose tabs that way if you are not careful. It is a common convention to prefix your subject line with [PATCH]. This lets people easily distinguish patches from other -e-mail discussions. +e-mail discussions. Use of additional markers after PATCH and +the closing bracket to mark the nature of the patch is also +encouraged. E.g. [PATCH/RFC] is often used when the patch is +not ready to be applied but it is for discussion, [PATCH v2], +[PATCH v3] etc. are often seen when you are sending an update to +what you have previously sent. "git format-patch" command follows the best current practice to format the body of an e-mail message. At the beginning of the @@ -157,7 +162,8 @@ Note that your maintainer does not necessarily read everything on the git mailing list. If your patch is for discussion first, send it "To:" the mailing list, and optionally "cc:" him. If it is trivially correct or after the list reached a consensus, send -it "To:" the maintainer and optionally "cc:" the list. +it "To:" the maintainer and optionally "cc:" the list for +inclusion. Also note that your maintainer does not actively involve himself in maintaining what are in contrib/ hierarchy. When you send fixes and @@ -210,10 +216,53 @@ then you just add a line saying This line can be automatically added by git if you run the git-commit command with the -s option. -Some people also put extra tags at the end. They'll just be ignored for -now, but you can do this to mark internal company procedures or just -point out some special detail about the sign-off. +Notice that you can place your own Signed-off-by: line when +forwarding somebody else's patch with the above rules for +D-C-O. Indeed you are encouraged to do so. Do not forget to +place an in-body "From: " line at the beginning to properly attribute +the change to its true author (see (2) above). +Some people also put extra tags at the end. + +"Acked-by:" says that the patch was reviewed by the person who +is more familiar with the issues and the area the patch attempts +to modify. "Tested-by:" says the patch was tested by the person +and found to have the desired effect. + +------------------------------------------------ +An ideal patch flow + +Here is an ideal patch flow for this project the current maintainer +suggests to the contributors: + + (0) You come up with an itch. You code it up. + + (1) Send it to the list and cc people who may need to know about + the change. + + The people who may need to know are the ones whose code you + are butchering. These people happen to be the ones who are + most likely to be knowledgeable enough to help you, but + they have no obligation to help you (i.e. you ask for help, + don't demand). "git log -p -- $area_you_are_modifying" would + help you find out who they are. + + (2) You get comments and suggestions for improvements. You may + even get them in a "on top of your change" patch form. + + (3) Polish, refine, and re-send to the list and the people who + spend their time to improve your patch. Go back to step (2). + + (4) The list forms consensus that the last round of your patch is + good. Send it to the list and cc the maintainer. + + (5) A topic branch is created with the patch and is merged to 'next', + and cooked further and eventually graduates to 'master'. + +In any time between the (2)-(3) cycle, the maintainer may pick it up +from the list and queue it to 'pu', in order to make it easier for +people play with it without having to pick up and apply the patch to +their trees themselves. ------------------------------------------------ MUA specific hints diff --git a/Documentation/config.txt b/Documentation/config.txt index f9bdb164e0..f2f6a774e0 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -139,6 +139,51 @@ core.autocrlf:: "text" (i.e. be subjected to the autocrlf mechanism) is decided purely based on the contents. +core.safecrlf:: + If true, makes git check if converting `CRLF` as controlled by + `core.autocrlf` is reversible. Git will verify if a command + modifies a file in the work tree either directly or indirectly. + For example, committing a file followed by checking out the + same file should yield the original file in the work tree. If + this is not the case for the current setting of + `core.autocrlf`, git will reject the file. The variable can + be set to "warn", in which case git will only warn about an + irreversible conversion but continue the operation. ++ +CRLF conversion bears a slight chance of corrupting data. +autocrlf=true will convert CRLF to LF during commit and LF to +CRLF during checkout. A file that contains a mixture of LF and +CRLF before the commit cannot be recreated by git. For text +files this is the right thing to do: it corrects line endings +such that we have only LF line endings in the repository. +But for binary files that are accidentally classified as text the +conversion can corrupt data. ++ +If you recognize such corruption early you can easily fix it by +setting the conversion type explicitly in .gitattributes. Right +after committing you still have the original file in your work +tree and this file is not yet corrupted. You can explicitly tell +git that this file is binary and git will handle the file +appropriately. ++ +Unfortunately, the desired effect of cleaning up text files with +mixed line endings and the undesired effect of corrupting binary +files cannot be distinguished. In both cases CRLFs are removed +in an irreversible way. For text files this is the right thing +to do because CRLFs are line endings, while for binary files +converting CRLFs corrupts data. ++ +Note, this safety check does not mean that a checkout will generate a +file identical to the original file for a different setting of +`core.autocrlf`, but only for the current one. For example, a text +file with `LF` would be accepted with `core.autocrlf=input` and could +later be checked out with `core.autocrlf=true`, in which case the +resulting file would contain `CRLF`, although the original file +contained `LF`. However, in both work trees the line endings would be +consistent, that is either all `LF` or all `CRLF`, but never mixed. A +file with mixed line endings would be reported by the `core.safecrlf` +mechanism. + core.symlinks:: If false, symbolic links are checked out as small plain files that contain the link text. linkgit:git-update-index[1] and diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 9d2ac865d2..47799097ce 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -74,8 +74,8 @@ OPTIONS Update only files that git already knows about. This is similar to what "git commit -a" does in preparation for making a commit, except that the update is limited to paths specified on the - command line. If no paths are specified, all tracked files are - updated. + command line. If no paths are specified, all tracked files in the + current directory and its subdirectories are updated. \--refresh:: Don't add the file(s), but only refresh their stat() diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 2341881614..975824301a 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -62,12 +62,13 @@ 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. ++ +*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. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index c3725b2ed9..b4ae61ff46 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -280,8 +280,8 @@ order). HOOKS ----- -This command can run `commit-msg`, `pre-commit`, and -`post-commit` hooks. See link:hooks.html[hooks] for more +This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`, +and `post-commit` hooks. See link:hooks.html[hooks] for more information. diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index dbce503694..6f91b9ea2a 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -107,8 +107,8 @@ If you need to pass multiple options, separate them with a comma. -M <regex>:: Attempt to detect merges based on the commit message with a custom - regex. It can be used with '-m' to also see the default regexes. - You must escape forward slashes. + regex. It can be used with '-m' to enable the default regexes + as well. You must escape forward slashes. -S <regex>:: Skip paths matching the regex. diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index bd625ababf..96f6767075 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -805,6 +805,93 @@ Placing a `progress` command immediately after a `checkpoint` will inform the reader when the `checkpoint` has been completed and it can safely access the refs that fast-import updated. +Crash Reports +------------- +If fast-import is supplied invalid input it will terminate with a +non-zero exit status and create a crash report in the top level of +the Git repository it was importing into. Crash reports contain +a snapshot of the internal fast-import state as well as the most +recent commands that lead up to the crash. + +All recent commands (including stream comments, file changes and +progress commands) are shown in the command history within the crash +report, but raw file data and commit messages are excluded from the +crash report. This exclusion saves space within the report file +and reduces the amount of buffering that fast-import must perform +during execution. + +After writing a crash report fast-import will close the current +packfile and export the marks table. This allows the frontend +developer to inspect the repository state and resume the import from +the point where it crashed. The modified branches and tags are not +updated during a crash, as the import did not complete successfully. +Branch and tag information can be found in the crash report and +must be applied manually if the update is needed. + +An example crash: + +==== + $ cat >in <<END_OF_INPUT + # my very first test commit + commit refs/heads/master + committer Shawn O. Pearce <spearce> 19283 -0400 + # who is that guy anyway? + data <<EOF + this is my commit + EOF + M 644 inline .gitignore + data <<EOF + .gitignore + EOF + M 777 inline bob + END_OF_INPUT + + $ git-fast-import <in + fatal: Corrupt mode: M 777 inline bob + fast-import: dumping crash report to .git/fast_import_crash_8434 + + $ cat .git/fast_import_crash_8434 + fast-import crash report: + fast-import process: 8434 + parent process : 1391 + at Sat Sep 1 00:58:12 2007 + + fatal: Corrupt mode: M 777 inline bob + + Most Recent Commands Before Crash + --------------------------------- + # my very first test commit + commit refs/heads/master + committer Shawn O. Pearce <spearce> 19283 -0400 + # who is that guy anyway? + data <<EOF + M 644 inline .gitignore + data <<EOF + * M 777 inline bob + + Active Branch LRU + ----------------- + active_branches = 1 cur, 5 max + + pos clock name + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 1) 0 refs/heads/master + + Inactive Branches + ----------------- + refs/heads/master: + status : active loaded dirty + tip commit : 0000000000000000000000000000000000000000 + old tree : 0000000000000000000000000000000000000000 + cur tree : 0000000000000000000000000000000000000000 + commit clock: 0 + last pack : + + + ------------------- + END OF CRASH REPORT +==== + Tips and Tricks --------------- The following tips and tricks have been collected from various diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index fb77ca3a57..0926dc12ba 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -47,27 +47,9 @@ OPTIONS + The web browser can be specified using the configuration variable 'help.browser', or 'web.browser' if the former is not set. If none of -these config variables is set, the 'git-help--browse' helper script -(called by 'git-help') will pick a suitable default. -+ -You can explicitly provide a full path to your preferred browser by -setting the configuration variable 'browser.<tool>.path'. For example, -you can configure the absolute path to firefox by setting -'browser.firefox.path'. Otherwise, 'git-help--browse' assumes the tool -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 +these config variables is set, the 'git-web--browse' helper script +(called by 'git-help') will pick a suitable default. See +linkgit:git-web--browse[1] for more information about this. CONFIGURATION VARIABLES ----------------------- @@ -84,7 +66,7 @@ line option: The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also be checked if the 'web' format is chosen (either by command line option or configuration variable). See '-w|--web' in the OPTIONS -section above. +section above and linkgit:git-web--browse[1]. Note that these configuration variables should probably be set using the '--global' flag, for example like this: diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt index 841e8fac7f..51f1532ef7 100644 --- a/Documentation/git-instaweb.txt +++ b/Documentation/git-instaweb.txt @@ -38,10 +38,11 @@ OPTIONS The port number to bind the httpd to. (Default: 1234) -b|--browser:: - - The web browser command-line to execute to view the gitweb page. - If blank, the URL of the gitweb instance will be printed to - stdout. (Default: 'firefox') + The web browser that should be used to view the gitweb + page. This will be passed to the 'git-web--browse' helper + script along with the URL of the gitweb instance. See + linkgit:git-web--browse[1] for more information about this. If + the script fails, the URL will be printed to stdout. --start:: Start the httpd instance and exit. This does not generate @@ -72,7 +73,8 @@ You may specify configuration in your .git/config ----------------------------------------------------------------------- If the configuration variable 'instaweb.browser' is not set, -'web.browser' will be used instead if it is defined. +'web.browser' will be used instead if it is defined. See +linkgit:git-web--browse[1] for more information about this. Author ------ diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 179bdfc69d..737894390d 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -15,6 +15,7 @@ DESCRIPTION ----------- Runs `git-fetch` with the given parameters, and calls `git-merge` to merge the retrieved head(s) into the current branch. +With `--rebase`, calls `git-rebase` instead of `git-merge`. Note that you can use `.` (current directory) as the <repository> to pull from the local repository -- this is useful @@ -26,19 +27,14 @@ OPTIONS include::merge-options.txt[] :git-pull: 1 -include::fetch-options.txt[] - -include::pull-fetch-param.txt[] - -include::urls-remotes.txt[] - -include::merge-strategies.txt[] \--rebase:: 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. + to avoid rebasing non-local changes. To make this the default + for branch `<name>`, set configuration `branch.<name>.rebase` + to `true`. + *NOTE:* This is a potentially _dangerous_ mode of operation. It rewrites history, which does not bode well when you @@ -48,6 +44,14 @@ unless you have read linkgit:git-rebase[1] carefully. \--no-rebase:: Override earlier \--rebase. +include::fetch-options.txt[] + +include::pull-fetch-param.txt[] + +include::urls-remotes.txt[] + +include::merge-strategies.txt[] + DEFAULT BEHAVIOUR ----------------- diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 5f2494495b..3128170bcd 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -47,9 +47,9 @@ even if it does not result in a fast forward update. + Note: If no explicit refspec is found, (that is neither on the command line nor in any Push line of the -corresponding remotes file---see below), then all the -heads that exist both on the local side and on the remote -side are updated. +corresponding remotes file---see below), then "matching" heads are +pushed: for every head that exists on the local side, the remote side is +updated if a head of the same name already exists on the remote side. + `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`. + @@ -108,6 +108,55 @@ the remote repository. include::urls-remotes.txt[] +OUTPUT +------ + +The output of "git push" depends on the transport method used; this +section describes the output when pushing over the git protocol (either +locally or via ssh). + +The status of the push is output in tabular form, with each line +representing the status of a single ref. Each line is of the form: + +------------------------------- + <flag> <summary> <from> -> <to> (<reason>) +------------------------------- + +flag:: + A single character indicating the status of the ref. This is + blank for a successfully pushed ref, `!` for a ref that was + rejected or failed to push, and '=' for a ref that was up to + date and did not need pushing (note that the status of up to + date refs is shown only when `git push` is running verbosely). + +summary:: + For a successfully pushed ref, the summary shows the old and new + values of the ref in a form suitable for using as an argument to + `git log` (this is `<old>..<new>` in most cases, and + `<old>...<new>` for forced non-fast forward updates). For a + failed update, more details are given for the failure. + The string `rejected` indicates that git did not try to send the + ref at all (typically because it is not a fast forward). The + string `remote rejected` indicates that the remote end refused + the update; this rejection is typically caused by a hook on the + remote side. The string `remote failure` indicates that the + remote end did not report the successful update of the ref + (perhaps because of a temporary error on the remote side, a + break in the network connection, or other transient error). + +from:: + The name of the local ref being pushed, minus its + `refs/<type>/` prefix. In the case of deletion, the + name of the local ref is omitted. + +to:: + The name of the remote ref being updated, minus its + `refs/<type>/` prefix. + +reason:: + A human-readable explanation. In the case of successfully pushed + refs, no explanation is needed. For a failed ref, the reason for + failure is described. Examples -------- diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index a4e0a779de..fac59c9726 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git-reset' [--mixed | --soft | --hard] [-q] [<commit>] -'git-reset' [--mixed] [-q] [<commit>] [--] <paths>... +'git reset' [--mixed | --soft | --hard] [-q] [<commit>] +'git reset' [-q] [<commit>] [--] <paths>... DESCRIPTION ----------- @@ -37,7 +37,7 @@ OPTIONS --soft:: Does not touch the index file nor the working tree at all, but requires them to be in a good order. This leaves all your changed - files "Added but not yet committed", as linkgit:git-status[1] would + files "Changes to be committed", as linkgit:git-status[1] would put it. --hard:: @@ -176,6 +176,23 @@ $ git reset <3> committed as 'snapshot WIP'. This updates the index to show your WIP files as uncommitted. +Reset a single file in the index:: ++ +Suppose you have added a file to your index, but later decide you do not +want to add it to your commit. You can remove the file from the index +while keeping your changes with git reset. ++ +------------ +$ git reset -- frotz.c <1> +$ git commit -m "Commit files in index" <2> +$ git add frotz.c <3> +------------ ++ +<1> This removes the file from the index while keeping it in the working + directory. +<2> This commits all other changes in the index. +<3> Adds the file to the index again. + Author ------ Written by Junio C Hamano <junkio@cox.net> and Linus Torvalds <torvalds@osdl.org> diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index cd0dc1bd9d..48e6f5a3f7 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -43,7 +43,7 @@ save [<message>]:: subcommand is given. The <message> part is optional and gives the description along with the stashed state. -list:: +list [<options>]:: List the stashes that you currently have. Each 'stash' is listed with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is @@ -55,6 +55,9 @@ list:: stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation stash@{1}: On master: 9cc0589... Add git-stash ---------------------------------------------------------------- ++ +The command takes options applicable to the linkgit:git-log[1] +command to control what is shown and how. show [<stash>]:: diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt new file mode 100644 index 0000000000..df57d010e5 --- /dev/null +++ b/Documentation/git-web--browse.txt @@ -0,0 +1,78 @@ +git-web--browse(1) +================== + +NAME +---- +git-web--browse - git helper script to launch a web browser + +SYNOPSIS +-------- +'git-web--browse' [OPTIONS] URL/FILE ... + +DESCRIPTION +----------- + +This script tries, as much as possible, to display the URLs and FILEs +that are passed as arguments, as HTML pages in new tabs on an already +opened web browser. + +The following browsers (or commands) are currently supported: + +* 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 graphical environments) +* links +* lynx +* dillo +* open (this is the default under Mac OS X GUI) + +OPTIONS +------- +-b BROWSER|--browser=BROWSER:: + Use the specified BROWSER. It must be in the list of supported + browsers. + +-t BROWSER|--tool=BROWSER:: + Same as above. + +-c CONF.VAR|--config=CONF.VAR:: + CONF.VAR is looked up in the git config files. If it's set, + then its value specify the browser that should be used. + +CONFIGURATION VARIABLES +----------------------- + +The web browser can be specified using a configuration variable passed +with the -c (or --config) command line option, or the 'web.browser' +configuration variable if the former is not used. + +You can explicitly provide a full path to your preferred browser by +setting the configuration variable 'browser.<tool>.path'. For example, +you can configure the absolute path to firefox by setting +'browser.firefox.path'. Otherwise, 'git-web--browse' assumes the tool +is available in PATH. + +Note that these configuration variables should probably be set using +the '--global' flag, for example like this: + +------------------------------------------------ +$ git config --global web.browser firefox +------------------------------------------------ + +as they are probably more user specific than repository specific. +See linkgit:git-config[1] for more information about this. + +Author +------ +Written by Christian Couder <chriscool@tuxfamily.org> and the git-list +<git@vger.kernel.org>, based on git-mergetool by Theodore Y. Ts'o. + +Documentation +------------- +Documentation by Christian Couder <chriscool@tuxfamily.org> and the +git-list <git@vger.kernel.org>. + +GIT +--- +Part of the linkgit:git[7] suite diff --git a/Documentation/git.txt b/Documentation/git.txt index 17aee93ec5..d57bed618f 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +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] +* link:v1.5.4.2/git.html[documentation for release 1.5.4.2] * release notes for + link:RelNotes-1.5.4.2.txt[1.5.4.2], + link:RelNotes-1.5.4.1.txt[1.5.4.1], link:RelNotes-1.5.4.txt[1.5.4]. * link:v1.5.3.8/git.html[documentation for release 1.5.3.8] diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 35a29fd60c..84ec9623a2 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -133,6 +133,26 @@ When `core.autocrlf` is set to "input", line endings are converted to LF upon checkin, but there is no conversion done upon checkout. +If `core.safecrlf` is set to "true" or "warn", git verifies if +the conversion is reversible for the current setting of +`core.autocrlf`. For "true", git rejects irreversible +conversions; for "warn", git only prints a warning but accepts +an irreversible conversion. The safety triggers to prevent such +a conversion done to the files in the work tree, but there are a +few exceptions. Even though... + +- "git add" itself does not touch the files in the work tree, the + next checkout would, so the safety triggers; + +- "git apply" to update a text file with a patch does touch the files + in the work tree, but the operation is about text files and CRLF + conversion is about fixing the line ending inconsistencies, so the + safety does not trigger; + +- "git diff" itself does not touch the files in the work tree, it is + often run to inspect the changes you intend to next "git add". To + catch potential problems early, safety triggers. + `ident` ^^^^^^^ diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 08373f52bb..e847b3ba63 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -57,6 +57,13 @@ Patterns have the following format: included again. If a negated pattern matches, this will override lower precedence patterns sources. + - If the pattern ends with a slash, it is removed for the + purpose of the following description, but it would only find + a match with a directory. In other words, `foo/` will match a + directory `foo` and paths underneath it, but will not match a + regular file or a symbolic link `foo` (this is consistent + with the way how pathspec works in general in git). + - If the pattern does not contain a slash '/', git treats it as a shell glob pattern and checks for a match against the pathname without leading directories. diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt index f110162b01..76b8d77460 100644 --- a/Documentation/hooks.txt +++ b/Documentation/hooks.txt @@ -61,6 +61,35 @@ The default 'pre-commit' hook, when enabled, catches introduction of lines with trailing whitespaces and aborts the commit when such a line is found. +All the `git-commit` hooks are invoked with the environment +variable `GIT_EDITOR=:` if the command will not bring up an editor +to modify the commit message. + +prepare-commit-msg +------------------ + +This hook is invoked by `git-commit` right after preparing the +default log message, and before the editor is started. + +It takes one to three parameters. The first is the name of the file +that the commit log message. The second is the source of the commit +message, and can be: `message` (if a `\-m` or `\-F` option was +given); `template` (if a `\-t` option was given or the +configuration option `commit.template` is set); `merge` (if the +commit is a merge or a `.git/MERGE_MSG` file exists); `squash` +(if a `.git/SQUASH_MSG` file exists); or `commit`, followed by +a commit SHA1 (if a `\-c`, `\-C` or `\--amend` option was given). + +If the exit status is non-zero, `git-commit` will abort. + +The purpose of the hook is to edit the message file in place, and +it is not suppressed by the `\--no-verify` option. A non-zero exit +means a failure of the hook and aborts the commit. It should not +be used as replacement for pre-commit hook. + +The sample `prepare-commit-msg` hook that comes with git comments +out the `Conflicts:` part of a merge's commit message. + commit-msg ---------- diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt new file mode 100644 index 0000000000..073b22bd83 --- /dev/null +++ b/Documentation/technical/api-remote.txt @@ -0,0 +1,123 @@ +Remotes configuration API +========================= + +The API in remote.h gives access to the configuration related to +remotes. It handles all three configuration mechanisms historically +and currently used by git, and presents the information in a uniform +fashion. Note that the code also handles plain URLs without any +configuration, giving them just the default information. + +struct remote +------------- + +`name`:: + + The user's nickname for the remote + +`url`:: + + An array of all of the url_nr URLs configured for the remote + +`push`:: + + An array of refspecs configured for pushing, with + push_refspec being the literal strings, and push_refspec_nr + being the quantity. + +`fetch`:: + + An array of refspecs configured for fetching, with + fetch_refspec being the literal strings, and fetch_refspec_nr + being the quantity. + +`fetch_tags`:: + + The setting for whether to fetch tags (as a separate rule from + the configured refspecs); -1 means never to fetch tags, 0 + means to auto-follow tags based on the default heuristic, 1 + means to always auto-follow tags, and 2 means to fetch all + tags. + +`receivepack`, `uploadpack`:: + + The configured helper programs to run on the remote side, for + git-native protocols. + +`http_proxy`:: + + The proxy to use for curl (http, https, ftp, etc.) URLs. + +struct remotes can be found by name with remote_get(), and iterated +through with for_each_remote(). remote_get(NULL) will return the +default remote, given the current branch and configuration. + +struct refspec +-------------- + +A struct refspec holds the parsed interpretation of a refspec. If it +will force updates (starts with a '+'), force is true. If it is a +pattern (sides end with '*') pattern is true. src and dest are the two +sides (if a pattern, only the part outside of the wildcards); if there +is only one side, it is src, and dst is NULL; if sides exist but are +empty (i.e., the refspec either starts or ends with ':'), the +corresponding side is "". + +This parsing can be done to an array of strings to give an array of +struct refpsecs with parse_ref_spec(). + +remote_find_tracking(), given a remote and a struct refspec with +either src or dst filled out, will fill out the other such that the +result is in the "fetch" specification for the remote (note that this +evaluates patterns and returns a single result). + +struct branch +------------- + +Note that this may end up moving to branch.h + +struct branch holds the configuration for a branch. It can be looked +up with branch_get(name) for "refs/heads/{name}", or with +branch_get(NULL) for HEAD. + +It contains: + +`name`:: + + The short name of the branch. + +`refname`:: + + The full path for the branch ref. + +`remote_name`:: + + The name of the remote listed in the configuration. + +`remote`:: + + The struct remote for that remote. + +`merge_name`:: + + An array of the "merge" lines in the configuration. + +`merge`:: + + An array of the struct refspecs used for the merge lines. That + is, merge[i]->dst is a local tracking ref which should be + merged into this branch by default. + +`merge_nr`:: + + The number of merge configurations + +branch_has_merge_config() returns true if the given branch has merge +configuration given. + +Other stuff +----------- + +There is other stuff in remote.h that is related, in general, to the +process of interacting with remotes. + +(Daniel Barkalow) diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt index 19d2f64f73..dfbf9ac5d0 100644 --- a/Documentation/technical/api-run-command.txt +++ b/Documentation/technical/api-run-command.txt @@ -1,10 +1,171 @@ run-command API =============== -Talk about <run-command.h>, and things like: +The run-command API offers a versatile tool to run sub-processes with +redirected input and output as well as with a modified environment +and an alternate current directory. -* Environment the command runs with (e.g. GIT_DIR); -* File descriptors and pipes; -* Exit status; +A similar API offers the capability to run a function asynchronously, +which is primarily used to capture the output that the function +produces in the caller in order to process it. -(Hannes, Dscho, Shawn) + +Functions +--------- + +`start_command`:: + + Start a sub-process. Takes a pointer to a `struct child_process` + that specifies the details and returns pipe FDs (if requested). + See below for details. + +`finish_command`:: + + Wait for the completion of a sub-process that was started with + start_command(). + +`run_command`:: + + A convenience function that encapsulates a sequence of + start_command() followed by finish_command(). Takes a pointer + to a `struct child_process` that specifies the details. + +`run_command_v_opt`, `run_command_v_opt_dir`, `run_command_v_opt_cd_env`:: + + Convenience functions that encapsulate a sequence of + start_command() followed by finish_command(). The argument argv + specifies the program and its arguments. The argument opt is zero + or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or + `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members + .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`. + The argument dir corresponds the member .dir. The argument env + corresponds to the member .env. + +`start_async`:: + + Run a function asynchronously. Takes a pointer to a `struct + async` that specifies the details and returns a pipe FD + from which the caller reads. See below for details. + +`finish_async`:: + + Wait for the completeion of an asynchronous function that was + started with start_async(). + + +Data structures +--------------- + +* `struct child_process` + +This describes the arguments, redirections, and environment of a +command to run in a sub-process. + +The caller: + +1. allocates and clears (memset(&chld, '0', sizeof(chld));) a + struct child_process variable; +2. initializes the members; +3. calls start_command(); +4. processes the data; +5. closes file descriptors (if necessary; see below); +6. calls finish_command(). + +The .argv member is set up as an array of string pointers (NULL +terminated), of which .argv[0] is the program name to run (usually +without a path). If the command to run is a git command, set argv[0] to +the command name without the 'git-' prefix and set .git_cmd = 1. + +The members .in, .out, .err are used to redirect stdin, stdout, +stderr as follows: + +. Specify 0 to request no special redirection. No new file descriptor + is allocated. The child process simply inherits the channel from the + parent. + +. Specify -1 to have a pipe allocated; start_command() replaces -1 + by the pipe FD in the following way: + + .in: Returns the writable pipe end into which the caller writes; + the readable end of the pipe becomes the child's stdin. + + .out, .err: Returns the readable pipe end from which the caller + reads; the writable end of the pipe end becomes child's + stdout/stderr. + + The caller of start_command() must close the so returned FDs + after it has completed reading from/writing to it! + +. Specify a file descriptor > 0 to be used by the child: + + .in: The FD must be readable; it becomes child's stdin. + .out: The FD must be writable; it becomes child's stdout. + .err > 0 is not supported. + + The specified FD is closed by start_command(), even if it fails to + run the sub-process! + +. Special forms of redirection are available by setting these members + to 1: + + .no_stdin, .no_stdout, .no_stderr: The respective channel is + redirected to /dev/null. + + .stdout_to_stderr: stdout of the child is redirected to the + parent's stderr (i.e. *not* to what .err or + .no_stderr specify). + +To modify the environment of the sub-process, specify an array of +string pointers (NULL terminated) in .env: + +. If the string is of the form "VAR=value", i.e. it contains '=' + the variable is added to the child process's environment. + +. If the string does not contain '=', it names an environement + variable that will be removed from the child process's envionment. + +To specify a new initial working directory for the sub-process, +specify it in the .dir member. + + +* `struct async` + +This describes a function to run asynchronously, whose purpose is +to produce output that the caller reads. + +The caller: + +1. allocates and clears (memset(&asy, '0', sizeof(asy));) a + struct async variable; +2. initializes .proc and .data; +3. calls start_async(); +4. processes the data by reading from the fd in .out; +5. closes .out; +6. calls finish_async(). + +The function pointer in .proc has the following signature: + + int proc(int fd, void *data); + +. fd specifies a writable file descriptor to which the function must + write the data that it produces. The function *must* close this + descriptor before it returns. + +. data is the value that the caller has specified in the .data member + of struct async. + +. The return value of the function is 0 on success and non-zero + on failure. If the function indicates failure, finish_async() will + report failure as well. + + +There are serious restrictions on what the asynchronous function can do +because this facility is implemented by a pipe to a forked process on +UNIX, but by a thread in the same address space on Windows: + +. It cannot change the program's state (global variables, environment, + etc.) in a way that the caller notices; in other words, .out is the + only communication channel to the caller. + +. It must not change the program's state that the caller of the + facility also uses. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 38a3273974..1ad324e236 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -15,7 +15,8 @@ elif test -d .git && VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && case "$VN" in *$LF*) (exit 1) ;; - v[0-9]*) : happy ;; + v[0-9]*) + git diff-index --quiet HEAD || VN="$VN-dirty" ;; esac then VN=$(echo "$VN" | sed -e 's/-/./g'); @@ -25,14 +26,6 @@ fi VN=$(expr "$VN" : v*'\(.*\)') -dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty= -case "$dirty" in -'') - ;; -*) - VN="$VN-dirty" ;; -esac - if test -r $GVF then VC=$(sed -e 's/^GIT_VERSION = //' <$GVF) @@ -140,6 +140,10 @@ all:: # Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit # parallel delta searching when packing objects. # +# Define INTERNAL_QSORT to use Git's implementation of qsort(), which +# is a simplified version of the merge sort used in glibc. This is +# recommended if Git triggers O(n^2) behavior in your platform's qsort(). +# GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @$(SHELL_PATH) ./GIT-VERSION-GEN @@ -234,7 +238,7 @@ SCRIPT_SH = \ git-lost-found.sh git-quiltimport.sh git-submodule.sh \ git-filter-branch.sh \ git-stash.sh \ - git-help--browse.sh + git-web--browse.sh SCRIPT_PERL = \ git-add--interactive.perl \ @@ -729,6 +733,10 @@ ifdef NO_MEMMEM COMPAT_CFLAGS += -DNO_MEMMEM COMPAT_OBJS += compat/memmem.o endif +ifdef INTERNAL_QSORT + COMPAT_CFLAGS += -DINTERNAL_QSORT + COMPAT_OBJS += compat/qsort.o +endif ifdef THREADED_DELTA_SEARCH BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH @@ -826,6 +834,7 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS) help.o: help.c common-cmds.h GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ + '-DGIT_HTML_PATH="$(htmldir_SQ)"' \ '-DGIT_MAN_PATH="$(mandir_SQ)"' \ '-DGIT_INFO_PATH="$(infodir_SQ)"' $< @@ -846,7 +855,6 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ - -e 's|@@HTMLDIR@@|$(htmldir_SQ)|g' \ $@.sh >$@+ && \ chmod +x $@+ && \ mv $@+ $@ @@ -1 +1 @@ -Documentation/RelNotes-1.5.4.txt
\ No newline at end of file +Documentation/RelNotes-1.5.5.txt
\ No newline at end of file diff --git a/builtin-add.c b/builtin-add.c index 4a91e3eb11..820110e085 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -228,6 +228,18 @@ int cmd_add(int argc, const char **argv, const char *prefix) goto finish; } + if (*argv) { + /* Was there an invalid path? */ + if (pathspec) { + int num; + for (num = 0; pathspec[num]; num++) + ; /* just counting */ + if (argc != num) + exit(1); /* error message already given */ + } else + exit(1); /* error message already given */ + } + fill_directory(&dir, pathspec, ignored_too); if (show_only) { diff --git a/builtin-apply.c b/builtin-apply.c index 46dad5b2a1..6a88ff018d 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1430,7 +1430,7 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) case S_IFREG: if (strbuf_read_file(buf, path, st->st_size) != st->st_size) return error("unable to open or read %s", path); - convert_to_git(path, buf->buf, buf->len, buf); + convert_to_git(path, buf->buf, buf->len, buf, 0); return 0; default: return -1; diff --git a/builtin-blame.c b/builtin-blame.c index c7e68874e7..59d7237f21 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1894,9 +1894,7 @@ static unsigned parse_score(const char *arg) static const char *add_prefix(const char *prefix, const char *path) { - if (!prefix || !prefix[0]) - return path; - return prefix_path(prefix, strlen(prefix), path); + return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); } /* @@ -2073,7 +2071,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con if (strbuf_read(&buf, 0, 0) < 0) die("read error %s from stdin", strerror(errno)); } - convert_to_git(path, buf.buf, buf.len, &buf); + convert_to_git(path, buf.buf, buf.len, &buf, 0); origin->file.ptr = buf.buf; origin->file.size = buf.len; pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1); @@ -2369,7 +2367,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) * bottom commits we would reach while traversing as * uninteresting. */ - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); if (is_null_sha1(sb.final->object.sha1)) { char *buf; diff --git a/builtin-commit.c b/builtin-commit.c index 717eb18da0..6612b4f405 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -317,6 +317,10 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) if (write_cache(fd, active_cache, active_nr) || close_lock_file(&false_lock)) die("unable to write temporary index file"); + + discard_cache(); + read_cache_from(false_lock.filename); + return false_lock.filename; } @@ -343,45 +347,107 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int return s.commitable; } +static int run_hook(const char *index_file, const char *name, ...) +{ + struct child_process hook; + const char *argv[10], *env[2]; + char index[PATH_MAX]; + va_list args; + int i; + + va_start(args, name); + argv[0] = git_path("hooks/%s", name); + i = 0; + do { + if (++i >= ARRAY_SIZE(argv)) + die ("run_hook(): too many arguments"); + argv[i] = va_arg(args, const char *); + } while (argv[i]); + va_end(args); + + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); + env[0] = index; + env[1] = NULL; + + if (access(argv[0], X_OK) < 0) + return 0; + + memset(&hook, 0, sizeof(hook)); + hook.argv = argv; + hook.no_stdin = 1; + hook.stdout_to_stderr = 1; + hook.env = env; + + return run_command(&hook); +} + +static int is_a_merge(const unsigned char *sha1) +{ + struct commit *commit = lookup_commit(sha1); + if (!commit || parse_commit(commit)) + die("could not parse HEAD commit"); + return !!(commit->parents && commit->parents->next); +} + static const char sign_off_header[] = "Signed-off-by: "; -static int prepare_log_message(const char *index_file, const char *prefix) +static int prepare_to_commit(const char *index_file, const char *prefix) { struct stat statbuf; int commitable, saved_color_setting; struct strbuf sb; char *buffer; FILE *fp; + const char *hook_arg1 = NULL; + const char *hook_arg2 = NULL; + + if (!no_verify && run_hook(index_file, "pre-commit", NULL)) + return 0; strbuf_init(&sb, 0); if (message.len) { strbuf_addbuf(&sb, &message); + hook_arg1 = "message"; } else if (logfile && !strcmp(logfile, "-")) { if (isatty(0)) fprintf(stderr, "(reading log message from standard input)\n"); if (strbuf_read(&sb, 0, 0) < 0) die("could not read log from standard input"); + hook_arg1 = "message"; } else if (logfile) { if (strbuf_read_file(&sb, logfile, 0) < 0) die("could not read log file '%s': %s", logfile, strerror(errno)); + hook_arg1 = "message"; } else if (use_message) { buffer = strstr(use_message_buffer, "\n\n"); if (!buffer || buffer[2] == '\0') die("commit has empty message"); strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); + hook_arg1 = "commit"; + hook_arg2 = use_message; } else if (!stat(git_path("MERGE_MSG"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0) die("could not read MERGE_MSG: %s", strerror(errno)); + hook_arg1 = "merge"; } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) { if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0) die("could not read SQUASH_MSG: %s", strerror(errno)); + hook_arg1 = "squash"; } else if (template_file && !stat(template_file, &statbuf)) { if (strbuf_read_file(&sb, template_file, 0) < 0) die("could not read %s: %s", template_file, strerror(errno)); + hook_arg1 = "template"; } + /* + * This final case does not modify the template message, + * it just sets the argument to the prepare-commit-msg hook. + */ + else if (in_merge) + hook_arg1 = "merge"; + fp = fopen(git_path(commit_editmsg), "w"); if (fp == NULL) die("could not open %s", git_path(commit_editmsg)); @@ -413,13 +479,38 @@ static int prepare_log_message(const char *index_file, const char *prefix) strbuf_release(&sb); - if (!use_editor) { + if (use_editor) { + if (in_merge) + fprintf(fp, + "#\n" + "# It looks like you may be committing a MERGE.\n" + "# If this is not correct, please remove the file\n" + "# %s\n" + "# and try again.\n" + "#\n", + git_path("MERGE_HEAD")); + + fprintf(fp, + "\n" + "# Please enter the commit message for your changes.\n" + "# (Comment lines starting with '#' will "); + if (cleanup_mode == CLEANUP_ALL) + fprintf(fp, "not be included)\n"); + else /* CLEANUP_SPACE, that is. */ + fprintf(fp, "be kept.\n" + "# You can remove them yourself if you want to)\n"); + if (only_include_assumed) + fprintf(fp, "# %s\n", only_include_assumed); + + saved_color_setting = wt_status_use_color; + wt_status_use_color = 0; + commitable = run_status(fp, index_file, prefix, 1); + wt_status_use_color = saved_color_setting; + } else { struct rev_info rev; unsigned char sha1[20]; const char *parent = "HEAD"; - fclose(fp); - if (!active_nr && read_cache() < 0) die("Cannot read index"); @@ -427,48 +518,60 @@ static int prepare_log_message(const char *index_file, const char *prefix) parent = "HEAD^1"; if (get_sha1(parent, sha1)) - return !!active_nr; + commitable = !!active_nr; + else { + init_revisions(&rev, ""); + rev.abbrev = 0; + setup_revisions(0, NULL, &rev, parent); + DIFF_OPT_SET(&rev.diffopt, QUIET); + DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); + run_diff_index(&rev, 1 /* cached */); + + commitable = !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES); + } + } - init_revisions(&rev, ""); - rev.abbrev = 0; - setup_revisions(0, NULL, &rev, parent); - DIFF_OPT_SET(&rev.diffopt, QUIET); - DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); - run_diff_index(&rev, 1 /* cached */); + fclose(fp); - return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES); + if (!commitable && !in_merge && !allow_empty && + !(amend && is_a_merge(head_sha1))) { + run_status(stdout, index_file, prefix, 0); + unlink(commit_editmsg); + return 0; } - if (in_merge) - fprintf(fp, - "#\n" - "# It looks like you may be committing a MERGE.\n" - "# If this is not correct, please remove the file\n" - "# %s\n" - "# and try again.\n" - "#\n", - git_path("MERGE_HEAD")); - - fprintf(fp, - "\n" - "# Please enter the commit message for your changes.\n" - "# (Comment lines starting with '#' will "); - if (cleanup_mode == CLEANUP_ALL) - fprintf(fp, "not be included)\n"); - else /* CLEANUP_SPACE, that is. */ - fprintf(fp, "be kept.\n" - "# You can remove them yourself if you want to)\n"); - if (only_include_assumed) - fprintf(fp, "# %s\n", only_include_assumed); - - saved_color_setting = wt_status_use_color; - wt_status_use_color = 0; - commitable = run_status(fp, index_file, prefix, 1); - wt_status_use_color = saved_color_setting; + /* + * Re-read the index as pre-commit hook could have updated it, + * and write it out as a tree. We must do this before we invoke + * the editor and after we invoke run_status above. + */ + discard_cache(); + read_cache_from(index_file); + if (!active_cache_tree) + active_cache_tree = cache_tree(); + if (cache_tree_update(active_cache_tree, + active_cache, active_nr, 0, 0) < 0) { + error("Error building trees"); + return 0; + } - fclose(fp); + if (run_hook(index_file, "prepare-commit-msg", + git_path(commit_editmsg), hook_arg1, hook_arg2, NULL)) + return 0; + + if (use_editor) { + char index[PATH_MAX]; + const char *env[2] = { index, NULL }; + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); + launch_editor(git_path(commit_editmsg), NULL, env); + } + + if (!no_verify && + run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) { + return 0; + } - return commitable; + return 1; } /* @@ -565,6 +668,8 @@ static int parse_and_validate_options(int argc, const char *argv[], use_editor = 0; if (edit_flag) use_editor = 1; + if (!use_editor) + setenv("GIT_EDITOR", ":", 1); if (get_sha1("HEAD", head_sha1)) initial_commit = 1; @@ -677,31 +782,6 @@ int cmd_status(int argc, const char **argv, const char *prefix) return commitable ? 0 : 1; } -static int run_hook(const char *index_file, const char *name, const char *arg) -{ - struct child_process hook; - const char *argv[3], *env[2]; - char index[PATH_MAX]; - - argv[0] = git_path("hooks/%s", name); - argv[1] = arg; - argv[2] = NULL; - snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); - env[0] = index; - env[1] = NULL; - - if (access(argv[0], X_OK) < 0) - return 0; - - memset(&hook, 0, sizeof(hook)); - hook.argv = argv; - hook.no_stdin = 1; - hook.stdout_to_stderr = 1; - hook.env = env; - - return run_command(&hook); -} - static void print_summary(const char *prefix, const unsigned char *sha1) { struct rev_info rev; @@ -752,14 +832,6 @@ int git_commit_config(const char *k, const char *v) return git_status_config(k, v); } -static int is_a_merge(const unsigned char *sha1) -{ - struct commit *commit = lookup_commit(sha1); - if (!commit || parse_commit(commit)) - die("could not parse HEAD commit"); - return !!(commit->parents && commit->parents->next); -} - static const char commit_utf8_warn[] = "Warning: commit message does not conform to UTF-8.\n" "You may want to amend it after fixing the message, or set the config\n" @@ -791,33 +863,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix) index_file = prepare_index(argc, argv, prefix); - if (!no_verify && run_hook(index_file, "pre-commit", NULL)) { + /* Set up everything for writing the commit object. This includes + running hooks, writing the trees, and interacting with the user. */ + if (!prepare_to_commit(index_file, prefix)) { rollback_index_files(); return 1; } - if (!prepare_log_message(index_file, prefix) && !in_merge && - !allow_empty && !(amend && is_a_merge(head_sha1))) { - run_status(stdout, index_file, prefix, 0); - rollback_index_files(); - unlink(commit_editmsg); - return 1; - } - - /* - * Re-read the index as pre-commit hook could have updated it, - * and write it out as a tree. - */ - discard_cache(); - read_cache_from(index_file); - if (!active_cache_tree) - active_cache_tree = cache_tree(); - if (cache_tree_update(active_cache_tree, - active_cache, active_nr, 0, 0) < 0) { - rollback_index_files(); - die("Error building trees"); - } - /* * The commit object */ @@ -869,19 +921,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) strbuf_addf(&sb, "encoding %s\n", git_commit_encoding); strbuf_addch(&sb, '\n'); - /* Get the commit message and validate it */ + /* Finally, get the commit message */ header_len = sb.len; - if (use_editor) { - char index[PATH_MAX]; - const char *env[2] = { index, NULL }; - snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); - launch_editor(git_path(commit_editmsg), NULL, env); - } - if (!no_verify && - run_hook(index_file, "commit-msg", git_path(commit_editmsg))) { - rollback_index_files(); - exit(1); - } if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { rollback_index_files(); die("could not read commit message"); diff --git a/builtin-config.c b/builtin-config.c index 077d8ef2df..2b9a4261d4 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -79,9 +79,10 @@ static int get_value(const char* key_, const char* regex_) local = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!local) local = repo_config = xstrdup(git_path("config")); - if (home) + if (git_config_global() && home) global = xstrdup(mkpath("%s/.gitconfig", home)); - system_wide = git_etc_gitconfig(); + if (git_config_system()) + system_wide = git_etc_gitconfig(); } key = xstrdup(key_); diff --git a/builtin-fast-export.c b/builtin-fast-export.c index ef27eee71b..f741df5220 100755 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -383,7 +383,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) get_tags_and_duplicates(&revs.pending, &extra_refs); - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); revs.diffopt.format_callback = show_filemodify; DIFF_OPT_SET(&revs.diffopt, RECURSIVE); while ((commit = get_revision(&revs))) { diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index 6163bd4975..ebb3f37cf1 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -187,7 +187,8 @@ static void shortlog(const char *name, unsigned char *sha1, add_pending_object(rev, branch, name); add_pending_object(rev, &head->object, "^HEAD"); head->object.flags |= UNINTERESTING; - prepare_revision_walk(rev); + if (prepare_revision_walk(rev)) + die("revision walk setup failed"); while ((commit = get_revision(rev)) != NULL) { char *oneline, *bol, *eol; diff --git a/builtin-log.c b/builtin-log.c index 99d69f0791..5fea64abad 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -197,7 +197,8 @@ static int cmd_log_walk(struct rev_info *rev) if (rev->early_output) setup_early_output(rev); - prepare_revision_walk(rev); + if (prepare_revision_walk(rev)) + die("revision walk setup failed"); if (rev->early_output) finish_early_output(rev); @@ -556,7 +557,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha o2->flags ^= UNINTERESTING; add_pending_object(&check_rev, o1, "o1"); add_pending_object(&check_rev, o2, "o2"); - prepare_revision_walk(&check_rev); + if (prepare_revision_walk(&check_rev)) + die("revision walk setup failed"); while ((commit = get_revision(&check_rev)) != NULL) { /* ignore merges */ @@ -781,7 +783,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); - prepare_revision_walk(&rev); + if (prepare_revision_walk(&rev)) + die("revision walk setup failed"); while ((commit = get_revision(&rev)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) @@ -923,7 +926,8 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) die("Unknown commit %s", limit); /* reverse the list of commits */ - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) diff --git a/builtin-ls-files.c b/builtin-ls-files.c index d56e33e251..25dbfb4499 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -238,7 +238,8 @@ static void show_files(struct dir_struct *dir, const char *prefix) if (show_cached | show_stage) { for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; - if (excluded(dir, ce->name) != dir->show_ignored) + int dtype = ce_to_dtype(ce); + if (excluded(dir, ce->name, &dtype) != dir->show_ignored) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -252,7 +253,8 @@ static void show_files(struct dir_struct *dir, const char *prefix) struct cache_entry *ce = active_cache[i]; struct stat st; int err; - if (excluded(dir, ce->name) != dir->show_ignored) + int dtype = ce_to_dtype(ce); + if (excluded(dir, ce->name, &dtype) != dir->show_ignored) continue; err = lstat(ce->name, &st); if (show_deleted && err) @@ -572,8 +574,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) pathspec = get_pathspec(prefix, argv + i); /* Verify that the pathspec matches the prefix */ - if (pathspec) + if (pathspec) { + if (argc != i) { + int cnt; + for (cnt = 0; pathspec[cnt]; cnt++) + ; + if (cnt != (argc - i)) + exit(1); /* error message already given */ + } prefix = verify_pathspec(prefix); + } else if (argc != i) + exit(1); /* error message already given */ /* Treat unmatching pathspec elements as errors */ if (pathspec && error_unmatch) { diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 2600847974..11f154b31f 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -818,6 +818,7 @@ static void handle_body(void) switch (transfer_encoding) { case TE_BASE64: + case TE_QP: { char *op = line; diff --git a/builtin-mv.c b/builtin-mv.c index 990e21355d..68aa2a68bb 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -19,6 +19,7 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, int count, int base_name) { int i; + int len = prefix ? strlen(prefix) : 0; const char **result = xmalloc((count + 1) * sizeof(const char *)); memcpy(result, pathspec, count * sizeof(const char *)); result[count] = NULL; @@ -32,8 +33,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, if (last_slash) result[i] = last_slash + 1; } + result[i] = prefix_path(prefix, len, result[i]); + if (!result[i]) + exit(1); /* error already given */ } - return get_pathspec(prefix, result); + return result; } static void show_list(const char *label, struct path_list *list) @@ -164,7 +168,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } dst = add_slash(dst); - dst_len = strlen(dst) - 1; + dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = @@ -172,7 +176,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, - path + length); + path + length + 1); modes[argc + j] = INDEX; } argc += last - first; diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index acb05554d4..d2bb12e574 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1464,7 +1464,7 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n) return m; } -static unsigned long free_unpacked_data(struct unpacked *n) +static unsigned long free_unpacked(struct unpacked *n) { unsigned long freed_mem = sizeof_delta_index(n->index); free_delta_index(n->index); @@ -1474,12 +1474,6 @@ static unsigned long free_unpacked_data(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; @@ -1520,7 +1514,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_data(array + tail); + mem_usage -= free_unpacked(array + tail); count--; } @@ -1553,9 +1547,6 @@ 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) @@ -2042,7 +2033,8 @@ static void get_object_list(int ac, const char **av) die("bad revision '%s'", line); } - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); mark_edges_uninteresting(revs.commits, &revs, show_edge); traverse_commit_list(&revs, show_commit, show_object); diff --git a/builtin-push.c b/builtin-push.c index c8cb63e238..9f727c00f6 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -90,7 +90,7 @@ static int do_push(const char *repo, int flags) if (!err) continue; - error("failed to push to '%s'", remote->url[i]); + error("failed to push some refs to '%s'", remote->url[i]); errs++; } return !!errs; diff --git a/builtin-rev-list.c b/builtin-rev-list.c index de80158fd4..9426081dae 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -609,7 +609,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) revs.limited = 1; - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); if (revs.tree_objects) mark_edges_uninteresting(revs.commits, &revs, show_edge); diff --git a/builtin-shortlog.c b/builtin-shortlog.c index fa8bc7d02a..0055a57aeb 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -136,7 +136,8 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list) { struct commit *commit; - prepare_revision_walk(rev); + if (prepare_revision_walk(rev)) + die("revision walk setup failed"); while ((commit = get_revision(rev)) != NULL) { const char *author = NULL, *buffer; diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 65051d14fd..a323633e29 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -86,6 +86,9 @@ match: sha1_to_hex(sha1)); if (obj->type == OBJ_TAG) { obj = deref_tag(obj, refname, 0); + if (!obj) + die("git-show-ref: bad tag at ref %s (%s)", refname, + sha1_to_hex(sha1)); hex = find_unique_abbrev(obj->sha1, abbrev); printf("%s %s^{}\n", hex, refname); } @@ -128,7 +128,8 @@ int verify_bundle(struct bundle_header *header, int verbose) add_object_array(e->item, e->name, &refs); } - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); i = req_nr; while (i && (commit = get_revision(&revs))) @@ -178,6 +178,18 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in } return create_ce_mode(mode); } +static inline int ce_to_dtype(const struct cache_entry *ce) +{ + unsigned ce_mode = ntohl(ce->ce_mode); + if (S_ISREG(ce_mode)) + return DT_REG; + else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode)) + return DT_DIR; + else if (S_ISLNK(ce_mode)) + return DT_LNK; + else + return DT_UNKNOWN; +} #define canon_mode(mode) \ (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \ S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK) @@ -371,6 +383,14 @@ extern size_t packed_git_limit; extern size_t delta_base_cache_limit; extern int auto_crlf; +enum safe_crlf { + SAFE_CRLF_FALSE = 0, + SAFE_CRLF_FAIL = 1, + SAFE_CRLF_WARN = 2, +}; + +extern enum safe_crlf safe_crlf; + #define GIT_REPO_VERSION 0 extern int repository_format_version; extern int check_repository_format(void); @@ -625,11 +645,15 @@ extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); extern int git_config_bool(const char *, const char *); +extern int git_config_string(const char **, const char *, const char *); extern int git_config_set(const char *, const char *); 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 git_env_bool(const char *, int); +extern int git_config_system(void); +extern int git_config_global(void); extern int config_error_nonbool(const char *); #define MAX_GITNAME (1000) @@ -650,12 +674,12 @@ extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char /* pager.c */ extern void setup_pager(void); -extern char *pager_program; +extern const char *pager_program; extern int pager_in_use(void); extern int pager_use_color; -extern char *editor_program; -extern char *excludes_file; +extern const char *editor_program; +extern const char *excludes_file; /* base85 */ int decode_85(char *dst, const char *line, int linelen); @@ -675,7 +699,8 @@ extern void trace_argv_printf(const char **argv, const char *format, ...); /* convert.c */ /* returns 1 if *dst was used */ -extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst); +extern int convert_to_git(const char *path, const char *src, size_t len, + struct strbuf *dst, enum safe_crlf checksafe); extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* add */ @@ -311,6 +311,8 @@ int parse_commit(struct commit *item) unsigned long size; int ret; + if (!item) + return -1; if (item->object.parsed) return 0; buffer = read_sha1_file(item->object.sha1, &type, &size); @@ -385,8 +387,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list, while (parents) { struct commit *commit = parents->item; - parse_commit(commit); - if (!(commit->object.flags & mark)) { + if (!parse_commit(commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; insert_by_date(commit, list); } @@ -552,8 +553,10 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) */ return commit_list_insert(one, &result); - parse_commit(one); - parse_commit(two); + if (parse_commit(one)) + return NULL; + if (parse_commit(two)) + return NULL; one->object.flags |= PARENT1; two->object.flags |= PARENT2; @@ -586,7 +589,8 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) parents = parents->next; if ((p->object.flags & flags) == flags) continue; - parse_commit(p); + if (parse_commit(p)) + return NULL; p->object.flags |= flags; insert_by_date(p, &list); } diff --git a/compat/qsort.c b/compat/qsort.c new file mode 100644 index 0000000000..d93dce2cf8 --- /dev/null +++ b/compat/qsort.c @@ -0,0 +1,62 @@ +#include "../git-compat-util.h" + +/* + * A merge sort implementation, simplified from the qsort implementation + * by Mike Haertel, which is a part of the GNU C Library. + */ + +static void msort_with_tmp(void *b, size_t n, size_t s, + int (*cmp)(const void *, const void *), + char *t) +{ + char *tmp; + char *b1, *b2; + size_t n1, n2; + + if (n <= 1) + return; + + n1 = n / 2; + n2 = n - n1; + b1 = b; + b2 = (char *)b + (n1 * s); + + msort_with_tmp(b1, n1, s, cmp, t); + msort_with_tmp(b2, n2, s, cmp, t); + + tmp = t; + + while (n1 > 0 && n2 > 0) { + if (cmp(b1, b2) <= 0) { + memcpy(tmp, b1, s); + tmp += s; + b1 += s; + --n1; + } else { + memcpy(tmp, b2, s); + tmp += s; + b2 += s; + --n2; + } + } + if (n1 > 0) + memcpy(tmp, b1, n1 * s); + memcpy(b, t, (n - n2) * s); +} + +void git_qsort(void *b, size_t n, size_t s, + int (*cmp)(const void *, const void *)) +{ + const size_t size = n * s; + char buf[1024]; + + if (size < sizeof(buf)) { + /* The temporary array fits on the small on-stack buffer. */ + msort_with_tmp(b, n, s, cmp, buf); + } else { + /* It's somewhat large, so malloc it. */ + char *tmp = malloc(size); + msort_with_tmp(b, n, s, cmp, tmp); + free(tmp); + } +} @@ -309,6 +309,14 @@ int git_config_bool(const char *name, const char *value) return git_config_int(name, value) != 0; } +int git_config_string(const char **dest, const char *var, const char *value) +{ + if (!value) + return config_error_nonbool(var); + *dest = xstrdup(value); + return 0; +} + int git_default_config(const char *var, const char *value) { /* This needs a better name */ @@ -407,6 +415,15 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.safecrlf")) { + if (value && !strcasecmp(value, "warn")) { + safe_crlf = SAFE_CRLF_WARN; + return 0; + } + safe_crlf = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "user.name")) { if (!value) return config_error_nonbool(var); @@ -421,46 +438,25 @@ int git_default_config(const char *var, const char *value) 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; - } + if (!strcmp(var, "i18n.commitencoding")) + return git_config_string(&git_commit_encoding, var, value); + if (!strcmp(var, "i18n.logoutputencoding")) + return git_config_string(&git_log_output_encoding, var, value); if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { pager_use_color = git_config_bool(var,value); return 0; } - if (!strcmp(var, "core.pager")) { - if (!value) - return config_error_nonbool(var); - pager_program = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.pager")) + return git_config_string(&pager_program, var, value); - if (!strcmp(var, "core.editor")) { - if (!value) - return config_error_nonbool(var); - editor_program = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.editor")) + return git_config_string(&editor_program, var, value); - if (!strcmp(var, "core.excludesfile")) { - if (!value) - return config_error_nonbool(var); - excludes_file = xstrdup(value); - return 0; - } + if (!strcmp(var, "core.excludesfile")) + return git_config_string(&excludes_file, var, value); if (!strcmp(var, "core.whitespace")) { if (!value) @@ -506,6 +502,22 @@ const char *git_etc_gitconfig(void) return system_wide; } +int git_env_bool(const char *k, int def) +{ + const char *v = getenv(k); + return v ? git_config_bool(k, v) : def; +} + +int git_config_system(void) +{ + return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); +} + +int git_config_global(void) +{ + return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0); +} + int git_config(config_fn_t fn) { int ret = 0; @@ -518,7 +530,7 @@ int git_config(config_fn_t fn) * config file otherwise. */ filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { - if (!access(git_etc_gitconfig(), R_OK)) + if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) ret += git_config_from_file(fn, git_etc_gitconfig()); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); @@ -526,7 +538,7 @@ int git_config(config_fn_t fn) filename = repo_config = xstrdup(git_path("config")); } - if (home) { + if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) ret = git_config_from_file(fn, user_config); diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index a8bf0ef883..f69b697f8d 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -185,9 +185,8 @@ if there is already one that displays the same directory." (defun git-call-process-env (buffer env &rest args) "Wrapper for call-process that sets environment strings." - (if env - (apply #'call-process "env" nil buffer nil - (append (git-get-env-strings env) (list "git") args)) + (let ((process-environment (append (git-get-env-strings env) + process-environment))) (apply #'call-process "git" nil buffer nil args))) (defun git-call-process-display-error (&rest args) @@ -204,7 +203,7 @@ if there is already one that displays the same directory." (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." +and returns the process output as a string, or nil if the git failed." (with-temp-buffer (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index c35b15860d..d72ffbb777 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -111,7 +111,7 @@ hgparents["0"] = (None, None) hgbranch["0"] = "master" for cset in range(1, int(tip) + 1): hgchildren[str(cset)] = () - prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().split(' ') + prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ') prnts = map(lambda x: x[:x.find(':')], prnts) if prnts[0] != '': parent = prnts[0].strip() @@ -85,8 +85,39 @@ static int is_binary(unsigned long size, struct text_stat *stats) return 0; } +static void check_safe_crlf(const char *path, int action, + struct text_stat *stats, enum safe_crlf checksafe) +{ + if (!checksafe) + return; + + if (action == CRLF_INPUT || auto_crlf <= 0) { + /* + * CRLFs would not be restored by checkout: + * check if we'd remove CRLFs + */ + if (stats->crlf) { + if (checksafe == SAFE_CRLF_WARN) + warning("CRLF will be replaced by LF in %s.", path); + else /* i.e. SAFE_CRLF_FAIL */ + die("CRLF would be replaced by LF in %s.", path); + } + } else if (auto_crlf > 0) { + /* + * CRLFs would be added by checkout: + * check if we have "naked" LFs + */ + if (stats->lf != stats->crlf) { + if (checksafe == SAFE_CRLF_WARN) + warning("LF will be replaced by CRLF in %s", path); + else /* i.e. SAFE_CRLF_FAIL */ + die("LF would be replaced by CRLF in %s", path); + } + } +} + static int crlf_to_git(const char *path, const char *src, size_t len, - struct strbuf *buf, int action) + struct strbuf *buf, int action, enum safe_crlf checksafe) { struct text_stat stats; char *dst; @@ -95,9 +126,6 @@ static int crlf_to_git(const char *path, const char *src, size_t len, return 0; gather_stats(src, len, &stats); - /* No CR? Nothing to convert, regardless. */ - if (!stats.cr) - return 0; if (action == CRLF_GUESS) { /* @@ -115,6 +143,12 @@ static int crlf_to_git(const char *path, const char *src, size_t len, return 0; } + check_safe_crlf(path, action, &stats, checksafe); + + /* Optimization: No CR? Nothing to convert, regardless. */ + if (!stats.cr) + return 0; + /* only grow if not in place */ if (strbuf_avail(buf) + buf->len < len) strbuf_grow(buf, len - buf->len); @@ -536,7 +570,8 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check) return !!ATTR_TRUE(value); } -int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst) +int convert_to_git(const char *path, const char *src, size_t len, + struct strbuf *dst, enum safe_crlf checksafe) { struct git_attr_check check[3]; int crlf = CRLF_GUESS; @@ -558,7 +593,7 @@ int convert_to_git(const char *path, const char *src, size_t len, struct strbuf src = dst->buf; len = dst->len; } - ret |= crlf_to_git(path, src, len, dst, crlf); + ret |= crlf_to_git(path, src, len, dst, crlf, checksafe); if (ret) { src = dst->buf; len = dst->len; @@ -57,7 +57,7 @@ static int parse_diff_color_slot(const char *var, int ofs) static struct ll_diff_driver { const char *name; struct ll_diff_driver *next; - char *cmd; + const char *cmd; } *user_diff, **user_diff_tail; /* @@ -86,10 +86,7 @@ static int parse_lldiff_command(const char *var, const char *ep, const char *val user_diff_tail = &(drv->next); } - if (!value) - return error("%s: lacks value", var); - drv->cmd = strdup(value); - return 0; + return git_config_string(&(drv->cmd), var, value); } /* @@ -166,13 +163,8 @@ int git_diff_ui_config(const char *var, const char *value) if (!prefixcmp(var, "diff.")) { const char *ep = strrchr(var, '.'); - if (ep != var + 4) { - if (!strcmp(ep, ".command")) { - if (!value) - return config_error_nonbool(var); - return parse_lldiff_command(var, ep, value); - } - } + if (ep != var + 4 && !strcmp(ep, ".command")) + return parse_lldiff_command(var, ep, value); } return git_diff_basic_config(var, value); @@ -1021,6 +1013,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) char *err; if (line[0] == '+') { + data->lineno++; data->status = check_and_emit_line(line + 1, len - 1, data->ws_rule, NULL, NULL, NULL, NULL); if (!data->status) @@ -1031,13 +1024,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len) emit_line(set, reset, line, 1); (void)check_and_emit_line(line + 1, len - 1, data->ws_rule, stdout, set, reset, ws); - data->lineno++; } else if (line[0] == ' ') data->lineno++; else if (line[0] == '@') { char *plus = strchr(line, '+'); if (plus) - data->lineno = strtol(plus, NULL, 10); + data->lineno = strtol(plus, NULL, 10) - 1; else die("invalid diff"); } @@ -1639,7 +1631,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) * Convert from working tree format to canonical git format */ strbuf_init(&buf, 0); - if (convert_to_git(s->path, s->data, s->size, &buf)) { + if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) { size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; @@ -17,6 +17,7 @@ struct path_simplify { static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify); +static int get_dtype(struct dirent *de, const char *path); int common_prefix(const char **pathspec) { @@ -126,18 +127,34 @@ static int no_wildcard(const char *string) void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which) { - struct exclude *x = xmalloc(sizeof (*x)); + struct exclude *x; + size_t len; + int to_exclude = 1; + int flags = 0; - x->to_exclude = 1; if (*string == '!') { - x->to_exclude = 0; + to_exclude = 0; string++; } - x->pattern = string; + len = strlen(string); + if (len && string[len - 1] == '/') { + char *s; + x = xmalloc(sizeof(*x) + len); + s = (char*)(x+1); + memcpy(s, string, len - 1); + s[len - 1] = '\0'; + string = s; + x->pattern = s; + flags = EXC_FLAG_MUSTBEDIR; + } else { + x = xmalloc(sizeof(*x)); + x->pattern = string; + } + x->to_exclude = to_exclude; x->patternlen = strlen(string); x->base = base; x->baselen = baselen; - x->flags = 0; + x->flags = flags; if (!strchr(string, '/')) x->flags |= EXC_FLAG_NODIR; if (no_wildcard(string)) @@ -261,7 +278,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) * Return 1 for exclude, 0 for include and -1 for undecided. */ static int excluded_1(const char *pathname, - int pathlen, const char *basename, + int pathlen, const char *basename, int *dtype, struct exclude_list *el) { int i; @@ -272,6 +289,13 @@ static int excluded_1(const char *pathname, const char *exclude = x->pattern; int to_exclude = x->to_exclude; + if (x->flags & EXC_FLAG_MUSTBEDIR) { + if (*dtype == DT_UNKNOWN) + *dtype = get_dtype(NULL, pathname); + if (*dtype != DT_DIR) + continue; + } + if (x->flags & EXC_FLAG_NODIR) { /* match basename */ if (x->flags & EXC_FLAG_NOWILDCARD) { @@ -314,7 +338,7 @@ static int excluded_1(const char *pathname, return -1; /* undecided */ } -int excluded(struct dir_struct *dir, const char *pathname) +int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) { int pathlen = strlen(pathname); int st; @@ -323,7 +347,8 @@ int excluded(struct dir_struct *dir, const char *pathname) prep_exclude(dir, pathname, basename-pathname); for (st = EXC_CMDL; st <= EXC_FILE; st++) { - switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) { + switch (excluded_1(pathname, pathlen, basename, + dtype_p, &dir->exclude_list[st])) { case 0: return 0; case 1: @@ -508,7 +533,7 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si static int get_dtype(struct dirent *de, const char *path) { - int dtype = DTYPE(de); + int dtype = de ? DTYPE(de) : DT_UNKNOWN; struct stat st; if (dtype != DT_UNKNOWN) @@ -560,7 +585,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co if (simplify_away(fullname, baselen + len, simplify)) continue; - exclude = excluded(dir, fullname); + dtype = DTYPE(de); + exclude = excluded(dir, fullname, &dtype); if (exclude && dir->collect_ignored && in_pathspec(fullname, baselen + len, simplify)) dir_add_ignored(dir, fullname, baselen + len); @@ -572,7 +598,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co if (exclude && !dir->show_ignored) continue; - dtype = get_dtype(de, fullname); + if (dtype == DT_UNKNOWN) + dtype = get_dtype(de, fullname); /* * Do we want to see just the ignored files? @@ -9,6 +9,7 @@ struct dir_entry { #define EXC_FLAG_NODIR 1 #define EXC_FLAG_NOWILDCARD 2 #define EXC_FLAG_ENDSWITH 4 +#define EXC_FLAG_MUSTBEDIR 8 struct exclude_list { int nr; @@ -67,7 +68,7 @@ extern int match_pathspec(const char **pathspec, const char *name, int namelen, extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec); -extern int excluded(struct dir_struct *, const char *); +extern int excluded(struct dir_struct *, const char *, int *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which); diff --git a/environment.c b/environment.c index 18a1c4eec4..3527f1663f 100644 --- a/environment.c +++ b/environment.c @@ -30,11 +30,12 @@ int core_compression_seen; size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT; size_t delta_base_cache_limit = 16 * 1024 * 1024; -char *pager_program; +const char *pager_program; int pager_use_color = 1; -char *editor_program; -char *excludes_file; +const char *editor_program; +const char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ +enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; /* This is set by setup_git_dir_gently() and/or git_default_config() */ diff --git a/fast-import.c b/fast-import.c index a523b171e2..0d3449f2ce 100644 --- a/fast-import.c +++ b/fast-import.c @@ -372,6 +372,8 @@ static void write_branch_report(FILE *rpt, struct branch *b) fputc('\n', rpt); } +static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *); + static void write_crash_report(const char *err) { char *loc = git_path("fast_import_crash_%d", getpid()); @@ -430,12 +432,37 @@ static void write_crash_report(const char *err) write_branch_report(rpt, b); } + if (first_tag) { + struct tag *tg; + fputc('\n', rpt); + fputs("Annotated Tags\n", rpt); + fputs("--------------\n", rpt); + for (tg = first_tag; tg; tg = tg->next_tag) { + fputs(sha1_to_hex(tg->sha1), rpt); + fputc(' ', rpt); + fputs(tg->name, rpt); + fputc('\n', rpt); + } + } + + fputc('\n', rpt); + fputs("Marks\n", rpt); + fputs("-----\n", rpt); + if (mark_file) + fprintf(rpt, " exported to %s\n", mark_file); + else + dump_marks_helper(rpt, 0, marks); + fputc('\n', rpt); fputs("-------------------\n", rpt); fputs("END OF CRASH REPORT\n", rpt); fclose(rpt); } +static void end_packfile(void); +static void unkeep_all_packs(void); +static void dump_marks(void); + static NORETURN void die_nicely(const char *err, va_list params) { static int zombie; @@ -449,6 +476,9 @@ static NORETURN void die_nicely(const char *err, va_list params) if (!zombie) { zombie = 1; write_crash_report(message); + end_packfile(); + unkeep_all_packs(); + dump_marks(); } exit(128); } @@ -1204,6 +1234,8 @@ static void load_tree(struct tree_entry *root) die("Not a tree: %s", sha1_to_hex(sha1)); t->delta_depth = myoe->depth; buf = gfi_unpack_entry(myoe, &size); + if (!buf) + die("Can't load tree %s", sha1_to_hex(sha1)); } else { enum object_type type; buf = read_sha1_file(sha1, &type, &size); diff --git a/git-bisect.sh b/git-bisect.sh index 393fa35584..74715edf0b 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -135,7 +135,7 @@ bisect_write() { *) die "Bad bisect_write argument: $state" ;; esac git update-ref "refs/bisect/$tag" "$rev" - echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG" test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG" } @@ -331,9 +331,9 @@ bisect_visualize() { if test $# = 0 then - case "${DISPLAY+set}" in + case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in '') set git log ;; - set) set gitk ;; + set*) set gitk ;; esac else case "$1" in diff --git a/git-clone.sh b/git-clone.sh index b4e858c388..0d686c3a03 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -409,11 +409,12 @@ else cd "$D" || exit fi -if test -z "$bare" && test -f "$GIT_DIR/REMOTE_HEAD" +if test -z "$bare" then # a non-bare repository is always in separate-remote layout remote_top="refs/remotes/$origin" - head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` + head_sha1= + test ! -r "$GIT_DIR/REMOTE_HEAD" || head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` case "$head_sha1" in 'ref: refs/'*) # Uh-oh, the remote told us (http transport done against @@ -470,9 +471,16 @@ then git config branch."$head_points_at".merge "refs/heads/$head_points_at" ;; '') - # Source had detached HEAD pointing nowhere - git update-ref --no-deref HEAD "$head_sha1" && - rm -f "refs/remotes/$origin/HEAD" + if test -z "$head_sha1" + then + # Source had nonexistent ref in HEAD + echo >&2 "Warning: Remote HEAD refers to nonexistent ref, unable to checkout." + no_checkout=t + else + # Source had detached HEAD pointing nowhere + git update-ref --no-deref HEAD "$head_sha1" && + rm -f "refs/remotes/$origin/HEAD" + fi ;; esac diff --git a/git-compat-util.h b/git-compat-util.h index 46d5e93c72..2a40703c85 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -431,4 +431,10 @@ static inline int strtol_i(char const *s, int base, int *result) return 0; } +#ifdef INTERNAL_QSORT +void git_qsort(void *base, size_t nmemb, size_t size, + int(*compar)(const void *, const void *)); +#define qsort git_qsort +#endif + #endif diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index d2e50c3429..2a8ad1e9f4 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -5,6 +5,7 @@ use Getopt::Std; use File::Temp qw(tempdir); use Data::Dumper; use File::Basename qw(basename dirname); +use File::Spec; our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w); @@ -15,17 +16,15 @@ $opt_h && usage(); die "Need at least one commit identifier!" unless @ARGV; if ($opt_w) { + # Remember where GIT_DIR is before changing to CVS checkout unless ($ENV{GIT_DIR}) { - # Remember where our GIT_DIR is before changing to CVS checkout + # No GIT_DIR set. Figure it out for ourselves my $gd =`git-rev-parse --git-dir`; chomp($gd); - if ($gd eq '.git') { - my $wd = `pwd`; - chomp($wd); - $gd = $wd."/.git" ; - } $ENV{GIT_DIR} = $gd; } + # Make sure GIT_DIR is absolute + $ENV{GIT_DIR} = File::Spec->rel2abs($ENV{GIT_DIR}); if (! -d $opt_w."/CVS" ) { die "$opt_w is not a CVS checkout"; diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 5694978c46..9516242338 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -164,7 +164,7 @@ if ($#ARGV == 0) { our @mergerx = (); if ($opt_m) { - @mergerx = ( qr/\W(?:from|of|merge|merging|merged) (\w+)/i ); + @mergerx = ( qr/\b(?:from|of|merge|merging|merged) (\w+)/i ); } if ($opt_M) { push (@mergerx, qr/$opt_M/); diff --git a/git-filter-branch.sh b/git-filter-branch.sh index ff716cabb0..49e13f0bb1 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -276,10 +276,11 @@ while read commit parents; do eval "$filter_tree" < /dev/null || die "tree filter failed: $filter_tree" - git diff-index -r $commit | cut -f 2- | tr '\012' '\000' | \ - xargs -0 git update-index --add --replace --remove - git ls-files -z --others | \ - xargs -0 git update-index --add --replace --remove + ( + git diff-index -r --name-only $commit + git ls-files --others + ) | + git update-index --add --replace --remove --stdin fi eval "$filter_index" < /dev/null || diff --git a/git-gui/Makefile b/git-gui/Makefile index 34438cdf5c..081d7550a7 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -13,6 +13,7 @@ GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') +uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') SCRIPT_SH = git-gui.sh GITGUI_MAIN := git-gui @@ -93,7 +94,14 @@ endif TCL_PATH ?= tclsh TCLTK_PATH ?= wish -TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app + +ifeq ($(uname_S),Darwin) + TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app + ifeq ($(shell expr "$(uname_R)" : '9\.'),2) + TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish\ Shell.app + endif + TKEXECUTABLE = $(shell basename "$(TKFRAMEWORK)" .app) +endif ifeq ($(findstring $(MAKEFLAGS),s),s) QUIET_GEN = @@ -147,7 +155,7 @@ git-gui: GIT-VERSION-FILE GIT-GUI-VARS echo then >>$@+ && \ echo ' 'echo \'git-gui version '$(GITGUI_VERSION)'\' >>$@+ && \ echo else >>$@+ && \ - echo ' 'exec \''$(libdir_SQ)/Git Gui.app/Contents/MacOS/Wish'\' \ + echo ' 'exec \''$(libdir_SQ)/Git Gui.app/Contents/MacOS/$(subst \,,$(TKEXECUTABLE))'\' \ '"$$0" "$$@"' >>$@+ && \ echo fi >>$@+ && \ chmod +x $@+ && \ @@ -157,14 +165,15 @@ Git\ Gui.app: GIT-VERSION-FILE GIT-GUI-VARS \ macosx/Info.plist \ macosx/git-gui.icns \ macosx/AppMain.tcl \ - $(TKFRAMEWORK)/Contents/MacOS/Wish + $(TKFRAMEWORK)/Contents/MacOS/$(TKEXECUTABLE) $(QUIET_GEN)rm -rf '$@' '$@'+ && \ mkdir -p '$@'+/Contents/MacOS && \ mkdir -p '$@'+/Contents/Resources/Scripts && \ - cp '$(subst ','\'',$(TKFRAMEWORK))/Contents/MacOS/Wish' \ + cp '$(subst ','\'',$(subst \,,$(TKFRAMEWORK)/Contents/MacOS/$(TKEXECUTABLE)))' \ '$@'+/Contents/MacOS && \ cp macosx/git-gui.icns '$@'+/Contents/Resources && \ sed -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \ + -e 's/@@GITGUI_TKEXECUTABLE@@/$(TKEXECUTABLE)/g' \ macosx/Info.plist \ >'$@'+/Contents/Info.plist && \ sed -e 's|@@gitexecdir@@|$(gitexecdir_SQ)|' \ diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index f42e461fd4..5d65272e26 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -612,6 +612,7 @@ set default_config(gui.pruneduringfetch) false set default_config(gui.trustmtime) false set default_config(gui.diffcontext) 5 set default_config(gui.newbranchtemplate) {} +set default_config(gui.spellingdictionary) {} set default_config(gui.fontui) [font configure font_ui] set default_config(gui.fontdiff) [font configure font_diff] set font_descs { @@ -1683,6 +1684,7 @@ set is_quitting 0 proc do_quit {} { global ui_comm is_quitting repo_config commit_type global GITGUI_BCK_exists GITGUI_BCK_i + global ui_comm_spell if {$is_quitting} return set is_quitting 1 @@ -1710,6 +1712,12 @@ proc do_quit {} { } } + # -- Cancel our spellchecker if its running. + # + if {[info exists ui_comm_spell]} { + $ui_comm_spell stop + } + # -- Remove our editor backup, its not needed. # after cancel $GITGUI_BCK_i @@ -2454,7 +2462,7 @@ $ctxm add separator $ctxm add command \ -label [mc "Sign Off"] \ -command do_signoff -bind_button3 $ui_comm "tk_popup $ctxm %X %Y" +set ui_comm_ctxm $ctxm # -- Diff Header # @@ -2857,6 +2865,30 @@ if {[winfo exists $ui_comm]} { } backup_commit_buffer + + # -- If the user has aspell available we can drive it + # in pipe mode to spellcheck the commit message. + # + set spell_cmd [list |] + set spell_dict [get_config gui.spellingdictionary] + lappend spell_cmd aspell + if {$spell_dict ne {}} { + lappend spell_cmd --master=$spell_dict + } + lappend spell_cmd --mode=none + lappend spell_cmd --encoding=utf-8 + lappend spell_cmd pipe + if {$spell_dict eq {none} + || [catch {set spell_fd [open $spell_cmd r+]} spell_err]} { + bind_button3 $ui_comm [list tk_popup $ui_comm_ctxm %X %Y] + } else { + set ui_comm_spell [spellcheck::init \ + $spell_fd \ + $ui_comm \ + $ui_comm_ctxm \ + ] + } + unset -nocomplain spell_cmd spell_fd spell_err spell_dict } lock_index begin-read diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl index 719fc547b3..47be8eb97a 100644 --- a/git-gui/lib/about.tcl +++ b/git-gui/lib/about.tcl @@ -4,6 +4,7 @@ proc do_about {} { global appvers copyright oguilib global tcl_patchLevel tk_patchLevel + global ui_comm_spell set w .about_dialog toplevel $w @@ -40,6 +41,10 @@ proc do_about {} { append v "Tcl version $tcl_patchLevel" append v ", Tk version $tk_patchLevel" } + if {[info exists ui_comm_spell]} { + append v "\n" + append v [$ui_comm_spell version] + } set d {} append d "git wrapper: $::_git\n" diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl index f243966924..6e1411711b 100644 --- a/git-gui/lib/checkout_op.tcl +++ b/git-gui/lib/checkout_op.tcl @@ -280,7 +280,7 @@ The rescan will be automatically started now. } elseif {[is_config_true gui.trustmtime]} { _readtree $this } else { - ui_status {Refreshing file status...} + ui_status [mc "Refreshing file status..."] set fd [git_read update-index \ -q \ --unmerged \ @@ -320,7 +320,7 @@ method _readtree {} { set readtree_d {} $::main_status start \ [mc "Updating working directory to '%s'..." [_name $this]] \ - {files checked out} + [mc "files checked out"] set fd [git_read --stderr read-tree \ -m \ @@ -447,7 +447,7 @@ If you wanted to be on a branch, create one now starting from 'This Detached Che } else { repository_state commit_type HEAD MERGE_HEAD set PARENT $HEAD - ui_status "Checked out '$name'." + ui_status [mc "Checked out '%s'." $name] } delete_this } diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 947b201c32..40a7103557 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -218,7 +218,7 @@ A good commit message has the following format: return } - ui_status {Calling pre-commit hook...} + ui_status [mc "Calling pre-commit hook..."] set pch_error {} fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} fileevent $fd_ph readable \ @@ -233,7 +233,7 @@ proc commit_prehook_wait {fd_ph curHEAD msg_p} { if {[eof $fd_ph]} { if {[catch {close $fd_ph}]} { catch {file delete $msg_p} - ui_status {Commit declined by pre-commit hook.} + ui_status [mc "Commit declined by pre-commit hook."] hook_failed_popup pre-commit $pch_error unlock_index } else { @@ -256,7 +256,7 @@ proc commit_commitmsg {curHEAD msg_p} { return } - ui_status {Calling commit-msg hook...} + ui_status [mc "Calling commit-msg hook..."] set pch_error {} fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} fileevent $fd_ph readable \ @@ -271,7 +271,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} { if {[eof $fd_ph]} { if {[catch {close $fd_ph}]} { catch {file delete $msg_p} - ui_status {Commit declined by commit-msg hook.} + ui_status [mc "Commit declined by commit-msg hook."] hook_failed_popup commit-msg $pch_error unlock_index } else { @@ -284,7 +284,7 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} { } proc commit_writetree {curHEAD msg_p} { - ui_status {Committing changes...} + ui_status [mc "Committing changes..."] set fd_wt [git_read write-tree] fileevent $fd_wt readable \ [list commit_committree $fd_wt $curHEAD $msg_p] @@ -301,7 +301,7 @@ proc commit_committree {fd_wt curHEAD msg_p} { 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.} + ui_status [mc "Commit failed."] unlock_index return } @@ -345,7 +345,7 @@ A rescan will be automatically started now. 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.} + ui_status [mc "Commit failed."] unlock_index return } @@ -365,7 +365,7 @@ A rescan will be automatically started now. } err]} { catch {file delete $msg_p} error_popup [strcat [mc "update-ref failed:"] "\n\n$err"] - ui_status {Commit failed.} + ui_status [mc "Commit failed."] unlock_index return } diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index 30a244cc17..3c1fce7475 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -310,7 +310,7 @@ proc add_helper {txt paths} { update_index \ $txt \ $pathList \ - [concat $after {ui_status {Ready to commit.}}] + [concat $after {ui_status [mc "Ready to commit."]}] } } diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl index 63e14279c1..cc26b07808 100644 --- a/git-gui/lib/merge.tcl +++ b/git-gui/lib/merge.tcl @@ -116,8 +116,7 @@ method _start {} { lappend cmd HEAD lappend cmd $name - set msg [mc "Merging %s and %s" $current_branch $stitle] - ui_status "$msg..." + ui_status [mc "Merging %s and %s..." $current_branch $stitle] set cons [console::new [mc "Merge"] "merge $stitle"] console::exec $cons $cmd [cb _finish $cons] @@ -236,7 +235,7 @@ Continue with resetting the current changes?"] set fd [git_read --stderr read-tree --reset -u -v HEAD] fconfigure $fd -blocking 0 -translation binary fileevent $fd readable [namespace code [list _reset_wait $fd]] - $::main_status start [mc "Aborting"] {files reset} + $::main_status start [mc "Aborting"] [mc "files reset"] } else { unlock_index } diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index f812e5e89a..ea80df0092 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -5,6 +5,7 @@ proc save_config {} { global default_config font_descs global repo_config global_config global repo_config_new global_config_new + global ui_comm_spell foreach option $font_descs { set name [lindex $option 0] @@ -52,11 +53,23 @@ proc save_config {} { set repo_config($name) $value } } + + if {[info exists repo_config(gui.spellingdictionary)]} { + set value $repo_config(gui.spellingdictionary) + if {$value eq {none}} { + if {[info exists ui_comm_spell]} { + $ui_comm_spell stop + } + } elseif {[info exists ui_comm_spell]} { + $ui_comm_spell lang $value + } + } } proc do_options {} { global repo_config global_config font_descs global repo_config_new global_config_new + global ui_comm_spell array unset repo_config_new array unset global_config_new @@ -159,6 +172,32 @@ proc do_options {} { } } + set all_dicts [linsert \ + [spellcheck::available_langs] \ + 0 \ + none] + incr optid + foreach f {repo global} { + if {![info exists ${f}_config_new(gui.spellingdictionary)]} { + if {[info exists ui_comm_spell]} { + set value [$ui_comm_spell lang] + } else { + set value none + } + set ${f}_config_new(gui.spellingdictionary) $value + } + + frame $w.$f.$optid + label $w.$f.$optid.l -text [mc "Spelling Dictionary:"] + eval tk_optionMenu $w.$f.$optid.v \ + ${f}_config_new(gui.spellingdictionary) \ + $all_dicts + pack $w.$f.$optid.l -side left -anchor w -fill x + pack $w.$f.$optid.v -side right -anchor e -padx 5 + pack $w.$f.$optid -side top -anchor w -fill x + } + unset all_dicts + set all_fonts [lsort [font families]] foreach option $font_descs { set name [lindex $option 0] diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl new file mode 100644 index 0000000000..7f018e4009 --- /dev/null +++ b/git-gui/lib/spellcheck.tcl @@ -0,0 +1,363 @@ +# git-gui spellchecking support through aspell +# Copyright (C) 2008 Shawn Pearce + +class spellcheck { + +field s_fd {} ; # pipe to aspell +field s_version ; # aspell version string +field s_lang ; # current language code + +field w_text ; # text widget we are spelling +field w_menu ; # context menu for the widget +field s_menuidx 0 ; # last index of insertion into $w_menu + +field s_i ; # timer registration for _run callbacks +field s_clear 0 ; # did we erase mispelled tags yet? +field s_seen [list] ; # lines last seen from $w_text in _run +field s_checked [list] ; # lines already checked +field s_pending [list] ; # [$line $data] sent to aspell +field s_suggest ; # array, list of suggestions, keyed by misspelling + +constructor init {pipe_fd ui_text ui_menu} { + set w_text $ui_text + set w_menu $ui_menu + + _connect $this $pipe_fd + return $this +} + +method _connect {pipe_fd} { + fconfigure $pipe_fd \ + -encoding utf-8 \ + -eofchar {} \ + -translation lf + + if {[gets $pipe_fd s_version] <= 0} { + close $pipe_fd + error [mc "Not connected to aspell"] + } + if {{@(#) } ne [string range $s_version 0 4]} { + close $pipe_fd + error [strcat [mc "Unrecognized aspell version"] ": $s_version"] + } + set s_version [string range $s_version 5 end] + + puts $pipe_fd ! ; # enable terse mode + puts $pipe_fd {$$cr master} ; # fetch the language + flush $pipe_fd + + gets $pipe_fd s_lang + regexp {[/\\]([^/\\]+)\.[^\.]+$} $s_lang _ s_lang + + if {$::default_config(gui.spellingdictionary) eq {} + && [get_config gui.spellingdictionary] eq {}} { + set ::default_config(gui.spellingdictionary) $s_lang + } + + if {$s_fd ne {}} { + catch {close $s_fd} + } + set s_fd $pipe_fd + + fconfigure $s_fd -blocking 0 + fileevent $s_fd readable [cb _read] + + $w_text tag conf misspelled \ + -foreground red \ + -underline 1 + bind_button3 $w_text [cb _popup_suggest %X %Y @%x,%y] + + array unset s_suggest + set s_seen [list] + set s_checked [list] + set s_pending [list] + _run $this +} + +method lang {{n {}}} { + if {$n ne {} && $s_lang ne $n} { + set spell_cmd [list |] + lappend spell_cmd aspell + lappend spell_cmd --master=$n + lappend spell_cmd --mode=none + lappend spell_cmd --encoding=UTF-8 + lappend spell_cmd pipe + _connect $this [open $spell_cmd r+] + } + return $s_lang +} + +method version {} { + return "$s_version, $s_lang" +} + +method stop {} { + while {$s_menuidx > 0} { + $w_menu delete 0 + incr s_menuidx -1 + } + $w_text tag delete misspelled + + catch {close $s_fd} + catch {after cancel $s_i} + set s_fd {} + set s_i {} + set s_lang {} +} + +method _popup_suggest {X Y pos} { + while {$s_menuidx > 0} { + $w_menu delete 0 + incr s_menuidx -1 + } + + set b_loc [$w_text index "$pos wordstart"] + set e_loc [_wordend $this $b_loc] + set orig [$w_text get $b_loc $e_loc] + set tags [$w_text tag names $b_loc] + + if {[lsearch -exact $tags misspelled] >= 0} { + if {[info exists s_suggest($orig)]} { + set cnt 0 + foreach s $s_suggest($orig) { + if {$cnt < 5} { + $w_menu insert $s_menuidx command \ + -label $s \ + -command [cb _replace $b_loc $e_loc $s] + incr s_menuidx + incr cnt + } else { + break + } + } + } else { + $w_menu insert $s_menuidx command \ + -label [mc "No Suggestions"] \ + -state disabled + incr s_menuidx + } + $w_menu insert $s_menuidx separator + incr s_menuidx + } + + $w_text mark set saved-insert insert + tk_popup $w_menu $X $Y +} + +method _replace {b_loc e_loc word} { + $w_text configure -autoseparators 0 + $w_text edit separator + + $w_text delete $b_loc $e_loc + $w_text insert $b_loc $word + + $w_text edit separator + $w_text configure -autoseparators 1 + $w_text mark set insert saved-insert +} + +method _restart_timer {} { + set s_i [after 300 [cb _run]] +} + +proc _match_length {max_line arr_name} { + upvar $arr_name a + + if {[llength $a] > $max_line} { + set a [lrange $a 0 $max_line] + } + while {[llength $a] <= $max_line} { + lappend a {} + } +} + +method _wordend {pos} { + set pos [$w_text index "$pos wordend"] + set tags [$w_text tag names $pos] + while {[lsearch -exact $tags misspelled] >= 0} { + set pos [$w_text index "$pos +1c"] + set tags [$w_text tag names $pos] + } + return $pos +} + +method _run {} { + set cur_pos [$w_text index {insert -1c}] + set cur_line [lindex [split $cur_pos .] 0] + set max_line [lindex [split [$w_text index end] .] 0] + _match_length $max_line s_seen + _match_length $max_line s_checked + + # Nothing in the message buffer? Nothing to spellcheck. + # + if {$cur_line == 1 + && $max_line == 2 + && [$w_text get 1.0 end] eq "\n"} { + array unset s_suggest + _restart_timer $this + return + } + + set active 0 + for {set n 1} {$n <= $max_line} {incr n} { + set s [$w_text get "$n.0" "$n.end"] + + # Don't spellcheck the current line unless we are at + # a word boundary. The user might be typing on it. + # + if {$n == $cur_line + && ![regexp {^\W$} [$w_text get $cur_pos insert]]} { + + # If the current word is mispelled remove the tag + # but force a spellcheck later. + # + set tags [$w_text tag names $cur_pos] + if {[lsearch -exact $tags misspelled] >= 0} { + $w_text tag remove misspelled \ + "$cur_pos wordstart" \ + [_wordend $this $cur_pos] + lset s_seen $n $s + lset s_checked $n {} + } + + continue + } + + if {[lindex $s_seen $n] eq $s + && [lindex $s_checked $n] ne $s} { + # Don't send empty lines to Aspell it doesn't check them. + # + if {$s eq {}} { + lset s_checked $n $s + continue + } + + # Don't send typical s-b-o lines as the emails are + # almost always misspelled according to Aspell. + # + if {[regexp -nocase {^[a-z-]+-by:.*<.*@.*>$} $s]} { + $w_text tag remove misspelled "$n.0" "$n.end" + lset s_checked $n $s + continue + } + + puts $s_fd ^$s + lappend s_pending [list $n $s] + set active 1 + } else { + # Delay until another idle loop to make sure we don't + # spellcheck lines the user is actively changing. + # + lset s_seen $n $s + } + } + + if {$active} { + set s_clear 1 + flush $s_fd + } else { + _restart_timer $this + } +} + +method _read {} { + while {[gets $s_fd line] >= 0} { + set lineno [lindex $s_pending 0 0] + + if {$s_clear} { + $w_text tag remove misspelled "$lineno.0" "$lineno.end" + set s_clear 0 + } + + if {$line eq {}} { + lset s_checked $lineno [lindex $s_pending 0 1] + set s_pending [lrange $s_pending 1 end] + set s_clear 1 + continue + } + + set sugg [list] + switch -- [string range $line 0 1] { + {& } { + set line [split [string range $line 2 end] :] + set info [split [lindex $line 0] { }] + set orig [lindex $info 0] + set offs [lindex $info 2] + foreach s [split [lindex $line 1] ,] { + lappend sugg [string range $s 1 end] + } + } + {# } { + set info [split [string range $line 2 end] { }] + set orig [lindex $info 0] + set offs [lindex $info 1] + } + default { + puts stderr "<spell> $line" + continue + } + } + + incr offs -1 + set b_loc "$lineno.$offs" + set e_loc [$w_text index "$lineno.$offs wordend"] + set curr [$w_text get $b_loc $e_loc] + + # At least for English curr = "bob", orig = "bob's" + # so Tk didn't include the 's but Aspell did. We + # try to round out the word. + # + while {$curr ne $orig + && [string equal -length [string length $curr] $curr $orig]} { + set n_loc [$w_text index "$e_loc +1c"] + set n_curr [$w_text get $b_loc $n_loc] + if {$n_curr eq $curr} { + break + } + set curr $n_curr + set e_loc $n_loc + } + + if {$curr eq $orig} { + $w_text tag add misspelled $b_loc $e_loc + if {[llength $sugg] > 0} { + set s_suggest($orig) $sugg + } else { + unset -nocomplain s_suggest($orig) + } + } else { + unset -nocomplain s_suggest($orig) + } + } + + fconfigure $s_fd -block 1 + if {[eof $s_fd]} { + if {![catch {close $s_fd} err]} { + set err [mc "unexpected eof from aspell"] + } + catch {after cancel $s_i} + $w_text tag remove misspelled 1.0 end + error_popup [strcat "Spell Checker Failed" "\n\n" $err] + return + } + fconfigure $s_fd -block 0 + + if {[llength $s_pending] == 0} { + _restart_timer $this + } +} + +proc available_langs {} { + set langs [list] + catch { + set fd [open [list | aspell dump dicts] r] + while {[gets $fd line] >= 0} { + if {$line eq {}} continue + lappend langs $line + } + close $fd + } + return $langs +} + +} diff --git a/git-gui/macosx/Info.plist b/git-gui/macosx/Info.plist index 99913ec57a..b3bf15fa1c 100644 --- a/git-gui/macosx/Info.plist +++ b/git-gui/macosx/Info.plist @@ -5,7 +5,7 @@ <key>CFBundleDevelopmentRegion</key> <string>English</string> <key>CFBundleExecutable</key> - <string>Wish</string> + <string>@@GITGUI_TKEXECUTABLE@@</string> <key>CFBundleGetInfoString</key> <string>Git Gui @@GITGUI_VERSION@@ © 2006-2007 Shawn Pearce, et. al.</string> <key>CFBundleIconFile</key> diff --git a/git-gui/po/de.po b/git-gui/po/de.po index 2dfe07e06f..d7c38f9c73 100644 --- a/git-gui/po/de.po +++ b/git-gui/po/de.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: git-gui\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-11-24 10:36+0100\n" -"PO-Revision-Date: 2008-01-15 20:33+0100\n" +"POT-Creation-Date: 2008-02-02 10:14+0100\n" +"PO-Revision-Date: 2008-02-02 10:18+0100\n" "Last-Translator: Christian Stimming <stimming@tuhh.de>\n" "Language-Team: German\n" "MIME-Version: 1.0\n" @@ -343,7 +343,9 @@ msgstr "Online-Dokumentation" #: git-gui.sh:2201 #, tcl-format msgid "fatal: cannot stat path %s: No such file or directory" -msgstr "Fehler: Verzeichnis »%s« kann nicht gelesen werden: Datei oder Verzeichnis nicht gefunden" +msgstr "" +"Fehler: Verzeichnis »%s« kann nicht gelesen werden: Datei oder Verzeichnis " +"nicht gefunden" #: git-gui.sh:2234 msgid "Current Branch:" @@ -371,19 +373,19 @@ msgstr "Erste Versionsbeschreibung:" #: git-gui.sh:2370 msgid "Amended Commit Message:" -msgstr "Nachgebesserte Versionsbeschreibung:" +msgstr "Nachgebesserte Beschreibung:" #: git-gui.sh:2371 msgid "Amended Initial Commit Message:" -msgstr "Nachgebesserte erste Versionsbeschreibung:" +msgstr "Nachgebesserte erste Beschreibung:" #: git-gui.sh:2372 msgid "Amended Merge Commit Message:" -msgstr "Nachgebesserte Zusammenführungs-Versionsbeschreibung:" +msgstr "Nachgebesserte Zusammenführungs-Beschreibung:" #: git-gui.sh:2373 msgid "Merge Commit Message:" -msgstr "Zusammenführungs-Versionsbeschreibung:" +msgstr "Zusammenführungs-Beschreibung:" #: git-gui.sh:2374 msgid "Commit Message:" @@ -397,31 +399,31 @@ msgstr "Alle kopieren" msgid "File:" msgstr "Datei:" -#: git-gui.sh:2545 -msgid "Refresh" -msgstr "Aktualisieren" - -#: git-gui.sh:2566 +#: git-gui.sh:2573 msgid "Apply/Reverse Hunk" msgstr "Kontext anwenden/umkehren" -#: git-gui.sh:2572 -msgid "Decrease Font Size" -msgstr "Schriftgröße verkleinern" - -#: git-gui.sh:2576 -msgid "Increase Font Size" -msgstr "Schriftgröße vergrößern" - -#: git-gui.sh:2581 +#: git-gui.sh:2579 msgid "Show Less Context" msgstr "Weniger Zeilen anzeigen" -#: git-gui.sh:2588 +#: git-gui.sh:2586 msgid "Show More Context" msgstr "Mehr Zeilen anzeigen" -#: git-gui.sh:2602 +#: git-gui.sh:2594 +msgid "Refresh" +msgstr "Aktualisieren" + +#: git-gui.sh:2615 +msgid "Decrease Font Size" +msgstr "Schriftgröße verkleinern" + +#: git-gui.sh:2619 +msgid "Increase Font Size" +msgstr "Schriftgröße vergrößern" + +#: git-gui.sh:2630 msgid "Unstage Hunk From Commit" msgstr "Kontext aus Bereitstellung herausnehmen" @@ -542,7 +544,7 @@ msgstr "Kopiert oder verschoben durch:" #: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 msgid "Checkout Branch" -msgstr "Zweig umstellen" +msgstr "Auf Zweig umstellen" #: lib/branch_checkout.tcl:23 msgid "Checkout" @@ -805,11 +807,15 @@ msgstr "" msgid "Updating working directory to '%s'..." msgstr "Arbeitskopie umstellen auf »%s«..." +#: lib/checkout_op.tcl:323 +msgid "files checked out" +msgstr "Dateien aktualisiert" + #: lib/checkout_op.tcl:353 #, tcl-format msgid "Aborted checkout of '%s' (file level merging is required)." msgstr "" -"Zweig umstellen von »%s« abgebrochen (Zusammenführen der Dateien ist " +"Auf Zweig »%s« umstellen abgebrochen (Zusammenführen der Dateien ist " "notwendig)." #: lib/checkout_op.tcl:354 @@ -1069,15 +1075,21 @@ msgstr "Für Objekt konnte kein Hardlink erstellt werden: %s" #: lib/choose_repository.tcl:847 msgid "Cannot fetch branches and objects. See console output for details." -msgstr "Zweige und Objekte konnten nicht angefordert werden. Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben." +msgstr "" +"Zweige und Objekte konnten nicht angefordert werden. Kontrollieren Sie die " +"Ausgaben auf der Konsole für weitere Angaben." #: lib/choose_repository.tcl:858 msgid "Cannot fetch tags. See console output for details." -msgstr "Markierungen konnten nicht angefordert werden. Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben." +msgstr "" +"Markierungen konnten nicht angefordert werden. Kontrollieren Sie die " +"Ausgaben auf der Konsole für weitere Angaben." #: lib/choose_repository.tcl:882 msgid "Cannot determine HEAD. See console output for details." -msgstr "Die Zweigspitze (HEAD) konnte nicht gefunden werden. Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben." +msgstr "" +"Die Zweigspitze (HEAD) konnte nicht gefunden werden. Kontrollieren Sie die " +"Ausgaben auf der Konsole für weitere Angaben." #: lib/choose_repository.tcl:891 #, tcl-format @@ -1273,11 +1285,40 @@ msgstr "" "\n" "- Rest: Eine ausführliche Beschreibung, warum diese Änderung hilfreich ist.\n" -#: lib/commit.tcl:257 +#: lib/commit.tcl:207 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht." + +#: lib/commit.tcl:221 +msgid "Calling pre-commit hook..." +msgstr "" + +#: lib/commit.tcl:236 +msgid "Commit declined by pre-commit hook." +msgstr "" + +#: lib/commit.tcl:259 +msgid "Calling commit-msg hook..." +msgstr "" + +#: lib/commit.tcl:274 +msgid "Commit declined by commit-msg hook." +msgstr "" + +#: lib/commit.tcl:287 +msgid "Committing changes..." +msgstr "Änderungen eintragen..." + +#: lib/commit.tcl:303 msgid "write-tree failed:" msgstr "write-tree fehlgeschlagen:" -#: lib/commit.tcl:275 +#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368 +msgid "Commit failed." +msgstr "Eintragen fehlgeschlagen." + +#: lib/commit.tcl:321 #, tcl-format msgid "Commit %s appears to be corrupt" msgstr "Version »%s« scheint beschädigt zu sein" @@ -1301,12 +1342,7 @@ msgstr "" msgid "No changes to commit." msgstr "Keine Änderungen, die eingetragen werden können." -#: lib/commit.tcl:303 -#, tcl-format -msgid "warning: Tcl does not support encoding '%s'." -msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht." - -#: lib/commit.tcl:317 +#: lib/commit.tcl:347 msgid "commit-tree failed:" msgstr "commit-tree fehlgeschlagen:" @@ -1440,7 +1476,8 @@ msgstr "Fehler beim Laden des Vergleichs:" #: lib/diff.tcl:302 msgid "Failed to unstage selected hunk." -msgstr "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung." +msgstr "" +"Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung." #: lib/diff.tcl:309 msgid "Failed to stage selected hunk." @@ -1471,7 +1508,10 @@ msgstr "Fehler in Bereitstellung" msgid "" "Updating the Git index failed. A rescan will be automatically started to " "resynchronize git-gui." -msgstr "Das Aktualisieren der Git-Bereitstellung ist fehlgeschlagen. Eine allgemeine Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu synchronisieren." +msgstr "" +"Das Aktualisieren der Git-Bereitstellung ist fehlgeschlagen. Eine allgemeine " +"Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu " +"synchronisieren." #: lib/index.tcl:27 msgid "Continue" @@ -1486,6 +1526,10 @@ msgstr "Bereitstellung freigeben" msgid "Unstaging %s from commit" msgstr "Datei »%s« aus der Bereitstellung herausnehmen" +#: lib/index.tcl:313 +msgid "Ready to commit." +msgstr "Bereit zum Eintragen." + #: lib/index.tcl:326 #, tcl-format msgid "Adding %s" @@ -1503,7 +1547,8 @@ msgstr "Änderungen in den gewählten %i Dateien verwerfen?" #: lib/index.tcl:389 msgid "Any unstaged changes will be permanently lost by the revert." -msgstr "Alle nicht bereitgestellten Änderungen werden beim Verwerfen verloren gehen." +msgstr "" +"Alle nicht bereitgestellten Änderungen werden beim Verwerfen verloren gehen." #: lib/index.tcl:392 msgid "Do Nothing" @@ -1641,7 +1686,11 @@ msgstr "" msgid "Aborting" msgstr "Abbruch" -#: lib/merge.tcl:266 +#: lib/merge.tcl:238 +msgid "files reset" +msgstr "Dateien zurückgesetzt" + +#: lib/merge.tcl:265 msgid "Abort failed." msgstr "Abbruch fehlgeschlagen." diff --git a/git-gui/po/git-gui.pot b/git-gui/po/git-gui.pot index dfa48ae263..3f139da6c7 100644 --- a/git-gui/po/git-gui.pot +++ b/git-gui/po/git-gui.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-11-24 10:36+0100\n" +"POT-Creation-Date: 2008-02-02 10:14+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -386,31 +386,31 @@ msgstr "" msgid "File:" msgstr "" -#: git-gui.sh:2545 -msgid "Refresh" +#: git-gui.sh:2573 +msgid "Apply/Reverse Hunk" msgstr "" -#: git-gui.sh:2566 -msgid "Apply/Reverse Hunk" +#: git-gui.sh:2579 +msgid "Show Less Context" msgstr "" -#: git-gui.sh:2572 -msgid "Decrease Font Size" +#: git-gui.sh:2586 +msgid "Show More Context" msgstr "" -#: git-gui.sh:2576 -msgid "Increase Font Size" +#: git-gui.sh:2594 +msgid "Refresh" msgstr "" -#: git-gui.sh:2581 -msgid "Show Less Context" +#: git-gui.sh:2615 +msgid "Decrease Font Size" msgstr "" -#: git-gui.sh:2588 -msgid "Show More Context" +#: git-gui.sh:2619 +msgid "Increase Font Size" msgstr "" -#: git-gui.sh:2602 +#: git-gui.sh:2630 msgid "Unstage Hunk From Commit" msgstr "" @@ -766,6 +766,10 @@ msgstr "" msgid "Updating working directory to '%s'..." msgstr "" +#: lib/checkout_op.tcl:323 +msgid "files checked out" +msgstr "" + #: lib/checkout_op.tcl:353 #, tcl-format msgid "Aborted checkout of '%s' (file level merging is required)." @@ -1182,11 +1186,40 @@ msgid "" "- Remaining lines: Describe why this change is good.\n" msgstr "" -#: lib/commit.tcl:257 +#: lib/commit.tcl:207 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "" + +#: lib/commit.tcl:221 +msgid "Calling pre-commit hook..." +msgstr "" + +#: lib/commit.tcl:236 +msgid "Commit declined by pre-commit hook." +msgstr "" + +#: lib/commit.tcl:259 +msgid "Calling commit-msg hook..." +msgstr "" + +#: lib/commit.tcl:274 +msgid "Commit declined by commit-msg hook." +msgstr "" + +#: lib/commit.tcl:287 +msgid "Committing changes..." +msgstr "" + +#: lib/commit.tcl:303 msgid "write-tree failed:" msgstr "" -#: lib/commit.tcl:275 +#: lib/commit.tcl:304 lib/commit.tcl:348 lib/commit.tcl:368 +msgid "Commit failed." +msgstr "" + +#: lib/commit.tcl:321 #, tcl-format msgid "Commit %s appears to be corrupt" msgstr "" @@ -1204,12 +1237,7 @@ msgstr "" msgid "No changes to commit." msgstr "" -#: lib/commit.tcl:303 -#, tcl-format -msgid "warning: Tcl does not support encoding '%s'." -msgstr "" - -#: lib/commit.tcl:317 +#: lib/commit.tcl:347 msgid "commit-tree failed:" msgstr "" @@ -1373,6 +1401,10 @@ msgstr "" msgid "Unstaging %s from commit" msgstr "" +#: lib/index.tcl:313 +msgid "Ready to commit." +msgstr "" + #: lib/index.tcl:326 #, tcl-format msgid "Adding %s" @@ -1491,7 +1523,11 @@ msgstr "" msgid "Aborting" msgstr "" -#: lib/merge.tcl:266 +#: lib/merge.tcl:238 +msgid "files reset" +msgstr "" + +#: lib/merge.tcl:265 msgid "Abort failed." msgstr "" diff --git a/git-instaweb.sh b/git-instaweb.sh index 3e4452bc4b..6f91c8f845 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -24,8 +24,6 @@ restart restart the web server fqgitdir="$GIT_DIR" local="`git config --bool --get instaweb.local`" httpd="`git config --get instaweb.httpd`" -browser="`git config --get instaweb.browser`" -test -z "$browser" && browser="`git config --get web.browser`" port=`git config --get instaweb.port` module_path="`git config --get instaweb.modulepath`" @@ -36,9 +34,6 @@ conf="$GIT_DIR/gitweb/httpd.conf" # if installed, it doesn't need further configuration (module_path) test -z "$httpd" && httpd='lighttpd -f' -# probably the most popular browser among gitweb users -test -z "$browser" && browser='firefox' - # any untaken local port will do... test -z "$port" && port=1234 @@ -274,14 +269,11 @@ 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" && { - init_browser_path "$browser" - "$browser_path" $url -} || echo $url + +if test -n "$browser"; then + git web--browse -b "$browser" $url || echo $url +else + git web--browse -c "instaweb.browser" $url || echo $url +fi diff --git a/git-send-email.perl b/git-send-email.perl index 59601e36e8..ccb87a2d55 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -475,7 +475,7 @@ if ($thread && !defined $initial_reply_to && $prompting) { $initial_reply_to = $_; } -if (defined $initial_reply_to && $_ ne "") { +if (defined $initial_reply_to) { $initial_reply_to =~ s/^\s*<?/</; $initial_reply_to =~ s/>?\s*$/>/; } diff --git a/git-sh-setup.sh b/git-sh-setup.sh index aae14090bd..a44b1c74a3 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -119,7 +119,7 @@ get_author_ident_from_commit () { } ' encoding=$(git config i18n.commitencoding || echo UTF-8) - git show -s --pretty=raw --encoding="$encoding" "$1" | + git show -s --pretty=raw --encoding="$encoding" "$1" -- | LANG=C LC_ALL=C sed -ne "$pick_author_script" } @@ -127,20 +127,14 @@ get_author_ident_from_commit () { # if we require to be in a git repository. if test -z "$NONGIT_OK" then + GIT_DIR=$(git rev-parse --git-dir) || exit if [ -z "$SUBDIRECTORY_OK" ] then - : ${GIT_DIR=.git} test -z "$(git rev-parse --show-cdup)" || { exit=$? echo >&2 "You need to run this command from the toplevel of the working tree." exit $exit } - else - GIT_DIR=$(git rev-parse --git-dir) || { - exit=$? - echo >&2 "Failed to find a valid git directory." - exit $exit - } fi test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || { echo >&2 "Unable to determine absolute path of git directory" diff --git a/git-help--browse.sh b/git-web--browse.sh index 10b0a36a3d..1023b90859 100755 --- a/git-help--browse.sh +++ b/git-web--browse.sh @@ -16,21 +16,16 @@ # git maintainer. # -USAGE='[--browser=browser|--tool=browser] [cmd to display] ...' +USAGE='[--browser=browser|--tool=browser] [--config=conf.var] url/file ...' # This must be capable of running outside of git directory, so # the vanilla git-sh-setup should not be used. NONGIT_OK=Yes . git-sh-setup -# Install data. -html_dir="@@HTMLDIR@@" - -test -f "$html_dir/git.html" || die "No documentation directory found." - valid_tool() { case "$1" in - firefox | iceweasel | konqueror | w3m | links | lynx | dillo) + firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open) ;; # happy *) return 1 @@ -39,8 +34,8 @@ valid_tool() { } init_browser_path() { - browser_path=`git config browser.$1.path` - test -z "$browser_path" && browser_path=$1 + browser_path=$(git config "browser.$1.path") + test -z "$browser_path" && browser_path="$1" } while test $# != 0 @@ -58,6 +53,18 @@ do shift ;; esac ;; + -c|--config*) + case "$#,$1" in + *,*=*) + conf=`expr "z$1" : 'z-[^=]*=\(.*\)'` + ;; + 1,*) + usage ;; + *) + conf="$2" + shift ;; + esac + ;; --) break ;; @@ -71,17 +78,20 @@ do shift done +test $# = 0 && usage + if test -z "$browser" then - for opt in "help.browser" "web.browser" + for opt in "$conf" "web.browser" do + test -z "$opt" && continue browser="`git config $opt`" test -z "$browser" || break done if test -n "$browser" && ! valid_tool "$browser"; then - echo >&2 "git config option $opt set to unknown browser: $browser" - echo >&2 "Resetting to default..." - unset browser + echo >&2 "git config option $opt set to unknown browser: $browser" + echo >&2 "Resetting to default..." + unset browser fi fi @@ -94,6 +104,10 @@ if test -z "$browser" ; then else browser_candidates="w3m links lynx" fi + # SECURITYSESSIONID indicates an OS X GUI login session + if test -n "$SECURITYSESSIONID"; then + browser_candidates="open $browser_candidates" + fi for i in $browser_candidates; do init_browser_path $i @@ -113,16 +127,13 @@ else fi fi -pages=$(for p in "$@"; do echo "$html_dir/$p.html" ; done) -test -z "$pages" && pages="$html_dir/git.html" - case "$browser" in firefox|iceweasel) # Check version because firefox < 2.0 does not support "-new-tab". vers=$(expr "$($browser_path -version)" : '.* \([0-9][0-9]*\)\..*') NEWTAB='-new-tab' test "$vers" -lt 2 && NEWTAB='' - nohup "$browser_path" $NEWTAB $pages & + "$browser_path" $NEWTAB "$@" & ;; konqueror) case "$(basename "$browser_path")" in @@ -130,20 +141,20 @@ case "$browser" in # It's simpler to use kfmclient to open a new tab in konqueror. browser_path="$(echo "$browser_path" | sed -e 's/konqueror$/kfmclient/')" type "$browser_path" > /dev/null 2>&1 || die "No '$browser_path' found." - eval "$browser_path" newTab $pages + eval "$browser_path" newTab "$@" ;; kfmclient) - eval "$browser_path" newTab $pages + eval "$browser_path" newTab "$@" ;; *) - nohup "$browser_path" $pages & + "$browser_path" "$@" & ;; esac ;; - w3m|links|lynx) - eval "$browser_path" $pages + w3m|links|lynx|open) + eval "$browser_path" "$@" ;; dillo) - nohup "$browser_path" $pages & + "$browser_path" "$@" & ;; esac diff --git a/git.spec.in b/git.spec.in index 3f9f88815b..97a26be29a 100644 --- a/git.spec.in +++ b/git.spec.in @@ -3,7 +3,7 @@ Name: git Version: @@VERSION@@ Release: 1%{?dist} -Summary: Git core and tools +Summary: Core git tools License: GPL Group: Development/Tools URL: http://kernel.org/pub/software/scm/git/ @@ -11,80 +11,86 @@ Source: http://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz 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} -Requires: git-svn = %{version}-%{release} -Requires: git-cvs = %{version}-%{release} -Requires: git-arch = %{version}-%{release} -Requires: git-email = %{version}-%{release} -Requires: gitk = %{version}-%{release} -Requires: git-gui = %{version}-%{release} Requires: perl-Git = %{version}-%{release} +Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat +Provides: git-core = %{version}-%{release} +Obsoletes: git-core <= 1.5.4.2 +Obsoletes: git-p4 %description Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals. -This is a dummy package which brings in all subpackages. +The git rpm installs the core tools with minimal dependencies. To +install all git packages, including tools for integrating with other +SCMs, install the git-all meta-package. -%package core -Summary: Core git tools +%package all +Summary: Meta-package to pull in all git tools Group: Development/Tools -Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat -Obsoletes: git-p4 -%description core +Requires: git = %{version}-%{release} +Requires: git-svn = %{version}-%{release} +Requires: git-cvs = %{version}-%{release} +Requires: git-arch = %{version}-%{release} +Requires: git-email = %{version}-%{release} +Requires: gitk = %{version}-%{release} +Requires: git-gui = %{version}-%{release} +Obsoletes: git <= 1.5.4.2 + +%description all Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals. -These are the core tools with minimal dependencies. +This is a dummy package which brings in all subpackages. %package svn Summary: Git tools for importing Subversion repositories Group: Development/Tools -Requires: git-core = %{version}-%{release}, subversion +Requires: git = %{version}-%{release}, subversion %description svn Git tools for importing Subversion repositories. %package cvs Summary: Git tools for importing CVS repositories Group: Development/Tools -Requires: git-core = %{version}-%{release}, cvs, cvsps +Requires: git = %{version}-%{release}, cvs, cvsps %description cvs Git tools for importing CVS repositories. %package arch Summary: Git tools for importing Arch repositories Group: Development/Tools -Requires: git-core = %{version}-%{release}, tla +Requires: git = %{version}-%{release}, tla %description arch Git tools for importing Arch repositories. %package email Summary: Git tools for sending email Group: Development/Tools -Requires: git-core = %{version}-%{release} +Requires: git = %{version}-%{release} %description email Git tools for sending email. %package gui Summary: Git GUI tool Group: Development/Tools -Requires: git-core = %{version}-%{release}, tk >= 8.4 +Requires: git = %{version}-%{release}, tk >= 8.4 %description gui Git GUI tool %package -n gitk Summary: Git revision tree visualiser ('gitk') Group: Development/Tools -Requires: git-core = %{version}-%{release}, tk >= 8.4 +Requires: git = %{version}-%{release}, tk >= 8.4 %description -n gitk Git revision tree visualiser ('gitk') %package -n perl-Git Summary: Perl interface to Git Group: Development/Libraries -Requires: git-core = %{version}-%{release} +Requires: git = %{version}-%{release} Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) BuildRequires: perl(Error) @@ -121,8 +127,12 @@ rm -rf $RPM_BUILD_ROOT%{_mandir} %clean rm -rf $RPM_BUILD_ROOT -%files -# These are no files in the root package +%files -f bin-man-doc-files +%defattr(-,root,root) +%{_datadir}/git-core/ +%doc README COPYING Documentation/*.txt +%{!?_without_docs: %doc Documentation/*.html Documentation/howto} +%{!?_without_docs: %doc Documentation/technical} %files svn %defattr(-,root,root) @@ -173,14 +183,13 @@ rm -rf $RPM_BUILD_ROOT %files -n perl-Git -f perl-files %defattr(-,root,root) -%files core -f bin-man-doc-files -%defattr(-,root,root) -%{_datadir}/git-core/ -%doc README COPYING Documentation/*.txt -%{!?_without_docs: %doc Documentation/*.html Documentation/howto} -%{!?_without_docs: %doc Documentation/technical} +%files all +# No files for you! %changelog +* Fri Feb 15 2008 Kristian Høgsberg <krh@redhat.com> +- Rename git-core to just git and rename meta package from git to git-all. + * Sun Feb 03 2008 James Bowes <jbowes@dangerouslyinc.com> - Add a BuildRequires for gettext diff --git a/gitk-git/gitk b/gitk-git/gitk index 5560e4dc56..f1f21e97bf 100644 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -240,11 +240,12 @@ proc getcommitlines {fd view} { set listed 1 if {$j >= 0 && [string match "commit *" $cmit]} { set ids [string range $cmit 7 [expr {$j - 1}]] - if {[string match {[-<>]*} $ids]} { + if {[string match {[-^<>]*} $ids]} { switch -- [string index $ids 0] { "-" {set listed 0} - "<" {set listed 2} - ">" {set listed 3} + "^" {set listed 2} + "<" {set listed 3} + ">" {set listed 4} } set ids [string range $ids 1 end] } @@ -632,6 +633,7 @@ proc makewindow {} { global findtype findtypemenu findloc findstring fstring geometry global entries sha1entry sha1string sha1but global diffcontextstring diffcontext + global ignorespace global maincursor textcursor curtextcursor global rowctxmenu fakerowmenu mergemax wrapcomment global highlight_files gdttype @@ -849,6 +851,9 @@ proc makewindow {} { trace add variable diffcontextstring write diffcontextchange lappend entries .bleft.mid.diffcontext pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left + checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \ + -command changeignorespace -variable ignorespace + pack .bleft.mid.ignspace -side left -padx 5 set ctext .bleft.ctext text $ctext -background $bgcolor -foreground $fgcolor \ -state disabled -font textfont \ @@ -1307,45 +1312,45 @@ proc keys {} { } toplevel $w wm title $w [mc "Gitk key bindings"] - message $w.m -text [mc " -Gitk key bindings: - -<$M1T-Q> Quit -<Home> Move to first commit -<End> Move to last commit -<Up>, p, i Move up one commit -<Down>, n, k Move down one commit -<Left>, z, j Go back in history list -<Right>, x, l Go forward in history list -<PageUp> Move up one page in commit list -<PageDown> Move down one page in commit list -<$M1T-Home> Scroll to top of commit list -<$M1T-End> Scroll to bottom of commit list -<$M1T-Up> Scroll commit list up one line -<$M1T-Down> Scroll commit list down one line -<$M1T-PageUp> Scroll commit list up one page -<$M1T-PageDown> Scroll commit list down one page -<Shift-Up> Find backwards (upwards, later commits) -<Shift-Down> Find forwards (downwards, earlier commits) -<Delete>, b Scroll diff view up one page -<Backspace> Scroll diff view up one page -<Space> Scroll diff view down one page -u Scroll diff view up 18 lines -d Scroll diff view down 18 lines -<$M1T-F> Find -<$M1T-G> Move to next find hit -<Return> Move to next find hit -/ Move to next find hit, or redo find -? Move to previous find hit -f Scroll diff view to next file -<$M1T-S> Search for next hit in diff view -<$M1T-R> Search for previous hit in diff view -<$M1T-KP+> Increase font size -<$M1T-plus> Increase font size -<$M1T-KP-> Decrease font size -<$M1T-minus> Decrease font size -<F5> Update -"] \ + message $w.m -text " +[mc "Gitk key bindings:"] + +[mc "<%s-Q> Quit" $M1T] +[mc "<Home> Move to first commit"] +[mc "<End> Move to last commit"] +[mc "<Up>, p, i Move up one commit"] +[mc "<Down>, n, k Move down one commit"] +[mc "<Left>, z, j Go back in history list"] +[mc "<Right>, x, l Go forward in history list"] +[mc "<PageUp> Move up one page in commit list"] +[mc "<PageDown> Move down one page in commit list"] +[mc "<%s-Home> Scroll to top of commit list" $M1T] +[mc "<%s-End> Scroll to bottom of commit list" $M1T] +[mc "<%s-Up> Scroll commit list up one line" $M1T] +[mc "<%s-Down> Scroll commit list down one line" $M1T] +[mc "<%s-PageUp> Scroll commit list up one page" $M1T] +[mc "<%s-PageDown> Scroll commit list down one page" $M1T] +[mc "<Shift-Up> Find backwards (upwards, later commits)"] +[mc "<Shift-Down> Find forwards (downwards, earlier commits)"] +[mc "<Delete>, b Scroll diff view up one page"] +[mc "<Backspace> Scroll diff view up one page"] +[mc "<Space> Scroll diff view down one page"] +[mc "u Scroll diff view up 18 lines"] +[mc "d Scroll diff view down 18 lines"] +[mc "<%s-F> Find" $M1T] +[mc "<%s-G> Move to next find hit" $M1T] +[mc "<Return> Move to next find hit"] +[mc "/ Move to next find hit, or redo find"] +[mc "? Move to previous find hit"] +[mc "f Scroll diff view to next file"] +[mc "<%s-S> Search for next hit in diff view" $M1T] +[mc "<%s-R> Search for previous hit in diff view" $M1T] +[mc "<%s-KP+> Increase font size" $M1T] +[mc "<%s-plus> Increase font size" $M1T] +[mc "<%s-KP-> Decrease font size" $M1T] +[mc "<%s-minus> Decrease font size" $M1T] +[mc "<F5> Update"] +" \ -justify left -bg white -border 2 -relief groove pack $w.m -side top -fill both -padx 2 -pady 2 button $w.ok -text [mc "Close"] -command "destroy $w" -default active @@ -3627,23 +3632,23 @@ proc drawcmittext {id row col} { global linehtag linentag linedtag selectedline global canvxmax boldrows boldnamerows fgcolor nullid nullid2 - # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right + # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right set listed [lindex $commitlisted $row] if {$id eq $nullid} { set ofill red } elseif {$id eq $nullid2} { set ofill green } else { - set ofill [expr {$listed != 0? "blue": "white"}] + set ofill [expr {$listed != 0 ? $listed == 2 ? "gray" : "blue" : "white"}] } set x [xc $row $col] set y [yc $row] set orad [expr {$linespc / 3}] - if {$listed <= 1} { + if {$listed <= 2} { set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \ [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \ -fill $ofill -outline $fgcolor -width 1 -tags circle] - } elseif {$listed == 2} { + } elseif {$listed == 3} { # triangle pointing left for left-side commits set t [$canv create polygon \ [expr {$x - $orad}] $y \ @@ -5027,13 +5032,14 @@ proc getblobline {bf id} { proc mergediff {id l} { global diffmergeid mdifffd global diffids + global diffcontext global parentlist global limitdiffs viewfiles curview set diffmergeid $id set diffids $id # this doesn't seem to actually affect anything... - set cmd [concat | git diff-tree --no-commit-id --cc $id] + set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id] if {$limitdiffs && $viewfiles($curview) ne {}} { set cmd [concat $cmd -- $viewfiles($curview)] } @@ -5270,13 +5276,21 @@ proc diffcontextchange {n1 n2 op} { } } +proc changeignorespace {} { + reselectline +} + proc getblobdiffs {ids} { global blobdifffd diffids env global diffinhdr treediffs global diffcontext + global ignorespace global limitdiffs viewfiles curview set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] + if {$ignorespace} { + append cmd " -w" + } if {$limitdiffs && $viewfiles($curview) ne {}} { set cmd [concat $cmd -- $viewfiles($curview)] } @@ -6137,11 +6151,7 @@ proc domktag {} { return } if {[catch { - set dir [gitdir] - set fname [file join $dir "refs/tags" $tag] - set f [open $fname w] - puts $f $id - close $f + exec git tag $tag $id } err]} { error_popup "[mc "Error creating tag:"] $err" return @@ -8459,6 +8469,7 @@ set bgcolor white set fgcolor black set diffcolors {red "#00a000" blue} set diffcontext 3 +set ignorespace 0 set selectbgcolor gray85 ## For msgcat loading, first locate the installation location. diff --git a/gitweb/README b/gitweb/README index 4c8bedf744..2163071047 100644 --- a/gitweb/README +++ b/gitweb/README @@ -233,6 +233,10 @@ You can use the following files in repository: Displayed in the project summary page. You can use multiple-valued gitweb.url repository configuration variable for that, but the file takes precendence. + * gitweb.owner + You can use the gitweb.owner repository configuration variable to set + repository's owner. It is displayed in the project list and summary + page. If it's not set, filesystem directory's owner is used. * various gitweb.* config variables (in config) Read description of %feature hash for detailed list, and some descriptions. diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 5e88637b5e..8ed6d04417 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -611,6 +611,8 @@ sub href(%) { ); my %mapping = @mapping; + $params{'project'} = $project unless exists $params{'project'}; + if ($params{-replay}) { while (my ($name, $symbol) = each %mapping) { if (!exists $params{$name}) { @@ -620,8 +622,6 @@ sub href(%) { } } - $params{'project'} = $project unless exists $params{'project'}; - my ($use_pathinfo) = gitweb_check_feature('pathinfo'); if ($use_pathinfo) { # use PATH_INFO for project name @@ -753,29 +753,40 @@ sub esc_path { # Make control characters "printable", using character escape codes (CEC) sub quot_cec { my $cntrl = shift; + my %opts = @_; my %es = ( # character escape codes, aka escape sequences - "\t" => '\t', # tab (HT) - "\n" => '\n', # line feed (LF) - "\r" => '\r', # carrige return (CR) - "\f" => '\f', # form feed (FF) - "\b" => '\b', # backspace (BS) - "\a" => '\a', # alarm (bell) (BEL) - "\e" => '\e', # escape (ESC) - "\013" => '\v', # vertical tab (VT) - "\000" => '\0', # nul character (NUL) - ); + "\t" => '\t', # tab (HT) + "\n" => '\n', # line feed (LF) + "\r" => '\r', # carrige return (CR) + "\f" => '\f', # form feed (FF) + "\b" => '\b', # backspace (BS) + "\a" => '\a', # alarm (bell) (BEL) + "\e" => '\e', # escape (ESC) + "\013" => '\v', # vertical tab (VT) + "\000" => '\0', # nul character (NUL) + ); my $chr = ( (exists $es{$cntrl}) ? $es{$cntrl} : sprintf('\%03o', ord($cntrl)) ); - return "<span class=\"cntrl\">$chr</span>"; + if ($opts{-nohtml}) { + return $chr; + } else { + return "<span class=\"cntrl\">$chr</span>"; + } } # Alternatively use unicode control pictures codepoints, # Unicode "printable representation" (PR) sub quot_upr { my $cntrl = shift; + my %opts = @_; + my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl)); - return "<span class=\"cntrl\">$chr</span>"; + if ($opts{-nohtml}) { + return $chr; + } else { + return "<span class=\"cntrl\">$chr</span>"; + } } # git may return quoted and escaped filenames @@ -800,7 +811,7 @@ sub unquote { return chr(oct($seq)); } elsif (exists $es{$seq}) { # C escape sequence, aka character escape code - return $es{$seq} + return $es{$seq}; } # quoted ordinary character return $seq; @@ -866,8 +877,8 @@ sub chop_and_escape_str { if ($chopped eq $str) { return esc_html($chopped); } else { - return qq{<span title="} . esc_html($str) . qq{">} . - esc_html($chopped) . qq{</span>}; + $str =~ s/([[:cntrl:]])/?/g; + return $cgi->span({-title=>$str}, esc_html($chopped)); } } @@ -1759,6 +1770,7 @@ sub git_get_project_owner { my $owner; return undef unless $project; + $git_dir = "$projectroot/$project"; if (!defined $gitweb_project_owner) { git_get_project_list_from_file(); @@ -1767,8 +1779,11 @@ sub git_get_project_owner { if (exists $gitweb_project_owner->{$project}) { $owner = $gitweb_project_owner->{$project}; } + if (!defined $owner){ + $owner = git_get_project_config('owner'); + } if (!defined $owner) { - $owner = get_file_owner("$projectroot/$project"); + $owner = get_file_owner("$git_dir"); } return $owner; @@ -39,12 +39,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; - } + if (!strcmp(var, "help.format")) + return git_config_string(&help_default_format, var, value); return git_default_config(var, value); } @@ -330,10 +326,26 @@ static void show_info_page(const char *git_cmd) execlp("info", "info", "gitman", page, NULL); } +static void get_html_page_path(struct strbuf *page_path, const char *page) +{ + struct stat st; + + /* Check that we have a git documentation directory. */ + if (stat(GIT_HTML_PATH "/git.html", &st) || !S_ISREG(st.st_mode)) + die("'%s': not a documentation directory.", GIT_HTML_PATH); + + strbuf_init(page_path, 0); + strbuf_addf(page_path, GIT_HTML_PATH "/%s.html", page); +} + static void show_html_page(const char *git_cmd) { const char *page = cmd_to_page(git_cmd); - execl_git_cmd("help--browse", page, NULL); + struct strbuf page_path; /* it leaks but we exec bellow */ + + get_html_page_path(&page_path, page); + + execl_git_cmd("web--browse", "-c", "help.browser", page_path.buf, NULL); } void help_unknown_cmd(const char *cmd) diff --git a/http-push.c b/http-push.c index b2b410df90..63ff218b3c 100644 --- a/http-push.c +++ b/http-push.c @@ -2383,7 +2383,8 @@ int main(int argc, char **argv) /* Generate a list of objects that need to be pushed */ pushing = 0; - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); mark_edges_uninteresting(revs.commits); objects_to_send = get_delta(&revs, ref_lock); finish_all_active_slots(); diff --git a/list-objects.c b/list-objects.c index 4ef58e7ec0..c8b8375e49 100644 --- a/list-objects.c +++ b/list-objects.c @@ -18,6 +18,8 @@ static void process_blob(struct rev_info *revs, if (!revs->blob_objects) return; + if (!obj) + die("bad blob object"); if (obj->flags & (UNINTERESTING | SEEN)) return; obj->flags |= SEEN; @@ -69,6 +71,8 @@ static void process_tree(struct rev_info *revs, if (!revs->tree_objects) return; + if (!obj) + die("bad tree object"); if (obj->flags & (UNINTERESTING | SEEN)) return; if (parse_tree(tree) < 0) diff --git a/merge-recursive.c b/merge-recursive.c index dd52342539..55ef76f5a5 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1673,6 +1673,8 @@ static struct commit *get_ref(const char *ref) if (get_sha1(ref, sha1)) die("Could not resolve ref '%s'", ref); object = deref_tag(parse_object(sha1), ref, strlen(ref)); + if (!object) + return NULL; if (object->type == OBJ_TREE) return make_virtual_commit((struct tree*)object, better_branch_name(ref)); @@ -57,6 +57,7 @@ void setup_pager(void) /* return in the child */ if (!pid) { dup2(fd[1], 1); + dup2(fd[1], 2); close(fd[0]); close(fd[1]); return; diff --git a/reachable.c b/reachable.c index 00f289f2f4..3b1c18ff9b 100644 --- a/reachable.c +++ b/reachable.c @@ -15,6 +15,8 @@ static void process_blob(struct blob *blob, { struct object *obj = &blob->object; + if (!blob) + die("bad blob object"); if (obj->flags & SEEN) return; obj->flags |= SEEN; @@ -39,6 +41,8 @@ static void process_tree(struct tree *tree, struct name_entry entry; struct name_path me; + if (!tree) + die("bad tree object"); if (obj->flags & SEEN) return; obj->flags |= SEEN; @@ -79,7 +83,8 @@ static void process_tag(struct tag *tag, struct object_array *p, const char *nam if (parse_tag(tag) < 0) die("bad tag object %s", sha1_to_hex(obj->sha1)); - add_object(tag->tagged, p, NULL, name); + if (tag->tagged) + add_object(tag->tagged, p, NULL, name); } static void walk_commit_list(struct rev_info *revs) @@ -150,7 +155,8 @@ static int add_one_reflog(const char *path, const unsigned char *sha1, int flag, static void add_one_tree(const unsigned char *sha1, struct rev_info *revs) { struct tree *tree = lookup_tree(sha1); - add_pending_object(revs, &tree->object, ""); + if (tree) + add_pending_object(revs, &tree->object, ""); } static void add_cache_tree(struct cache_tree *it, struct rev_info *revs) @@ -215,6 +221,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog) * Set up the revision walk - this will move all commits * from the pending list to the commit walking list. */ - prepare_revision_walk(revs); + if (prepare_revision_walk(revs)) + die("revision walk setup failed"); walk_commit_list(revs); } @@ -343,6 +343,16 @@ struct refspec *parse_ref_spec(int nr_refspec, const char **refspec) return rs; } +static int valid_remote_nick(const char *name) +{ + if (!name[0] || /* not empty */ + (name[0] == '.' && /* not "." */ + (!name[1] || /* not ".." */ + (name[1] == '.' && !name[2])))) + return 0; + return !strchr(name, '/'); /* no slash */ +} + struct remote *remote_get(const char *name) { struct remote *ret; @@ -351,7 +361,7 @@ struct remote *remote_get(const char *name) if (!name) name = default_remote_name; ret = make_remote(name, 0); - if (name[0] != '/') { + if (valid_remote_nick(name)) { if (!ret->url) read_remotes_file(ret); if (!ret->url) diff --git a/revision.c b/revision.c index 6e85aaa3fb..b1aebf8999 100644 --- a/revision.c +++ b/revision.c @@ -46,6 +46,8 @@ void add_object(struct object *obj, static void mark_blob_uninteresting(struct blob *blob) { + if (!blob) + return; if (blob->object.flags & UNINTERESTING) return; blob->object.flags |= UNINTERESTING; @@ -57,6 +59,8 @@ void mark_tree_uninteresting(struct tree *tree) struct name_entry entry; struct object *obj = &tree->object; + if (!tree) + return; if (obj->flags & UNINTERESTING) return; obj->flags |= UNINTERESTING; @@ -173,6 +177,8 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object struct tag *tag = (struct tag *) object; if (revs->tag_objects && !(flags & UNINTERESTING)) add_pending_object(revs, object, tag->tag); + if (!tag->tagged) + die("bad tag"); object = parse_object(tag->tagged->sha1); if (!object) die("bad object %s", sha1_to_hex(tag->tagged->sha1)); @@ -685,6 +691,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags) it = get_reference(revs, arg, sha1, 0); if (it->type != OBJ_TAG) break; + if (!((struct tag*)it)->tagged) + return 0; hashcpy(sha1, ((struct tag*)it)->tagged->sha1); } if (it->type != OBJ_COMMIT) @@ -4,51 +4,118 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; -const char *prefix_path(const char *prefix, int len, const char *path) +static int sanitary_path_copy(char *dst, const char *src) { - const char *orig = path; + char *dst0 = dst; + + if (*src == '/') { + *dst++ = '/'; + while (*src == '/') + src++; + } + for (;;) { - char c; - if (*path != '.') - break; - c = path[1]; - /* "." */ - if (!c) { - path++; - break; + char c = *src; + + /* + * A path component that begins with . could be + * special: + * (1) "." and ends -- ignore and terminate. + * (2) "./" -- ignore them, eat slash and continue. + * (3) ".." and ends -- strip one and terminate. + * (4) "../" -- strip one, eat slash and continue. + */ + if (c == '.') { + switch (src[1]) { + case '\0': + /* (1) */ + src++; + break; + case '/': + /* (2) */ + src += 2; + while (*src == '/') + src++; + continue; + case '.': + switch (src[2]) { + case '\0': + /* (3) */ + src += 2; + goto up_one; + case '/': + /* (4) */ + src += 3; + while (*src == '/') + src++; + goto up_one; + } + } } - /* "./" */ + + /* copy up to the next '/', and eat all '/' */ + while ((c = *src++) != '\0' && c != '/') + *dst++ = c; if (c == '/') { - path += 2; - continue; - } - if (c != '.') + *dst++ = c; + while (c == '/') + c = *src++; + src--; + } else if (!c) break; - c = path[2]; - if (!c) - path += 2; - else if (c == '/') - path += 3; - else - break; - /* ".." and "../" */ - /* Remove last component of the prefix */ - do { - if (!len) - die("'%s' is outside repository", orig); - len--; - } while (len && prefix[len-1] != '/'); continue; + + up_one: + /* + * dst0..dst is prefix portion, and dst[-1] is '/'; + * go up one level. + */ + dst -= 2; /* go past trailing '/' if any */ + if (dst < dst0) + return -1; + while (1) { + if (dst <= dst0) + break; + c = *dst--; + if (c == '/') { + dst += 2; + break; + } + } } - if (len) { - int speclen = strlen(path); - char *n = xmalloc(speclen + len + 1); + *dst = '\0'; + return 0; +} - memcpy(n, prefix, len); - memcpy(n + len, path, speclen+1); - path = n; +const char *prefix_path(const char *prefix, int len, const char *path) +{ + const char *orig = path; + char *sanitized = xmalloc(len + strlen(path) + 1); + if (*orig == '/') + strcpy(sanitized, path); + else { + if (len) + memcpy(sanitized, prefix, len); + strcpy(sanitized + len, path); } - return path; + if (sanitary_path_copy(sanitized, sanitized)) + goto error_out; + if (*orig == '/') { + const char *work_tree = get_git_work_tree(); + size_t len = strlen(work_tree); + size_t total = strlen(sanitized) + 1; + if (strncmp(sanitized, work_tree, len) || + (sanitized[len] != '\0' && sanitized[len] != '/')) { + error_out: + error("'%s' is outside repository", orig); + free(sanitized); + return NULL; + } + if (sanitized[len] == '/') + len++; + memmove(sanitized, sanitized + len, total - len); + } + return sanitized; } /* @@ -114,7 +181,7 @@ void verify_non_filename(const char *prefix, const char *arg) const char **get_pathspec(const char *prefix, const char **pathspec) { const char *entry = *pathspec; - const char **p; + const char **src, **dst; int prefixlen; if (!prefix && !entry) @@ -128,12 +195,19 @@ const char **get_pathspec(const char *prefix, const char **pathspec) } /* Otherwise we have to re-write the entries.. */ - p = pathspec; + src = pathspec; + dst = pathspec; prefixlen = prefix ? strlen(prefix) : 0; - do { - *p = prefix_path(prefix, prefixlen, entry); - } while ((entry = *++p) != NULL); - return (const char **) pathspec; + while (*src) { + const char *p = prefix_path(prefix, prefixlen, *src); + if (p) + *(dst++) = p; + src++; + } + *dst = NULL; + if (!*pathspec) + return NULL; + return pathspec; } /* diff --git a/sha1_file.c b/sha1_file.c index 66a4e00fa8..d9da7c8f75 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1943,7 +1943,8 @@ void *read_object_with_reference(const unsigned char *sha1, } ref_length = strlen(ref_type); - if (memcmp(buffer, ref_type, ref_length) || + if (ref_length + 40 > isize || + memcmp(buffer, ref_type, ref_length) || get_sha1_hex((char *) buffer + ref_length, actual_sha1)) { free(buffer); return NULL; @@ -2358,7 +2359,8 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) { struct strbuf nbuf; strbuf_init(&nbuf, 0); - if (convert_to_git(path, buf, size, &nbuf)) { + if (convert_to_git(path, buf, size, &nbuf, + write_object ? safe_crlf : 0)) { munmap(buf, size); buf = strbuf_detach(&nbuf, &size); re_allocated = 1; diff --git a/sha1_name.c b/sha1_name.c index be8489e4e5..c2805e736b 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -494,8 +494,11 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) return error("%.*s: expected %s type, but the object dereferences to %s type", len, name, typename(expected_type), typename(o->type)); + if (!o) + return -1; if (!o->parsed) - parse_object(o->sha1); + if (!parse_object(o->sha1)) + return -1; } } return 0; @@ -578,8 +581,11 @@ static int handle_one_ref(const char *path, struct object *object = parse_object(sha1); if (!object) return 0; - if (object->type == OBJ_TAG) + if (object->type == OBJ_TAG) { object = deref_tag(object, path, strlen(path)); + if (!object) + return 0; + } if (object->type != OBJ_COMMIT) return 0; insert_by_date((struct commit *)object, list); @@ -617,7 +623,8 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) unsigned long size; commit = pop_most_recent_commit(&list, ONELINE_SEEN); - parse_object(commit->object.sha1); + if (!parse_object(commit->object.sha1)) + continue; if (temp_commit_buffer) free(temp_commit_buffer); if (commit->buffer) @@ -56,7 +56,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, if (i < heads->nr) { commit = (struct commit *) deref_tag(heads->objects[i++].item, NULL, 0); - if (commit->object.type != OBJ_COMMIT) { + if (!commit || commit->object.type != OBJ_COMMIT) { commit = NULL; continue; } @@ -70,7 +70,8 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, cur_depth = *(int *)commit->util; } } - parse_commit(commit); + if (parse_commit(commit)) + die("invalid commit"); commit->object.flags |= not_shallow_flag; cur_depth++; for (p = commit->parents, commit = NULL; p; p = p->next) { diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh index 8b27aa892b..90ea081db6 100755 --- a/t/t0020-crlf.sh +++ b/t/t0020-crlf.sh @@ -8,6 +8,10 @@ q_to_nul () { tr Q '\000' } +q_to_cr () { + tr Q '\015' +} + append_cr () { sed -e 's/$/Q/' | tr Q '\015' } @@ -42,6 +46,60 @@ test_expect_success setup ' echo happy. ' +test_expect_success 'safecrlf: autocrlf=input, all CRLF' ' + + git config core.autocrlf input && + git config core.safecrlf true && + + for w in I am all CRLF; do echo $w; done | append_cr >allcrlf && + ! git add allcrlf +' + +test_expect_success 'safecrlf: autocrlf=input, mixed LF/CRLF' ' + + git config core.autocrlf input && + git config core.safecrlf true && + + for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed && + ! git add mixed +' + +test_expect_success 'safecrlf: autocrlf=true, all LF' ' + + git config core.autocrlf true && + git config core.safecrlf true && + + for w in I am all LF; do echo $w; done >alllf && + ! git add alllf +' + +test_expect_success 'safecrlf: autocrlf=true mixed LF/CRLF' ' + + git config core.autocrlf true && + git config core.safecrlf true && + + for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed && + ! git add mixed +' + +test_expect_success 'safecrlf: print warning only once' ' + + git config core.autocrlf input && + git config core.safecrlf warn && + + for w in I am all LF; do echo $w; done >doublewarn && + git add doublewarn && + git commit -m "nowarn" && + for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn && + test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1 +' + +test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' ' + git config core.autocrlf false && + git config core.safecrlf false && + git reset --hard HEAD^ +' + test_expect_success 'update with autocrlf=input' ' rm -f tmp one dir/two three && diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index e474b3f1d5..38a2bf09af 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -33,9 +33,9 @@ test_rev_parse() { test_rev_parse toplevel false false true '' cd .git || exit 1 -test_rev_parse .git/ true true false '' +test_rev_parse .git/ false true false '' cd objects || exit 1 -test_rev_parse .git/objects/ true true false '' +test_rev_parse .git/objects/ false true false '' cd ../.. || exit 1 mkdir -p sub/dir || exit 1 diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index e25b255683..b4297bacf2 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -99,4 +99,45 @@ EOF test_expect_success 'git-status honours core.excludesfile' \ 'diff -u expect output' +test_expect_success 'trailing slash in exclude allows directory match(1)' ' + + git ls-files --others --exclude=one/ >output && + if grep "^one/" output + then + echo Ooops + false + else + : happy + fi + +' + +test_expect_success 'trailing slash in exclude allows directory match (2)' ' + + git ls-files --others --exclude=one/two/ >output && + if grep "^one/two/" output + then + echo Ooops + false + else + : happy + fi + +' + +test_expect_success 'trailing slash in exclude forces directory match (1)' ' + + >two + git ls-files --others --exclude=two/ >output && + grep "^two" output + +' + +test_expect_success 'trailing slash in exclude forces directory match (2)' ' + + git ls-files --others --exclude=one/a.1/ >output && + grep "^one/a.1" output + +' + test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index e33ea4e9f4..e5ed74545b 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -340,4 +340,26 @@ test_expect_success 'rebase a commit violating pre-commit' ' ' +test_expect_success 'rebase with a file named HEAD in worktree' ' + + rm -fr .git/hooks && + git reset --hard && + git checkout -b branch3 A && + + ( + GIT_AUTHOR_NAME="Squashed Away" && + export GIT_AUTHOR_NAME && + >HEAD && + git add HEAD && + git commit -m "Add head" && + >BODY && + git add BODY && + git commit -m "Add body" + ) && + + FAKE_LINES="1 squash 2" git rebase -i to-be-rebased && + test "$(git show -s --pretty=format:%an)" = "Squashed Away" + +' + test_done diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index d30169fbdc..83c54b747f 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -326,4 +326,13 @@ test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: ! git diff --check ' + +test_expect_success 'line numbers in --check output are correct' ' + + echo "" > x && + echo "foo(); " >> x && + git diff --check | grep "x:2:" + +' + test_done diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index 9b1a74542a..d6c55c1157 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -11,7 +11,7 @@ test_expect_success 'split sample box' \ 'git mailsplit -o. ../t5100/sample.mbox >last && last=`cat last` && echo total is $last && - test `cat last` = 8' + test `cat last` = 9' for mail in `echo 00*` do diff --git a/t/t5100/info0009 b/t/t5100/info0009 new file mode 100644 index 0000000000..2a66321c80 --- /dev/null +++ b/t/t5100/info0009 @@ -0,0 +1,5 @@ +Author: F U Bar +Email: f.u.bar@example.com +Subject: updates +Date: Mon, 17 Sep 2001 00:00:00 +0900 + diff --git a/t/t5100/msg0009 b/t/t5100/msg0009 new file mode 100644 index 0000000000..9ffe131489 --- /dev/null +++ b/t/t5100/msg0009 @@ -0,0 +1,2 @@ +This is to fix diff-format documentation. + diff --git a/t/t5100/patch0009 b/t/t5100/patch0009 new file mode 100644 index 0000000000..65615c34af --- /dev/null +++ b/t/t5100/patch0009 @@ -0,0 +1,13 @@ +diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt +index b426a14..97756ec 100644 +--- a/Documentation/diff-format.txt ++++ b/Documentation/diff-format.txt +@@ -81,7 +81,7 @@ The "diff" formatting options can be customized via the + environment variable 'GIT_DIFF_OPTS'. For example, if you + prefer context diff: + +- GIT_DIFF_OPTS=-c git-diff-index -p $(cat .git/HEAD) ++ GIT_DIFF_OPTS=-c git-diff-index -p HEAD + + + 2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index 070c1661b9..0476b96c33 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -407,3 +407,26 @@ Subject: [PATCH] another patch Hey you forgot the patch! +From nobody Mon Sep 17 00:00:00 2001 +From: A U Thor <a.u.thor@example.com> +Date: Mon, 17 Sep 2001 00:00:00 +0900 +Mime-Version: 1.0 +Content-Type: Text/Plain; charset=us-ascii +Content-Transfer-Encoding: Quoted-Printable + +=0A=0AFrom: F U Bar <f.u.bar@example.com> +Subject: [PATCH] updates=0A=0AThis is to fix diff-format documentation. + +diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt +index b426a14..97756ec 100644 +--- a/Documentation/diff-format.txt ++++ b/Documentation/diff-format.txt +@@ -81,7 +81,7 @@ The "diff" formatting options can be customized via the + environment variable 'GIT_DIFF_OPTS'. For example, if you + prefer context diff: +=20 +- GIT_DIFF_OPTS=3D-c git-diff-index -p $(cat .git/HEAD) ++ GIT_DIFF_OPTS=3D-c git-diff-index -p HEAD +=20 +=20 + 2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index 822ac8c28e..59a165a6d4 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -63,4 +63,12 @@ test_expect_success 'Even without -l, local will make a hardlink' ' test 0 = $copied ' +test_expect_success 'local clone of repo with nonexistent ref in HEAD' ' + cd "$D" && + echo "ref: refs/heads/nonexistent" > a.git/HEAD && + git clone a d && + cd d && + git fetch && + test ! -e .git/refs/remotes/origin/HEAD' + test_done diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index b1243b4163..fa382c58da 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -118,4 +118,42 @@ test_expect_success "Sergey Vlasov's test case" ' git mv ab a ' +test_expect_success 'absolute pathname' '( + + rm -fr mine && + mkdir mine && + cd mine && + test_create_repo one && + cd one && + mkdir sub && + >sub/file && + git add sub/file && + + git mv sub "$(pwd)/in" && + ! test -d sub && + test -d in && + git ls-files --error-unmatch in/file + + +)' + +test_expect_success 'absolute pathname outside should fail' '( + + rm -fr mine && + mkdir mine && + cd mine && + out=$(pwd) && + test_create_repo one && + cd one && + mkdir sub && + >sub/file && + git add sub/file && + + ! git mv sub "$out/out" && + test -d sub && + ! test -d ../in && + git ls-files --error-unmatch sub/file + +)' + test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 5f60b22d87..868babc4b2 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -165,4 +165,18 @@ test_expect_success '"map" works in commit filter' ' git rev-parse --verify master ' +test_expect_success 'Name needing quotes' ' + + git checkout -b rerere A && + mkdir foo && + name="れれれ" && + >foo/$name && + git add foo && + git commit -m "Adding a file" && + git filter-branch --tree-filter "rm -fr foo" && + ! git ls-files --error-unmatch "foo/$name" && + test $(git rev-parse --verify rerere) != $(git rev-parse --verify A) + +' + test_done diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh new file mode 100755 index 0000000000..e809e0e2c9 --- /dev/null +++ b/t/t7010-setup.sh @@ -0,0 +1,164 @@ +#!/bin/sh + +test_description='setup taking and sanitizing funny paths' + +. ./test-lib.sh + +test_expect_success setup ' + + mkdir -p a/b/c a/e && + D=$(pwd) && + >a/b/c/d && + >a/e/f + +' + +test_expect_success 'git add (absolute)' ' + + git add "$D/a/b/c/d" && + git ls-files >current && + echo a/b/c/d >expect && + diff -u expect current + +' + + +test_expect_success 'git add (funny relative)' ' + + rm -f .git/index && + ( + cd a/b && + git add "../e/./f" + ) && + git ls-files >current && + echo a/e/f >expect && + diff -u expect current + +' + +test_expect_success 'git rm (absolute)' ' + + rm -f .git/index && + git add a && + git rm -f --cached "$D/a/b/c/d" && + git ls-files >current && + echo a/e/f >expect && + diff -u expect current + +' + +test_expect_success 'git rm (funny relative)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + git rm -f --cached "../e/./f" + ) && + git ls-files >current && + echo a/b/c/d >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (absolute)' ' + + rm -f .git/index && + git add a && + git ls-files "$D/a/e/../b" >current && + echo a/b/c/d >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (relative #1)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + git ls-files "../b/c" + ) >current && + echo c/d >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (relative #2)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + git ls-files --full-name "../e/f" + ) >current && + echo a/e/f >expect && + diff -u expect current + +' + +test_expect_success 'git ls-files (relative #3)' ' + + rm -f .git/index && + git add a && + ( + cd a/b && + if git ls-files "../e/f" + then + echo Gaah, should have failed + exit 1 + else + : happy + fi + ) + +' + +test_expect_success 'commit using absolute path names' ' + git commit -m "foo" && + echo aa >>a/b/c/d && + git commit -m "aa" "$(pwd)/a/b/c/d" +' + +test_expect_success 'log using absolute path names' ' + echo bb >>a/b/c/d && + git commit -m "bb" $(pwd)/a/b/c/d && + + git log a/b/c/d >f1.txt && + git log "$(pwd)/a/b/c/d" >f2.txt && + diff -u f1.txt f2.txt +' + +test_expect_success 'blame using absolute path names' ' + git blame a/b/c/d >f1.txt && + git blame "$(pwd)/a/b/c/d" >f2.txt && + diff -u f1.txt f2.txt +' + +test_expect_success 'setup deeper work tree' ' + test_create_repo tester +' + +test_expect_success 'add a directory outside the work tree' '( + cd tester && + d1="$(cd .. ; pwd)" && + git add "$d1" +)' + +test_expect_success 'add a file outside the work tree, nasty case 1' '( + cd tester && + f="$(pwd)x" && + echo "$f" && + touch "$f" && + git add "$f" +)' + +test_expect_success 'add a file outside the work tree, nasty case 2' '( + cd tester && + f="$(pwd | sed "s/.$//")x" && + echo "$f" && + touch "$f" && + git add "$f" +)' + +test_done diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index aaf497e6a5..b780fddc08 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -154,4 +154,33 @@ test_expect_success 'cleanup commit messages (strip,-F,-e)' ' ' +pwd=`pwd` +cat >> .git/FAKE_EDITOR << EOF +#! /bin/sh +echo editor started > "$pwd/.git/result" +exit 0 +EOF +chmod +x .git/FAKE_EDITOR + +test_expect_success 'do not fire editor in the presence of conflicts' ' + + git clean + echo f>g + git add g + git commit -myes + git branch second + echo master>g + echo g>h + git add g h + git commit -mmaster + git checkout second + echo second>g + git add g + git commit -msecond + git cherry-pick -n master + echo "editor not started" > .git/result + GIT_EDITOR=`pwd`/.git/FAKE_EDITOR git commit && exit 1 # should fail + test "`cat .git/result`" = "editor not started" +' + test_done diff --git a/t/t7502-status.sh b/t/t7502-status.sh index 9ce50cade8..e00607490b 100755 --- a/t/t7502-status.sh +++ b/t/t7502-status.sh @@ -17,6 +17,9 @@ test_expect_success 'setup' ' : > dir1/tracked && : > dir1/modified && git add . && + + git status >output && + test_tick && git commit -m initial && : > untracked && @@ -28,6 +31,12 @@ test_expect_success 'setup' ' git add dir2/added ' +test_expect_success 'status (1)' ' + + grep -e "use \"git rm --cached <file>\.\.\.\" to unstage" output + +' + cat > expect << \EOF # On branch master # Changes to be committed: @@ -51,7 +60,7 @@ cat > expect << \EOF # untracked EOF -test_expect_success 'status' ' +test_expect_success 'status (2)' ' git status > output && git diff expect output @@ -119,4 +128,25 @@ test_expect_success 'status without relative paths' ' ' +cat <<EOF >expect +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/ +# expect +# output +# untracked +EOF +test_expect_success 'status of partial commit excluding new file in index' ' + git status dir1/modified >output && + diff -u expect output +' + test_done diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh new file mode 100755 index 0000000000..7ddec99a64 --- /dev/null +++ b/t/t7505-prepare-commit-msg-hook.sh @@ -0,0 +1,155 @@ +#!/bin/sh + +test_description='prepare-commit-msg hook' + +. ./test-lib.sh + +test_expect_success 'with no hook' ' + + echo "foo" > file && + git add file && + git commit -m "first" + +' + +# set up fake editor for interactive editing +cat > fake-editor <<'EOF' +#!/bin/sh +exit 0 +EOF +chmod +x fake-editor +FAKE_EDITOR="$(pwd)/fake-editor" +export FAKE_EDITOR + +# now install hook that always succeeds and adds a message +HOOKDIR="$(git rev-parse --git-dir)/hooks" +HOOK="$HOOKDIR/prepare-commit-msg" +mkdir -p "$HOOKDIR" +cat > "$HOOK" <<'EOF' +#!/bin/sh +if test "$2" = commit; then + source=$(git-rev-parse "$3") +else + source=${2-default} +fi +if test "$GIT_EDITOR" = :; then + sed -e "1s/.*/$source (no editor)/" "$1" > msg.tmp +else + sed -e "1s/.*/$source/" "$1" > msg.tmp +fi +mv msg.tmp "$1" +exit 0 +EOF +chmod +x "$HOOK" + +echo dummy template > "$(git rev-parse --git-dir)/template" + +test_expect_success 'with hook (-m)' ' + + echo "more" >> file && + git add file && + git commit -m "more" && + test "`git log -1 --pretty=format:%s`" = "message (no editor)" + +' + +test_expect_success 'with hook (-m editor)' ' + + echo "more" >> file && + git add file && + GIT_EDITOR="$FAKE_EDITOR" git commit -e -m "more more" && + test "`git log -1 --pretty=format:%s`" = message + +' + +test_expect_success 'with hook (-t)' ' + + echo "more" >> file && + git add file && + git commit -t "$(git rev-parse --git-dir)/template" && + test "`git log -1 --pretty=format:%s`" = template + +' + +test_expect_success 'with hook (-F)' ' + + echo "more" >> file && + git add file && + (echo more | git commit -F -) && + test "`git log -1 --pretty=format:%s`" = "message (no editor)" + +' + +test_expect_success 'with hook (-F editor)' ' + + echo "more" >> file && + git add file && + (echo more more | GIT_EDITOR="$FAKE_EDITOR" git commit -e -F -) && + test "`git log -1 --pretty=format:%s`" = message + +' + +test_expect_success 'with hook (-C)' ' + + head=`git rev-parse HEAD` && + echo "more" >> file && + git add file && + git commit -C $head && + test "`git log -1 --pretty=format:%s`" = "$head (no editor)" + +' + +test_expect_success 'with hook (editor)' ' + + echo "more more" >> file && + git add file && + GIT_EDITOR="$FAKE_EDITOR" git commit && + test "`git log -1 --pretty=format:%s`" = default + +' + +test_expect_success 'with hook (--amend)' ' + + head=`git rev-parse HEAD` && + echo "more" >> file && + git add file && + GIT_EDITOR="$FAKE_EDITOR" git commit --amend && + test "`git log -1 --pretty=format:%s`" = "$head" + +' + +test_expect_success 'with hook (-c)' ' + + head=`git rev-parse HEAD` && + echo "more" >> file && + git add file && + GIT_EDITOR="$FAKE_EDITOR" git commit -c $head && + test "`git log -1 --pretty=format:%s`" = "$head" + +' + +cat > "$HOOK" <<'EOF' +#!/bin/sh +exit 1 +EOF + +test_expect_success 'with failing hook' ' + + head=`git rev-parse HEAD` && + echo "more" >> file && + git add file && + ! GIT_EDITOR="$FAKE_EDITOR" git commit -c $head + +' + +test_expect_success 'with failing hook (--no-verify)' ' + + head=`git rev-parse HEAD` && + echo "more" >> file && + git add file && + ! GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify -c $head + +' + + +test_done diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index a15222ced4..49d57a81ec 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -2,7 +2,7 @@ # # Copyright (c) Robin Rosenberg # -test_description='CVS export comit. ' +test_description='Test export of commits to CVS' . ./test-lib.sh @@ -246,4 +246,20 @@ test_expect_success \ ;; esac +test_expect_success '-w option should work with relative GIT_DIR' ' + mkdir W && + echo foobar >W/file1.txt && + echo bazzle >W/file2.txt && + git add W/file1.txt && + git add W/file2.txt && + git commit -m "More updates" && + id=$(git rev-list --max-count=1 HEAD) && + (cd "$GIT_DIR" && + GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id && + check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" && + diff -u "$CVSWORK/W/file1.txt" ../W/file1.txt && + diff -u "$CVSWORK/W/file2.txt" ../W/file2.txt + ) +' + test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index da47bd7c9e..83889c4f46 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -324,8 +324,11 @@ test_done () { PATH=$(pwd)/..:$PATH GIT_EXEC_PATH=$(pwd)/.. GIT_TEMPLATE_DIR=$(pwd)/../templates/blt -GIT_CONFIG=.git/config -export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG +unset GIT_CONFIG +unset GIT_CONFIG_LOCAL +GIT_CONFIG_NOSYSTEM=1 +GIT_CONFIG_NOGLOBAL=1 +export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git export GITPERLLIB @@ -9,7 +9,10 @@ const char *tag_type = "tag"; struct object *deref_tag(struct object *o, const char *warn, int warnlen) { while (o && o->type == OBJ_TAG) - o = parse_object(((struct tag *)o)->tagged->sha1); + if (((struct tag *)o)->tagged) + o = parse_object(((struct tag *)o)->tagged->sha1); + else + o = NULL; if (!o && warn) { if (!warnlen) warnlen = strlen(warn); diff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg index c5cdb9d7ee..4ef86eb244 100644 --- a/templates/hooks--commit-msg +++ b/templates/hooks--commit-msg @@ -9,6 +9,9 @@ # To enable this hook, make this file executable. # Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/templates/hooks--prepare-commit-msg b/templates/hooks--prepare-commit-msg new file mode 100644 index 0000000000..ff0f42a1d9 --- /dev/null +++ b/templates/hooks--prepare-commit-msg @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by git-commit with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, make this file executable. + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2 $3" in + merge) + sed -i '/^Conflicts:/,/#/!b;s/^/# &/;s/^# #/#/' "$1" ;; + +# ""|template) +# perl -i -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/unpack-trees.c b/unpack-trees.c index ff46fd62fd..ec558f9005 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -521,8 +521,9 @@ static void verify_absent(struct cache_entry *ce, const char *action, if (!lstat(ce->name, &st)) { int cnt; + int dtype = ce_to_dtype(ce); - if (o->dir && excluded(o->dir, ce->name)) + if (o->dir && excluded(o->dir, ce->name, &dtype)) /* * ce->name is explicitly excluded, so it is Ok to * overwrite it. diff --git a/upload-pack.c b/upload-pack.c index 7e04311027..b26d05331d 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -129,7 +129,8 @@ static int do_rev_list(int fd, void *create_full_pack) } setup_revisions(0, NULL, &revs, NULL); } - prepare_revision_walk(&revs); + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); mark_edges_uninteresting(revs.commits, &revs, show_edge); traverse_commit_list(&revs, show_commit, show_object); return 0; @@ -533,7 +534,8 @@ static void receive_needs(void) /* make sure the real parents are parsed */ unregister_shallow(object->sha1); object->parsed = 0; - parse_commit((struct commit *)object); + if (parse_commit((struct commit *)object)) + die("invalid commit"); parents = ((struct commit *)object)->parents; while (parents) { add_object_array(&parents->item->object, @@ -575,7 +577,8 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo } if (o->type == OBJ_TAG) { o = deref_tag(o, refname, 0); - packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname); + if (o) + packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname); } return 0; } @@ -620,6 +623,9 @@ int main(int argc, char **argv) if (i != argc-1) usage(upload_pack_usage); + + setup_path(NULL); + dir = argv[i]; if (!enter_repo(dir, strict)) diff --git a/wt-status.c b/wt-status.c index f14022f2a9..0b060934e2 100644 --- a/wt-status.c +++ b/wt-status.c @@ -60,7 +60,7 @@ static void wt_status_print_cached_header(struct wt_status *s) { const char *c = color(WT_STATUS_HEADER); color_fprintf_ln(s->fp, c, "# Changes to be committed:"); - if (s->reference) { + if (!s->is_initial) { color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference); } else { color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)"); |