Code

Merge branch 'lt/dirstat'
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2008 02:14:53 +0000 (18:14 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Feb 2008 02:14:53 +0000 (18:14 -0800)
* lt/dirstat:
  diff --dirstat: saner handling of binary and unmerged files
  Add "--dirstat" for some directory statistics

156 files changed:
.gitignore
.mailmap
Documentation/RelNotes-1.5.4.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.4.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.4.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.5.5.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/git-add.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-cvsimport.txt
Documentation/git-fast-import.txt
Documentation/git-grep.txt
Documentation/git-help.txt
Documentation/git-instaweb.txt
Documentation/git-merge-index.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-reset.txt
Documentation/git-stash.txt
Documentation/git-web--browse.txt [new file with mode: 0644]
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitignore.txt
Documentation/hooks.txt
Documentation/technical/api-remote.txt [new file with mode: 0644]
Documentation/technical/api-run-command.txt
GIT-VERSION-GEN
Makefile
RelNotes
builtin-add.c
builtin-apply.c
builtin-blame.c
builtin-branch.c
builtin-clean.c
builtin-commit.c
builtin-config.c
builtin-diff.c
builtin-fast-export.c
builtin-fmt-merge-msg.c
builtin-grep.c
builtin-log.c
builtin-ls-files.c
builtin-mailinfo.c
builtin-mv.c
builtin-pack-objects.c
builtin-push.c
builtin-read-tree.c
builtin-rev-list.c
builtin-shortlog.c
builtin-show-ref.c
builtin-tag.c
bundle.c
cache.h
color.c
color.h
commit.c
compat/fopen.c [new file with mode: 0644]
compat/qsort.c [new file with mode: 0644]
config.c
contrib/emacs/git.el
contrib/hg-to-git/hg-to-git.py
convert.c
diff.c
diff.h
dir.c
dir.h
environment.c
fast-import.c
git-add--interactive.perl
git-bisect.sh
git-checkout.sh
git-clone.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-filter-branch.sh
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/about.tcl
git-gui/lib/checkout_op.tcl
git-gui/lib/choose_repository.tcl
git-gui/lib/commit.tcl
git-gui/lib/error.tcl
git-gui/lib/index.tcl
git-gui/lib/merge.tcl
git-gui/lib/option.tcl
git-gui/lib/spellcheck.tcl [new file with mode: 0644]
git-gui/macosx/Info.plist
git-gui/po/de.po
git-gui/po/git-gui.pot
git-help--browse.sh [deleted file]
git-instaweb.sh
git-pull.sh
git-send-email.perl
git-sh-setup.sh
git-web--browse.sh [new file with mode: 0755]
git.spec.in
gitk-git/gitk
gitweb/README
gitweb/gitweb.perl
hash.c
help.c
http-push.c
list-objects.c
log-tree.c
merge-index.c
merge-recursive.c
pager.c
pretty.c
reachable.c
read-cache.c
refs.c
remote.c
revision.c
revision.h
setup.c
sha1_file.c
sha1_name.c
shallow.c
strbuf.c
strbuf.h
t/t0020-crlf.sh
t/t1500-rev-parse.sh
t/t3001-ls-files-others-exclude.sh
t/t3404-rebase-interactive.sh
t/t3701-add-interactive.sh [new file with mode: 0755]
t/t4015-diff-whitespace.sh
t/t4019-diff-wserror.sh
t/t4105-apply-fuzz.sh [new file with mode: 0755]
t/t4125-apply-ws-fuzz.sh [new file with mode: 0755]
t/t5100-mailinfo.sh
t/t5100/info0009 [new file with mode: 0644]
t/t5100/msg0009 [new file with mode: 0644]
t/t5100/patch0009 [new file with mode: 0644]
t/t5100/sample.mbox
t/t5701-clone-local.sh
t/t7001-mv.sh
t/t7003-filter-branch.sh
t/t7010-setup.sh [new file with mode: 0755]
t/t7104-reset.sh [new file with mode: 0755]
t/t7300-clean.sh
t/t7502-commit.sh
t/t7502-status.sh
t/t7505-prepare-commit-msg-hook.sh [new file with mode: 0755]
t/t9001-send-email.sh
t/t9200-git-cvsexportcommit.sh
t/test-lib.sh
tag.c
templates/hooks--commit-msg
templates/hooks--prepare-commit-msg [new file with mode: 0644]
unpack-trees.c
upload-pack.c
ws.c
wt-status.c

index 7f8421dcd0f6ea01c3a5c0757c588bcbfb9b493c..165b256e2e81ef36ca6b8f9dd8db8d0cc4f164d4 100644 (file)
@@ -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-*/?*
index a32d9e2a3f95a4d6cdfb050f2454e31192dea34e..f88ae77a1f298004320bb8cc9e6a2bd9b0cafd06 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -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>
diff --git a/Documentation/RelNotes-1.5.4.1.txt b/Documentation/RelNotes-1.5.4.1.txt
new file mode 100644 (file)
index 0000000..d4e44b8
--- /dev/null
@@ -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 (file)
index 0000000..21d0df5
--- /dev/null
@@ -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.4.3.txt b/Documentation/RelNotes-1.5.4.3.txt
new file mode 100644 (file)
index 0000000..b0fc67f
--- /dev/null
@@ -0,0 +1,27 @@
+GIT v1.5.4.3 Release Notes
+==========================
+
+Fixes since v1.5.4.2
+--------------------
+
+ * RPM spec used to pull in everything with 'git'.  This has been
+   changed so that 'git' package contains just the core parts,
+   and we now supply 'git-all' metapackage to slurp in everything.
+   This should match end user's expectation better.
+
+ * When some refs failed to update, git-push reported "failure"
+   which was unclear if some other refs were updated or all of
+   them failed atomically (the answer is the former).  Reworded
+   the message to clarify this.
+
+ * "git clone" from a repository whose HEAD was misconfigured
+   did not set up the remote properly.  Now it tries to do
+   better.
+
+ * Updated git-push documentation to clarify what "matching"
+   means, in order to reduce user confusion.
+
+ * Updated git-add documentation to clarify "add -u" operates in
+   the current subdirectory you are in, just like other commands.
+
+ * git-gui updates to work on OSX and Windows better.
diff --git a/Documentation/RelNotes-1.5.5.txt b/Documentation/RelNotes-1.5.5.txt
new file mode 100644 (file)
index 0000000..c8b4f72
--- /dev/null
@@ -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
+
index de08d094e3e3683913bac49f643bb6b49fbec04d..0e155c936c25255706bc6d47651c2b336c628417 100644 (file)
@@ -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
index f9bdb164e054ec2d633baab3da69ddd47e14931f..fb6dae0cc2d399821c708c77904565e00ff2eb39 100644 (file)
@@ -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
@@ -308,6 +353,10 @@ core.whitespace::
   error (enabled by default).
 * `indent-with-non-tab` treats a line that is indented with 8 or more
   space characters as an error (not enabled by default).
+* `cr-at-eol` treats a carriage-return at the end of line as
+  part of the line terminator, i.e. with it, `trailing-space`
+  does not trigger if the character before such a carriage-return
+  is not a whitespace (not enabled by default).
 
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
@@ -444,6 +493,13 @@ color.status.<slot>::
 commit.template::
        Specify a file to use as the template for new commit messages.
 
+color.ui::
+       When set to `always`, always use colors in all git commands which
+       are capable of colored output. When false (or `never`), never. When
+       set to `true` or `auto`, use colors only when the output is to the
+       terminal. When more specific variables of color.* are set, they always
+       take precedence over this setting. Defaults to false.
+
 diff.autorefreshindex::
        When using `git diff` to compare with work tree
        files, do not consider stat-only change as changed.
index 9d2ac865d29164dd594c801dd42e3492128f7b91..47799097ce97da34fac60eafabd25576e077e362 100644 (file)
@@ -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()
index 23418816146d0b65eeabdd7a3d67dcd7e8c760e1..975824301a45bfa297f2ecd5c19e429cb9c86c65 100644 (file)
@@ -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.
 
 
 
index c3725b2ed9958cdc02b312895f35d016e3579d10..b4ae61ff46f4b7ff9e71e9e91a999fdc8fc17560 100644 (file)
@@ -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.
 
 
index dbce503694049ced5232423ffa11bb9a896e3c52..6f91b9ea2a17df8fc8e3c00fdf0d5c74a6c17950 100644 (file)
@@ -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.
index bd625ababfc950318714b3271b02da938fbf609b..96f6767075b4173b81acf3eb413f5e1a35acb940 100644 (file)
@@ -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
index f3cb24f252e2091d4d2f346b61ce87088fa7b02e..71a73354f8cfa960f641d9563ee8af405b4f881f 100644 (file)
@@ -75,9 +75,11 @@ OPTIONS
 -n::
        Prefix the line number to matching lines.
 
--l | --files-with-matches | -L | --files-without-match::
+-l | --files-with-matches | --name-only | -L | --files-without-match::
        Instead of showing every matched line, show only the
        names of files that contain (or do not contain) matches.
+       For better compatability with git-diff, --name-only is a
+       synonym for --files-with-matches.
 
 -c | --count::
        Instead of showing every matched line, show the number of
index fb77ca3a57d9377e8939c6d32601cf1386f0261b..0926dc12bab23285256b8cd7906b79b7e5f5616f 100644 (file)
@@ -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:
index 841e8fac7fd61f8e5250c1747935156c93999394..51f1532ef7ce34786ec0dcae32b9e60b710cc9c4 100644 (file)
@@ -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
 ------
index 5d816d0d8b02222517cd4bf320dc3d345fb8d94b..19ee017aede4bf8fa0a0046b2bf347e3d75c4a12 100644 (file)
@@ -8,7 +8,7 @@ git-merge-index - Run a merge for files needing merging
 
 SYNOPSIS
 --------
-'git-merge-index' [-o] [-q] <merge-program> (-a | \-- | <file>\*)
+'git-merge-index' [-o] [-q] <merge-program> (-a | [--] <file>\*)
 
 DESCRIPTION
 -----------
index 179bdfc69ddfeff9d272ec309721e4098f3b051e..737894390d8033bf470b3bfe3aeade858f38717e 100644 (file)
@@ -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
 -----------------
 
index 5f2494495bcec47d0de2daf109d46ec3c76cd912..3128170bcd1c3e3eb5981ed4c2f35221dfb06658 100644 (file)
@@ -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
 --------
index a4e0a779de6a8e2de7ec9012bc66f15ca351e2aa..fac59c972659111aa2bba365c6aba94577d95702 100644 (file)
@@ -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>
index cd0dc1bd9d53282d65c52e6b9ded6d40fe087598..48e6f5a3f7a85e886639381dfa70434ecdeccfc2 100644 (file)
@@ -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 (file)
index 0000000..df57d01
--- /dev/null
@@ -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
index e22c4c4ae9c9b1cefca1c4c64ae03562bc3f301b..741ae0e4c8ec9a3aaaef19430b0f76e4baedb546 100644 (file)
@@ -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.1/git.html[documentation for release 1.5.4.1]
+* link:v1.5.4.3/git.html[documentation for release 1.5.4.3]
 
 * release notes for
+  link:RelNotes-1.5.4.3.txt[1.5.4.3],
+  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].
 
index 35a29fd60c4aa70fbbd7c05ed40e4b545225c74d..84ec9623a26e2c36f7346a430a46a4a4915e0382 100644 (file)
@@ -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`
 ^^^^^^^
index 08373f52bb7e5537049b12abff3e07c02769e7b6..e847b3ba63f3fae3d5497d6ede1ea0ee416a74de 100644 (file)
@@ -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.
index f110162b0155b3b17bc3133c5f42504290c1de4d..76b8d77460e961fda9a5505a4ce0cf20788de133 100644 (file)
@@ -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 (file)
index 0000000..073b22b
--- /dev/null
@@ -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)
index 19d2f64f73f34b12c6fdb954174b33946562b304..dfbf9ac5d06e32a7961afdd08910df6eccb912c7 100644 (file)
 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.
index 38a3273974e3313550218cdb8b50f3f2a8b4dec6..6ddf04d2161e5fd959c2fd69a7331520e8075d98 100755 (executable)
@@ -15,7 +15,9 @@ 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]*)
+               test -z "$(git diff-index --name-only HEAD)" ||
+               VN="$VN-dirty" ;;
        esac
 then
        VN=$(echo "$VN" | sed -e 's/-/./g');
@@ -25,14 +27,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)
index 92341c4bbe3f65671271211c9cbac3d4ffdc3eef..a055f11aa65a89489344fc9b80332c0669a2549e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,9 @@ all::
 
 # Define V=1 to have a more verbose compile.
 #
+# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
+# when attempting to read from an fopen'ed directory.
+#
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 # This also implies MOZILLA_SHA1.
 #
@@ -137,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
@@ -231,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 \
@@ -618,6 +625,10 @@ endif
 ifdef NO_C99_FORMAT
        BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
+ifdef FREAD_READS_DIRECTORIES
+       COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
+       COMPAT_OBJS += compat/fopen.o
+endif
 ifdef NO_SYMLINK_HEAD
        BASIC_CFLAGS += -DNO_SYMLINK_HEAD
 endif
@@ -722,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
@@ -819,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)"' $<
 
@@ -839,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 $@+ $@
@@ -1087,7 +1102,7 @@ git.spec: git.spec.in
        mv $@+ $@
 
 GIT_TARNAME=git-$(GIT_VERSION)
-dist: git.spec git-archive configure
+dist: git.spec git-archive$(X) configure
        ./git-archive --format=tar \
                --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
        @mkdir -p $(GIT_TARNAME)
index 46308cee0ba1ca2cf7d7d9c3de6abafe20c1493f..3e77358f704fde7271f18ccbf90a57419550b265 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -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
index 4a91e3eb118850882fbc22e8d8f37e8bbfaa7617..820110e085f20d8615c74167cf3ae4cfd1fe6912 100644 (file)
@@ -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) {
index 46dad5b2a1ce6d70d540df8a40da94702dde68c7..a3f075df4bd91de8d3423420cfcffd9722ad9586 100644 (file)
@@ -161,6 +161,84 @@ struct patch {
        struct patch *next;
 };
 
+/*
+ * A line in a file, len-bytes long (includes the terminating LF,
+ * except for an incomplete line at the end if the file ends with
+ * one), and its contents hashes to 'hash'.
+ */
+struct line {
+       size_t len;
+       unsigned hash : 24;
+       unsigned flag : 8;
+#define LINE_COMMON     1
+};
+
+/*
+ * This represents a "file", which is an array of "lines".
+ */
+struct image {
+       char *buf;
+       size_t len;
+       size_t nr;
+       size_t alloc;
+       struct line *line_allocated;
+       struct line *line;
+};
+
+static uint32_t hash_line(const char *cp, size_t len)
+{
+       size_t i;
+       uint32_t h;
+       for (i = 0, h = 0; i < len; i++) {
+               if (!isspace(cp[i])) {
+                       h = h * 3 + (cp[i] & 0xff);
+               }
+       }
+       return h;
+}
+
+static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
+{
+       ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
+       img->line_allocated[img->nr].len = len;
+       img->line_allocated[img->nr].hash = hash_line(bol, len);
+       img->line_allocated[img->nr].flag = flag;
+       img->nr++;
+}
+
+static void prepare_image(struct image *image, char *buf, size_t len,
+                         int prepare_linetable)
+{
+       const char *cp, *ep;
+
+       memset(image, 0, sizeof(*image));
+       image->buf = buf;
+       image->len = len;
+
+       if (!prepare_linetable)
+               return;
+
+       ep = image->buf + image->len;
+       cp = image->buf;
+       while (cp < ep) {
+               const char *next;
+               for (next = cp; next < ep && *next != '\n'; next++)
+                       ;
+               if (next < ep)
+                       next++;
+               add_line_info(image, cp, next - cp, 0);
+               cp = next;
+       }
+       image->line = image->line_allocated;
+}
+
+static void clear_image(struct image *image)
+{
+       free(image->buf);
+       image->buf = NULL;
+       image->len = 0;
+}
+
 static void say_patch_name(FILE *output, const char *pre,
                           struct patch *patch, const char *post)
 {
@@ -1430,234 +1508,345 @@ 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;
        }
 }
 
-static int find_offset(const char *buf, unsigned long size,
-                      const char *fragment, unsigned long fragsize,
-                      int line, int *lines)
+static void update_pre_post_images(struct image *preimage,
+                                  struct image *postimage,
+                                  char *buf,
+                                  size_t len)
 {
-       int i;
-       unsigned long start, backwards, forwards;
+       int i, ctx;
+       char *new, *old, *fixed;
+       struct image fixed_preimage;
 
-       if (fragsize > size)
-               return -1;
+       /*
+        * Update the preimage with whitespace fixes.  Note that we
+        * are not losing preimage->buf -- apply_one_fragment() will
+        * free "oldlines".
+        */
+       prepare_image(&fixed_preimage, buf, len, 1);
+       assert(fixed_preimage.nr == preimage->nr);
+       for (i = 0; i < preimage->nr; i++)
+               fixed_preimage.line[i].flag = preimage->line[i].flag;
+       free(preimage->line_allocated);
+       *preimage = fixed_preimage;
 
-       start = 0;
-       if (line > 1) {
-               unsigned long offset = 0;
-               i = line-1;
-               while (offset + fragsize <= size) {
-                       if (buf[offset++] == '\n') {
-                               start = offset;
-                               if (!--i)
-                                       break;
-                       }
+       /*
+        * Adjust the common context lines in postimage, in place.
+        * This is possible because whitespace fixing does not make
+        * the string grow.
+        */
+       new = old = postimage->buf;
+       fixed = preimage->buf;
+       for (i = ctx = 0; i < postimage->nr; i++) {
+               size_t len = postimage->line[i].len;
+               if (!(postimage->line[i].flag & LINE_COMMON)) {
+                       /* an added line -- no counterparts in preimage */
+                       memmove(new, old, len);
+                       old += len;
+                       new += len;
+                       continue;
                }
+
+               /* a common context -- skip it in the original postimage */
+               old += len;
+
+               /* and find the corresponding one in the fixed preimage */
+               while (ctx < preimage->nr &&
+                      !(preimage->line[ctx].flag & LINE_COMMON)) {
+                       fixed += preimage->line[ctx].len;
+                       ctx++;
+               }
+               if (preimage->nr <= ctx)
+                       die("oops");
+
+               /* and copy it in, while fixing the line length */
+               len = preimage->line[ctx].len;
+               memcpy(new, fixed, len);
+               new += len;
+               fixed += len;
+               postimage->line[i].len = len;
+               ctx++;
        }
 
-       /* Exact line number? */
-       if ((start + fragsize <= size) &&
-           !memcmp(buf + start, fragment, fragsize))
-               return start;
+       /* Fix the length of the whole thing */
+       postimage->len = new - postimage->buf;
+}
+
+static int match_fragment(struct image *img,
+                         struct image *preimage,
+                         struct image *postimage,
+                         unsigned long try,
+                         int try_lno,
+                         unsigned ws_rule,
+                         int match_beginning, int match_end)
+{
+       int i;
+       char *fixed_buf, *buf, *orig, *target;
+
+       if (preimage->nr + try_lno > img->nr)
+               return 0;
+
+       if (match_beginning && try_lno)
+               return 0;
+
+       if (match_end && preimage->nr + try_lno != img->nr)
+               return 0;
+
+       /* Quick hash check */
+       for (i = 0; i < preimage->nr; i++)
+               if (preimage->line[i].hash != img->line[try_lno + i].hash)
+                       return 0;
+
+       /*
+        * Do we have an exact match?  If we were told to match
+        * at the end, size must be exactly at try+fragsize,
+        * otherwise try+fragsize must be still within the preimage,
+        * and either case, the old piece should match the preimage
+        * exactly.
+        */
+       if ((match_end
+            ? (try + preimage->len == img->len)
+            : (try + preimage->len <= img->len)) &&
+           !memcmp(img->buf + try, preimage->buf, preimage->len))
+               return 1;
+
+       if (ws_error_action != correct_ws_error)
+               return 0;
+
+       /*
+        * The hunk does not apply byte-by-byte, but the hash says
+        * it might with whitespace fuzz.
+        */
+       fixed_buf = xmalloc(preimage->len + 1);
+       buf = fixed_buf;
+       orig = preimage->buf;
+       target = img->buf + try;
+       for (i = 0; i < preimage->nr; i++) {
+               size_t fixlen; /* length after fixing the preimage */
+               size_t oldlen = preimage->line[i].len;
+               size_t tgtlen = img->line[try_lno + i].len;
+               size_t tgtfixlen; /* length after fixing the target line */
+               char tgtfixbuf[1024], *tgtfix;
+               int match;
+
+               /* Try fixing the line in the preimage */
+               fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+
+               /* Try fixing the line in the target */
+               if (sizeof(tgtfixbuf) < tgtlen)
+                       tgtfix = tgtfixbuf;
+               else
+                       tgtfix = xmalloc(tgtlen);
+               tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+
+               /*
+                * If they match, either the preimage was based on
+                * a version before our tree fixed whitespace breakage,
+                * or we are lacking a whitespace-fix patch the tree
+                * the preimage was based on already had (i.e. target
+                * has whitespace breakage, the preimage doesn't).
+                * In either case, we are fixing the whitespace breakages
+                * so we might as well take the fix together with their
+                * real change.
+                */
+               match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+
+               if (tgtfix != tgtfixbuf)
+                       free(tgtfix);
+               if (!match)
+                       goto unmatch_exit;
+
+               orig += oldlen;
+               buf += fixlen;
+               target += tgtlen;
+       }
+
+       /*
+        * Yes, the preimage is based on an older version that still
+        * has whitespace breakages unfixed, and fixing them makes the
+        * hunk match.  Update the context lines in the postimage.
+        */
+       update_pre_post_images(preimage, postimage,
+                              fixed_buf, buf - fixed_buf);
+       return 1;
+
+ unmatch_exit:
+       free(fixed_buf);
+       return 0;
+}
+
+static int find_pos(struct image *img,
+                   struct image *preimage,
+                   struct image *postimage,
+                   int line,
+                   unsigned ws_rule,
+                   int match_beginning, int match_end)
+{
+       int i;
+       unsigned long backwards, forwards, try;
+       int backwards_lno, forwards_lno, try_lno;
+
+       if (preimage->nr > img->nr)
+               return -1;
+
+       /*
+        * If match_begining or match_end is specified, there is no
+        * point starting from a wrong line that will never match and
+        * wander around and wait for a match at the specified end.
+        */
+       if (match_beginning)
+               line = 0;
+       else if (match_end)
+               line = img->nr - preimage->nr;
+
+       if (line > img->nr)
+               line = img->nr;
+
+       try = 0;
+       for (i = 0; i < line; i++)
+               try += img->line[i].len;
 
        /*
         * There's probably some smart way to do this, but I'll leave
         * that to the smart and beautiful people. I'm simple and stupid.
         */
-       backwards = start;
-       forwards = start;
+       backwards = try;
+       backwards_lno = line;
+       forwards = try;
+       forwards_lno = line;
+       try_lno = line;
+
        for (i = 0; ; i++) {
-               unsigned long try;
-               int n;
+               if (match_fragment(img, preimage, postimage,
+                                  try, try_lno, ws_rule,
+                                  match_beginning, match_end))
+                       return try_lno;
+
+       again:
+               if (backwards_lno == 0 && forwards_lno == img->nr)
+                       break;
 
-               /* "backward" */
                if (i & 1) {
-                       if (!backwards) {
-                               if (forwards + fragsize > size)
-                                       break;
-                               continue;
+                       if (backwards_lno == 0) {
+                               i++;
+                               goto again;
                        }
-                       do {
-                               --backwards;
-                       } while (backwards && buf[backwards-1] != '\n');
+                       backwards_lno--;
+                       backwards -= img->line[backwards_lno].len;
                        try = backwards;
+                       try_lno = backwards_lno;
                } else {
-                       while (forwards + fragsize <= size) {
-                               if (buf[forwards++] == '\n')
-                                       break;
+                       if (forwards_lno == img->nr) {
+                               i++;
+                               goto again;
                        }
+                       forwards += img->line[forwards_lno].len;
+                       forwards_lno++;
                        try = forwards;
+                       try_lno = forwards_lno;
                }
 
-               if (try + fragsize > size)
-                       continue;
-               if (memcmp(buf + try, fragment, fragsize))
-                       continue;
-               n = (i >> 1)+1;
-               if (i & 1)
-                       n = -n;
-               *lines = n;
-               return try;
        }
-
-       /*
-        * We should start searching forward and backward.
-        */
        return -1;
 }
 
-static void remove_first_line(const char **rbuf, int *rsize)
+static void remove_first_line(struct image *img)
 {
-       const char *buf = *rbuf;
-       int size = *rsize;
-       unsigned long offset;
-       offset = 0;
-       while (offset <= size) {
-               if (buf[offset++] == '\n')
-                       break;
-       }
-       *rsize = size - offset;
-       *rbuf = buf + offset;
+       img->buf += img->line[0].len;
+       img->len -= img->line[0].len;
+       img->line++;
+       img->nr--;
 }
 
-static void remove_last_line(const char **rbuf, int *rsize)
+static void remove_last_line(struct image *img)
 {
-       const char *buf = *rbuf;
-       int size = *rsize;
-       unsigned long offset;
-       offset = size - 1;
-       while (offset > 0) {
-               if (buf[--offset] == '\n')
-                       break;
-       }
-       *rsize = offset + 1;
+       img->len -= img->line[--img->nr].len;
 }
 
-static int apply_line(char *output, const char *patch, int plen,
-                     unsigned ws_rule)
+static void update_image(struct image *img,
+                        int applied_pos,
+                        struct image *preimage,
+                        struct image *postimage)
 {
        /*
-        * plen is number of bytes to be copied from patch,
-        * starting at patch+1 (patch[0] is '+').  Typically
-        * patch[plen] is '\n', unless this is the incomplete
-        * last line.
+        * remove the copy of preimage at offset in img
+        * and replace it with postimage
         */
-       int i;
-       int add_nl_to_tail = 0;
-       int fixed = 0;
-       int last_tab_in_indent = 0;
-       int last_space_in_indent = 0;
-       int need_fix_leading_space = 0;
-       char *buf;
-
-       if ((ws_error_action != correct_ws_error) || !whitespace_error ||
-           *patch != '+') {
-               memcpy(output, patch + 1, plen);
-               return plen;
-       }
-
-       /*
-        * Strip trailing whitespace
-        */
-       if ((ws_rule & WS_TRAILING_SPACE) &&
-           (1 < plen && isspace(patch[plen-1]))) {
-               if (patch[plen] == '\n')
-                       add_nl_to_tail = 1;
-               plen--;
-               while (0 < plen && isspace(patch[plen]))
-                       plen--;
-               fixed = 1;
-       }
-
-       /*
-        * Check leading whitespaces (indent)
-        */
-       for (i = 1; i < plen; i++) {
-               char ch = patch[i];
-               if (ch == '\t') {
-                       last_tab_in_indent = i;
-                       if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
-                           0 < last_space_in_indent)
-                           need_fix_leading_space = 1;
-               } else if (ch == ' ') {
-                       last_space_in_indent = i;
-                       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
-                           8 <= i - last_tab_in_indent)
-                               need_fix_leading_space = 1;
-               }
-               else
-                       break;
-       }
-
-       buf = output;
-       if (need_fix_leading_space) {
-               int consecutive_spaces = 0;
-               int last = last_tab_in_indent + 1;
-
-               if (ws_rule & WS_INDENT_WITH_NON_TAB) {
-                       /* have "last" point at one past the indent */
-                       if (last_tab_in_indent < last_space_in_indent)
-                               last = last_space_in_indent + 1;
-                       else
-                               last = last_tab_in_indent + 1;
-               }
+       int i, nr;
+       size_t remove_count, insert_count, applied_at = 0;
+       char *result;
 
+       for (i = 0; i < applied_pos; i++)
+               applied_at += img->line[i].len;
+
+       remove_count = 0;
+       for (i = 0; i < preimage->nr; i++)
+               remove_count += img->line[applied_pos + i].len;
+       insert_count = postimage->len;
+
+       /* Adjust the contents */
+       result = xmalloc(img->len + insert_count - remove_count + 1);
+       memcpy(result, img->buf, applied_at);
+       memcpy(result + applied_at, postimage->buf, postimage->len);
+       memcpy(result + applied_at + postimage->len,
+              img->buf + (applied_at + remove_count),
+              img->len - (applied_at + remove_count));
+       free(img->buf);
+       img->buf = result;
+       img->len += insert_count - remove_count;
+       result[img->len] = '\0';
+
+       /* Adjust the line table */
+       nr = img->nr + postimage->nr - preimage->nr;
+       if (preimage->nr < postimage->nr) {
                /*
-                * between patch[1..last], strip the funny spaces,
-                * updating them to tab as needed.
+                * NOTE: this knows that we never call remove_first_line()
+                * on anything other than pre/post image.
                 */
-               for (i = 1; i < last; i++, plen--) {
-                       char ch = patch[i];
-                       if (ch != ' ') {
-                               consecutive_spaces = 0;
-                               *output++ = ch;
-                       } else {
-                               consecutive_spaces++;
-                               if (consecutive_spaces == 8) {
-                                       *output++ = '\t';
-                                       consecutive_spaces = 0;
-                               }
-                       }
-               }
-               while (0 < consecutive_spaces--)
-                       *output++ = ' ';
-               fixed = 1;
-               i = last;
+               img->line = xrealloc(img->line, nr * sizeof(*img->line));
+               img->line_allocated = img->line;
        }
-       else
-               i = 1;
-
-       memcpy(output, patch + i, plen);
-       if (add_nl_to_tail)
-               output[plen++] = '\n';
-       if (fixed)
-               applied_after_fixing_ws++;
-       return output + plen - buf;
+       if (preimage->nr != postimage->nr)
+               memmove(img->line + applied_pos + postimage->nr,
+                       img->line + applied_pos + preimage->nr,
+                       (img->nr - (applied_pos + preimage->nr)) *
+                       sizeof(*img->line));
+       memcpy(img->line + applied_pos,
+              postimage->line,
+              postimage->nr * sizeof(*img->line));
+       img->nr = nr;
 }
 
-static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
+static int apply_one_fragment(struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule)
 {
        int match_beginning, match_end;
        const char *patch = frag->patch;
-       int offset, size = frag->size;
-       char *old = xmalloc(size);
-       char *new = xmalloc(size);
-       const char *oldlines, *newlines;
-       int oldsize = 0, newsize = 0;
+       int size = frag->size;
+       char *old, *new, *oldlines, *newlines;
        int new_blank_lines_at_end = 0;
        unsigned long leading, trailing;
-       int pos, lines;
+       int pos, applied_pos;
+       struct image preimage;
+       struct image postimage;
+
+       memset(&preimage, 0, sizeof(preimage));
+       memset(&postimage, 0, sizeof(postimage));
+       oldlines = xmalloc(size);
+       newlines = xmalloc(size);
 
+       old = oldlines;
+       new = newlines;
        while (size > 0) {
                char first;
                int len = linelen(patch, size);
-               int plen;
+               int plen, added;
                int added_blank_line = 0;
 
                if (!len)
@@ -1670,7 +1859,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                 * followed by "\ No newline", then we also remove the
                 * last one (which is the newline, of course).
                 */
-               plen = len-1;
+               plen = len - 1;
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
@@ -1687,25 +1876,40 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                        if (plen < 0)
                                /* ... followed by '\No newline'; nothing */
                                break;
-                       old[oldsize++] = '\n';
-                       new[newsize++] = '\n';
+                       *old++ = '\n';
+                       *new++ = '\n';
+                       add_line_info(&preimage, "\n", 1, LINE_COMMON);
+                       add_line_info(&postimage, "\n", 1, LINE_COMMON);
                        break;
                case ' ':
                case '-':
-                       memcpy(old + oldsize, patch + 1, plen);
-                       oldsize += plen;
+                       memcpy(old, patch + 1, plen);
+                       add_line_info(&preimage, old, plen,
+                                     (first == ' ' ? LINE_COMMON : 0));
+                       old += plen;
                        if (first == '-')
                                break;
                /* Fall-through for ' ' */
                case '+':
-                       if (first != '+' || !no_add) {
-                               int added = apply_line(new + newsize, patch,
-                                                      plen, ws_rule);
-                               newsize += added;
-                               if (first == '+' &&
-                                   added == 1 && new[newsize-1] == '\n')
-                                       added_blank_line = 1;
+                       /* --no-add does not add new lines */
+                       if (first == '+' && no_add)
+                               break;
+
+                       if (first != '+' ||
+                           !whitespace_error ||
+                           ws_error_action != correct_ws_error) {
+                               memcpy(new, patch + 1, plen);
+                               added = plen;
                        }
+                       else {
+                               added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+                       }
+                       add_line_info(&postimage, new, added,
+                                     (first == '+' ? 0 : LINE_COMMON));
+                       new += added;
+                       if (first == '+' &&
+                           added == 1 && new[-1] == '\n')
+                               added_blank_line = 1;
                        break;
                case '@': case '\\':
                        /* Ignore it, we already handled it */
@@ -1722,16 +1926,13 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                patch += len;
                size -= len;
        }
-
        if (inaccurate_eof &&
-           oldsize > 0 && old[oldsize - 1] == '\n' &&
-           newsize > 0 && new[newsize - 1] == '\n') {
-               oldsize--;
-               newsize--;
+           old > oldlines && old[-1] == '\n' &&
+           new > newlines && new[-1] == '\n') {
+               old--;
+               new--;
        }
 
-       oldlines = old;
-       newlines = new;
        leading = frag->leading;
        trailing = frag->trailing;
 
@@ -1752,33 +1953,21 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                match_end = !trailing;
        }
 
-       lines = 0;
-       pos = frag->newpos;
+       pos = frag->newpos ? (frag->newpos - 1) : 0;
+       preimage.buf = oldlines;
+       preimage.len = old - oldlines;
+       postimage.buf = newlines;
+       postimage.len = new - newlines;
+       preimage.line = preimage.line_allocated;
+       postimage.line = postimage.line_allocated;
+
        for (;;) {
-               offset = find_offset(buf->buf, buf->len,
-                                    oldlines, oldsize, pos, &lines);
-               if (match_end && offset + oldsize != buf->len)
-                       offset = -1;
-               if (match_beginning && offset)
-                       offset = -1;
-               if (offset >= 0) {
-                       if (ws_error_action == correct_ws_error &&
-                           (buf->len - oldsize - offset == 0)) /* end of file? */
-                               newsize -= new_blank_lines_at_end;
-
-                       /* Warn if it was necessary to reduce the number
-                        * of context lines.
-                        */
-                       if ((leading != frag->leading) ||
-                           (trailing != frag->trailing))
-                               fprintf(stderr, "Context reduced to (%ld/%ld)"
-                                       " to apply fragment at %d\n",
-                                       leading, trailing, pos + lines);
-
-                       strbuf_splice(buf, offset, oldsize, newlines, newsize);
-                       offset = 0;
+
+               applied_pos = find_pos(img, &preimage, &postimage, pos,
+                                      ws_rule, match_beginning, match_end);
+
+               if (applied_pos >= 0)
                        break;
-               }
 
                /* Am I at my context limits? */
                if ((leading <= p_context) && (trailing <= p_context))
@@ -1787,33 +1976,64 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
                        match_beginning = match_end = 0;
                        continue;
                }
+
                /*
                 * Reduce the number of context lines; reduce both
                 * leading and trailing if they are equal otherwise
                 * just reduce the larger context.
                 */
                if (leading >= trailing) {
-                       remove_first_line(&oldlines, &oldsize);
-                       remove_first_line(&newlines, &newsize);
+                       remove_first_line(&preimage);
+                       remove_first_line(&postimage);
                        pos--;
                        leading--;
                }
                if (trailing > leading) {
-                       remove_last_line(&oldlines, &oldsize);
-                       remove_last_line(&newlines, &newsize);
+                       remove_last_line(&preimage);
+                       remove_last_line(&postimage);
                        trailing--;
                }
        }
 
-       if (offset && apply_verbosely)
-               error("while searching for:\n%.*s", oldsize, oldlines);
+       if (applied_pos >= 0) {
+               if (ws_error_action == correct_ws_error &&
+                   new_blank_lines_at_end &&
+                   postimage.nr + applied_pos == img->nr) {
+                       /*
+                        * If the patch application adds blank lines
+                        * at the end, and if the patch applies at the
+                        * end of the image, remove those added blank
+                        * lines.
+                        */
+                       while (new_blank_lines_at_end--)
+                               remove_last_line(&postimage);
+               }
 
-       free(old);
-       free(new);
-       return offset;
+               /*
+                * Warn if it was necessary to reduce the number
+                * of context lines.
+                */
+               if ((leading != frag->leading) ||
+                   (trailing != frag->trailing))
+                       fprintf(stderr, "Context reduced to (%ld/%ld)"
+                               " to apply fragment at %d\n",
+                               leading, trailing, applied_pos+1);
+               update_image(img, applied_pos, &preimage, &postimage);
+       } else {
+               if (apply_verbosely)
+                       error("while searching for:\n%.*s",
+                             (int)(old - oldlines), oldlines);
+       }
+
+       free(oldlines);
+       free(newlines);
+       free(preimage.line_allocated);
+       free(postimage.line_allocated);
+
+       return (applied_pos < 0);
 }
 
-static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
+static int apply_binary_fragment(struct image *img, struct patch *patch)
 {
        struct fragment *fragment = patch->fragments;
        unsigned long len;
@@ -1830,22 +2050,26 @@ static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
        }
        switch (fragment->binary_patch_method) {
        case BINARY_DELTA_DEFLATED:
-               dst = patch_delta(buf->buf, buf->len, fragment->patch,
+               dst = patch_delta(img->buf, img->len, fragment->patch,
                                  fragment->size, &len);
                if (!dst)
                        return -1;
-               /* XXX patch_delta NUL-terminates */
-               strbuf_attach(buf, dst, len, len + 1);
+               clear_image(img);
+               img->buf = dst;
+               img->len = len;
                return 0;
        case BINARY_LITERAL_DEFLATED:
-               strbuf_reset(buf);
-               strbuf_add(buf, fragment->patch, fragment->size);
+               clear_image(img);
+               img->len = fragment->size;
+               img->buf = xmalloc(img->len+1);
+               memcpy(img->buf, fragment->patch, img->len);
+               img->buf[img->len] = '\0';
                return 0;
        }
        return -1;
 }
 
-static int apply_binary(struct strbuf *buf, struct patch *patch)
+static int apply_binary(struct image *img, struct patch *patch)
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
@@ -1866,7 +2090,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
                 * See if the old one matches what the patch
                 * applies to.
                 */
-               hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
+               hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
                        return error("the patch applies to '%s' (%s), "
                                     "which does not match the "
@@ -1875,14 +2099,14 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
        }
        else {
                /* Otherwise, the old one must be empty. */
-               if (buf->len)
+               if (img->len)
                        return error("the patch applies to an empty "
                                     "'%s' but it is not empty", name);
        }
 
        get_sha1_hex(patch->new_sha1_prefix, sha1);
        if (is_null_sha1(sha1)) {
-               strbuf_release(buf);
+               clear_image(img);
                return 0; /* deletion patch */
        }
 
@@ -1897,20 +2121,21 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
                        return error("the necessary postimage %s for "
                                     "'%s' cannot be read",
                                     patch->new_sha1_prefix, name);
-               /* XXX read_sha1_file NUL-terminates */
-               strbuf_attach(buf, result, size, size + 1);
+               clear_image(img);
+               img->buf = result;
+               img->len = size;
        } else {
                /*
                 * We have verified buf matches the preimage;
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
-               if (apply_binary_fragment(buf, patch))
+               if (apply_binary_fragment(img, patch))
                        return error("binary patch does not apply to '%s'",
                                     name);
 
                /* verify that the result matches */
-               hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
+               hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
                        return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
                                name, patch->new_sha1_prefix, sha1_to_hex(sha1));
@@ -1919,7 +2144,7 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
        return 0;
 }
 
-static int apply_fragments(struct strbuf *buf, struct patch *patch)
+static int apply_fragments(struct image *img, struct patch *patch)
 {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
@@ -1927,10 +2152,10 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)
        unsigned inaccurate_eof = patch->inaccurate_eof;
 
        if (patch->is_binary)
-               return apply_binary(buf, patch);
+               return apply_binary(img, patch);
 
        while (frag) {
-               if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
+               if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
@@ -1966,6 +2191,9 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
        struct strbuf buf;
+       struct image image;
+       size_t len;
+       char *img;
 
        strbuf_init(&buf, 0);
        if (cached) {
@@ -1988,9 +2216,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
                }
        }
 
-       if (apply_fragments(&buf, patch) < 0)
+       img = strbuf_detach(&buf, &len);
+       prepare_image(&image, img, len, !patch->is_binary);
+
+       if (apply_fragments(&image, patch) < 0)
                return -1; /* note with --reject this succeeds. */
-       patch->result = strbuf_detach(&buf, &patch->resultsize);
+       patch->result = image.buf;
+       patch->resultsize = image.len;
+       free(image.line_allocated);
 
        if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
index c7e68874e7f02ac150343ab6d7a0aa95601f2ba3..59d7237f21f406b71aabc66ea7997faf98981869 100644 (file)
@@ -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;
index e414c8898317736d632064d6c0545d7d128c6bc8..9edf2eb8162eb7214e1b4d76dca8949c63e72847 100644 (file)
@@ -31,7 +31,7 @@ static unsigned char head_sha1[20];
 
 static int branch_track = 1;
 
-static int branch_use_color;
+static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
        "\033[m",       /* reset */
        "",             /* PLAIN (normal) */
@@ -79,12 +79,12 @@ static int git_branch_config(const char *var, const char *value)
                branch_track = git_config_bool(var, value);
                return 0;
        }
-       return git_default_config(var, value);
+       return git_color_default_config(var, value);
 }
 
 static const char *branch_get_color(enum color_branch ix)
 {
-       if (branch_use_color)
+       if (branch_use_color > 0)
                return branch_colors[ix];
        return "";
 }
@@ -588,6 +588,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        };
 
        git_config(git_branch_config);
+
+       if (branch_use_color == -1)
+               branch_use_color = git_use_color_default;
+
        track = branch_track;
        argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
        if (!!delete + !!rename + !!force_create > 1)
index eb853a37cf993a875f2acd1992ee5191783740a1..3b220d5060b90318e2d2331d3cd0f5b6a70164ee 100644 (file)
@@ -29,7 +29,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 {
        int i;
        int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
-       int ignored_only = 0, baselen = 0, config_set = 0;
+       int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
        struct strbuf directory;
        struct dir_struct dir;
        const char *path, *base;
@@ -137,12 +137,15 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                        if (show_only && (remove_directories || matches)) {
                                printf("Would remove %s\n",
                                       directory.buf + prefix_offset);
-                       } else if (quiet && (remove_directories || matches)) {
-                               remove_dir_recursively(&directory, 0);
                        } else if (remove_directories || matches) {
-                               printf("Removing %s\n",
-                                      directory.buf + prefix_offset);
-                               remove_dir_recursively(&directory, 0);
+                               if (!quiet)
+                                       printf("Removing %s\n",
+                                              directory.buf + prefix_offset);
+                               if (remove_dir_recursively(&directory, 0) != 0) {
+                                       warning("failed to remove '%s'",
+                                               directory.buf + prefix_offset);
+                                       errors++;
+                               }
                        } else if (show_only) {
                                printf("Would not remove %s\n",
                                       directory.buf + prefix_offset);
@@ -162,11 +165,14 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                printf("Removing %s\n",
                                       ent->name + prefix_offset);
                        }
-                       unlink(ent->name);
+                       if (unlink(ent->name) != 0) {
+                               warning("failed to remove '%s'", ent->name);
+                               errors++;
+                       }
                }
        }
        free(seen);
 
        strbuf_release(&directory);
-       return 0;
+       return (errors != 0);
 }
index 717eb18da0e0cbe901dd8a660649388ccc0a24e1..065e1f7b7fbf426210d08062800ed81448a30d4a 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "cache.h"
 #include "cache-tree.h"
+#include "color.h"
 #include "dir.h"
 #include "builtin.h"
 #include "diff.h"
@@ -317,6 +318,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 +348,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 +480,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 +519,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;
 
-       return commitable;
+       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 1;
 }
 
 /*
@@ -565,6 +669,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;
@@ -666,6 +772,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        git_config(git_status_config);
 
+       if (wt_status_use_color == -1)
+               wt_status_use_color = git_use_color_default;
+
        argc = parse_and_validate_options(argc, argv, builtin_status_usage);
 
        index_file = prepare_index(argc, argv, prefix);
@@ -677,31 +786,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 +836,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 +867,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 +925,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");
index 077d8ef2df4ae38d668293cc35810b39e0488f6b..2b9a4261d4e98281855ce856041eb730565a4869 100644 (file)
@@ -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_);
index 8d7a5697f2b429f6240a89e28422f48ef18aee3b..8f53f52dcbe74c1fdb6b7a54a8b250537bc821ee 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 #include "cache.h"
+#include "color.h"
 #include "commit.h"
 #include "blob.h"
 #include "tag.h"
@@ -229,6 +230,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 
        prefix = setup_git_directory_gently(&nongit);
        git_config(git_diff_ui_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
 
index ef27eee71bdef2cac6f751e07b1da71588989b03..f741df522014b9495f18cf13c2e01382da71c41d 100755 (executable)
@@ -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))) {
index 6163bd4975e3e361e36ffc89ea4c91d0edd02949..ebb3f37cf158dc479f364111893279805fa9a230 100644 (file)
@@ -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;
 
index 9180b39e3f22e5d3805649c655c61aafc7861968..f4f4ecb11b587c74a009c94cb7b230e7c93590e4 100644 (file)
@@ -578,6 +578,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp("-l", arg) ||
+                   !strcmp("--name-only", arg) ||
                    !strcmp("--files-with-matches", arg)) {
                        opt.name_only = 1;
                        continue;
index 99d69f079132ed67e2d5bc83bf95e6e60ba22f6f..c67d63cb1cf588877fa10a7546757f2cd249c5c6 100644 (file)
@@ -5,6 +5,7 @@
  *              2006 Junio Hamano
  */
 #include "cache.h"
+#include "color.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
@@ -197,7 +198,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);
@@ -235,6 +237,10 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.simplify_history = 0;
@@ -307,6 +313,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
        int i, count, ret = 0;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.combine_merges = 1;
@@ -367,6 +377,10 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        init_reflog_walk(&rev.reflog_info);
        rev.abbrev_commit = 1;
@@ -395,6 +409,10 @@ int cmd_log(int argc, const char **argv, const char *prefix)
        struct rev_info rev;
 
        git_config(git_log_config);
+
+       if (diff_use_color_default == -1)
+               diff_use_color_default = git_use_color_default;
+
        init_revisions(&rev, prefix);
        rev.always_show_header = 1;
        cmd_log_init(argc, argv, prefix, &rev);
@@ -556,7 +574,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 +800,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 +943,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)
index d56e33e251036274dbe48e300fcebd8289624cbe..25dbfb44999566d0491b3f870a47f7df80aa7649 100644 (file)
@@ -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) {
index 2600847974f8a44bdd2148da6ff82ecf761fb193..11f154b31fcbd5c788299855e7d69f54ae8c7e70 100644 (file)
@@ -818,6 +818,7 @@ static void handle_body(void)
 
                switch (transfer_encoding) {
                case TE_BASE64:
+               case TE_QP:
                {
                        char *op = line;
 
index 990e21355d189096bdc0c808fb536b7bfb85b0a1..68aa2a68bb8f4232732829ba7d9459bbef3c7ad5 100644 (file)
@@ -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;
index acb05554d499598677bc1f0cec3b6ff37e796d88..d2bb12e574fb8e18f9b1e3d241f73f732ac740d3 100644 (file)
@@ -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);
 
index c8cb63e23840915ecd7445d1fe4a18f9c6cb694d..9f727c00f689ae23373502c7fb21b2234a1c1748 100644 (file)
@@ -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;
index 57854017535a36fc1fcd7bb997f198956391199d..7bdc312e3861103fa83b20b939962a8bc8e6cc0a 100644 (file)
@@ -41,11 +41,12 @@ static int read_cache_unmerged(void)
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce)) {
+                       remove_index_entry(ce);
                        if (last && !strcmp(ce->name, last->name))
                                continue;
                        cache_tree_invalidate_path(active_cache_tree, ce->name);
                        last = ce;
-                       ce->ce_flags |= CE_REMOVE;
+                       continue;
                }
                *dst++ = ce;
        }
index de80158fd4762aa692193edb7c0f8e85e6189877..6f7d5f8214e54c24983e9c8c295edfefba54fb2b 100644 (file)
@@ -60,6 +60,8 @@ static void show_commit(struct commit *commit)
                fputs(header_prefix, stdout);
        if (commit->object.flags & BOUNDARY)
                putchar('-');
+       else if (commit->object.flags & UNINTERESTING)
+               putchar('^');
        else if (revs.left_right) {
                if (commit->object.flags & SYMMETRIC_LEFT)
                        putchar('<');
@@ -84,7 +86,7 @@ static void show_commit(struct commit *commit)
        else
                putchar('\n');
 
-       if (revs.verbose_header) {
+       if (revs.verbose_header && commit->buffer) {
                struct strbuf buf;
                strbuf_init(&buf, 0);
                pretty_print_commit(revs.commit_format, commit,
@@ -609,7 +611,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);
 
index fa8bc7d02a2268d99dcc829cb3adbee10c949306..0055a57aeb24392de0dcf337bb93f30fceccd88f 100644 (file)
@@ -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;
 
index 65051d14fde44c14d12099df656ac423bc1c347e..a323633e296cef1797ab1d218cea83c891bc3b8d 100644 (file)
@@ -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);
                }
index 4a4a88c10b4b2f9e55662bd4c1c17051ae7e6aac..716b4fff323f7df638f8bed0940a1119e43c2590 100644 (file)
@@ -236,9 +236,6 @@ static int do_sign(struct strbuf *buffer)
        if (finish_command(&gpg) || !len || len < 0)
                return error("gpg failed to sign the tag");
 
-       if (len < 0)
-               return error("could not read the entire signature from gpg.");
-
        return 0;
 }
 
index 5c95eca07d19a6d3ed4e9000b5bb47eef90ff66e..bd12ec8537781c5c82e77637312ccabb708d5040 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -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)))
diff --git a/cache.h b/cache.h
index 3867ba7ff5c3946e0449c78b26880a71107730a0..4fa69f0ee463967e848ffe9860e94e03fe187753 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -110,7 +110,6 @@ struct ondisk_cache_entry {
 };
 
 struct cache_entry {
-       struct cache_entry *next;
        unsigned int ce_ctime;
        unsigned int ce_mtime;
        unsigned int ce_dev;
@@ -121,6 +120,7 @@ struct cache_entry {
        unsigned int ce_size;
        unsigned int ce_flags;
        unsigned char sha1[20];
+       struct cache_entry *next;
        char name[FLEX_ARRAY]; /* more */
 };
 
@@ -133,7 +133,39 @@ struct cache_entry {
 #define CE_UPDATE    (0x10000)
 #define CE_REMOVE    (0x20000)
 #define CE_UPTODATE  (0x40000)
-#define CE_UNHASHED  (0x80000)
+
+#define CE_HASHED    (0x100000)
+#define CE_UNHASHED  (0x200000)
+
+/*
+ * Copy the sha1 and stat state of a cache entry from one to
+ * another. But we never change the name, or the hash state!
+ */
+#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
+static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
+{
+       unsigned int state = dst->ce_flags & CE_STATE_MASK;
+
+       /* Don't copy hash chain and name */
+       memcpy(dst, src, offsetof(struct cache_entry, next));
+
+       /* Restore the hash state */
+       dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
+}
+
+/*
+ * We don't actually *remove* it, we can just mark it invalid so that
+ * we won't find it in lookups.
+ *
+ * Not only would we have to search the lists (simple enough), but
+ * we'd also have to rehash other hash buckets in case this makes the
+ * hash bucket empty (common). So it's much better to just mark
+ * it.
+ */
+static inline void remove_index_entry(struct cache_entry *ce)
+{
+       ce->ce_flags |= CE_UNHASHED;
+}
 
 static inline unsigned create_ce_flags(size_t len, unsigned stage)
 {
@@ -178,6 +210,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 +415,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 +677,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 +706,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 +731,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 */
@@ -694,6 +751,7 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i
 #define WS_TRAILING_SPACE      01
 #define WS_SPACE_BEFORE_TAB    02
 #define WS_INDENT_WITH_NON_TAB 04
+#define WS_CR_AT_EOL           010
 #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 extern unsigned whitespace_rule_cfg;
 extern unsigned whitespace_rule(const char *);
@@ -702,6 +760,7 @@ extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
     FILE *stream, const char *set,
     const char *reset, const char *ws);
 extern char *whitespace_error_string(unsigned ws);
+extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
 
 /* ls-files */
 int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
diff --git a/color.c b/color.c
index cb70340420b6285786b988d4a98d2023a37d4ffc..12a6453f90eb4ce2b39f85762bf3acd1cff2b9f2 100644 (file)
--- a/color.c
+++ b/color.c
@@ -3,6 +3,8 @@
 
 #define COLOR_RESET "\033[m"
 
+int git_use_color_default = 0;
+
 static int parse_color(const char *name, int len)
 {
        static const char * const color_names[] = {
@@ -143,6 +145,16 @@ int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
        return 0;
 }
 
+int git_color_default_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "color.ui")) {
+               git_use_color_default = git_config_colorbool(var, value, -1);
+               return 0;
+       }
+
+       return git_default_config(var, value);
+}
+
 static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
                va_list args, const char *trail)
 {
diff --git a/color.h b/color.h
index ff63513d39b1553e65230ef98583549506670950..ecda5569a2cccabaf16fcf0aad3bce57dfb3aca4 100644 (file)
--- a/color.h
+++ b/color.h
@@ -4,6 +4,17 @@
 /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 #define COLOR_MAXLEN 24
 
+/*
+ * This variable stores the value of color.ui
+ */
+extern int git_use_color_default;
+
+
+/*
+ * Use this instead of git_default_config if you need the value of color.ui.
+ */
+int git_color_default_config(const char *var, const char *value);
+
 int git_config_colorbool(const char *var, const char *value, int stdout_is_tty);
 void color_parse(const char *var, const char *value, char *dst);
 int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
index 8b8fb04d1f6107da15cb142485d80cabe7a3828e..22ce7768639465f896e52b08a73cf5605940700b 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -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/fopen.c b/compat/fopen.c
new file mode 100644 (file)
index 0000000..ccb9e89
--- /dev/null
@@ -0,0 +1,26 @@
+#include "../git-compat-util.h"
+#undef fopen
+FILE *git_fopen(const char *path, const char *mode)
+{
+       FILE *fp;
+       struct stat st;
+
+       if (mode[0] == 'w' || mode[0] == 'a')
+               return fopen(path, mode);
+
+       if (!(fp = fopen(path, mode)))
+               return NULL;
+
+       if (fstat(fileno(fp), &st)) {
+               fclose(fp);
+               return NULL;
+       }
+
+       if (S_ISDIR(st.st_mode)) {
+               fclose(fp);
+               errno = EISDIR;
+               return NULL;
+       }
+
+       return fp;
+}
diff --git a/compat/qsort.c b/compat/qsort.c
new file mode 100644 (file)
index 0000000..d93dce2
--- /dev/null
@@ -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);
+       }
+}
index 3e72778e94267044780a180145eae0a6a229e3a3..cba2bcfb6722bb26ed80e8b7e5a50616cb4702e1 100644 (file)
--- a/config.c
+++ b/config.c
@@ -280,11 +280,18 @@ int git_parse_ulong(const char *value, unsigned long *ret)
        return 0;
 }
 
+static void die_bad_config(const char *name)
+{
+       if (config_file_name)
+               die("bad config value for '%s' in %s", name, config_file_name);
+       die("bad config value for '%s'", name);
+}
+
 int git_config_int(const char *name, const char *value)
 {
        long ret;
        if (!git_parse_long(value, &ret))
-               die("bad config value for '%s' in %s", name, config_file_name);
+               die_bad_config(name);
        return ret;
 }
 
@@ -292,7 +299,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
 {
        unsigned long ret;
        if (!git_parse_ulong(value, &ret))
-               die("bad config value for '%s' in %s", name, config_file_name);
+               die_bad_config(name);
        return ret;
 }
 
@@ -309,6 +316,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 +422,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 +445,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 +509,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 +537,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 +545,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);
index a8bf0ef883358c7eaf9a13dde8f371a86ff840f0..f69b697f8d5891b7a16e6d4769bdc3642209b747 100644 (file)
@@ -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))))
index c35b15860d3a4edcf2cd93d082308ec7ce80e5f5..d72ffbb7773268514d8de29c5d5c33fdaf31f339 100755 (executable)
@@ -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()
index 552707e8e65997ebfc2120887783c4fc5698e19f..d8c94cb3edeceff7a79b2f5e6ba7cad250e6b301 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -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;
diff --git a/diff.c b/diff.c
index bcc323f19fd5878776fecde7074e230c61667de6..99daca6103d0235a851ed6de8c16bd86df194158 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -20,7 +20,7 @@
 
 static int diff_detect_rename_default;
 static int diff_rename_limit_default = 100;
-static int diff_use_color_default;
+int diff_use_color_default = -1;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
 
@@ -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);
@@ -199,7 +191,7 @@ int git_diff_basic_config(const char *var, const char *value)
                }
        }
 
-       return git_default_config(var, value);
+       return git_color_default_config(var, value);
 }
 
 static char *quote_two(const char *one, const char *two)
@@ -1105,6 +1097,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)
@@ -1115,13 +1108,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");
        }
@@ -1291,7 +1283,7 @@ static struct builtin_funcname_pattern {
                        "new\\|return\\|switch\\|throw\\|while\\)\n"
                        "^[     ]*\\(\\([       ]*"
                        "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
-                       "[      ]*([^;]*$\\)" },
+                       "[      ]*([^;]*\\)$" },
        { "tex", "^\\(\\\\\\(sub\\)*section{.*\\)$" },
 };
 
@@ -1723,7 +1715,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;
@@ -2148,7 +2140,7 @@ void diff_setup(struct diff_options *options)
 
        options->change = diff_change;
        options->add_remove = diff_addremove;
-       if (diff_use_color_default)
+       if (diff_use_color_default > 0)
                DIFF_OPT_SET(options, COLOR_DIFF);
        else
                DIFF_OPT_CLR(options, COLOR_DIFF);
diff --git a/diff.h b/diff.h
index 8c6bb54119507633b5823757d7485925b346a07b..c5d3a4f965380fd56791fc99864fa27b758da7ca 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -177,6 +177,7 @@ extern void diff_unmerge(struct diff_options *,
 
 extern int git_diff_basic_config(const char *var, const char *value);
 extern int git_diff_ui_config(const char *var, const char *value);
+extern int diff_use_color_default;
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
 extern int diff_setup_done(struct diff_options *);
diff --git a/dir.c b/dir.c
index 6543105b9622212430a9e5ed131a81074e019d9a..1f507daff2c278a70da768e3754a68891b946973 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -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?
diff --git a/dir.h b/dir.h
index d8814dccb2dd57af21d75c91a52026e09fdf1b95..2df15defb6720a742282f24721233c4816deceb6 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -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);
index 18a1c4eec49bfddcb81d6669c83f8f97a218d7bf..3527f1663f3f948085ab56a81609ed9764b4e6e9 100644 (file)
@@ -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() */
index a523b171e22f83d6a656b372cf20afe52d27d1b2..0d3449f2cef93ae0ece87f4204d5e817d6e62439 100644 (file)
@@ -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);
index 17ca5b84f0c077d61b9392bd7900954ce35d6cbf..a0a81f134a6288dfc1d87431698f29597ed5e488 100755 (executable)
@@ -82,6 +82,19 @@ sub list_untracked {
 my $status_fmt = '%12s %12s %s';
 my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
 
+{
+       my $initial;
+       sub is_initial_commit {
+               $initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0
+                       unless defined $initial;
+               return $initial;
+       }
+}
+
+sub get_empty_tree {
+       return '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
+}
+
 # Returns list of hashes, contents of each of which are:
 # VALUE:       pathname
 # BINARY:      is a binary path
@@ -103,8 +116,10 @@ sub list_modified {
                return if (!@tracked);
        }
 
+       my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
        for (run_cmd_pipe(qw(git diff-index --cached
-                            --numstat --summary HEAD --), @tracked)) {
+                            --numstat --summary), $reference,
+                            '--', @tracked)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        my ($change, $bin);
@@ -476,21 +491,27 @@ sub revert_cmd {
                                       HEADER => $status_head, },
                                     list_modified());
        if (@update) {
-               my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
-                                        map { $_->{VALUE} } @update);
-               my $fh;
-               open $fh, '| git update-index --index-info'
-                   or die;
-               for (@lines) {
-                       print $fh $_;
+               if (is_initial_commit()) {
+                       system(qw(git rm --cached),
+                               map { $_->{VALUE} } @update);
                }
-               close($fh);
-               for (@update) {
-                       if ($_->{INDEX_ADDDEL} &&
-                           $_->{INDEX_ADDDEL} eq 'create') {
-                               system(qw(git update-index --force-remove --),
-                                      $_->{VALUE});
-                               print "note: $_->{VALUE} is untracked now.\n";
+               else {
+                       my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
+                                                map { $_->{VALUE} } @update);
+                       my $fh;
+                       open $fh, '| git update-index --index-info'
+                           or die;
+                       for (@lines) {
+                               print $fh $_;
+                       }
+                       close($fh);
+                       for (@update) {
+                               if ($_->{INDEX_ADDDEL} &&
+                                   $_->{INDEX_ADDDEL} eq 'create') {
+                                       system(qw(git update-index --force-remove --),
+                                              $_->{VALUE});
+                                       print "note: $_->{VALUE} is untracked now.\n";
+                               }
                        }
                }
                refresh();
@@ -956,7 +977,9 @@ sub diff_cmd {
                                     HEADER => $status_head, },
                                   @mods);
        return if (!@them);
-       system(qw(git diff -p --cached HEAD --), map { $_->{VALUE} } @them);
+       my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+       system(qw(git diff -p --cached), $reference, '--',
+               map { $_->{VALUE} } @them);
 }
 
 sub quit_cmd {
index 393fa355849f88d6c5227f90cbf6cbb45b39631e..74715edf0b197376250e5d111407a36a6c237678 100755 (executable)
@@ -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
index bd74d701a1f0abb63e2bcdcb47035cb2c6eee745..1a7689a48f07a6ed2bb156f745bfea19a10e3eb9 100755 (executable)
@@ -210,11 +210,14 @@ then
     git read-tree $v --reset -u $new
 else
     git update-index --refresh >/dev/null
-    merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
-       case "$merge" in
-       '')
-               echo >&2 "$merge_error"
+    git read-tree $v -m -u --exclude-per-directory=.gitignore $old $new || (
+       case "$merge,$v" in
+       ,*)
                exit 1 ;;
+       1,)
+               ;; # quiet
+       *)
+               echo >&2 "Falling back to 3-way merge..." ;;
        esac
 
        # Match the index to the working tree, and do a three-way.
index b4e858c3885dab55398670d9497304eb83545d1f..0d686c3a03a15d73e086d3a54afeee269365ffcd 100755 (executable)
@@ -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
 
index 4df90cb34e61deb56ecb49797e48e67bdc98ff3b..2a40703c851d7011723588dd7643dc034db49c31 100644 (file)
@@ -204,6 +204,11 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
                 const void *needle, size_t needlelen);
 #endif
 
+#ifdef FREAD_READS_DIRECTORIES
+#define fopen(a,b) git_fopen(a,b)
+extern FILE *git_fopen(const char*, const char*);
+#endif
+
 #ifdef __GLIBC_PREREQ
 #if __GLIBC_PREREQ(2, 1)
 #define HAVE_STRCHRNUL
@@ -426,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
index d2e50c34292bc0ccb6e914ed595da9fd8a7e141d..b6036bd4d305215b4a70b6fd0fe54d7607dbe068 100755 (executable)
@@ -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";
@@ -198,15 +197,39 @@ if (@canstatusfiles) {
       my @updated = xargs_safe_pipe_capture([@cvs, 'update'], @canstatusfiles);
       print @updated;
     }
-    my @cvsoutput;
-    @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles);
-    my $matchcount = 0;
-    foreach my $l (@cvsoutput) {
-        chomp $l;
-        if ( $l =~ /^File:/ and  $l =~ /Status: (.*)$/ ) {
-            $cvsstat{$canstatusfiles[$matchcount]} = $1;
-            $matchcount++;
+    # "cvs status" reorders the parameters, notably when there are multiple
+    # arguments with the same basename.  So be precise here.
+
+    my %added = map { $_ => 1 } @afiles;
+    my %todo = map { $_ => 1 } @canstatusfiles;
+
+    while (%todo) {
+      my @canstatusfiles2 = ();
+      my %fullname = ();
+      foreach my $name (keys %todo) {
+       my $basename = basename($name);
+
+       $basename = "no file " . $basename if (exists($added{$basename}));
+       chomp($basename);
+
+       if (!exists($fullname{$basename})) {
+         $fullname{$basename} = $name;
+         push (@canstatusfiles2, $name);
+         delete($todo{$name});
         }
+      }
+      my @cvsoutput;
+      @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles2);
+      foreach my $l (@cvsoutput) {
+        chomp $l;
+        if ($l =~ /^File:\s+(.*\S)\s+Status: (.*)$/) {
+         if (!exists($fullname{$1})) {
+           print STDERR "Huh? Status reported for unexpected file '$1'\n";
+         } else {
+           $cvsstat{$fullname{$1}} = $2;
+         }
+       }
+      }
     }
 }
 
index 5694978c466df8e97483b5bdf5e9ae00b481c56c..9516242338d95687b4354a6d21692cbd10547899 100755 (executable)
@@ -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/);
index ff716cabb084526ddb5655c3b7c6dd02f4ed1b39..49e13f0bb1ed2bcb6e85455f24dffa912927d67a 100755 (executable)
@@ -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 ||
index 34438cdf5cc219ac71cbc90a23c23550276f5af2..01e0a46ba56faa8a17ed8710346b18dd215899c7 100644 (file)
@@ -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
@@ -91,9 +92,20 @@ ifndef V
        REMOVE_F1 = && echo '   ' REMOVE `basename "$$dst"` && $(RM_RF) "$$dst"
 endif
 
-TCL_PATH   ?= tclsh
 TCLTK_PATH ?= wish
-TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app
+ifeq (./,$(dir $(TCLTK_PATH)))
+       TCL_PATH ?= $(subst wish,tclsh,$(TCLTK_PATH))
+else
+       TCL_PATH ?= $(dir $(TCLTK_PATH))$(notdir $(subst wish,tclsh,$(TCLTK_PATH)))
+endif
+
+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 =
@@ -119,7 +131,17 @@ GITGUI_MACOSXAPP :=
 
 ifeq ($(uname_O),Cygwin)
        GITGUI_SCRIPT := `cygpath --windows --absolute "$(GITGUI_SCRIPT)"`
-       gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)")
+
+       # Is this a Cygwin Tcl/Tk binary?  If so it knows how to do
+       # POSIX path translation just like cygpath does and we must
+       # keep libdir in POSIX format so Cygwin packages of git-gui
+       # work no matter where the user installs them.
+       #
+       ifeq ($(shell echo 'puts [file normalize /]' | '$(TCL_PATH_SQ)'),$(shell cygpath --mixed --absolute /))
+               gg_libdir_sed_in := $(gg_libdir)
+       else
+               gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)")
+       endif
 else
        ifeq ($(exedir),$(gg_libdir))
                GITGUI_RELATIVE := 1
@@ -147,7 +169,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 +179,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)|' \
index f42e461fd42ad37075d433f3509d6bd415a0b97a..238a2393ff7811675003e0e846f3d9409fb04e0c 100755 (executable)
@@ -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 {
@@ -662,7 +663,7 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
 }
 
 set _real_git_version $_git_version
-regsub -- {-dirty$} $_git_version {} _git_version
+regsub -- {[\-\.]dirty$} $_git_version {} _git_version
 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
 regsub {\.rc[0-9]+$} $_git_version {} _git_version
 regsub {\.GIT$} $_git_version {} _git_version
@@ -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
index 719fc547b3e157cdb14a8a09ba77a1d7c5f1f585..47be8eb97ab79188bba8dca18f3784327302ba9a 100644 (file)
@@ -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"
index f24396692474e3da66f9502b2f06f13583a3deeb..6e1411711bd46df08c09fd2bc371b017050a8368 100644 (file)
@@ -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
 }
index 86faf24cc8788701983f4ff49dc407ecce6d3148..0adcf9d958b76b5a52386effe6afbca95f0abaf9 100644 (file)
@@ -11,6 +11,7 @@ field w_quit      ; # Quit button
 field o_cons      ; # Console object (if active)
 field w_types     ; # List of type buttons in clone
 field w_recentlist ; # Listbox containing recent repositories
+field w_localpath  ; # Entry widget bound to local_path
 
 field done              0 ; # Finished picking the repository?
 field local_path       {} ; # Where this repository is locally
@@ -385,6 +386,7 @@ method _do_new {} {
        button $w_body.where.b \
                -text [mc "Browse"] \
                -command [cb _new_local_path]
+       set w_localpath $w_body.where.t
 
        pack $w_body.where.b -side right
        pack $w_body.where.l -side left
@@ -416,6 +418,7 @@ method _new_local_path {} {
                return
        }
        set local_path $p
+       $w_localpath icursor end
 }
 
 method _do_new2 {} {
@@ -481,6 +484,7 @@ method _do_clone {} {
                -text [mc "Browse"] \
                -command [cb _new_local_path]
        grid $args.where_l $args.where_t $args.where_b -sticky ew
+       set w_localpath $args.where_t
 
        label $args.type_l -text [mc "Clone Type:"]
        frame $args.type_f
index 947b201c328eef044cfe21b463e8228031f6f8cd..40a710355751836e78b65101592b753266f507ca 100644 (file)
@@ -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
        }
index 0fdd7531dac07e68315cebc2efa87e99947821b6..08a24622c7ff199399805ef34f0396f6cb81613c 100644 (file)
@@ -1,6 +1,14 @@
 # git-gui branch (create/delete) support
 # Copyright (C) 2006, 2007 Shawn Pearce
 
+proc _error_parent {} {
+       set p [grab current .]
+       if {$p eq {}} {
+               return .
+       }
+       return $p
+}
+
 proc error_popup {msg} {
        set title [appname]
        if {[reponame] ne {}} {
@@ -11,8 +19,8 @@ proc error_popup {msg} {
                -type ok \
                -title [append "$title: " [mc "error"]] \
                -message $msg]
-       if {[winfo ismapped .]} {
-               lappend cmd -parent .
+       if {[winfo ismapped [_error_parent]]} {
+               lappend cmd -parent [_error_parent]
        }
        eval $cmd
 }
@@ -27,13 +35,13 @@ proc warn_popup {msg} {
                -type ok \
                -title [append "$title: " [mc "warning"]] \
                -message $msg]
-       if {[winfo ismapped .]} {
-               lappend cmd -parent .
+       if {[winfo ismapped [_error_parent]]} {
+               lappend cmd -parent [_error_parent]
        }
        eval $cmd
 }
 
-proc info_popup {msg {parent .}} {
+proc info_popup {msg} {
        set title [appname]
        if {[reponame] ne {}} {
                append title " ([reponame])"
@@ -56,8 +64,8 @@ proc ask_popup {msg} {
                -type yesno \
                -title $title \
                -message $msg]
-       if {[winfo ismapped .]} {
-               lappend cmd -parent .
+       if {[winfo ismapped [_error_parent]]} {
+               lappend cmd -parent [_error_parent]
        }
        eval $cmd
 }
index 30a244cc170a344ba99be1c7be07fdebe4333bde..3c1fce7475d362d1880d915ff4bdf168fda28593 100644 (file)
@@ -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."]}]
        }
 }
 
index 63e14279c183b1d0b8a62926816bb44ab6dc519c..cc26b0780882b6e8bde122c2a724d110f439d84f 100644 (file)
@@ -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
        }
index f812e5e89a1f21e2ee96a90e83958a472539bdd5..ea80df009226d0ff14e8fe423abfe3d7a94ef56b 100644 (file)
@@ -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 (file)
index 0000000..7f018e4
--- /dev/null
@@ -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
+}
+
+}
index 99913ec57a346ad996fefa2d3f1f51be3265170b..b3bf15fa1c1a41265460664417caf47265553a4f 100644 (file)
@@ -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>
index 2dfe07e06f7bbbdc1dd439431e8c56ecf9c60cae..d7c38f9c73fbeb887f8e0dcfe73af84d3edf8138 100644 (file)
@@ -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."
 
index dfa48ae26364b5d30a810bfa9b55984bba582692..3f139da6c7aae3cdc81312289083c1752c6e2629 100644 (file)
@@ -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-help--browse.sh b/git-help--browse.sh
deleted file mode 100755 (executable)
index 10b0a36..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/bin/sh
-#
-# This program launch a web browser on the html page
-# describing a git command.
-#
-# Copyright (c) 2007 Christian Couder
-# Copyright (c) 2006 Theodore Y. Ts'o
-#
-# This file is heavily stolen from git-mergetool.sh, by
-# Theodore Y. Ts'o (thanks) that is:
-#
-# Copyright (c) 2006 Theodore Y. Ts'o
-#
-# This file is licensed under the GPL v2, or a later version
-# at the discretion of Junio C Hamano or any other official
-# git maintainer.
-#
-
-USAGE='[--browser=browser|--tool=browser] [cmd to display] ...'
-
-# 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)
-                       ;; # happy
-               *)
-                       return 1
-                       ;;
-       esac
-}
-
-init_browser_path() {
-       browser_path=`git config browser.$1.path`
-       test -z "$browser_path" && browser_path=$1
-}
-
-while test $# != 0
-do
-    case "$1" in
-       -b|--browser*|-t|--tool*)
-           case "$#,$1" in
-               *,*=*)
-                   browser=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-                   ;;
-               1,*)
-                   usage ;;
-               *)
-                   browser="$2"
-                   shift ;;
-           esac
-           ;;
-       --)
-           break
-           ;;
-       -*)
-           usage
-           ;;
-       *)
-           break
-           ;;
-    esac
-    shift
-done
-
-if test -z "$browser"
-then
-    for opt in "help.browser" "web.browser"
-    do
-       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
-    fi
-fi
-
-if test -z "$browser" ; then
-    if test -n "$DISPLAY"; then
-       browser_candidates="firefox iceweasel konqueror w3m links lynx dillo"
-       if test "$KDE_FULL_SESSION" = "true"; then
-           browser_candidates="konqueror $browser_candidates"
-       fi
-    else
-       browser_candidates="w3m links lynx"
-    fi
-
-    for i in $browser_candidates; do
-       init_browser_path $i
-       if type "$browser_path" > /dev/null 2>&1; then
-           browser=$i
-           break
-       fi
-    done
-    test -z "$browser" && die "No known browser available."
-else
-    valid_tool "$browser" || die "Unknown browser '$browser'."
-
-    init_browser_path "$browser"
-
-    if ! type "$browser_path" > /dev/null 2>&1; then
-       die "The browser $browser is not available as '$browser_path'."
-    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 &
-       ;;
-    konqueror)
-       case "$(basename "$browser_path")" in
-           konqueror)
-               # 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
-               ;;
-           kfmclient)
-               eval "$browser_path" newTab $pages
-               ;;
-           *)
-               nohup "$browser_path" $pages &
-               ;;
-       esac
-       ;;
-    w3m|links|lynx)
-       eval "$browser_path" $pages
-       ;;
-    dillo)
-       nohup "$browser_path" $pages &
-       ;;
-esac
index 3e4452bc4bc1fe57c75848ccd9c073aeb065a980..6f91c8f845b2c4ef426a6451ac07abae2bce9488 100755 (executable)
@@ -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
index 46da0f4ca2bfc2b30e4da8eb551565a06c876fb1..3ce32b5f211bd20f1d154c860eb5bf7f9005ca3c 100755 (executable)
@@ -174,6 +174,7 @@ fi
 
 merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
-       exec git-rebase --onto $merge_head ${oldremoteref:-$merge_head}
+       exec git-rebase $strategy_args --onto $merge_head \
+       ${oldremoteref:-$merge_head}
 exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \
        "$merge_name" HEAD $merge_head
index 59601e36e854195c93f0a49bc3884567ed502595..29b1105c4c3c83258ebdf53c8885f767baa0ace0 100755 (executable)
@@ -170,7 +170,9 @@ my $envelope_sender;
 
 my $repo = Git->repository();
 my $term = eval {
-       new Term::ReadLine 'git-send-email';
+       $ENV{"GIT_SEND_EMAIL_NOTTY"}
+               ? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT
+               : new Term::ReadLine 'git-send-email';
 };
 if ($@) {
        $term = new FakeTerm "$@: going non-interactive";
@@ -475,9 +477,10 @@ if ($thread && !defined $initial_reply_to && $prompting) {
 
        $initial_reply_to = $_;
 }
-if (defined $initial_reply_to && $_ ne "") {
-       $initial_reply_to =~ s/^\s*<?/</;
-       $initial_reply_to =~ s/>?\s*$/>/;
+if (defined $initial_reply_to) {
+       $initial_reply_to =~ s/^\s*<?//;
+       $initial_reply_to =~ s/>?\s*$//;
+       $initial_reply_to = "<$initial_reply_to>" if $initial_reply_to ne '';
 }
 
 if (!defined $smtp_server) {
index aae14090bd884920c7b5cb7530db66719df98ddd..a44b1c74a38d6310d7a1bde969cbbcb51f977188 100755 (executable)
@@ -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-web--browse.sh b/git-web--browse.sh
new file mode 100755 (executable)
index 0000000..1023b90
--- /dev/null
@@ -0,0 +1,160 @@
+#!/bin/sh
+#
+# This program launch a web browser on the html page
+# describing a git command.
+#
+# Copyright (c) 2007 Christian Couder
+# Copyright (c) 2006 Theodore Y. Ts'o
+#
+# This file is heavily stolen from git-mergetool.sh, by
+# Theodore Y. Ts'o (thanks) that is:
+#
+# Copyright (c) 2006 Theodore Y. Ts'o
+#
+# This file is licensed under the GPL v2, or a later version
+# at the discretion of Junio C Hamano or any other official
+# git maintainer.
+#
+
+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
+
+valid_tool() {
+       case "$1" in
+               firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open)
+                       ;; # happy
+               *)
+                       return 1
+                       ;;
+       esac
+}
+
+init_browser_path() {
+       browser_path=$(git config "browser.$1.path")
+       test -z "$browser_path" && browser_path="$1"
+}
+
+while test $# != 0
+do
+    case "$1" in
+       -b|--browser*|-t|--tool*)
+           case "$#,$1" in
+               *,*=*)
+                   browser=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+                   ;;
+               1,*)
+                   usage ;;
+               *)
+                   browser="$2"
+                   shift ;;
+           esac
+           ;;
+       -c|--config*)
+           case "$#,$1" in
+               *,*=*)
+                   conf=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+                   ;;
+               1,*)
+                   usage ;;
+               *)
+                   conf="$2"
+                   shift ;;
+           esac
+           ;;
+       --)
+           break
+           ;;
+       -*)
+           usage
+           ;;
+       *)
+           break
+           ;;
+    esac
+    shift
+done
+
+test $# = 0 && usage
+
+if test -z "$browser"
+then
+    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
+    fi
+fi
+
+if test -z "$browser" ; then
+    if test -n "$DISPLAY"; then
+       browser_candidates="firefox iceweasel konqueror w3m links lynx dillo"
+       if test "$KDE_FULL_SESSION" = "true"; then
+           browser_candidates="konqueror $browser_candidates"
+       fi
+    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
+       if type "$browser_path" > /dev/null 2>&1; then
+           browser=$i
+           break
+       fi
+    done
+    test -z "$browser" && die "No known browser available."
+else
+    valid_tool "$browser" || die "Unknown browser '$browser'."
+
+    init_browser_path "$browser"
+
+    if ! type "$browser_path" > /dev/null 2>&1; then
+       die "The browser $browser is not available as '$browser_path'."
+    fi
+fi
+
+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=''
+       "$browser_path" $NEWTAB "$@" &
+       ;;
+    konqueror)
+       case "$(basename "$browser_path")" in
+           konqueror)
+               # 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 "$@"
+               ;;
+           kfmclient)
+               eval "$browser_path" newTab "$@"
+               ;;
+           *)
+               "$browser_path" "$@" &
+               ;;
+       esac
+       ;;
+    w3m|links|lynx|open)
+       eval "$browser_path" "$@"
+       ;;
+    dillo)
+       "$browser_path" "$@" &
+       ;;
+esac
index 3f9f88815b865cb6c405268d83f67f1b2f858f88..97a26be29a5e6e64ca33760ba0010693e0fa07f9 100644 (file)
@@ -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
 
index 5560e4dc560587c4a551c24a1ee59df394726fc9..f1f21e97bf5e89bd7609cf5b6d1f417e35a43b6c 100644 (file)
@@ -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.
index 4c8bedf7449266229e8102efac249e68fd9d8b39..2163071047a0f4a7620eebf2f4e18008f592c049 100644 (file)
@@ -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.
index 5e88637b5e0e67143692bac336a57ab6cc1c0c38..e8226b16f4096673180b24d4362aa45ffe1e2e9a 100755 (executable)
@@ -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;
@@ -3769,18 +3784,24 @@ sub git_search_grep_body {
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
                      "<td><i>" . $author . "</i></td>\n" .
                      "<td>" .
-                     $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"},
-                              chop_and_escape_str($co{'title'}, 50) . "<br/>");
+                     $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+                              -class => "list subject"},
+                             chop_and_escape_str($co{'title'}, 50) . "<br/>");
                my $comment = $co{'comment'};
                foreach my $line (@$comment) {
                        if ($line =~ m/^(.*)($search_regexp)(.*)$/i) {
-                               my $lead = esc_html($1) || "";
-                               $lead = chop_str($lead, 30, 10);
-                               my $match = esc_html($2) || "";
-                               my $trail = esc_html($3) || "";
-                               $trail = chop_str($trail, 30, 10);
-                               my $text = "$lead<span class=\"match\">$match</span>$trail";
-                               print chop_str($text, 80, 5) . "<br/>\n";
+                               my ($lead, $match, $trail) = ($1, $2, $3);
+                               $match = chop_str($match, 70, 5);       # in case match is very long
+                               my $contextlen = int((80 - length($match))/2); # for the remainder
+                               $contextlen = 30 if ($contextlen > 30); # but not too much
+                               $lead  = chop_str($lead,  $contextlen, 10);
+                               $trail = chop_str($trail, $contextlen, 10);
+
+                               $lead  = esc_html($lead);
+                               $match = esc_html($match);
+                               $trail = esc_html($trail);
+
+                               print "$lead<span class=\"match\">$match</span>$trail<br />";
                        }
                }
                print "</td>\n" .
diff --git a/hash.c b/hash.c
index 7b492d4fc039898b975a1122b4e4eb90e3eb8923..d9ec82fa663804210e8ef200f2f300e914662f22 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -70,7 +70,7 @@ void *lookup_hash(unsigned int hash, struct hash_table *table)
 {
        if (!table->array)
                return NULL;
-       return &lookup_hash_entry(hash, table)->ptr;
+       return lookup_hash_entry(hash, table)->ptr;
 }
 
 void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table)
diff --git a/help.c b/help.c
index 95e7640fedebb1574a4259d85d7b95d5220f0e6f..8143dcdd38218ef03c47535c7c2e761cf1a868ce 100644 (file)
--- a/help.c
+++ b/help.c
@@ -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)
index b2b410df902f2a4f2bca634d82cf103d288c9042..0beb7406c34a19403062a6a2e2aeeee35566a45f 100644 (file)
@@ -1634,12 +1634,19 @@ static struct object_list **process_tree(struct tree *tree,
 
        init_tree_desc(&desc, tree->buffer, tree->size);
 
-       while (tree_entry(&desc, &entry)) {
-               if (S_ISDIR(entry.mode))
+       while (tree_entry(&desc, &entry))
+               switch (object_type(entry.mode)) {
+               case OBJ_TREE:
                        p = process_tree(lookup_tree(entry.sha1), p, &me, name);
-               else
+                       break;
+               case OBJ_BLOB:
                        p = process_blob(lookup_blob(entry.sha1), p, &me, name);
-       }
+                       break;
+               default:
+                       /* Subproject commit - not in this repository */
+                       break;
+               }
+
        free(tree->buffer);
        tree->buffer = NULL;
        return p;
@@ -2383,7 +2390,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();
@@ -2398,15 +2406,17 @@ int main(int argc, char **argv)
                fill_active_slots();
                add_fill_function(NULL, fill_active_slot);
 #endif
-               finish_all_active_slots();
+               do {
+                       finish_all_active_slots();
+#ifdef USE_CURL_MULTI
+                       fill_active_slots();
+#endif
+               } while (request_queue_head && !aborted);
 
                /* Update the remote branch if all went well */
-               if (aborted || !update_remote(ref->new_sha1, ref_lock)) {
+               if (aborted || !update_remote(ref->new_sha1, ref_lock))
                        rc = 1;
-                       goto unlock;
-               }
 
-       unlock:
                if (!rc)
                        fprintf(stderr, "    done\n");
                unlock_remote(ref_lock);
index 4ef58e7ec01ebdfc6f036ccafaf837b2b38ed7a5..c8b8375e4983794e601ba69a1c217a3c711835e9 100644 (file)
@@ -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)
index 1f3fcf16ad7a101eb9eab53da84bd2640f97ab00..e9ba6df9d230e8adc12df32b502729c09c8e7d5a 100644 (file)
@@ -149,10 +149,12 @@ void show_log(struct rev_info *opt, const char *sep)
 
        opt->loginfo = NULL;
        if (!opt->verbose_header) {
-               if (opt->left_right) {
-                       if (commit->object.flags & BOUNDARY)
-                               putchar('-');
-                       else if (commit->object.flags & SYMMETRIC_LEFT)
+               if (commit->object.flags & BOUNDARY)
+                       putchar('-');
+               else if (commit->object.flags & UNINTERESTING)
+                       putchar('^');
+               else if (opt->left_right) {
+                       if (commit->object.flags & SYMMETRIC_LEFT)
                                putchar('<');
                        else
                                putchar('>');
@@ -250,6 +252,8 @@ void show_log(struct rev_info *opt, const char *sep)
                        fputs("commit ", stdout);
                if (commit->object.flags & BOUNDARY)
                        putchar('-');
+               else if (commit->object.flags & UNINTERESTING)
+                       putchar('^');
                else if (opt->left_right) {
                        if (commit->object.flags & SYMMETRIC_LEFT)
                                putchar('<');
@@ -278,6 +282,9 @@ void show_log(struct rev_info *opt, const char *sep)
                }
        }
 
+       if (!commit->buffer)
+               return;
+
        /*
         * And then the pretty-printed message itself
         */
index bbb700b54eab2a14f60e090231a196874ff3a9e0..7491c56ad25332fb4aae6a075bf0577a1d800c3b 100644 (file)
@@ -91,7 +91,7 @@ int main(int argc, char **argv)
        signal(SIGCHLD, SIG_DFL);
 
        if (argc < 3)
-               usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
+               usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
 
        setup_git_directory();
        read_cache();
index dd52342539cd05e59bb13e956d51251417f21e3c..55ef76f5a5f855475f796dfda39fa3417179be8b 100644 (file)
@@ -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));
diff --git a/pager.c b/pager.c
index 0376953cb1b8a4095346e0d12ecef6e7db9c48c9..ca002f9f791b6cdb8257836be9e13fe648afa1cc 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -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;
index b987ff245b310a6693dc69ba8c71ef2915da7864..997f5837d521ded5f158d12abc0c025f74e83341 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -282,59 +282,59 @@ static char *logmsg_reencode(const struct commit *commit,
        return out;
 }
 
-static void format_person_part(struct strbuf *sb, char part,
+static size_t format_person_part(struct strbuf *sb, char part,
                                const char *msg, int len)
 {
+       /* currently all placeholders have same length */
+       const int placeholder_len = 2;
        int start, end, tz = 0;
-       unsigned long date;
+       unsigned long date = 0;
        char *ep;
 
-       /* parse name */
+       /* advance 'end' to point to email start delimiter */
        for (end = 0; end < len && msg[end] != '<'; end++)
                ; /* do nothing */
+
        /*
-        * If it does not even have a '<' and '>', that is
-        * quite a bogus commit author and we discard it;
-        * this is in line with add_user_info() that is used
-        * in the normal codepath.  When end points at the '<'
-        * that we found, it should have matching '>' later,
-        * which means start (beginning of email address) must
-        * be strictly below len.
+        * When end points at the '<' that we found, it should have
+        * matching '>' later, which means 'end' must be strictly
+        * below len - 1.
         */
-       start = end + 1;
-       if (start >= len - 1)
-               return;
-       while (end > 0 && isspace(msg[end - 1]))
-               end--;
+       if (end >= len - 2)
+               goto skip;
+
        if (part == 'n') {      /* name */
+               while (end > 0 && isspace(msg[end - 1]))
+                       end--;
                strbuf_add(sb, msg, end);
-               return;
+               return placeholder_len;
        }
+       start = ++end; /* save email start position */
 
-       /* parse email */
-       for (end = start; end < len && msg[end] != '>'; end++)
+       /* advance 'end' to point to email end delimiter */
+       for ( ; end < len && msg[end] != '>'; end++)
                ; /* do nothing */
 
        if (end >= len)
-               return;
+               goto skip;
 
        if (part == 'e') {      /* email */
                strbuf_add(sb, msg + start, end - start);
-               return;
+               return placeholder_len;
        }
 
-       /* parse date */
+       /* advance 'start' to point to date start delimiter */
        for (start = end + 1; start < len && isspace(msg[start]); start++)
                ; /* do nothing */
        if (start >= len)
-               return;
+               goto skip;
        date = strtoul(msg + start, &ep, 10);
        if (msg + start == ep)
-               return;
+               goto skip;
 
        if (part == 't') {      /* date, UNIX timestamp */
                strbuf_add(sb, msg + start, ep - (msg + start));
-               return;
+               return placeholder_len;
        }
 
        /* parse tz */
@@ -349,17 +349,28 @@ static void format_person_part(struct strbuf *sb, char part,
        switch (part) {
        case 'd':       /* date */
                strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
-               return;
+               return placeholder_len;
        case 'D':       /* date, RFC2822 style */
                strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
-               return;
+               return placeholder_len;
        case 'r':       /* date, relative */
                strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
-               return;
+               return placeholder_len;
        case 'i':       /* date, ISO 8601 */
                strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
-               return;
+               return placeholder_len;
        }
+
+skip:
+       /*
+        * bogus commit, 'sb' cannot be updated, but we still need to
+        * compute a valid return value.
+        */
+       if (part == 'n' || part == 'e' || part == 't' || part == 'd'
+           || part == 'D' || part == 'r' || part == 'i')
+               return placeholder_len;
+
+       return 0; /* unknown placeholder */
 }
 
 struct chunk {
@@ -440,7 +451,7 @@ static void parse_commit_header(struct format_commit_context *context)
        context->commit_header_parsed = 1;
 }
 
-static void format_commit_item(struct strbuf *sb, const char *placeholder,
+static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                                void *context)
 {
        struct format_commit_context *c = context;
@@ -451,23 +462,23 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
        /* these are independent of the commit */
        switch (placeholder[0]) {
        case 'C':
-               switch (placeholder[3]) {
-               case 'd':       /* red */
+               if (!prefixcmp(placeholder + 1, "red")) {
                        strbuf_addstr(sb, "\033[31m");
-                       return;
-               case 'e':       /* green */
+                       return 4;
+               } else if (!prefixcmp(placeholder + 1, "green")) {
                        strbuf_addstr(sb, "\033[32m");
-                       return;
-               case 'u':       /* blue */
+                       return 6;
+               } else if (!prefixcmp(placeholder + 1, "blue")) {
                        strbuf_addstr(sb, "\033[34m");
-                       return;
-               case 's':       /* reset color */
+                       return 5;
+               } else if (!prefixcmp(placeholder + 1, "reset")) {
                        strbuf_addstr(sb, "\033[m");
-                       return;
-               }
+                       return 6;
+               } else
+                       return 0;
        case 'n':               /* newline */
                strbuf_addch(sb, '\n');
-               return;
+               return 1;
        }
 
        /* these depend on the commit */
@@ -477,34 +488,34 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
        switch (placeholder[0]) {
        case 'H':               /* commit hash */
                strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
-               return;
+               return 1;
        case 'h':               /* abbreviated commit hash */
                if (add_again(sb, &c->abbrev_commit_hash))
-                       return;
+                       return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
                                                     DEFAULT_ABBREV));
                c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
-               return;
+               return 1;
        case 'T':               /* tree hash */
                strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
-               return;
+               return 1;
        case 't':               /* abbreviated tree hash */
                if (add_again(sb, &c->abbrev_tree_hash))
-                       return;
+                       return 1;
                strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
                                                     DEFAULT_ABBREV));
                c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
-               return;
+               return 1;
        case 'P':               /* parent hashes */
                for (p = commit->parents; p; p = p->next) {
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
                        strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
                }
-               return;
+               return 1;
        case 'p':               /* abbreviated parent hashes */
                if (add_again(sb, &c->abbrev_parent_hashes))
-                       return;
+                       return 1;
                for (p = commit->parents; p; p = p->next) {
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
@@ -513,14 +524,14 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
                }
                c->abbrev_parent_hashes.len = sb->len -
                                              c->abbrev_parent_hashes.off;
-               return;
+               return 1;
        case 'm':               /* left/right/bottom */
                strbuf_addch(sb, (commit->object.flags & BOUNDARY)
                                 ? '-'
                                 : (commit->object.flags & SYMMETRIC_LEFT)
                                 ? '<'
                                 : '>');
-               return;
+               return 1;
        }
 
        /* For the rest we have to parse the commit header. */
@@ -528,66 +539,33 @@ static void format_commit_item(struct strbuf *sb, const char *placeholder,
                parse_commit_header(c);
 
        switch (placeholder[0]) {
-       case 's':
+       case 's':       /* subject */
                strbuf_add(sb, msg + c->subject.off, c->subject.len);
-               return;
-       case 'a':
-               format_person_part(sb, placeholder[1],
+               return 1;
+       case 'a':       /* author ... */
+               return format_person_part(sb, placeholder[1],
                                   msg + c->author.off, c->author.len);
-               return;
-       case 'c':
-               format_person_part(sb, placeholder[1],
+       case 'c':       /* committer ... */
+               return format_person_part(sb, placeholder[1],
                                   msg + c->committer.off, c->committer.len);
-               return;
-       case 'e':
+       case 'e':       /* encoding */
                strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
-               return;
-       case 'b':
+               return 1;
+       case 'b':       /* body */
                strbuf_addstr(sb, msg + c->body_off);
-               return;
+               return 1;
        }
+       return 0;       /* unknown placeholder */
 }
 
 void format_commit_message(const struct commit *commit,
                            const void *format, struct strbuf *sb)
 {
-       const char *placeholders[] = {
-               "H",            /* commit hash */
-               "h",            /* abbreviated commit hash */
-               "T",            /* tree hash */
-               "t",            /* abbreviated tree hash */
-               "P",            /* parent hashes */
-               "p",            /* abbreviated parent hashes */
-               "an",           /* author name */
-               "ae",           /* author email */
-               "ad",           /* author date */
-               "aD",           /* author date, RFC2822 style */
-               "ar",           /* author date, relative */
-               "at",           /* author date, UNIX timestamp */
-               "ai",           /* author date, ISO 8601 */
-               "cn",           /* committer name */
-               "ce",           /* committer email */
-               "cd",           /* committer date */
-               "cD",           /* committer date, RFC2822 style */
-               "cr",           /* committer date, relative */
-               "ct",           /* committer date, UNIX timestamp */
-               "ci",           /* committer date, ISO 8601 */
-               "e",            /* encoding */
-               "s",            /* subject */
-               "b",            /* body */
-               "Cred",         /* red */
-               "Cgreen",       /* green */
-               "Cblue",        /* blue */
-               "Creset",       /* reset color */
-               "n",            /* newline */
-               "m",            /* left/right/bottom */
-               NULL
-       };
        struct format_commit_context context;
 
        memset(&context, 0, sizeof(context));
        context.commit = commit;
-       strbuf_expand(sb, format, placeholders, format_commit_item, &context);
+       strbuf_expand(sb, format, format_commit_item, &context);
 }
 
 static void pp_header(enum cmit_fmt fmt,
index 00f289f2f470c0f4b95e0fcac043aa2e054d1ce6..3b1c18ff9b9060d0dd437ce89aedb8871c66c54c 100644 (file)
@@ -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);
 }
index e45f4b3d61c2982ca20c910a7fc11c5bd89204be..fee0c80734db2d5106c4c6fd6bc396af1831c392 100644 (file)
@@ -37,8 +37,13 @@ static unsigned int hash_name(const char *name, int namelen)
 static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
 {
        void **pos;
-       unsigned int hash = hash_name(ce->name, ce_namelen(ce));
+       unsigned int hash;
 
+       if (ce->ce_flags & CE_HASHED)
+               return;
+       ce->ce_flags |= CE_HASHED;
+       ce->next = NULL;
+       hash = hash_name(ce->name, ce_namelen(ce));
        pos = insert_hash(hash, ce, &istate->name_hash);
        if (pos) {
                ce->next = *pos;
@@ -59,33 +64,18 @@ static void lazy_init_name_hash(struct index_state *istate)
 
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
+       ce->ce_flags &= ~CE_UNHASHED;
        istate->cache[nr] = ce;
        if (istate->name_hash_initialized)
                hash_index_entry(istate, ce);
 }
 
-/*
- * We don't actually *remove* it, we can just mark it invalid so that
- * we won't find it in lookups.
- *
- * Not only would we have to search the lists (simple enough), but
- * we'd also have to rehash other hash buckets in case this makes the
- * hash bucket empty (common). So it's much better to just mark
- * it.
- */
-static void remove_hash_entry(struct index_state *istate, struct cache_entry *ce)
-{
-       ce->ce_flags |= CE_UNHASHED;
-}
-
 static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
        struct cache_entry *old = istate->cache[nr];
 
-       if (ce != old) {
-               remove_hash_entry(istate, old);
-               set_index_entry(istate, nr, ce);
-       }
+       remove_index_entry(old);
+       set_index_entry(istate, nr, ce);
        istate->cache_changed = 1;
 }
 
@@ -413,7 +403,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 {
        struct cache_entry *ce = istate->cache[pos];
 
-       remove_hash_entry(istate, ce);
+       remove_index_entry(ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
diff --git a/refs.c b/refs.c
index 67d2a502afb60050f0ce750c21ae1a42fa5cb803..fb33da111240d9a3d579dfebb05eef951fecee23 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -506,7 +506,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
 
        /* fallback - callers should not call this for unpacked refs */
        o = parse_object(base);
-       if (o->type == OBJ_TAG) {
+       if (o && o->type == OBJ_TAG) {
                o = deref_tag(o, ref, 0);
                if (o) {
                        hashcpy(sha1, o->sha1);
index 20abbc07acb2e48aefb2129683b5dc159661211e..6b56473f5bb7d8ab2226ed83805f30c9e21ba773 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -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)
index 6e85aaa3fb30e98f3b02f094af7f1763577cdb8d..d3e8658104b63886bbade78366466eb4828efc28 100644 (file)
@@ -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));
@@ -558,6 +564,12 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
        free_patch_ids(&ids);
 }
 
+static void add_to_list(struct commit_list **p, struct commit *commit, struct commit_list *n)
+{
+       p = &commit_list_insert(commit, p)->next;
+       *p = n;
+}
+
 static int limit_list(struct rev_info *revs)
 {
        struct commit_list *list = revs->commits;
@@ -579,9 +591,13 @@ static int limit_list(struct rev_info *revs)
                        return -1;
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
-                       if (everybody_uninteresting(list))
+                       if (everybody_uninteresting(list)) {
+                               if (revs->show_all)
+                                       add_to_list(p, commit, list);
                                break;
-                       continue;
+                       }
+                       if (!revs->show_all)
+                               continue;
                }
                if (revs->min_age != -1 && (commit->date > revs->min_age))
                        continue;
@@ -685,6 +701,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)
@@ -1055,6 +1073,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->dense = 0;
                                continue;
                        }
+                       if (!strcmp(arg, "--show-all")) {
+                               revs->show_all = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--remove-empty")) {
                                revs->remove_empty_trees = 1;
                                continue;
@@ -1438,6 +1460,8 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
                return commit_ignore;
        if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
                return commit_ignore;
+       if (revs->show_all)
+               return commit_show;
        if (commit->object.flags & UNINTERESTING)
                return commit_ignore;
        if (revs->min_age != -1 && (commit->date > revs->min_age))
index 8572315954d1fde8c36c426d66c665534c22d1f2..b5f01f8309b6630be7169eb72f613e5deb0ebcab 100644 (file)
@@ -33,6 +33,7 @@ struct rev_info {
                        prune:1,
                        no_merges:1,
                        no_walk:1,
+                       show_all:1,
                        remove_empty_trees:1,
                        simplify_history:1,
                        lifo:1,
diff --git a/setup.c b/setup.c
index 4509598d577baba8b1d7e8782d8e6ff8e74f9556..dc247a84c49709d2bff00440e5ac976df83acd2e 100644 (file)
--- a/setup.c
+++ b/setup.c
 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 (is_absolute_path(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 (is_absolute_path(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;
 }
 
 /*
index 66a4e00fa83fd9fc853a1ba8a308b05cdc030967..1ddb96bb82b60f4da8489664d13f398d88bf5a15 100644 (file)
@@ -1845,6 +1845,15 @@ static struct cached_object {
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
 
+static struct cached_object empty_tree = {
+       /* empty tree sha1: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 */
+       "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60"
+       "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04",
+       OBJ_TREE,
+       "",
+       0
+};
+
 static struct cached_object *find_cached_object(const unsigned char *sha1)
 {
        int i;
@@ -1854,6 +1863,8 @@ static struct cached_object *find_cached_object(const unsigned char *sha1)
                if (!hashcmp(co->sha1, sha1))
                        return co;
        }
+       if (!hashcmp(sha1, empty_tree.sha1))
+               return &empty_tree;
        return NULL;
 }
 
@@ -1943,7 +1954,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 +2370,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;
index be8489e4e5fc98c6e38036a40fb5ca8c0db77ebc..c2805e736b3086a0e33271a4b5c73cea7d2c55cd 100644 (file)
@@ -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)
index dbd9f5ad0ac21e70fc3a095d8e2938f245c238d3..4d90eda19efe0a80c1cb39e8897ab3ed5e6fcf56 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -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) {
index 5efcfc8860a82766b53a0d579fbdd0844d0f9b62..4aed75265e945d7b8dfafb36913006376768b4d3 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -146,11 +146,12 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
        strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_expand(struct strbuf *sb, const char *format,
-                   const char **placeholders, expand_fn_t fn, void *context)
+void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
+                  void *context)
 {
        for (;;) {
-               const char *percent, **p;
+               const char *percent;
+               size_t consumed;
 
                percent = strchrnul(format, '%');
                strbuf_add(sb, format, percent - format);
@@ -158,14 +159,10 @@ void strbuf_expand(struct strbuf *sb, const char *format,
                        break;
                format = percent + 1;
 
-               for (p = placeholders; *p; p++) {
-                       if (!prefixcmp(format, *p))
-                               break;
-               }
-               if (*p) {
-                       fn(sb, *p, context);
-                       format += strlen(*p);
-               } else
+               consumed = fn(sb, format, context);
+               if (consumed)
+                       format += consumed;
+               else
                        strbuf_addch(sb, '%');
        }
 }
index 36d61db65728f61188ef3bedbdbe88d0f2d9a0b9..faec2291d9e622c76dcdb3ef13d0876c5e3e6f28 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -103,8 +103,8 @@ static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) {
 }
 extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
 
-typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
-extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context);
+typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
+extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
 
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
index 8b27aa892b2b56056b21874cb81c1ddd7956a60a..90ea081db666d707246e37affc5676e6c3502741 100755 (executable)
@@ -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 &&
index e474b3f1d5482305356239980248e2ebccbcf561..38a2bf09af286bf681deadb9cdaec80175d13e0c 100755 (executable)
@@ -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
index e25b25568337ae36a5ef1377a14df031d5dfaeb1..b4297bacf2bd3d6da0f7fd01fba65741cf568003 100755 (executable)
@@ -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
index e33ea4e9f4c33cc1f94cd184c371a8fa05bedf3b..62e65d704b37eb92509a8ad7e844cea8a675ed50 100755 (executable)
@@ -61,8 +61,8 @@ test_expect_success 'setup' '
        git tag I
 '
 
-cat > fake-editor.sh <<\EOF
-#!/bin/sh
+echo "#!$SHELL" >fake-editor
+cat >> fake-editor.sh <<\EOF
 case "$1" in
 */COMMIT_EDITMSG)
        test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
@@ -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/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
new file mode 100755 (executable)
index 0000000..c8dc1ac
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='add -i basic tests'
+. ./test-lib.sh
+
+test_expect_success 'setup (initial)' '
+       echo content >file &&
+       git add file &&
+       echo more >>file &&
+       echo lines >>file
+'
+test_expect_success 'status works (initial)' '
+       git add -i </dev/null >output &&
+       grep "+1/-0 *+2/-0 file" output
+'
+cat >expected <<EOF
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ b/file
+@@ -0,0 +1 @@
++content
+EOF
+test_expect_success 'diff works (initial)' '
+       (echo d; echo 1) | git add -i >output &&
+       sed -ne "/new file/,/content/p" <output >diff &&
+       diff -u expected diff
+'
+test_expect_success 'revert works (initial)' '
+       git add file &&
+       (echo r; echo 1) | git add -i &&
+       git ls-files >output &&
+       ! grep . output
+'
+
+test_expect_success 'setup (commit)' '
+       echo baseline >file &&
+       git add file &&
+       git commit -m commit &&
+       echo content >>file &&
+       git add file &&
+       echo more >>file &&
+       echo lines >>file
+'
+test_expect_success 'status works (commit)' '
+       git add -i </dev/null >output &&
+       grep "+1/-0 *+2/-0 file" output
+'
+cat >expected <<EOF
+index 180b47c..b6f2c08 100644
+--- a/file
++++ b/file
+@@ -1 +1,2 @@
+ baseline
++content
+EOF
+test_expect_success 'diff works (commit)' '
+       (echo d; echo 1) | git add -i >output &&
+       sed -ne "/^index/,/content/p" <output >diff &&
+       diff -u expected diff
+'
+test_expect_success 'revert works (commit)' '
+       git add file &&
+       (echo r; echo 1) | git add -i &&
+       git add -i </dev/null >output &&
+       grep "unchanged *+3/-0 file" output
+'
+
+test_done
index d30169fbdcafdf51561f024e887f05aa800fc9d4..83c54b747f4efe1f99b67284a7ba72b89df07a6d 100755 (executable)
@@ -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
index 67e080bdbe5a98a9c446af8aee41acc9fb9129b4..0d9cbb62615c0d94da784f63907989988b0e8151 100755 (executable)
@@ -12,6 +12,7 @@ test_expect_success setup '
        echo "         Eight SP indent" >>F &&
        echo "  HT and SP indent" >>F &&
        echo "With trailing SP " >>F &&
+       echo "Carriage ReturnQ" | tr Q "\015" >>F &&
        echo "No problem" >>F
 
 '
@@ -27,6 +28,7 @@ test_expect_success default '
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
        grep With error >/dev/null &&
+       grep Return error >/dev/null &&
        grep No normal >/dev/null
 
 '
@@ -41,6 +43,7 @@ test_expect_success 'without -trail' '
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
        grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
        grep No normal >/dev/null
 
 '
@@ -56,6 +59,7 @@ test_expect_success 'without -trail (attribute)' '
        grep Eight normal >/dev/null &&
        grep HT error >/dev/null &&
        grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
        grep No normal >/dev/null
 
 '
@@ -71,6 +75,7 @@ test_expect_success 'without -space' '
        grep Eight normal >/dev/null &&
        grep HT normal >/dev/null &&
        grep With error >/dev/null &&
+       grep Return error >/dev/null &&
        grep No normal >/dev/null
 
 '
@@ -86,6 +91,7 @@ test_expect_success 'without -space (attribute)' '
        grep Eight normal >/dev/null &&
        grep HT normal >/dev/null &&
        grep With error >/dev/null &&
+       grep Return error >/dev/null &&
        grep No normal >/dev/null
 
 '
@@ -101,6 +107,7 @@ test_expect_success 'with indent-non-tab only' '
        grep Eight error >/dev/null &&
        grep HT normal >/dev/null &&
        grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
        grep No normal >/dev/null
 
 '
@@ -116,6 +123,39 @@ test_expect_success 'with indent-non-tab only (attribute)' '
        grep Eight error >/dev/null &&
        grep HT normal >/dev/null &&
        grep With normal >/dev/null &&
+       grep Return normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'with cr-at-eol' '
+
+       rm -f .gitattributes
+       git config core.whitespace cr-at-eol
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight normal >/dev/null &&
+       grep HT error >/dev/null &&
+       grep With error >/dev/null &&
+       grep Return normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'with cr-at-eol (attribute)' '
+
+       git config --unset core.whitespace
+       echo "F whitespace=trailing,cr-at-eol" >.gitattributes
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight normal >/dev/null &&
+       grep HT error >/dev/null &&
+       grep With error >/dev/null &&
+       grep Return normal >/dev/null &&
        grep No normal >/dev/null
 
 '
diff --git a/t/t4105-apply-fuzz.sh b/t/t4105-apply-fuzz.sh
new file mode 100755 (executable)
index 0000000..0e8d25f
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='apply with fuzz and offset'
+
+. ./test-lib.sh
+
+dotest () {
+       name="$1" && shift &&
+       test_expect_success "$name" "
+               git checkout-index -f -q -u file &&
+               git apply $* &&
+               diff -u expect file
+       "
+}
+
+test_expect_success setup '
+
+       for i in 1 2 3 4 5 6 7 8 9 10 11 12
+       do
+               echo $i
+       done >file &&
+       git update-index --add file &&
+       for i in 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12
+       do
+               echo $i
+       done >file &&
+       cat file >expect &&
+       git diff >O0.diff &&
+
+       sed -e "s/@@ -5,6 +5,11 @@/@@ -2,6 +2,11 @@/" >O1.diff O0.diff &&
+       sed -e "s/@@ -5,6 +5,11 @@/@@ -7,6 +7,11 @@/" >O2.diff O0.diff &&
+       sed -e "s/@@ -5,6 +5,11 @@/@@ -19,6 +19,11 @@/" >O3.diff O0.diff &&
+
+       sed -e "s/^ 5/ S/" >F0.diff O0.diff &&
+       sed -e "s/^ 5/ S/" >F1.diff O1.diff &&
+       sed -e "s/^ 5/ S/" >F2.diff O2.diff &&
+       sed -e "s/^ 5/ S/" >F3.diff O3.diff
+
+'
+
+dotest 'unmodified patch' O0.diff
+
+dotest 'minus offset' O1.diff
+
+dotest 'plus offset' O2.diff
+
+dotest 'big offset' O3.diff
+
+dotest 'fuzz with no offset' -C2 F0.diff
+
+dotest 'fuzz with minus offset' -C2 F1.diff
+
+dotest 'fuzz with plus offset' -C2 F2.diff
+
+dotest 'fuzz with big offset' -C2 F3.diff
+
+test_done
diff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh
new file mode 100755 (executable)
index 0000000..d6f15be
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='applying patch that has broken whitespaces in context'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       >file &&
+       git add file &&
+
+       # file-0 is full of whitespace breakages
+       for l in a bb c d eeee f ggg h
+       do
+               echo "$l "
+       done >file-0 &&
+
+       # patch-0 creates a whitespace broken file
+       cat file-0 >file &&
+       git diff >patch-0 &&
+       git add file &&
+
+       # file-1 is still full of whitespace breakages,
+       # but has one line updated, without fixing any
+       # whitespaces.
+       # patch-1 records that change.
+       sed -e "s/d/D/" file-0 >file-1 &&
+       cat file-1 >file &&
+       git diff >patch-1 &&
+
+       # patch-all is the effect of both patch-0 and patch-1
+       >file &&
+       git add file &&
+       cat file-1 >file &&
+       git diff >patch-all &&
+
+       # patch-2 is the same as patch-1 but is based
+       # on a version that already has whitespace fixed,
+       # and does not introduce whitespace breakages.
+       sed -e "s/ $//" patch-1 >patch-2 &&
+
+       # If all whitespace breakages are fixed the contents
+       # should look like file-fixed
+       sed -e "s/ $//" file-1 >file-fixed
+
+'
+
+test_expect_success nofix '
+
+       >file &&
+       git add file &&
+
+       # Baseline.  Applying without fixing any whitespace
+       # breakages.
+       git apply --whitespace=nowarn patch-0 &&
+       git apply --whitespace=nowarn patch-1 &&
+
+       # The result should obviously match.
+       diff -u file-1 file
+'
+
+test_expect_success 'withfix (forward)' '
+
+       >file &&
+       git add file &&
+
+       # The first application will munge the context lines
+       # the second patch depends on.  We should be able to
+       # adjust and still apply.
+       git apply --whitespace=fix patch-0 &&
+       git apply --whitespace=fix patch-1 &&
+
+       diff -u file-fixed file
+'
+
+test_expect_success 'withfix (backward)' '
+
+       >file &&
+       git add file &&
+
+       # Now we have a whitespace breakages on our side.
+       git apply --whitespace=nowarn patch-0 &&
+
+       # And somebody sends in a patch based on image
+       # with whitespace already fixed.
+       git apply --whitespace=fix patch-2 &&
+
+       # The result should accept the whitespace fixed
+       # postimage.  But the line with "h" is beyond context
+       # horizon and left unfixed.
+
+       sed -e /h/d file-fixed >fixed-head &&
+       sed -e /h/d file >file-head &&
+       diff -u fixed-head file-head &&
+
+       sed -n -e /h/p file-fixed >fixed-tail &&
+       sed -n -e /h/p file >file-tail &&
+
+       ! diff -u fixed-tail file-tail
+
+'
+
+test_done
index 9b1a74542a848ef702c1e6208d6ff2537b0f6ac0..d6c55c115779730fdef8d05fbdb039fe90e3fad7 100755 (executable)
@@ -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 (file)
index 0000000..2a66321
--- /dev/null
@@ -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 (file)
index 0000000..9ffe131
--- /dev/null
@@ -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 (file)
index 0000000..65615c3
--- /dev/null
@@ -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
index 070c1661b9be8530e619cd0c297673d1e5e958a3..0476b96c33de8bbc6c4b3bc4f510e9fc50aa6440 100644 (file)
@@ -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
index 822ac8c28e112dc1da61cb7fecdab1b4f25717ec..59a165a6d44190a1ce9ef9575dc6c8a2ce9f4efb 100755 (executable)
@@ -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
index b1243b4163d231bb0f29fd91fe7fa15c34463c82..fa382c58da08a6eeca1aa51d4af47215e3e1c107 100755 (executable)
@@ -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
index 5f60b22d872b5d034e137e0c6c7c5a9d664e57ee..868babc4b2350526192a8df53f6cbec3f81c644e 100755 (executable)
@@ -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 (executable)
index 0000000..e809e0e
--- /dev/null
@@ -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/t7104-reset.sh b/t/t7104-reset.sh
new file mode 100755 (executable)
index 0000000..f136ee7
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='reset --hard unmerged'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       mkdir before later &&
+       >before/1 &&
+       >before/2 &&
+       >hello &&
+       >later/3 &&
+       git add before hello later &&
+       git commit -m world &&
+
+       H=$(git rev-parse :hello) &&
+       git rm --cached hello &&
+       echo "100644 $H 2       hello" | git update-index --index-info &&
+
+       rm -f hello &&
+       mkdir -p hello &&
+       >hello/world &&
+       test "$(git ls-files -o)" = hello/world
+
+'
+
+test_expect_success 'reset --hard should restore unmerged ones' '
+
+       git reset --hard &&
+       git ls-files --error-unmatch before/1 before/2 hello later/3 &&
+       test -f hello
+
+'
+
+test_expect_success 'reset --hard did not corrupt index nor cached-tree' '
+
+       T=$(git write-tree) &&
+       rm -f .git/index &&
+       git add before hello later &&
+       U=$(git write-tree) &&
+       test "$T" = "$U"
+
+'
+
+test_done
index dfd118878fd37c096ccd426aa01fa2ac36581367..38403643a6271ae4e6bb5a9c7d768ae4b50df34d 100755 (executable)
@@ -316,4 +316,14 @@ test_expect_success 'core.excludesfile' '
 
 '
 
+test_expect_success 'removal failure' '
+
+       mkdir foo &&
+       touch foo/bar &&
+       chmod 0 foo &&
+       ! git clean -f -d
+
+'
+chmod 755 foo
+
 test_done
index aaf497e6a5948ffa393ea07380bf577ceb5f347b..b780fddc08f9c01eaf1143b9178ab5b7ecd032db 100755 (executable)
@@ -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
index 9ce50cade8981bb5317397c10b0fabeba4893fc8..e00607490bcb0439c6557003a3419d84823a8f00 100755 (executable)
@@ -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 (executable)
index 0000000..7ddec99
--- /dev/null
@@ -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
index 08f7c3d8d7666aeb7a116f475127c9c183e64d5a..2efaed441d6ae238ce5d6a6105230e088d5e56ff 100755 (executable)
@@ -108,4 +108,25 @@ test_expect_success 'allow long lines with --no-validate' '
                2>errors
 '
 
+test_expect_success 'Invalid In-Reply-To' '
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --in-reply-to=" " \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $patches
+               2>errors
+       ! grep "^In-Reply-To: < *>" msgtxt
+'
+
+test_expect_success 'Valid In-Reply-To when prompting' '
+       (echo "From Example <from@example.com>"
+        echo "To Example <to@example.com>"
+        echo ""
+       ) | env GIT_SEND_EMAIL_NOTTY=1 git send-email \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               $patches 2>errors &&
+       ! grep "^In-Reply-To: < *>" msgtxt
+'
+
 test_done
index a15222ced4d75e70f49df08159cff9dd8d4167a4..58c59ed5ae0159810b4c8258f9182991bad33636 100755 (executable)
@@ -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,55 @@ 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_expect_success 'check files before directories' '
+
+       echo Notes > release-notes &&
+       git add release-notes &&
+       git commit -m "Add release notes" release-notes &&
+       id=$(git rev-parse HEAD) &&
+       git cvsexportcommit -w "$CVSWORK" -c $id &&
+
+       echo new > DS &&
+       echo new > E/DS &&
+       echo modified > release-notes &&
+       git add DS E/DS release-notes &&
+       git commit -m "Add two files with the same basename" &&
+       id=$(git rev-parse HEAD) &&
+       git cvsexportcommit -w "$CVSWORK" -c $id &&
+       check_entries "$CVSWORK/E" "DS/1.1/|newfile5.txt/1.1/" &&
+       check_entries "$CVSWORK" "DS/1.1/|release-notes/1.2/" &&
+       diff -u "$CVSWORK/DS" DS &&
+       diff -u "$CVSWORK/E/DS" E/DS &&
+       diff -u "$CVSWORK/release-notes" release-notes
+
+'
+
+test_expect_success 'commit a file with leading spaces in the name' '
+
+       echo space > " space" &&
+       git add " space" &&
+       git commit -m "Add a file with a leading space" &&
+       id=$(git rev-parse HEAD) &&
+       git cvsexportcommit -w "$CVSWORK" -c $id &&
+       check_entries "$CVSWORK" " space/1.1/|DS/1.1/|release-notes/1.2/" &&
+       diff -u "$CVSWORK/ space" " space"
+
+'
+
 test_done
index da47bd7c9eaac7f01106c290fab8364e1ce838a4..83889c4f4690d30fd2d5e969d5d445dfb47ad579 100644 (file)
@@ -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
diff --git a/tag.c b/tag.c
index 38bf9134f97c18973fe189c8703438f5e1135e49..990134fe7ab14043d40b97230571ffea109b9129 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -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);
index c5cdb9d7eeebab7edf048e77eb92437de24751bf..4ef86eb244bbbde9b793fd8cfab92042adb51485 100644 (file)
@@ -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 (file)
index 0000000..ff0f42a
--- /dev/null
@@ -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"
index ff46fd62fdceeefc2ebd98ae1c9483964cb37bd5..56c1ffbc199c534a53da9615aa16b4357656e320 100644 (file)
@@ -301,7 +301,7 @@ static void check_updates(struct cache_entry **src, int nr,
                }
 
                progress = start_progress_delay("Checking out files",
-                                               total, 50, 2);
+                                               total, 50, 1);
                cnt = 0;
        }
 
@@ -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.
@@ -589,7 +590,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                 * a match.
                 */
                if (same(old, merge)) {
-                       memcpy(merge, old, offsetof(struct cache_entry, name));
+                       copy_cache_entry(merge, old);
                } else {
                        verify_uptodate(old, o);
                        invalidate_ce_path(old);
index 7e04311027176fc87c1de7dd619000d2a75d4eb9..b26d05331d7eeab4efea01594ecf196bc5fb50ed 100644 (file)
@@ -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/ws.c b/ws.c
index d09b9df89a7e19367640d4c6ad64ff828d01d26f..522f646ed7f492a91ea288d03aabfcf5db69c196 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -14,6 +14,7 @@ static struct whitespace_rule {
        { "trailing-space", WS_TRAILING_SPACE },
        { "space-before-tab", WS_SPACE_BEFORE_TAB },
        { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB },
+       { "cr-at-eol", WS_CR_AT_EOL },
 };
 
 unsigned parse_whitespace_rule(const char *string)
@@ -124,6 +125,7 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
        int written = 0;
        int trailing_whitespace = -1;
        int trailing_newline = 0;
+       int trailing_carriage_return = 0;
        int i;
 
        /* Logic is simpler if we temporarily ignore the trailing newline. */
@@ -131,6 +133,11 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
                trailing_newline = 1;
                len--;
        }
+       if ((ws_rule & WS_CR_AT_EOL) &&
+           len > 0 && line[len - 1] == '\r') {
+               trailing_carriage_return = 1;
+               len--;
+       }
 
        /* Check for trailing whitespace. */
        if (ws_rule & WS_TRAILING_SPACE) {
@@ -176,8 +183,10 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
        }
 
        if (stream) {
-               /* Now the rest of the line starts at written.
-                * The non-highlighted part ends at trailing_whitespace. */
+               /*
+                * Now the rest of the line starts at "written".
+                * The non-highlighted part ends at "trailing_whitespace".
+                */
                if (trailing_whitespace == -1)
                        trailing_whitespace = len;
 
@@ -196,8 +205,114 @@ unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
                            len - trailing_whitespace, 1, stream);
                        fputs(reset, stream);
                }
+               if (trailing_carriage_return)
+                       fputc('\r', stream);
                if (trailing_newline)
                        fputc('\n', stream);
        }
        return result;
 }
+
+/* Copy the line to the buffer while fixing whitespaces */
+int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *error_count)
+{
+       /*
+        * len is number of bytes to be copied from src, starting
+        * at src.  Typically src[len-1] is '\n', unless this is
+        * the incomplete last line.
+        */
+       int i;
+       int add_nl_to_tail = 0;
+       int add_cr_to_tail = 0;
+       int fixed = 0;
+       int last_tab_in_indent = -1;
+       int last_space_in_indent = -1;
+       int need_fix_leading_space = 0;
+       char *buf;
+
+       /*
+        * Strip trailing whitespace
+        */
+       if ((ws_rule & WS_TRAILING_SPACE) &&
+           (2 < len && isspace(src[len-2]))) {
+               if (src[len - 1] == '\n') {
+                       add_nl_to_tail = 1;
+                       len--;
+                       if (1 < len && src[len - 1] == '\r') {
+                               add_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);
+                               len--;
+                       }
+               }
+               if (0 < len && isspace(src[len - 1])) {
+                       while (0 < len && isspace(src[len-1]))
+                               len--;
+                       fixed = 1;
+               }
+       }
+
+       /*
+        * Check leading whitespaces (indent)
+        */
+       for (i = 0; i < len; i++) {
+               char ch = src[i];
+               if (ch == '\t') {
+                       last_tab_in_indent = i;
+                       if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
+                           0 <= last_space_in_indent)
+                           need_fix_leading_space = 1;
+               } else if (ch == ' ') {
+                       last_space_in_indent = i;
+                       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+                           8 <= i - last_tab_in_indent)
+                               need_fix_leading_space = 1;
+               } else
+                       break;
+       }
+
+       buf = dst;
+       if (need_fix_leading_space) {
+               /* Process indent ourselves */
+               int consecutive_spaces = 0;
+               int last = last_tab_in_indent + 1;
+
+               if (ws_rule & WS_INDENT_WITH_NON_TAB) {
+                       /* have "last" point at one past the indent */
+                       if (last_tab_in_indent < last_space_in_indent)
+                               last = last_space_in_indent + 1;
+                       else
+                               last = last_tab_in_indent + 1;
+               }
+
+               /*
+                * between src[0..last-1], strip the funny spaces,
+                * updating them to tab as needed.
+                */
+               for (i = 0; i < last; i++) {
+                       char ch = src[i];
+                       if (ch != ' ') {
+                               consecutive_spaces = 0;
+                               *dst++ = ch;
+                       } else {
+                               consecutive_spaces++;
+                               if (consecutive_spaces == 8) {
+                                       *dst++ = '\t';
+                                       consecutive_spaces = 0;
+                               }
+                       }
+               }
+               while (0 < consecutive_spaces--)
+                       *dst++ = ' ';
+               len -= last;
+               src += last;
+               fixed = 1;
+       }
+
+       memcpy(dst, src, len);
+       if (add_cr_to_tail)
+               dst[len++] = '\r';
+       if (add_nl_to_tail)
+               dst[len++] = '\n';
+       if (fixed && error_count)
+               (*error_count)++;
+       return dst + len - buf;
+}
index f14022f2a9b8ce740067f7c6bf03bd51203cf2ca..32d780af1e4f6e701f75d9759dfd4ebec56e6425 100644 (file)
@@ -9,7 +9,7 @@
 #include "diffcore.h"
 
 int wt_status_relative_paths = 1;
-int wt_status_use_color = 0;
+int wt_status_use_color = -1;
 static char wt_status_colors[][COLOR_MAXLEN] = {
        "",         /* WT_STATUS_HEADER: normal */
        "\033[32m", /* WT_STATUS_UPDATED: green */
@@ -40,7 +40,7 @@ static int parse_status_slot(const char *var, int offset)
 
 static const char* color(int slot)
 {
-       return wt_status_use_color ? wt_status_colors[slot] : "";
+       return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
 }
 
 void wt_status_prepare(struct wt_status *s)
@@ -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)");
@@ -401,5 +401,5 @@ int git_status_config(const char *k, const char *v)
                wt_status_relative_paths = git_config_bool(k, v);
                return 0;
        }
-       return git_default_config(k, v);
+       return git_color_default_config(k, v);
 }